Racket 替换语法对象中的变量
我想将语法对象中出现的所有Racket 替换语法对象中的变量,racket,Racket,我想将语法对象中出现的所有v1(或(和v1 v2)(和v1 v3))替换为v4,以获得#'(或(和v4 v2)(和v4 v3))。在球拍中最简单的方法是什么?我应该将语法转换为列表还是字符串以替换并将其转换回语法?将与语法一起使用 (with-syntax ([v1 #'v4]) #'(or (and v1 v2) (and v1 v3))) 输出: #<syntax:3:4 (or (and v4 v2) (and v4 v3))> 如果要在宏中使用replace id,请
v1
(或(和v1 v2)(和v1 v3))替换为v4
,以获得#'(或(和v4 v2)(和v4 v3))
。在球拍中最简单的方法是什么?我应该将语法转换为列表还是字符串以替换并将其转换回语法?将与语法一起使用
(with-syntax ([v1 #'v4])
#'(or (and v1 v2) (and v1 v3)))
输出:
#<syntax:3:4 (or (and v4 v2) (and v4 v3))>
如果要在宏中使用replace id
,请将begin syntax
环绕
定义,以便在阶段1中定义它。根据最终语法对象的用途,您可以使用几种不同的策略。特别是,这取决于是否可以扩展语法以获得具有相同行为的不同语法对象,或者是否必须使所有内容保持原样
1.如果最终语法对象用作宏输出中的表达式
如果最终的语法对象仅用作宏输出中的表达式,那么扩展语法对象就可以了,因为重要的是运行时行为,而不是语法的确切形式。在这种情况下,您可以在包含替换的内部定义上下文中展开syntax对象
;; create a context where x-old is renamed to x-new
(define ctx (syntax-local-make-definition-context))
(syntax-local-bind-syntaxes
(list x-old)
#`(make-rename-transformer (quote-syntax #,x-new))
ctx)
;; expand the syntax in that context
(local-expand stx 'expression '() ctx)
2.如果最终语法对象应该保持原样,并且不能扩展
如果除了替换之外,最终的语法对象应该保持原样,那么就不能扩展它。你必须以某种方式遍历它来进行替换。如果要替换的代码可能使用某些功能,如quote
或syntax->datum
,则会出现一些问题。但是,有时也需要这样做,对于这些情况,我使用带有此签名的遍历stx/recur
函数:
;; traverse-stx/recur : Stx [Stx -> Stx] -> Stx
;; Traverses `stx`, calling the `recur` function on every sub-piece
(define (traverse-stx/recur stx recur)
....)
我是这样使用的:
;; stx-subst : Stx Id Id -> Stx
;; Replaces every instance of `x-old` with `x-new` in the syntax `stx`
(define (stx-subst stx x-old x-new)
;; traverse : Stx -> Stx
(define (traverse s)
(cond [(and (identifier? stx) (free-identifier=? stx x-old))
x-new]
[else
;; pass "yourself" as the recur callback, so that it calls
;; you on every sub-piece
(traverse-stx/recur stx traverse)]))
(traverse s))
遍历stx/recur
的定义可能取决于您正在遍历的语言,但是如果它实际上只是任意的s表达式,没有您害怕更改的“意义”,那么它可以像正常的s表达式遍历一样构造,尽管使用stx null?
,stx car
,stx cdr
,等(从语法/stx
库),而不是正常的null?
、car
、cdr
等
注意:无论您如何为您的语言定义遍历,像这样的助手函数可能会很有用:
;; restore : Stx Any -> Stx
;; Any Any -> Any
(define (restore orig datum)
(if (syntax? orig) (datum->syntax orig datum orig orig) datum))
3.当你需要依赖和保留不同核心语言的“意义”时
在一些罕见的情况下,他们可能希望扩展到与球拍的核心形式不同的“核心语言”。这仍然是一个活跃的研究领域,尚未完全弄清楚。然而,当前的策略包括手动遍历语法对象(如(2)),同时使用内部定义上下文(如(1))扩展语法,并在扩展后重建语法
到目前为止,我所看到的最好的解释就是这个。但是这很难做到,而且你的“核心语言”越复杂,就越难做到。谢谢。如果我想把它包装成一个函数呢(define(foo stx)(使用语法([v1#'v4])stx))
似乎不起作用?您能解释一下(使用语法([from#'to])so)
在替换id
中做了什么吗?我知道(\u replace-id from to so)
将so
匹配为#'(或(和v1 v2)(和v1 v3))
,但是如何使用语法执行替换?我阅读了带有语法的文档,但没有得到它:(首先(带有语法([from#'to])body)
将from
绑定到语法对象。如果from
出现在body内的语法模板中,则标识符from
将被替换为它绑定到的值(此处为#'to')。也就是说:与syntax
不匹配,它只是绑定可以在语法模板中引用的标识符。因为define syntax
的主体需要返回语法转换器。您可以使用定义语法规则
。如果我没记错的话:(定义语法规则(将id从替换为stx)(使用语法([from#'to])stx))
。替换模板中标识符的是。
akasyntax
。语法为
的单独对stx
没有任何作用。谢谢。但是如何在else
案例中创建语法对象,这需要连接(stx car stx)
和(traverse stx/recur stx traverse)
?我已经更新了答案,以包含恢复
辅助函数。每当遍历stx/recur
函数解构表单时,例如stx对?
,它应该使用(restore stx(cons(recur(stx car stx))(recur(stx cdr stx)))
之类的方法重新构造表单。对于您的解决方案#2,在这种情况下,您可能真的想要绑定标识符=?
而不是自由标识符=?
,具体取决于细节。在我使用solution#2时,通常情况下,定义来自我正在替换的语法对象之外。由于这个外部定义,自由标识符=?
似乎是正确的。我的理解是,boundidentifier=?
是当可能的定义和引用都在内部时应该使用的,并且应该忽略外部的定义。
;; restore : Stx Any -> Stx
;; Any Any -> Any
(define (restore orig datum)
(if (syntax? orig) (datum->syntax orig datum orig orig) datum))