首页 文章

如何使用语法宏

提问于
浏览
2

我是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 回答

  • 2

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

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

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

    但是,如果你运行它,你会得到一个关于 lambda 在第2阶段未绑定的错误 . 那是因为 lambda 用在编译时定义的宏转换器中,因此它必须在编译时's compile time! That is, there aren'只有两个阶段可用; "compile time"可以有很多层次 . 我们将"run time"标记为阶段0,将"compile time"标记为阶段1,将"compile time's compile time"标记为阶段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 是第1阶段的参考,受 (require (for-syntax racket/base)) 约束;它导致 with-syntax1 被定义为阶段1的宏,变换器表达式处于阶段2 .

    • 在第C行, lambda 是第_ 2期由 (require (for-meta 2 racket/base)) 约束的参考 .

    • 在第D行, syntax-case 出现在语法模板中,该模板不计算在宏中使用它 . 它还不是引用,但它会在使用 with-syntax 宏时生成引用 . with-syntax1 宏在阶段1定义,因此必须在阶段1使用,这意味着它产生的对 syntax-case 的引用必须在阶段1绑定 . 并且它受第二个要求约束 .

    • 对于作业,标签行E,F和G :)

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

    (模块with-syntax1-mod _) (module or1-mod _ (require(for-syntax'with-syntax-mod))___)

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

    我建议阅读Racket Guidemacros section,尤其是相位的后期部分 . Fear of Macros也有助于揭开围绕宏的一些问题的神秘面纱 .

  • 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 ...(2) 匹配,因此宏扩展为嵌套在其中的语法... (or 1 2) ,导致无限扩展 .

相关问题