<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<meta http-equiv="content-type" content="text/html;
charset=ISO-8859-1">
<pre style="color: rgb(0, 0, 0); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; word-wrap: break-word; white-space: pre-wrap; ">Today's macro PSA is about macro-generating-macro forms. If you use a macro to generate a transformer then you must be careful about nesting `syntax' forms otherwise you will interpolate templates too many times. Specifically #'#'x will interpolate x twice. Given the following bindings:
chicken = (syntax (egg ...))
pattern = (syntax chicken)
The problem is if an additional `syntax' is wrapped around `pattern'. After one interpolation of `syntax' we will have
(syntax (egg ...))
At which point `egg' had better be bound as a pattern variable otherwise interpolation will fail.
To work around this problem the `quote-syntax' form should be used:
#'(quote-syntax pattern)
which prevents the (syntax (egg ...)) layer from being interpolated because `quote-syntax' does not do interpolation.
The following example demonstrates the issue. On the line 'HERE' the inner syntax should be `quote-syntax' otherwise the following error is produced
x.rkt:27:24: syntax: no pattern variables before ellipses in template at: ... in: (_ x ...)
To be fair this issue only comes up because I used `make-transformer' in the body of a phase 2 function so the output of `make-transformer' had to have two levels of syntax, because its a macro that should ultimately evaluate to a syntax form. A simpler solution would have been to do
#'(define-syntax name (make-transformer pattern action))
So that `make-transformer' only had to return one level of syntax. But anyway I got into this situation for other reasons.
#lang racket
(require (for-meta 0 racket/splicing)
(for-meta 1 syntax/parse)
(for-meta 2 syntax/parse
racket/base))
(begin-for-syntax
(define-syntax (make-transformer stx)
(syntax-parse stx
[(_ (pattern ...) action)
#'#'(lambda (stx) ;; HERE change to #'(quote-syntax
(syntax-parse stx
[(_ pattern ...) action]))])))
(define-syntax (macro-generator stx)
(syntax-parse stx
[(_ name pattern action)
#'(splicing-let-syntax
([make (lambda (stx)
(syntax-parse stx
[(_ name)
(with-syntax ([transformer (make-transformer pattern action)])
#'(define-syntax name transformer))]))])
(make name))]))
(macro-generator foo (x ...) #'(list x ...))
(foo 1 2 3)</pre>
</body>
</html>