On 09/17/2017 01:00 AM, Philip McGrath wrote:
[...]

I have a macro like `example-macro`, but more complicated and with many, many more potential keyword arguments, so I wanted to write a macro that would let me define `example-macro` with a more declarative syntax, like this:

(define-example-macro/working generated-example-macro
   [#:a 'default-a symbol?]
   [#:b "default-b" string?])

My initial attempt looked something like this:

(define-syntax (define-example-macro/buggy stx)
   (define-syntax-class arg-clause
     (pattern [kw:keyword default:expr contract:expr]))
   (syntax-parse stx
     [(_ name clause:arg-clause ...+)
      ;#:with (arg ...) (generate-temporaries #'(clause.kw ...))
      ;#:with (arg.c ...) (generate-temporaries #'(clause.kw ...))
      #`(define-syntax name
          (syntax-parser
            [(_ (~alt (~optional
                       (~seq clause.kw (~var arg (expr/c #'clause.contract)))
                       #:defaults ([arg.c #'clause.default]))
                      ...)
                (... ...))
             #`(list arg.c ...)]))]))

which raises a syntax error "syntax: no pattern variables before ellipsis in template".

The problem seems to be that `arg.c` is not recognized as a pattern variable derived from `arg`. I tried several variants on this, none of which allowed me to refer to `arg.c`. For example, if I uncomment the two `#:with` lines, I get the error "~optional: attribute bound in defaults but not in pattern".

If you uncomment the two "#:with" lines and look at the first expansion step of your example, you get the following:

  (define-example-macro generated-example-macro
    [#:a 'default-a symbol?]
    [#:b "default-b" string?])
=>
  (define-syntax generated-example-macro
     (syntax-parser
      ((_
        (~alt
         (~optional
          (~seq #:a (~var a5 (expr/c #'symbol?)))
          #:defaults
          ((a7 #''default-a)))
         (~optional
          (~seq #:b (~var b6 (expr/c #'string?)))
          #:defaults
          ((b8 #'"default-b"))))
        ...)
       #`(list a7 b8))))

The problem is that a7 and b8 need to be a5.c and b6.c, respectively. So rather than two independent calls to generate-temporaries, you should build the arg.cs from the args:

  #:with (arg ...) (generate-temporaries #'(clause.kw ...))
  #:with (arg.c ...) (map (lambda (arg)
                            (format-id arg "~a.c" arg))
                          (syntax->list #'(arg ...)))

Ryan

--
You received this message because you are subscribed to the Google Groups "Racket 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to