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?
To support variable length arguments in macro, we must include ellipses (…) in macro definition. There is also “__VA_ARGS__” preprocessing identifier which takes care of variable length argument substitutions which are provided to macro.
macro (array[x = y, x + 1]) passes two arguments to macro : array[x = y and x + 1] .
For portability, you should not have more than 31 parameters for a macro. The parameter list may end with an ellipsis (…).
Macro Arguments (DEFINE-! ENDDEFINE command) The macro definition can include macro arguments, which can be assigned specific values in the macro call. There are two types of arguments: keyword and positional. Keyword arguments are assigned names in the macro definition; in the macro call, they are identified by name.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With