试图理解语言扩展中的要求

时间:2015-07-25 08:09:43

标签: racket

我正在尝试在球拍中定义一种新语言,让我们称之为wibble。 Wibble将允许加载模块,因此必须将其表单转换为Racket需求表单。但是当我在语言扩展中使用时,我无法获得工作要求。我最终将我的问题追溯到以下奇怪的行为。

这是我的读者重新定义 NSArray* workspaceWindowControllers = [NSClassFromString(@"IDEWorkspaceWindowController") workspaceWindowControllers]; if (workspaceWindowControllers.count < 1) return nil; id workspace = [workspaceWindowControllers[0] valueForKey:@"_workspace"]; read

read-syntax

这是我简化的语言模块,这将=== wibble/lang/reader.rkt === #lang racket/base (provide (rename-out (wibble-read read) (wibble-read-syntax read-syntax))) (define (wibble-read in) (wibble-read-syntax #f in)) (define (wibble-read-syntax src in) #`(module #,(module-name src) wibble/lang #,@(read-all src in))) (define (module-name src) (if (path? src) (let-values (((base name dir?) (split-path src))) (string->symbol (path->string (path-replace-suffix name #"")))) 'anonymous-module)) (define (read-all src in) (let loop ((all '())) (let ((obj (read-syntax src in))) (if (eof-object? obj) (reverse all) (loop (cons obj all)))))) 引入每个wibble模块

(require racket/base)

使用上面的代码然后这个wibble代码“有效”,即没有错误

=== wibble/lang.rkt ===
#lang racket/base

(require (for-syntax racket/base))

(provide (rename-out (wibble-module-begin #%module-begin)) #%app #%datum #%top)

(define-syntax wibble-module-begin
  (lambda (stx)
    (syntax-case stx ()
      ((_ x ...) #`(#%module-begin (require #,(datum->syntax stx 'racket/base)) x ...)))))

但是以下

#lang wibble
(cons 1 2)
(cons 3 4)

提供错误消息#lang wibble (cons 1 2)

真的,我只是在寻找关于发生了什么的解释。我确信这与球拍文档(球拍参考3.1)

有关
  

如果提供单个表单,则会在a中部分展开   模块开始上下文。如果扩展导致#%plain-module-begin,   那么#%plain-module-begin的主体就是模块的主体。   如果部分扩展导致任何其他原始形式,那么表单   使用模块的词法上下文用#%module-begin包装   身体;此标识符必须由初始模块路径导入绑定,   并且它的扩展必须产生#%plain-module-begin来提供   模块体。最后,如果提供了多个表单,它们将被包装   使用#%module-begin,就像单个表单没有的那样   扩展到#%plain-module-begin。

但即便如此,我也不明白为什么单一形式有所不同,这似乎与部分扩张的时机有关,但我不太确定。我也不理解为什么Racket将单个表格视为特殊情况。

顺便说一下,我可以通过稍微修改我的阅读器来解决问题

cons: unbound identifier in module in: cons

(define (wibble-read-syntax src in) #`(module #,(module-name src) wibble/lang #,@(read-all src in) (void))) 形式进行硬编码意味着我总是有多个表单并且eveything正常工作。

对于这篇长篇文章感到抱歉,我只是想了解这些内容是如何运作的。

2 个答案:

答案 0 :(得分:5)

好吧,我认为我已经弄清楚了。

你的直觉是正确的,因为问题在于单一模块体的部分扩展时间。在reader.rkt文件中,您会生成(module ...)表单。正如您问题中引用的摘录所述,然后会对此forms ...部分进行特殊处理,因为只有一个。让我们看一下部分扩展文档的摘录:

  

作为一种特殊情况,当扩展否则会向表达式添加#%app#%datum#%top标识符,并且绑定结果为原始{{1 }},#%app#%datum形式,然后展开停止而不添加标识符。

我几乎可以肯定,此时发生的部分扩展会对#%top标识符产生影响。这是我仍然不确定的一部分...我的直觉告诉我,发生的是部分扩展试图找到cons标识符的绑定(因为它是第一部分)在括号中,标识符可以绑定到应该扩展的宏,因此需要进行检查但是无法进行,因此会引发发脾气。请注意,即使cons没有阶段1(语法扩展时间)绑定,宏扩展器仍然期望对标识符进行阶段0(运行时)绑定(除此之外,这有助于扩展器保持卫生)。因为所有这些部分扩展都发生在cons表单的正文中(在您注入(module ...)表单的(#%module-begin ...)表单之前已完成,} { {1}}在扩张期间没有任何约束力,所以扩张,我相信,失败了。

尽管如此,对您的问题的一个天真的解决方法是重写(#%require ...),如下所示:

cons

然后,您可以从wibble-read-syntax宏中删除(define (wibble-read-syntax src in) (let* ((read-in (read-all src in)) (in-stx (and (pair? read-in) (car read-in)))) #`(module #,(module-name src) wibble/lang (require #,(datum->syntax in-stx 'racket/base)) #,@read-in)) 表单。

然而,在我看来,这并不是解决问题的最佳方式。作为一个清洁问题,使用(#%require ...)形式的(#%module-begin ...)形式的硬编码会require wibble/lang.rkt lang.rkt。执行您尝试执行的操作的更简单方法是将=== wibble/lang.rkt === #lang racket/base (require (for-syntax racket/base)) (provide (rename-out (wibble-module-begin #%module-begin)) (except-out (all-from-out racket/base) #%module-begin #%app #%datum #%top) #%app #%datum #%top) (define-syntax wibble-module-begin (lambda (stx) (syntax-case stx () ((_ x ...) #`(#%module-begin x ...))))) 文件更新为:

(require ...)

在此约定中编写不需要任何硬编码的#%module-begin形式,并防止出现您发现的那些细微错误。如果您对此原因感到困惑,请记住您已使用此文件提供了#lang wibble标识符,该文件随后会绑定在所有function i=classify(x) [m, n]=size(x); if n==1 && m==1 i=0; elseif (m==0 && n==0)|| (m>=1 && n==0) || (m==0 && n>=1) i=-1; elseif (n>=1 && m==1) || (n==1 && m>=1) i=1; else i=2; end 个文件中。原则上,您可以以这种方式绑定的标识符没有限制。如果你想进一步阅读,这里有一段无耻的自我宣传make Eli Barzilay and co. cry一段时间回到这个主题上。

我希望我能帮到你。

答案 1 :(得分:1)

问题在于require(虽然我不确定我100%了解所有行为)。

(require X)使用X的词汇上下文从#'X导入绑定。 #'X此处的上下文为stx,这是整个#'(module-begin x ...),这不是您想要的上下文。您需要cons个表达式之一的上下文,即#'x个中的一个。

这样的事情应该有效:

(define-syntax wibble-module-begin
  (lambda (stx)
    (syntax-case stx ()
      [(_) #'(#%module-begin)]
      [(m x y ...)
       #`(#%module-begin
          (require #,(datum->syntax #'x 'racket/base))
          x y ...)])))

尽管如@belph所警告的那样,可能还有一种更惯用的方法可以达到你想要的效果。

原始程序的行为,以及您的直觉,可能与module对单个和多个子表单的不同处理有关,但我认为&#34;工作&# 34; case可能是一个意外,可能是球拍编译器中的错误。