3

我正在使用 guile 方案开发语言翻译器,并且需要处理基本情况,即您尝试转换单个单词。

(define var 5)
(translate var)

这应该返回字符串var而不是数字5
如何使用 R5RS 方案宏(define-syntax样式)来做到这一点?

编辑:
我正在从 Scheme 翻译成 Coffeescript。

4

3 回答 3

5
(define-syntax translate
  (syntax-rules ()
    [(_ v) 'v]))

如果你想要一个字符串:

(define-syntax translate
  (syntax-rules ()
    [(_ v) (symbol->string 'v)]))

希望 Guile 的编译器足够聪明,可以折叠结果表达式,使其本质上成为一个常量字符串。

于 2012-03-02T01:44:57.853 回答
4

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返回的未指定值,而不是引发语法错误。

于 2012-03-02T02:36:47.203 回答
1

其他答案已经足够有用了,但我想我只是指出可以以一种非常有用的方式概括这种技术:用于打印表达式及其调试结果的宏:

(define-syntax log-expr
  (syntax-rules ()
    ((_ expr)
     (let ((result expr))
       (write (quote expr))
       (display " evaluates to ")
       (write result)
       (newline)
       result))))
于 2012-03-02T23:11:25.907 回答