我想将v1
之类的语法对象中所有出现的#'(or (and v1 v2) (and v1 v3))
替换为v4
,以获得#'(or (and v4 v2) (and v4 v3))
。在Racket中最简单的方法是什么?我应该将语法转换为列表或字符串以替换,然后将其转换回语法吗?
答案 0 :(得分:2)
根据最终语法对象的用途,可以使用几种不同的策略。特别是,这取决于是否可以扩展语法以获取具有相同行为的其他语法对象,或者是否必须完全保留所有内容。
如果最终语法对象仅用作宏输出中的表达式,则扩展语法对象就可以了,因为运行时行为很重要,而不是确切的形式的语法。在这种情况下,您可以在包含替换的 internal-definition-context 中扩展语法对象。
;; 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)
如果最终语法对象应该保留原样,除了替换,那么您将无法对其进行扩展。您必须以某种方式遍历它才能进行替换。如果您要替换的代码可能使用某些功能,例如quote
或syntax->datum
,则会带来一些问题。但是,有时需要使用它,而在这些时候,我使用带有此签名的traverse-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))
traverse-stx/recur
的定义可能取决于您正在使用的语言,但是如果它实际上是任意的s表达式,而您没有害怕改变的“含义”,那么它可以像正常的s表达式遍历,尽管使用stx-null?
库中的stx-car
,stx-cdr
,syntax/stx
等(而非正常的null?
,{ {1}},car
等
注意:但是,您为语言定义遍历时,这样的辅助函数可能会有用:
cdr
在极少数情况下,可能想扩展到与Racket核心形式不同的“核心语言”。这仍然是一个活跃的研究领域,尚未完全弄清楚。但是,当前的策略包括像(2)一样手动遍历语法对象,同时还像<1>那样使用 internal-definition-context 扩展语法,并在扩展后重建语法。
到目前为止,关于此操作的最佳解释是在此Blog Post by Alexis King中。但这很难正确地完成,而且“核心语言”越复杂,它就越难。
答案 1 :(得分:1)
使用with-syntax
。
(with-syntax ([v1 #'v4])
#'(or (and v1 v2) (and v1 v3)))
输出:
#<syntax:3:4 (or (and v4 v2) (and v4 v3))>
将其转换为宏看起来像:
#lang racket
(require (for-syntax syntax/parse))
(define-syntax (replace-id stx)
(syntax-parse stx
[(_replace-id from to so)
(syntax/loc stx
(with-syntax ([from #'to])
#'so))]))
(replace-id v1 v4 #'(or (and v1 v2) (and v1 v3)))
如果您想在宏中使用replace-id
,请在begin-syntax
周围加上
定义,以便在阶段1中对其进行定义。