替换语法对象中的变量

时间:2018-07-07 22:51:30

标签: racket

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

2 个答案:

答案 0 :(得分:2)

根据最终语法对象的用途,可以使用几种不同的策略。特别是,这取决于是否可以扩展语法以获取具有相同行为的其他语法对象,或者是否必须完全保留所有内容。

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

如果最终语法对象仅用作宏输出中的表达式,则扩展语法对象就可以了,因为运行时行为很重要,而不是确切的形式的语法。在这种情况下,您可以在包含替换的 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)

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-cdrsyntax/stx等(而非正常的null?,{ {1}},car

注意:但是,您为语言定义遍历时,这样的辅助函数可能会有用:

cdr

3。当您需要依赖并保留另一种核心语言的“含义”时

在极少数情况下,可能想扩展到与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中对其进行定义。