检查标识符是否在宏扩展点词法绑定?
Check if an identifier is lexically bound at the point of macro expansion?
这可能是个新手问题,但我正在尝试编写一个宏来检测标识符是否在宏扩展点词法绑定,并相应地更改其输出。这在 R6RS 方案中是否可行,如果可行,如何实现?
为什么我想知道
我正在 Chez Scheme 中使用宏编写玩具 Objective-C 绑定层,我最初的挑战是有效地处理 Objective-C 选择器。程序必须在运行时查询 Objective-C 运行时,以获取与每个选择器名称对应的实际 SEL
对象。程序中使用的选择器名称在扩展期间将是静态已知的,并且很容易让我的宏插入该查询代码,但我想避免重复查询相同的选择器名称。
我的第一个想法是为绑定到 SEL
外部对象的 Scheme 定义制定一些命名约定。这样,我可以为每个唯一选择器设置一个 (define)
,因此每个选择器都有一个运行时查询。这取决于我的宏能够检测任何给定选择器的这些绑定,并在它们尚不存在时引入它们,因此我的问题。
这个解决方案仍然不完善,因为我的宏可能会在内部范围内扩展,但这对我来说是最明显的。有没有更好的方法在扩展时 "intern" 表达式?
我不完全确定这是否可以通过指定的行为来实现,但你可以像下面这样:
#!r6rs
(library (bound helper)
(export make-binding-check)
(import (rnrs))
(define-syntax make-binding-check
(lambda (x)
(syntax-case x ()
((_ if bound?)
#'(begin
(define-syntax if
(lambda (xx)
(syntax-case xx ()
((_ b then els)
(free-identifier=? (datum->syntax #'if (syntax->datum #'b)) #'b)
#'els)
((_ b then els)
#'then))))
(define-syntax bound?
(lambda (xx)
(syntax-case xx ()
((_ b)
#'(if b #t #f))))))))))
)
(library (bound)
(export bound? if-bound)
(import (bound helper))
(make-binding-check if-bound bound?)
)
(import (rnrs) (bound))
(display (bound? foo)) ;; #f
(let ((foo 1))
(display (bound? foo))) ;; #t
(newline)
这个想法是使用 free-identifier=?
检查给定的标识符是否已绑定。 make-binding-check
宏有 2 个标识符,当宏被扩展时,它们都应该被解除绑定。为了制作这样的未绑定标识符,代码由两部分组成,实现和环境:第一个是 (bound helper)
,它提供了标识符比较的实现。另一个是 (bound)
,它几乎不提供任何绑定环境。
比较是通过从环境库传递的标识符和您要检查的实际标识符完成的。如果实际标识符绑定到任何东西,那么它就不会与未绑定的标识符相同,因此 bound?
应该 return #f
。 (如果您定义 make-binding-check
并检查,它会 return #t 因为它是在 (bound)
库中定义的。)
注意
这可能有效,也可能无效,具体取决于您使用的实施方式,我不确定这是否适用于 Chez。
这可能是个新手问题,但我正在尝试编写一个宏来检测标识符是否在宏扩展点词法绑定,并相应地更改其输出。这在 R6RS 方案中是否可行,如果可行,如何实现?
为什么我想知道
我正在 Chez Scheme 中使用宏编写玩具 Objective-C 绑定层,我最初的挑战是有效地处理 Objective-C 选择器。程序必须在运行时查询 Objective-C 运行时,以获取与每个选择器名称对应的实际 SEL
对象。程序中使用的选择器名称在扩展期间将是静态已知的,并且很容易让我的宏插入该查询代码,但我想避免重复查询相同的选择器名称。
我的第一个想法是为绑定到 SEL
外部对象的 Scheme 定义制定一些命名约定。这样,我可以为每个唯一选择器设置一个 (define)
,因此每个选择器都有一个运行时查询。这取决于我的宏能够检测任何给定选择器的这些绑定,并在它们尚不存在时引入它们,因此我的问题。
这个解决方案仍然不完善,因为我的宏可能会在内部范围内扩展,但这对我来说是最明显的。有没有更好的方法在扩展时 "intern" 表达式?
我不完全确定这是否可以通过指定的行为来实现,但你可以像下面这样:
#!r6rs
(library (bound helper)
(export make-binding-check)
(import (rnrs))
(define-syntax make-binding-check
(lambda (x)
(syntax-case x ()
((_ if bound?)
#'(begin
(define-syntax if
(lambda (xx)
(syntax-case xx ()
((_ b then els)
(free-identifier=? (datum->syntax #'if (syntax->datum #'b)) #'b)
#'els)
((_ b then els)
#'then))))
(define-syntax bound?
(lambda (xx)
(syntax-case xx ()
((_ b)
#'(if b #t #f))))))))))
)
(library (bound)
(export bound? if-bound)
(import (bound helper))
(make-binding-check if-bound bound?)
)
(import (rnrs) (bound))
(display (bound? foo)) ;; #f
(let ((foo 1))
(display (bound? foo))) ;; #t
(newline)
这个想法是使用 free-identifier=?
检查给定的标识符是否已绑定。 make-binding-check
宏有 2 个标识符,当宏被扩展时,它们都应该被解除绑定。为了制作这样的未绑定标识符,代码由两部分组成,实现和环境:第一个是 (bound helper)
,它提供了标识符比较的实现。另一个是 (bound)
,它几乎不提供任何绑定环境。
比较是通过从环境库传递的标识符和您要检查的实际标识符完成的。如果实际标识符绑定到任何东西,那么它就不会与未绑定的标识符相同,因此 bound?
应该 return #f
。 (如果您定义 make-binding-check
并检查,它会 return #t 因为它是在 (bound)
库中定义的。)
注意 这可能有效,也可能无效,具体取决于您使用的实施方式,我不确定这是否适用于 Chez。