我正在使用 guile 方案开发语言翻译器,并且需要处理基本情况,即您尝试转换单个单词。
(define var 5)
(translate var)
这应该返回字符串var
而不是数字5
。
如何使用 R5RS 方案宏(define-syntax
样式)来做到这一点?
编辑:
我正在从 Scheme 翻译成 Coffeescript。
(define-syntax translate
(syntax-rules ()
[(_ v) 'v]))
如果你想要一个字符串:
(define-syntax translate
(syntax-rules ()
[(_ v) (symbol->string 'v)]))
希望 Guile 的编译器足够聪明,可以折叠结果表达式,使其本质上成为一个常量字符串。
syntax-case
及其后卫支持:
(define-syntax translate
(lambda (stx)
(syntax-case stx ()
[(_ v) (identifier? #'v)
#'(symbol->string 'v)]
[(_ v) (number? (syntax-e #'v))
#'(number->string v)])))
(我使用方括号方便与 Eli 的回答进行比较,但是,这不是我通常的风格。;-))
但是,如果您正在使用syntax-case
,那么您也可以在语法级别进行转换,而不是生成在运行时执行此操作的代码:
(define-syntax translate
(lambda (stx)
(syntax-case stx ()
[(_ v) (identifier? #'v)
(datum->syntax stx (symbol->string (syntax->datum #'v)))]
[(_ v) (number? (syntax-e #'v))
(datum->syntax stx (number->string (syntax->datum #'v)))])))
这里的主要内容是宏代码现在是普通方案,例如,您可以将公共部分抽象为帮助程序:
(define-syntax translate
(lambda (stx)
(define (rewrap convert x)
(datum->syntax stx (convert (syntax->datum x))))
(syntax-case stx ()
[(_ v) (identifier? #'v) (rewrap symbol->string #'v)]
[(_ v) (number? (syntax-e #'v)) (rewrap number->string #'v)])))
同样,如果这个宏很简单,那么syntax-case
除了拉出子表达式之外就没有真正的需要了:
(define-syntax translate
(lambda (stx)
(syntax-case stx ()
[(_ v) (let ([d (syntax->datum #'v)])
(datum->syntax
stx
((cond [(number? d) number->string]
[(symbol? d) symbol->string])
d)))])))
请注意,顺便说一句,syntax-case
在这个简单的模式中,你可以自己提取值:
(define-syntax translate
(lambda (stx)
(let ([d (cadr (syntax->datum #'v))])
(datum->syntax
stx
((cond [(number? d) number->string]
[(symbol? d) symbol->string])
d)))))
syntax-case
最后一个版本丢失了一些样板文件:
如果你以一种意想不到的方式使用宏,(translate)
那么这个版本将抛出一个错误cadr
而不是一个更容易理解的语法错误
同样,如果您使用,(translate 1 2)
那么这个版本只会默默地忽略2
而不是错误。
如果它与既不是标识符也不是数字的东西(例如,(translate (+ 1 2))
)一起使用,那么这将取决于cond
返回的未指定值,而不是引发语法错误。
其他答案已经足够有用了,但我想我只是指出可以以一种非常有用的方式概括这种技术:用于打印表达式及其调试结果的宏:
(define-syntax log-expr
(syntax-rules ()
((_ expr)
(let ((result expr))
(write (quote expr))
(display " evaluates to ")
(write result)
(newline)
result))))