cisco / ChezScheme

Chez Scheme
Apache License 2.0
6.95k stars 982 forks source link

Exception: extra ellipsis in syntax form #681

Closed bjornkihlberg closed 1 year ago

bjornkihlberg commented 1 year ago

Hello, I am having trouble understanding why some macros allow nested ellipses and some don't.

Here's an example that works, taken from the book:

(define-syntax with-syntax
  (lambda (x)
    (syntax-case x ()
      [(_ ((p e) ...) b1 b2 ...)
       #'(syntax-case (list e ...) ()
           [(p ...) (let () b1 b2 ...)])])))

Here's an example that doesn't work:

(define-syntax my-macro
  (syntax-rules ()
    [(_ args ...)
      (letrec-syntax ([f (syntax-rules ()
                          [(_ x ...) (g x ...)])]
                      [g (syntax-rules ()
                          [(_ 0) 1337]
                          [(_ x) x])])
        (f args ...))]))

Trying to compile this throws

Exception: extra ellipsis in syntax form (syntax (letrec-syntax ((...) (...)) (f args ...)))

I'm trying to understand why the nested ellipses is rejected in the second example but not the first. Can someone help me understand this?

soegaard commented 1 year ago

Since ... means a literal ellipsis in the template, you need to quote it. To quote it write (... ...).

(define-syntax my-macro
  (syntax-rules ()
    [(_ args ...)
      (letrec-syntax ([f (syntax-rules ()
                          [(_ x (... ...)) (g x (... ...))])]
                      [g (syntax-rules ()
                          [(_ 0) 1337]
                          [(_ x) x])])
        (f args ...))]))

Or maybe bind the ellipsis to ooo to get something a little more readable:

(define-syntax my-macro
  (syntax-rules ()
    [(_ args ...)
     (with-syntax ([ooo #'(... ...)])
       (letrec-syntax ([f (syntax-rules ()
                            [(_ x ooo) (g x ooo)])]
                       [g (syntax-rules ()
                            [(_ 0) 1337]
                            [(_ x) x])])
         (f args ...)))]))

Btw - if I try your initial example in Racket, I get the error:

 syntax/loc: no pattern variables before ellipsis in template
    at: x

Since the rule of the outer macro is: (syntax-rules () [(_ args ...) more) the only pattern variable is args. Since x is part of the template (of the outer macro) it is not a pattern variable.

The ... after x is thus a literal ... meant to occur in the template, so it needs to be quoted.

bjornkihlberg commented 1 year ago

@soegaard Thank you very much!

bjornkihlberg commented 1 year ago

@soegaard I would just add a small correction for future reference if someone else runs into this.

This doesn't work exactly:

(define-syntax my-macro
  (syntax-rules ()
    [(_ args ...)
     (with-syntax ([ooo #'(... ...)])
       (letrec-syntax ([f (syntax-rules ()
                            [(_ x ooo) (g x ooo)])]
                       [g (syntax-rules ()
                            [(_ 0) 1337]
                            [(_ x) x])])
         (f args ...)))]))

You need to use syntax-case:

(define-syntax (my-macro code)
  (syntax-case code ()
    [(_ args ...)
     (with-syntax ([ooo #'(... ...)])
       #'(letrec-syntax ([f (syntax-rules ()
                              [(_ x ooo) (g x ooo)])]
                         [g (syntax-rules ()
                              [(_ 0) 1337]
                              [(_ x) x])])
           (f args ...)))]))