【发布时间】:2015-10-15 22:39:26
【问题描述】:
我正在尝试在球拍中定义一种新语言,我们称之为 wibble。 Wibble 将允许加载模块,因此它必须将其表单转换为 Racket 需要的表单。但是在语言扩展中使用时,我无法让 require 工作。我最终将我的问题归结为以下奇怪的行为。
这是我的阅读器,它重新定义了 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))))))
这是我大大简化的语言模块,它将(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 ...)))))
使用上面的代码,那么这个 wibble 代码“有效”,即没有错误
#lang wibble
(cons 1 2)
(cons 3 4)
但以下
#lang wibble
(cons 1 2)
给出错误消息cons: unbound identifier in module in: cons
真的,我只是在寻找关于发生了什么的解释。我确定差异与球拍文档(Racket Reference 3.1)中的这一点有关
如果提供了一个单一的形式,那么它被部分扩展为 模块开始上下文。如果展开导致#%plain-module-begin, 那么 #%plain-module-begin 的主体就是模块的主体。 如果部分展开导致任何其他原始形式,则该形式 使用模块的词法上下文用 #%module-begin 包装 身体;此标识符必须由初始模块路径导入绑定, 并且它的扩展必须产生一个#%plain-module-begin 来提供 模块体。最后,如果提供了多个表单,则将它们包装起来 使用 #%module-begin,如单个表单没有 展开为 #%plain-module-begin。
但即便如此,我也不明白为什么只有一个表单会产生任何影响,这似乎与部分扩展的时间有关,但我不太确定。我也不明白为什么 Racket 将单个表单视为特殊情况。
顺便说一句,我可以通过对我的阅读器稍作修改来解决这个问题
(define (wibble-read-syntax src in)
#`(module #,(module-name src) wibble/lang
#,@(read-all src in) (void)))
对(void) 表单进行硬编码意味着我总是有多个表单并且一切正常。
抱歉,这篇文章太长了,我只是想了解一下这些东西的工作原理。
【问题讨论】:
标签: racket