【问题标题】:Is it possible to just print the string that was passed into the Scheme macro?是否可以只打印传递给 Scheme 宏的字符串?
【发布时间】:2012-03-20 13:50:24
【问题描述】:

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

(define var 5)
(translate var)

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

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

【问题讨论】:

  • 我敢打赌,如果您提供更多关于您正在使用哪种语言翻译器的详细信息,您会得到更多有用的帮助。你从什么翻译成什么,为什么?

标签: macros scheme guile r5rs


【解决方案1】:
(define-syntax translate
  (syntax-rules ()
    [(_ v) 'v]))

如果你想要一个字符串:

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

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

【讨论】:

  • 感谢您的回复。我的意思是它应该返回一个实际的字符串(以便我可以在其上使用 (string-append)。
  • 在'v 周围使用symbol->string 是否可以接受?
  • 这适用于符号->字符串换行。现在我需要找到一个解决裸数字的方法。谢谢。
  • @chustar:我用symbol->string 更新了它。如果你想用数字做一些事情,原则上是相似的。
  • 感谢@EliBarzilay。我添加了一个 (if (?number 'v) (number->string 'v) (symbol->string 'v))) 来处理数字情况。谢谢。
【解决方案2】:

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

【讨论】:

  • 我想补充一点:克里斯的syntax-case宏可以在编译时做更多的工作,这样即使symbol->string和number->string的东西也不会发生在运行时。
  • 呸。这样做没有什么意义...如果您使用syntax-case,您也可以在那里进行转换,而不是扩展为执行这些操作的代码。
  • @Eli:我同意。我只是想匹配你的代码(我知道这行得通,因为你的帖子总是高质量的:-)),所以我不必费心测试我的代码。但我现在要编辑代码。
  • 哇,这比我整天在网上看到的有关该主题的信息还要多。谢谢。
  • @chustar:大部分信息来自 Eli。感谢他,真的。 :-D
【解决方案3】:

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

(define-syntax log-expr
  (syntax-rules ()
    ((_ expr)
     (let ((result expr))
       (write (quote expr))
       (display " evaluates to ")
       (write result)
       (newline)
       result))))

【讨论】:

  • 谢谢!这就是我要找的东西!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-18
  • 1970-01-01
  • 2021-09-28
  • 2016-10-23
  • 1970-01-01
相关资源
最近更新 更多