2

我正在尝试创建一个简单的嵌套宏。它适用于我的 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 ...)))))
  • 诡计投掷:

语法:缺少省略号

  • 球拍投掷:

模板中缺少带有模式变量的省略号

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

未绑定变量: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 ...)))))

抛出:

模板尺寸错误(椭圆太少?):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但是当尝试使用宏时它不起作用。它抛出:

未绑定变量:bar

即使在使用letrec-syntax.

4

3 回答 3

1

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

这就是为什么你会从 Guile 和 Racket 得到错误的原因。

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

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

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

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

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

于 2021-08-11T14:06:37.607 回答
1

问题是省略号x在您的外部宏中。这意味着生成的模板也应该有省略号x,但它没有。如果您将内部宏重命名xy它应该可以按预期工作。

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

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

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

如果您制作它,它起作用的原因(x y ...)y(以及它的省略号其余部分)根本不被模板消耗。

于 2021-08-12T05:04:38.330 回答
1

您的宏有几个问题。

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

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

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

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

考虑到这些,我最终得到了这个。在球拍中效果很好:

(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 " "))
       ...))))
于 2021-08-13T21:48:10.220 回答