替换语法对象中的变量



我想替换语法对象中所有出现的v1,例如#'(or (and v1 v2) (and v1 v3))v4以获得#'(or (and v4 v2) (and v4 v3))。在球拍中最简单的方法是什么?我应该将语法转换为列表或字符串以替换并将其转换回语法吗?

您可以使用几种不同的策略,具体取决于最终语法对象的用途。具体而言,这取决于是否可以扩展语法以获取具有相同行为的不同语法对象,或者是否必须保持所有内容原样。

1. 如果最终语法对象用作宏输出中的表达式

如果最终语法对象仅用作宏输出中的表达式,则可以扩展语法对象,因为运行时行为才是重要的,而不是语法的确切形式。在这种情况下,可以在包含替换的内部定义上下文中展开语法对象。

;; 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. 如果最终的语法对象应该保持原样,并且无法扩展

如果最终语法对象应该完全保持原样,除了替换之外,则无法扩展它。您必须以某种方式遍历它才能进行替换。如果您要替换的代码可能使用某些功能(如quotesyntax->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-carstx-cdr等(来自syntax/stx库(而不是正常的null?carcdr

注意:无论您如何定义语言的遍历,像这样的帮助程序函数可能会很有用:

;; restore : Stx Any -> Stx
;;           Any Any -> Any
(define (restore orig datum)
(if (syntax? orig) (datum->syntax orig datum orig orig) datum))

3. 当你需要依赖和保留不同核心语言中的"意义"时

在极少数情况下,可能希望扩展到与Racket核心形式不同的"核心语言"。这仍然是一个活跃的研究领域,尚未完全弄清楚。但是,当前的策略包括手动遍历语法对象(如 (2(,同时还使用内部定义上下文(如 (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 中定义它。

最新更新