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))
。替换模板中标识符的是
aka
syntax
。语法为
的单独
对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))