嵌套省略号宏在 Guile 和 Racket 中不起作用

Nested ellipsis macro doesn't work in Guile and Racket

我正在尝试创建一个简单的嵌套宏。它在我的 Scheme 实现中有效,但在 Guile 和 Racket 中无法 运行。

(define-syntax foo
  (syntax-rules (:c)
    ((_ x ...)
     (let-syntax ((bar (syntax-rules ::: (:c)
                         ((_ x)
                          (print x))
                         ((_ a b :::)
                          (begin
                            (display a)
                            (display " ")
                            (bar b :::))))))
       (bar x ...)))))

syntax: missing ellipsis

missing ellipsis with pattern variable in template

我也试过 运行 在 Gambit 中,但那个只是抛出:

Unbound variable: define-syntax

我猜你需要使用库才能使用基本方案。

在 Checken Scheme 中,更新省略号后:

(define-syntax foo
  (syntax-rules (:c)
    ((_ x ...)
     (let-syntax ((bar (syntax-rules <:::> (:c)
                         ((_ x)
                          (print x))
                         ((_ a b <:::>)
                          (begin
                            (display a)
                            (display " ")
                            (bar b <:::>))))))
       (bar x ...)))))

投掷:

template dimension error (too few ellipses?): x

这个宏有什么问题?为什么会抛出错误?

编辑:

看来这个模式是无效的:

(_ x ...)

但这是

(_ x y ...)

这是在某处指定的吗?为什么第一个语法无效?

为了完整起见,这段代码可以编译,但为什么第一个没有?

(define-syntax foo
  (syntax-rules ()
    ((_ x y ...)
     (let-syntax ((bar (syntax-rules <:::> ()
                         ((_ x)
                          (print x))
                         ((_ a b <:::>)
                          (begin
                            (display a)
                            (display " ")
                            (bar b <:::>))))))
       (bar x y ...)))))

但是当尝试使用 foo 宏时它不起作用。它抛出:

unbound variable: bar

即使使用 letrec-syntax.

外部语法规则中的此模式 (_ x ...) 会将模式变量 x 绑定到匹配列表中。这意味着,当 x 出现在模板中时,它后面必须(不一定直接)跟一个省略号。

这就是您从 Guile 和 Racket 中得到错误的原因。

现在考虑外部宏 (foo) 引入内部宏 (bar) 的情况,其定义包含省略号。当扩展器看到...是属于外宏定义还是属于内宏定义?

使用的约定(至少是 Racket 和所有语法规则的 psyntax 派生实现)是 ... 属于外层。 为了产生“内部省略号”,需要“引用”。所以生成一个内部省略号,写成(... ...).

在实践中这可能很烦人,所以我经常将内部定义包装在

(with-syntax ((ooo (... ...))
 code using ooo as an inner ellipsis here)

据我所知,您示例中的 (syntax-rules <:::> () etc) 是非标准的,因为语法是 (syntax-rules literals (pattern template) ...)。所以符号 <:::> 不允许作为文字列表。

问题是在您的外部宏中省略号跟在 x 之后。这意味着生成的模板在 x 之后也应该有省略号,但实际上没有。如果您将内部宏的 x 重命名为 y,它应该会按预期工作。

换句话说,你在这里所做的相当于:

(define-syntax foo
  (syntax-rules ()
    ((_ x ...)
     (display x))))

这也是不允许的。如果模式中的 x 带有省略号,则在其后的模板中使用 x 时也必须使用这些省略号。

如果您将其设置为 (x y ...),它会起作用的原因是模板根本不会消耗 y(及其省略号)。

您的宏有几个问题。

  1. 重用内部宏模板中的词法宏绑定。 x 将在结果中扩展,因此 let-syntax 中的 (_ x) 将被尝试替换,但是由于 x 模板有省略号,因此预计位置 x被插入在某处有省略号。我认为您的意思不是相同的绑定,因此用任何其他符号替换它都会修复它。例如。 single

  2. 使用 let-syntax 意味着你不能有使用相同语法规则的扩展,但在第二个模式中你打印第一个元素然后这样做。将其替换为 letrec-syntax 即可解决此问题。

  3. 模式 x ... 匹配 零个或多个 元素。因此 (foo) 匹配模式,但是 bar 没有兼容的模板。添加一个模式来捕捉无操作数的情况可以解决这个问题。

  4. Racket 不支持使用 SRFI-46。您根本不能指望开箱即用地支持它,因此您需要检查每个实现。在 Racket 中,您可以使用 (... ...) 作为替代。这与 R6RS / R7RS 相同。

考虑到这些,我最终得到了这个。在 Racket 中运行良好:

(define-syntax foo
  (syntax-rules (:c)
    ((_) (begin)) ; or you may signal an error 
    ((_ x...)
     (letrec-syntax ((bar (syntax-rules (:c)
                            ((_ single)
                             (print single))
                            ((_ first rest (... ...))
                             (begin
                               (display first)
                               (display " ")
                               (bar rest (... ...)))))))
       (bar x ...)))))

虽然我不知道你的例子有多简化,但可以用这个代替:

(define-syntax foo
  (syntax-rules ()
    ((_ x ...)
     (begin
       (begin
         (display x)
         (display " "))
       ...))))