方案:如何在没有括号的语法规则中扩展具有多个变量的模式
Scheme: How to expand a pattern with multiple variables in syntax-rules without parens
我正在尝试在 Scheme 中为 Picolisp 风格的 let
表达式编写一个宏,我们称这个宏为 let-slim
。为了更简洁(如 Picolisp),我希望它们的用法在仅声明一个变量时看起来像这样
(let-slim var-name initial-value
(display var-name))
或者像这样声明任意数量的变量(注意这是伪代码,我实际上不会包括省略号)
(let-slim (var-name-1 initital-value-1
var-name-2 initital-value-2
...
var-name-n initital-value-n)
(+ var-name-1 var-name-2 ... var-name-n))
第一个用例编写 syntax-rules
匹配模式相当简单,但我正在为后者苦苦挣扎。
这不起作用,因为只有 init
被重复
(define-syntax let-slim
(syntax-rules ()
[(_ (var init ...) body ...)
(let ((var init) ...)
body ... )]))
这不起作用,因为它被认为是错位的省略号
(define-syntax let-slim
(syntax-rules ()
[(_ (var ... init ...) body ...)
(let ((var init) ...)
body ... )]))
这不起作用,因为我需要在参考点使用括号(这意味着与内置 let
相比,它绝对没有任何变化)
(define-syntax let-slim
(syntax-rules ()
[(_ (var init) ...) body ...)
(let ((var init) ...)
body ... )]))
那么,有没有一种方法可以在 syntax-rules
中重复 2 个变量而无需将它们包装在括号中,或者我是否需要使用不同的宏系统(即 syntax-case
或 defmacro
)?
使用 syntax-rules ...
功能无法一次完成此操作,但您可以使用 syntax-rules 使用递归来完成此操作:
(define-syntax let-slim
(syntax-rules ()
((let-slim (var-1 val-1 . rest) . body)
(let-slim var-1 val-1 (let-slim rest . body)))
((let-slim var val . body)
;; single binding case you already implemented
))
唯一的问题是 syntax-rules 无法判断 'var' 应该是一个符号。您不会从这样的宏中获得良好的错误消息(例如,如果它与奇数个 var/val 绑定一起使用)。用 syntax-case 来实现这个宏可能会更好。之所以难以实现是因为它有点违反了为每个 AST 节点使用一对括号的想法。
使用 syntax-rules
执行此操作并不是最佳选择,但由于图灵已完成,因此可以完成:
(define-syntax let-slim
(syntax-rules (pair)
((_ pair bindings () body)
(let bindings . body))
((_ pair (acc ...) (k v . rest) body)
(let-slim pair (acc ... (k v)) rest body))
((_ (elements ...) . body)
(let-slim pair () (elements ...) body))))
我正在尝试在 Scheme 中为 Picolisp 风格的 let
表达式编写一个宏,我们称这个宏为 let-slim
。为了更简洁(如 Picolisp),我希望它们的用法在仅声明一个变量时看起来像这样
(let-slim var-name initial-value
(display var-name))
或者像这样声明任意数量的变量(注意这是伪代码,我实际上不会包括省略号)
(let-slim (var-name-1 initital-value-1
var-name-2 initital-value-2
...
var-name-n initital-value-n)
(+ var-name-1 var-name-2 ... var-name-n))
第一个用例编写 syntax-rules
匹配模式相当简单,但我正在为后者苦苦挣扎。
这不起作用,因为只有 init
被重复
(define-syntax let-slim
(syntax-rules ()
[(_ (var init ...) body ...)
(let ((var init) ...)
body ... )]))
这不起作用,因为它被认为是错位的省略号
(define-syntax let-slim
(syntax-rules ()
[(_ (var ... init ...) body ...)
(let ((var init) ...)
body ... )]))
这不起作用,因为我需要在参考点使用括号(这意味着与内置 let
相比,它绝对没有任何变化)
(define-syntax let-slim
(syntax-rules ()
[(_ (var init) ...) body ...)
(let ((var init) ...)
body ... )]))
那么,有没有一种方法可以在 syntax-rules
中重复 2 个变量而无需将它们包装在括号中,或者我是否需要使用不同的宏系统(即 syntax-case
或 defmacro
)?
使用 syntax-rules ...
功能无法一次完成此操作,但您可以使用 syntax-rules 使用递归来完成此操作:
(define-syntax let-slim
(syntax-rules ()
((let-slim (var-1 val-1 . rest) . body)
(let-slim var-1 val-1 (let-slim rest . body)))
((let-slim var val . body)
;; single binding case you already implemented
))
唯一的问题是 syntax-rules 无法判断 'var' 应该是一个符号。您不会从这样的宏中获得良好的错误消息(例如,如果它与奇数个 var/val 绑定一起使用)。用 syntax-case 来实现这个宏可能会更好。之所以难以实现是因为它有点违反了为每个 AST 节点使用一对括号的想法。
使用 syntax-rules
执行此操作并不是最佳选择,但由于图灵已完成,因此可以完成:
(define-syntax let-slim
(syntax-rules (pair)
((_ pair bindings () body)
(let bindings . body))
((_ pair (acc ...) (k v . rest) body)
(let-slim pair (acc ... (k v)) rest body))
((_ (elements ...) . body)
(let-slim pair () (elements ...) body))))