|
33 | 33 |
|
34 | 34 |
|
35 | 35 | (require guard |
36 | | - racket/file |
37 | | - racket/hash |
38 | 36 | racket/match |
39 | 37 | racket/path |
40 | 38 | racket/port |
| 39 | + racket/stream |
41 | 40 | rebellion/base/comparator |
42 | 41 | rebellion/base/immutable-string |
| 42 | + rebellion/base/option |
43 | 43 | rebellion/base/range |
44 | 44 | rebellion/collection/list |
45 | 45 | rebellion/collection/range-set |
| 46 | + rebellion/collection/sorted-map |
46 | 47 | rebellion/collection/vector/builder |
47 | 48 | rebellion/streaming/reducer |
48 | 49 | rebellion/streaming/transducer |
49 | 50 | rebellion/type/record |
50 | 51 | resyntax/private/fully-expanded-syntax |
51 | 52 | resyntax/private/linemap |
52 | 53 | resyntax/private/logger |
| 54 | + resyntax/private/syntax-movement |
53 | 55 | resyntax/private/syntax-neighbors |
54 | 56 | resyntax/private/syntax-path |
| 57 | + resyntax/private/syntax-traversal |
55 | 58 | syntax/id-table |
56 | 59 | syntax/modread |
57 | 60 | syntax/parse) |
|
137 | 140 | (define program-source-name (syntax-source program-stx)) |
138 | 141 | (define current-expand-observe (dynamic-require ''#%expobs 'current-expand-observe)) |
139 | 142 | (define original-visits (make-vector-builder)) |
140 | | - (define expanded-originals-by-path (make-hash)) |
141 | | - |
142 | | - (define (add-all-original-subforms! stx) |
143 | | - (when (resyntax-should-analyze-syntax? stx #:as-visit? #false) |
144 | | - (hash-set! expanded-originals-by-path (syntax-original-path stx) stx)) |
145 | | - (syntax-parse stx |
146 | | - [(subform ...) (for-each add-all-original-subforms! (attribute subform))] |
147 | | - [(subform ...+ . tail-form) |
148 | | - (for-each add-all-original-subforms! (attribute subform)) |
149 | | - (add-all-original-subforms! #'tail-form)] |
150 | | - [_ (void)])) |
151 | | - |
152 | | - (define (syntax-original-and-from-source? stx) |
153 | | - (and (syntax-original? stx) |
154 | | - ;; Some macros are able to bend hygiene and syntax properties in such a way that they |
155 | | - ;; introduce syntax objects into the program that are syntax-original?, but from a |
156 | | - ;; different file than the one being expanded. So in addition to checking for |
157 | | - ;; originality, we also check that they come from the same source as the main program |
158 | | - ;; syntax object. The (open ...) clause of the define-signature macro bends hygiene |
159 | | - ;; in this way, and is what originally motivated the addition of this check. |
160 | | - (equal? (syntax-source stx) program-source-name))) |
| 143 | + (define most-recent-visits-by-original-path (make-hash)) |
161 | 144 |
|
162 | 145 | (define/guard (resyntax-should-analyze-syntax? stx #:as-visit? [as-visit? #true]) |
163 | | - (guard (syntax-original-and-from-source? stx) #:else #false) |
| 146 | + (guard (syntax-original-and-from-source? stx program-source-name) #:else #false) |
164 | 147 | (guard as-visit? #:else #true) |
165 | 148 | (define stx-lines (syntax-line-range stx #:linemap code-linemap)) |
166 | 149 | (define overlaps? (range-set-overlaps? lines stx-lines)) |
|
178 | 161 | (define/match (observe-event! sig val) |
179 | 162 | [('visit (? syntax? visited)) |
180 | 163 | (when (resyntax-should-analyze-syntax? visited) |
181 | | - (vector-builder-add original-visits visited) |
182 | | - (add-all-original-subforms! visited))] |
| 164 | + (vector-builder-add original-visits visited)) |
| 165 | + (for ([visit-subform (in-stream (syntax-search-everything visited))] |
| 166 | + #:when (and (resyntax-should-analyze-syntax? visit-subform #:as-visit? #false) |
| 167 | + (syntax-has-original-path? visit-subform))) |
| 168 | + (define path (syntax-original-path visit-subform)) |
| 169 | + (hash-set! most-recent-visits-by-original-path path visit-subform))] |
183 | 170 | [(_ _) (void)]) |
184 | 171 |
|
185 | 172 | (define output-port (open-output-string)) |
|
195 | 182 | (namespace-require/expansion-time (extract-module-require-spec expanded)) |
196 | 183 |
|
197 | 184 | (define output (get-output-string output-port)) |
| 185 | + (define movement-table (syntax-movement-table expanded)) |
198 | 186 | (define binding-table (fully-expanded-syntax-binding-table expanded)) |
199 | 187 | (define original-binding-table-by-path |
200 | 188 | (for*/fold ([table (hash)]) |
201 | 189 | ([phase-table (in-hash-values binding-table)] |
202 | 190 | [(id uses) (in-free-id-table phase-table)] |
203 | | - #:when (syntax-original-and-from-source? id) |
| 191 | + #:when (syntax-original-and-from-source? id program-source-name) |
204 | 192 | [use (in-list uses)]) |
205 | 193 | (hash-update table (syntax-original-path id) (λ (previous) (cons use previous)) '()))) |
| 194 | + |
| 195 | + (define expanded-with-properties |
| 196 | + (syntax-traverse expanded |
| 197 | + [id:id |
| 198 | + #:do [(define path (syntax-original-path (attribute id)))] |
| 199 | + #:when path |
| 200 | + (define usages (hash-ref original-binding-table-by-path path '())) |
| 201 | + (syntax-property this-syntax 'identifier-usages usages)] |
| 202 | + #:parent-context-modifier values |
| 203 | + #:parent-srcloc-modifier values |
| 204 | + #:parent-props-modifier values)) |
206 | 205 |
|
207 | | - (add-all-original-subforms! expanded) |
208 | | - |
209 | | - (define/guard (add-usages stx) |
210 | | - (guard (identifier? stx) #:else stx) |
211 | | - (define usages (hash-ref original-binding-table-by-path (syntax-original-path stx) '())) |
212 | | - (syntax-property stx 'identifier-usages usages)) |
213 | | - |
214 | | - (define (enrich stx) |
215 | | - (define new-context |
216 | | - (add-usages |
217 | | - (or (hash-ref expanded-originals-by-path (syntax-original-path stx) #false) stx))) |
218 | | - (syntax-parse stx |
219 | | - [(subform ...) |
220 | | - (datum->syntax new-context |
221 | | - (map enrich (attribute subform)) |
222 | | - new-context |
223 | | - new-context)] |
224 | | - [(subform ...+ . tail-form) |
225 | | - (datum->syntax new-context |
226 | | - (append (map enrich (attribute subform)) (enrich #'tail-form)) |
227 | | - new-context |
228 | | - new-context)] |
229 | | - [_ new-context])) |
230 | | - |
| 206 | + (define (enrich stx #:skip-root? [skip-root? #false]) |
| 207 | + (syntax-traverse stx |
| 208 | + #:skip-root? skip-root? |
| 209 | + [child |
| 210 | + #:do [(define child-stx (attribute child)) |
| 211 | + (define orig-path (syntax-original-path child-stx))] |
| 212 | + #:when (and orig-path (sorted-map-contains-key? movement-table orig-path)) |
| 213 | + #:do [(define expansion |
| 214 | + (transduce (sorted-map-get movement-table orig-path) |
| 215 | + (mapping (λ (p) (syntax-ref expanded-with-properties p))) |
| 216 | + (filtering syntax-original?) |
| 217 | + #:into into-first))] |
| 218 | + #:when (present? expansion) |
| 219 | + (match-define (present expanded-child) expansion) |
| 220 | + (log-resyntax-debug "enriching ~a with scopes and properties from expansion" child-stx) |
| 221 | + (enrich (datum->syntax expanded-child (syntax-e child-stx) child-stx expanded-child) |
| 222 | + #:skip-root? #true)] |
| 223 | + [child |
| 224 | + #:do [(define child-stx (attribute child)) |
| 225 | + (define orig-path (syntax-original-path child-stx))] |
| 226 | + #:when (and orig-path (hash-has-key? most-recent-visits-by-original-path orig-path)) |
| 227 | + #:do [(define visit (hash-ref most-recent-visits-by-original-path orig-path))] |
| 228 | + (log-resyntax-debug "enriching ~a with scopes from visit" child-stx) |
| 229 | + (enrich (datum->syntax visit (syntax-e child-stx) child-stx child-stx) #:skip-root? #true)] |
| 230 | + #:parent-context-modifier values |
| 231 | + #:parent-srcloc-modifier values |
| 232 | + #:parent-props-modifier values)) |
231 | 233 |
|
232 | 234 | (define visited |
233 | 235 | (transduce (build-vector original-visits) |
|
254 | 256 | #:namespace ns))) |
255 | 257 |
|
256 | 258 |
|
| 259 | +(define (syntax-original-and-from-source? stx source-name) |
| 260 | + (and (syntax-original? stx) |
| 261 | + ;; Some macros are able to bend hygiene and syntax properties in such a way that they |
| 262 | + ;; introduce syntax objects into the program that are syntax-original?, but from a |
| 263 | + ;; different file than the one being expanded. So in addition to checking for |
| 264 | + ;; originality, we also check that they come from the same source as the main program |
| 265 | + ;; syntax object. The (open ...) clause of the define-signature macro bends hygiene |
| 266 | + ;; in this way, and is what originally motivated the addition of this check. |
| 267 | + (equal? (syntax-source stx) source-name))) |
| 268 | + |
| 269 | + |
257 | 270 | (define (extract-module-require-spec mod-stx) |
258 | 271 | (syntax-parse mod-stx |
259 | 272 | [(_ name _ . _) `',(syntax-e #'name)])) |
0 commit comments