通过嵌套宏中的省略号捕获可变数量的参数;缺少模式变量错误

时间:2016-07-08 21:41:17

标签: macros racket

考虑两个宏的场景:outer-macro定义某个实体的一般结构,inner-macro扩展到外部宏的范围。我的意图在以下代码中捕获,其中预期输出是print语句。此示例为内部宏的模式抛出以下错误:(_ value ...)

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

我打算以与外部宏的value ...模式相同的方式使用body ...。实际上,“值”列表正是我所需要的(不一定是非常灵活的“省略号模式”)。可悲的是,这种方式不起作用。如何在内部宏中捕获可变数量的参数?

#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)"

2 个答案:

答案 0 :(得分:7)

Alexis King的回答很好。然而,我觉得更容易思考的另一种方法是使用#:with模式(或with-syntax),将ooo之类的内容定义为文字省略号。< / p>

您可以使用quote-syntax创建文字省略号,因此#:with子句看起来像#:with ooo (quote-syntax ...)。然后,只要您想在宏的输出中生成省略号,就可以使用ooo

(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 ...)]))

答案 1 :(得分:5)

要在语法模板中“转义”省略号,您可以使用语法(... <form>),其中<form>是一种语法模板,其中...序列按字面处理。因此,您可以包装一段语法以包含文字省略号:

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

您可以使用它来包围内部宏定义以转义内部省略号:

(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 ...)]))

但是,这实际上并不完全正确,因为您的syntax-case正文是错误的 - 它不会返回语法对象。您只是在#'之前错过(printf ...)(或者您可以使用syntax-rules),因此正确的实施应如下:

(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 ...)]))

这应该按预期工作。