Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capturing a variable number of arguments via an ellipsis in a nested macro; Missing pattern variable error

Tags:

macros

racket

Consider a scenario of two macros: the outer-macro defines a general structure of some entity, and the inner-macro expands in the scope of the outer macro. My intent is captured in the following code, where the expected output is a print statement. This example throws the following error for the pattern of the inner macro: (_ value ...).

syntax: no pattern variables before ellipsis in template in: ...

I intend to use value ... in the same way as the body ... pattern of the outer macro. In fact, a list of the 'values' is exactly what I need (not necessarily a very flexible 'ellipsis pattern'). Sadly it does not work this way. How can I capture a variable amount of arguments in the inner macro?

#lang racket

(require
  racket/stxparam
  (for-syntax syntax/parse))

(define-syntax-parameter inner-macro
  (lambda (stx)
    (raise-syntax-error 'inner-macro "generic error message" stx)))

(define-syntax (outter-macro stx)
  (syntax-parse stx
    [(_ body:expr ...+)
     #'(syntax-parameterize
        ([inner-macro
          (lambda (stx)
            (syntax-case stx ()
              [(_ value ...)
               (printf "values are: ~a~n" (list value ...))]))])
        body ...)]))

(outter-macro
 (inner-macro 'a 'b 'c))

; expected result
; > "values are: (a b c)"
like image 352
Sam Avatar asked Jul 08 '16 21:07

Sam


2 Answers

To “escape” ellipses in syntax templates, you can use the syntax (... <form>), where <form> is a syntax template where ... sequences are treated literally. Therefore, you can wrap a piece of syntax to include literal ellipses:

> #'(... (syntax-rules ()
           [(x ...) (list x ...)]))
#<syntax:4:9 (syntax-rules () ((x ...) (li...>

You can use this to surround your inner macro definition to escape the inner ellipses:

(define-syntax (outer-macro stx)
  (syntax-parse stx
    [(_ body:expr ...+)
     #'(syntax-parameterize
        ([inner-macro
          (lambda (stx)
            (... (syntax-case stx ()
                   [(_ value ...)
                    (printf "values are: ~a~n" (list value ...))])))])
        body ...)]))

However, this is actually not quite right, because your syntax-case body is wrong—it does not return a syntax object. You are just missing a #' before the (printf ...) (or you could use syntax-rules), so the correct implementation should be the following:

(define-syntax (outer-macro stx)
  (syntax-parse stx
    [(_ body:expr ...+)
     #'(syntax-parameterize
        ([inner-macro
          (lambda (stx)
            (... (syntax-case stx ()
                   [(_ value ...)
                    #'(printf "values are: ~a~n" (list value ...))])))])
        body ...)]))

This should work as intended.

like image 59
Alexis King Avatar answered Nov 08 '22 09:11

Alexis King


Alexis King's answer is good. However another way to do it, which I find simpler to think about, is to use a #:with pattern (or a with-syntax), to define something like ooo as a literal ellipsis.

You can create a literal ellipsis with quote-syntax, so the #:with clause looks like #:with ooo (quote-syntax ...). Then you use ooo whenever you want to generate an ellipsis in the output of the macro.

(define-syntax (outer-macro stx)
  (syntax-parse stx
    [(_ body:expr ...+)
     #:with ooo (quote-syntax ...)
     #'(syntax-parameterize
        ([inner-macro
          (lambda (stx)
            (syntax-case stx ()
              [(_ value ooo)
               #'(printf "values are: ~a~n" (list value ooo))]))])
    body ...)]))
like image 10
Alex Knauth Avatar answered Nov 08 '22 09:11

Alex Knauth