未绑定标识符,没有 #%top 语法转换器



我正在使用一个流畅的界面(对于流畅的一个非常奇怪的定义(,以便我可以更好地学习球拍宏,所以我模拟了这段代码来玩。

#lang racket
(require racket/syntax)
(struct Person (name location))
(define-syntax what
  (syntax-rules (is)
    ((what is x thing) (what is x's thing))
    ((what is x quote-s thing)
     (let* ((x-str (~a (quote x)))
            (x-id (format-id #'x-id "~a" x-str))
            (thing-str (~a (quote thing)))
            (thing-proper-str (cadr (regexp-match #rx"(.*)\?" thing-str)))
            (thing-id (format-id #'thing-id "Person-~a" thing-proper-str)))
       ((eval thing-id) (eval (syntax-e x-id)))))))
(define John (Person "John Luser" "Here"))
(what is John's name?)

运行此操作会导致John: unbound identifier; also, no #%top syntax transformer is bound in: John 。如果我不得不猜测,我会说我对标识符和字符串所做的所有放置都剥夺了符号 John 的绑定,或者我在错误的环境中逃避,但我不知道如何解决其中任何一个。

只是为了比较:如果您将大部分工作推到编译时阶段,您的宏如下所示:

#lang racket
(require (for-syntax racket/format
                     racket/syntax))
(struct Person (name location))
(define-syntax (what stx)
  (syntax-case stx (is)
    ((what is x thing) 
     #'(what is x's thing))
    ((what is x quote-s thing)
     (let* ((thing-str (~a (syntax-e #'thing)))
            (thing-proper-str (cadr (regexp-match #rx"(.*)\?" thing-str)))
            (thing-id (format-id stx "Person-~a" thing-proper-str)))
       #`(#,thing-id x)))))
(define John (Person "John Luser" "Here"))
(what is John's name?)

需要注意的一件事是,我们正在做(require (for-syntax ...)),因为我们在编译时使用这些库,在程序源代码的预处理期间。

另外,请注意,它稍微简单一些,因为由于我们并没有真正对正在操作的x进行任何修改,因此我们可以将该语法保留在输出的语法中。


如果有帮助,请考虑一下您在其他语言中的体验。 如果您熟悉 Java,请考虑在程序中编写表达式:"hello " + "world"某处,然后编译程序时会发生什么情况。 您是否希望 Java 编译器生成字节码,该字节码在执行时构造"hello "字符串、"world"字符串以及两者之间的字符串连接? 大多数人会说"不,编译器应该将其重写为仅"hello world"文字作为编译的一部分"。

这是预处理。 当你在Racket中编写宏时,你正在教Racket更多这些预处理规则。

在这里,让我们尝试以下操作:我们将添加一个知道如何连接字符串的 cat 命令。 也就是说,让我们将该串联示例变为现实。

这是版本一:

#lang racket
(define (cat x y)
  (string-append x y))
(define msg-head "hello ")
(define msg-tail " world")
(cat msg-head msg-tail)    
(cat "hiya " "world")

现在,我们知道,作为一个常规函数,这里cat的两种用法最终都减少为对string-append函数的调用。 编译器不够聪明,不知道如何触摸cat的第二次使用。

但是,如果

我们想做同样类型的编译器重写规则:如果编译器预先看到源代码正在尝试cat两个字符串文字,为什么不在编译时这样做,以便发出的字节码更好?

好吧,让我们这样做。 版本二如下:

#lang racket
(define-syntax (cat stx)
  (syntax-case stx ()
    [(_ x y)
     ;; At this point, the following code is being run by the _compiler_.
     (cond
       ;; If in the source code we're transforming, both x and y are
       ;; strings, we can do the concatenation at _compile time_.
       [(and (string? (syntax-e #'x))
             (string? (syntax-e #'y)))
        (let ([xy
               (string-append (syntax-e #'x) (syntax-e #'y))])
          ;; Once we have this string, we still need to emit it back as the
          ;; result of the rewrite.  We want to produce a piece of _syntax_
          ;; in place of the original stx.
          (datum->syntax stx xy))]
       [else
        ;; Otherwise, we want to produce a piece of syntax that looks like a
        ;; plain function call to string-append.
        (datum->syntax stx (list 'string-append #'x #'y))])]))
(define msg-head "hello ")
(define msg-tail " world")
(cat msg-head msg-tail)
(cat "hiya " "world")

现在这种转变的问题在于观察:如果我们做对了,没有人能够分辨出其中的区别! :P 否则,这将是一个破碎的转换。 因此,要更轻松地查看差异,请按 DrRacket 工具栏上的宏步进器按钮:它会调用编译器,但会显示程序在转换为字节码之前发生的转换。

哇,我觉得自己很傻吗!发布此内容几分钟后修复了它,将((eval thing-id) (eval (syntax-e x-id)))更改为仅((eval thing-id) (eval x-id))

相关内容

  • 没有找到相关文章

最新更新