我正在尝试在球拍中定义一种新语言,让我们称之为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正常工作。
对于这篇长篇文章感到抱歉,我只是想了解这些内容是如何运作的。
答案 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可能是一个意外,可能是球拍编译器中的错误。