|
2 | 2 |
|
3 | 3 | (provide free-identifiers |
4 | 4 | binding-identifiers |
| 5 | + identifier=? |
5 | 6 | alpha-equivalent? |
| 7 | + subst |
6 | 8 | get-racket-referenced-identifiers |
7 | 9 | (rename-out [identifier=? compiled-identifier=?])) |
8 | 10 |
|
|
99 | 101 | ; Syntax, Syntax [#:allow-host? Boolean] -> Boolean |
100 | 102 | ; Are the two expressions alpha-equivalent? |
101 | 103 | (define (alpha-equivalent? stx-a stx-b #:allow-host? [allow-host? #f]) |
102 | | - (define bound-reference=? (alpha-equivalent?/bindings stx-a stx-b allow-host?)) |
| 104 | + (define bound-reference=? (syntaxes->bound-reference=? stx-a stx-b allow-host?)) |
103 | 105 | (and bound-reference=? |
104 | 106 | (alpha-equivalent?/references stx-a stx-b bound-reference=? allow-host?))) |
105 | 107 |
|
106 | 108 | ; Syntax Syntax Boolean -> (or/c #f (Identifier Identifier -> Boolean)) |
107 | | -; check that the bindings of both expressions can be alpha-equivalent. |
108 | | -; returns bound-reference=?, or #f if the binding check fails. |
109 | | -(define (alpha-equivalent?/bindings stx-a stx-b allow-host?) |
| 109 | +; if the two terms have corresponding binders, build bound-reference=? |
| 110 | +; If they have different numbers of binders, return #f |
| 111 | +; bound-reference=? answers "do these two references refer to corresponding binders?" |
| 112 | +(define (syntaxes->bound-reference=? stx-a stx-b allow-host?) |
110 | 113 | (define table-a (make-free-id-table)) |
111 | 114 | (define table-b (make-free-id-table)) |
| 115 | + ;; associate both binders with the same gensym |
112 | 116 | (define (bind! identifier-a identifier-b) |
113 | 117 | (define x (gensym)) |
114 | | - (free-id-table-set! table-a identifier-a x) |
115 | | - (free-id-table-set! table-b identifier-b x)) |
116 | | - (define (bound-reference=? identifier-a identifier-b) |
117 | | - (and (dict-has-key? table-a identifier-a) |
118 | | - (dict-has-key? table-b identifier-b) |
119 | | - (eq? (free-id-table-ref table-a identifier-a) |
120 | | - (free-id-table-ref table-b identifier-b)))) |
| 118 | + (free-id-table-set! table-a (compiled-from identifier-a) x) |
| 119 | + (free-id-table-set! table-b (compiled-from identifier-b) x)) |
121 | 120 | (define binders-a (binding-identifiers stx-a #:allow-host? allow-host?)) |
122 | 121 | (define binders-b (binding-identifiers stx-b #:allow-host? allow-host?)) |
123 | 122 | ; must traverse binders before references |
|
127 | 126 | [binder-b binders-b]) |
128 | 127 | (bind! binder-a binder-b)) |
129 | 128 | (and (= (length binders-a) (length binders-b)) |
130 | | - bound-reference=?)) |
| 129 | + (substitutions->bound-reference=? table-a table-b))) |
| 130 | + |
| 131 | +;; FreeIdTable FreeIdTable -> (Identifier Identifier -> Boolean) |
| 132 | +;; Do these two references refer to corresponding binders? |
| 133 | +;; table-a and table-b should map corresponding binders to the same, unique value |
| 134 | +(define ((substitutions->bound-reference=? table-a table-b) identifier-a identifier-b) |
| 135 | + (and (dict-has-key? table-a (compiled-from identifier-a)) |
| 136 | + (dict-has-key? table-b (compiled-from identifier-b)) |
| 137 | + (eq? (free-id-table-ref table-a (compiled-from identifier-a)) |
| 138 | + (free-id-table-ref table-b (compiled-from identifier-b))))) |
131 | 139 |
|
132 | 140 | ; Syntax Syntax (Identifier Identifier -> Boolean) Boolean -> Boolean |
133 | 141 | ; check that the references are alpha-equivalent. |
|
159 | 167 | [(a b) (equal? (syntax->datum #'a) |
160 | 168 | (syntax->datum #'b))]))) |
161 | 169 |
|
| 170 | +;; Syntax Syntax Syntax -> Syntax |
| 171 | +;; Replace all occurrences of target (by alpha equivalence) with replacement in stx. |
| 172 | +;; Leaves host expressions unchanged. |
| 173 | +(define (subst stx target replacement) |
| 174 | + (let loop ([stx stx]) |
| 175 | + (if (if (compiled-binder? target) |
| 176 | + (and (compiled-reference? stx) (identifier=? stx target)) |
| 177 | + (alpha-equivalent? stx target)) |
| 178 | + replacement |
| 179 | + (syntax-parse stx |
| 180 | + ;; ignore host expressions |
| 181 | + [((~literal #%host-expression) . _) this-syntax] |
| 182 | + [(a . b) |
| 183 | + (quasisyntax/loc this-syntax (#,(loop #'a) . #,(loop #'b)))] |
| 184 | + [_ stx])))) |
| 185 | + |
162 | 186 | (define current-referenced-vars (make-parameter #f)) |
163 | 187 |
|
164 | 188 | ; get the racket vars referenced in e of the provided binding classes |
|
174 | 198 | 'expression |
175 | 199 | '()) |
176 | 200 |
|
177 | | - (sequence->list (in-symbol-set (for/fold ([references (immutable-symbol-set)]) |
178 | | - ([x (in-symbol-set (current-referenced-vars))]) |
179 | | - (symbol-set-add references x)))))) |
| 201 | + (sequence->list (in-symbol-set (current-referenced-vars))))) |
180 | 202 |
|
181 | 203 | (define recording-reference-compiler |
182 | 204 | (make-variable-like-reference-compiler |
|
0 commit comments