如何使用语法宏

时间:2017-03-09 05:44:39

标签: macros scheme racket

我是Scheme的新人,在阅读SICP时,我发现:

- >我需要阅读“The Scheme programming language 4”,

- >我需要阅读r6rs,

--->我读了“又一个方案教程”,

--->我需要阅读“使用Syntax-Case在Scheme中编写卫生宏”。

在阅读最后一篇文章时,我尝试:

(define-syntax with-syntax1 ;;;racket has a with-syntax
  (lambda (x)
    (syntax-case x ()
      ((_ ((p e0) ...) e1 e2 ...)
       (syntax (syntax-case (list e0 ...) ()
                 ((p ...) (begin e1 e2 ...))))))))

(define-syntax or1
  (lambda (x)
    (syntax-case x ()
      ((_) (syntax #f))
      ((_ e) (syntax e))
      ((_ e1 e2 e3 ...)
       (with-syntax1 ((rest (syntax (or e2 e3 ...))))
         (syntax (let ((t e1)) (if t t rest))))))))

我收到了一个错误: rest:模块中的未绑定标识符(在阶段1,变换器环境中):rest

// --------------------------------------------- -------------------

使用球拍的“with-syntax”来定义另一个或:

(define-syntax or
  (lambda (x)
    (syntax-case x ()
      ((_) (syntax #f))
      ((_ e) (syntax e))
      ((_ e1 e2 e3 ...)
       ;;;use racket's with-syntax
       (with-syntax ((rest (syntax (or e2 e3 ...))))
         (syntax (let ((t e1)) (if t t rest))))))))

将其称为(或1 2),呼叫永远不会结束。

// ---找到了第二个问题的根本原因---------------------

我的问题是:

上述两个“或”中的问题是什么。

是否有路线图(或书籍清单,一个接一个)我可以关注学习计划/球拍?

我对Scheme中的“卫生宏”非常感兴趣,我想学习如何编写一个宏,并且,我也想知道卫生宏背后的理论。

2 个答案:

答案 0 :(得分:2)

第一个错误是由于阶段不匹配造成的。 Racket使用阶段来告知在编译时(即宏扩展时间)与运行时间之间需要执行的代码。阶段对于在具有宏和副作用的语言中获得可预测和可重复的编译是必要的。

在宏中,with-syntax1在宏变换器中使用 来计算结果语法,因此with-syntax1的定义必须在编译时发生(相对于顶层)。这是修复程序的第一步:

(begin-for-syntax
  (define-syntax with-syntax1 (lambda (x) ___)))
(define-syntax or1 ___)

如果您运行该操作,则会在第2阶段出现lambda未绑定的错误。因为lambda 在宏变换器中使用了在编译时定义,所以它必须在编译时编译时可用!也就是说,不仅仅是两个阶段;可以有很多级别的编译时间"。我们标注"运行时间"作为阶段0,"编译时间"作为第1阶段,"编译时间的编译时间"作为第1阶段,依此类推。

以下是对您的示例的全面修复:

(require racket/base               ;; phase 0
         (for-syntax racket/base)  ;; phase 1
         (for-meta 2 racket/base)) ;; phase 2

(begin-for-syntax                   ;; A
  (define-syntax with-syntax1       ;; B
    (lambda (x)                     ;; C
      (syntax-case x ()
        ((_ ((p e0) ...) e1 e2 ...)
         (syntax
          (syntax-case (list e0 ...) () ;; D
            ((p ...) (begin e1 e2 ...)))))))))

(define-syntax or1                  ;; E
  (lambda (x)                       ;; F
    (syntax-case x ()
      ((_) (syntax #f))
      ((_ e) (syntax e))
      ((_ e1 e2 e3 ...)
       (with-syntax1 ((rest (syntax (or e2 e3 ...)))) ;; G
         (syntax (let ((t e1)) (if t t rest))))))))

以下是本程序中绑定和阶段的一些注释:

  • 在A行,begin-for-syntax是第0阶段的一个特殊形式的参考,它将身体的一个阶段移动得更高(因此身体处于阶段1)。
  • 在第B行,define-syntax(require (for-syntax racket/base))绑定的第1阶段的引用;它导致with-syntax1在阶段1被定义为宏,变换器表达式在阶段2。
  • 在第C行,lambda(require (for-meta 2 racket/base))约束的第2阶段的参考。
  • 在第D行,syntax-case出现在语法模板中,在宏中不计算使用。它还不是引用,但是当with-syntax使用时,它将生成引用。在阶段1,with-syntax1宏是定义,因此在阶段1必须使用,这意味着它产生的对syntax-case的引用必须是在第1阶段受到约束。并且受到第二阶段的约束。
  • 对于作业,标签行E,F和G:)

还有其他方法来构建这样的程序。一种方法是将with-syntax1放入另一个模块并要求for-syntax

(模块with-syntax1-mod _)   (模块or1-mod _ (要求(for-syntax' with-syntax-mod))___)

阶段所施加的约束保持不变,但你现在必须小心标记阶段:"阶段1与 - syntax1-mod"对于or1-mod"是与"阶段2相同的阶段因为它需要for-syntax(即在阶段+1)。

我建议您阅读macros sectionRacket Guide,尤其是相位的后续部分。 Fear of Macros也有助于揭开围绕宏的一些问题的神秘面纱。

答案 1 :(得分:1)

第二个示例的问题很简单:您需要在_ee1之间留出空格。即:

(define-syntax or
  (lambda (x)
    (syntax-case x ()
      ((_) (syntax #f))
      ((_ e) (syntax e))
      ((_ e1 e2 e3 ...)
       ;;;use racket's with-syntax
       (with-syntax ((rest (syntax (or e2 e3 ...))))
         (syntax (let ((t e1)) (if t t rest))))))))

(or 1 2)

要查看其永久运行的原因:您的宏将标识符_e1与标识符or匹配,e21匹配,e2 ...与{ {1}},因此宏扩展为嵌套在其中的语法... (2),导致无限扩展。