Reputation: 6152
I am trying to write a macro-generating macro, where the macro it generates takes a variable number of arguments.
I am wondering if there is a way to make the following code work:
(define-syntax-rule (greet name)
(define-syntax-rule (name args ...)
(printf "hello ~a~n" (list args ...))))
Right now it says "no pattern variables
before ellipsis
in template
in: ...
"
If I take the inner define-syntax-rule
by itself it works fine, so why doesn't it work when it's being generated by another macro?
Upvotes: 6
Views: 356
Reputation: 8373
There are at least 3 "Styles" of doing this.
Soegaard already answered that you can replace every ...
in the body with (... ...)
, so that it gets interpreted as a literal ellipsis belonging to the inner macro instead of as a "meta" ellipsis belonging to the outer macro:
(define-syntax-rule (greet name)
(define-syntax-rule (name args (... ...))
(printf "hello ~a~n" (list args (... ...)))))
Advantages: Flexible, you can mix literal (... ...)
and meta ...
ellipses freely within the body
Disadvantages: Looks confusing if you haven't seen (... ...)
before
However, putting (... <something>)
around something is not limited to just ...
. If you put a whole template there, any ...
s within that template will also be "quoted", treated as literal instead of meta, in the same way:
(define-syntax-rule (greet name)
(...
(define-syntax-rule (name args ...)
(printf "hello ~a~n" (list args ...)))))
Advantages: If you have even greater nesting depths you wouldn't need ((... ...) (... ...))
as you would with option 1, you would just need (... <something-containing (... <something>)>)
Disadvantages: Rigid, if you put (... <something>)
around something you can't ever use a meta ellipsis inside that something. You can't mix literal and meta ellipses freely like you could with style 1 or 3.
Here's another way, which I find less confusing, but it requires using define-simple-macro
instead of define-syntax-rule
, so that you can bind new pattern variables using #:with
.
(require syntax/parse/define)
(define-simple-macro (<name> <arguments>)
#:with <pattern-variable> <expression>
<body-expression>)
You can use with #:with
to bind an ooo
pattern variable to a literal ellipsis: #:with ooo (quote-syntax ...)
(require syntax/parse/define)
(define-simple-macro (greet name)
#:with ooo (quote-syntax ...)
(define-syntax-rule (name args ooo)
(printf "hello ~a~n" (list args ooo))))
Advantages: Flexible, you can mix literal ooo
and meta ...
ellipses freely within the body. To me, it looks less confusing than (... ...)
or ((... ...) (... ...))
.
Disadvantages: For deeper nesting, you might need multiple #:with
-definitions, one on each meta-level.
Upvotes: 8
Reputation: 31145
The ...
belongs to the outer define-syntax-rule
. In order
to produce an ellipsis in the output you need to quote it with (... ...)
.
(define-syntax-rule (greet name)
(define-syntax-rule (name args (... ...))
(printf "hello ~a~n" (list args (... ...)))))
For fun: If you ever need to write a macro that produces a macro that produces a macro, you will need ((... ...) (... ...))
to produce an ellipsis.
Upvotes: 5