|
| 1 | +#lang racket/base |
| 2 | + |
| 3 | + |
| 4 | +(require racket/contract/base) |
| 5 | + |
| 6 | + |
| 7 | +(provide |
| 8 | + (contract-out |
| 9 | + ; Returns a map of sorted sets of syntax paths. Unfortunately I haven't implemented sorted map and |
| 10 | + ; set contracts. |
| 11 | + [syntax-movement-table (-> syntax? immutable-sorted-map?)])) |
| 12 | + |
| 13 | + |
| 14 | +(require racket/stream |
| 15 | + rebellion/collection/entry |
| 16 | + rebellion/collection/sorted-map |
| 17 | + rebellion/collection/sorted-set |
| 18 | + rebellion/streaming/transducer |
| 19 | + resyntax/private/syntax-neighbors |
| 20 | + resyntax/private/syntax-path |
| 21 | + resyntax/private/syntax-traversal |
| 22 | + syntax/parse) |
| 23 | + |
| 24 | + |
| 25 | +(module+ test |
| 26 | + (require (submod "..") |
| 27 | + rackunit)) |
| 28 | + |
| 29 | + |
| 30 | +;@---------------------------------------------------------------------------------------------------- |
| 31 | + |
| 32 | + |
| 33 | +; Traverses a syntax object `result-stx`, searching for all syntax objects that have a |
| 34 | +; syntax-original-path, then returns a table mapping each original path to the set of paths in |
| 35 | +; `result-stx` that corresponded to that original path. If `result-stx` is fully expanded syntax, this |
| 36 | +; amounts to returning a table mapping each (path-based) position in the original unexpanded syntax to |
| 37 | +; the set of positions in the fully expanded syntax which that original syntax expanded into. Note |
| 38 | +; that each input path maps to a set of output paths instead of a single path because macros can |
| 39 | +; duplicate forms. The returned table is in the form of an immutable sorted map of syntax paths to |
| 40 | +; sorted sets of syntax paths. |
| 41 | +(define (syntax-movement-table result-stx) |
| 42 | + |
| 43 | + (define (search parent-stx [include-self? #true]) |
| 44 | + (syntax-search parent-stx |
| 45 | + [child |
| 46 | + #:do [(define child-stx (attribute child))] |
| 47 | + #:when (syntax-original-path child-stx) |
| 48 | + #:when (or include-self? (not (equal? child-stx parent-stx))) |
| 49 | + (stream-cons child-stx (search child-stx #false))])) |
| 50 | + |
| 51 | + (transduce (search (syntax-label-paths result-stx 'final-syntax-path)) |
| 52 | + (bisecting syntax-original-path |
| 53 | + (λ (stx) (syntax-property stx 'final-syntax-path))) |
| 54 | + (grouping (into-sorted-set syntax-path<=>)) |
| 55 | + #:into (into-sorted-map syntax-path<=>))) |
| 56 | + |
| 57 | + |
| 58 | +(module+ test |
| 59 | + (test-case "syntax-movement-table smoke test" |
| 60 | + (define orig-stx |
| 61 | + (syntax-label-original-paths |
| 62 | + #'(module foo racket/base |
| 63 | + (void)))) |
| 64 | + (define expanded-stx (expand orig-stx)) |
| 65 | + |
| 66 | + (define table (syntax-movement-table expanded-stx)) |
| 67 | + |
| 68 | + (define expected-table |
| 69 | + (sorted-map |
| 70 | + #:key-comparator syntax-path<=> |
| 71 | + |
| 72 | + ; (module ...) |
| 73 | + empty-syntax-path |
| 74 | + (sorted-set empty-syntax-path (syntax-path (list 3)) #:comparator syntax-path<=>) |
| 75 | + |
| 76 | + ; module |
| 77 | + (syntax-path (list 0)) |
| 78 | + (sorted-set (syntax-path (list 0)) #:comparator syntax-path<=>) |
| 79 | + |
| 80 | + ; foo |
| 81 | + (syntax-path (list 1)) |
| 82 | + (sorted-set (syntax-path (list 1)) #:comparator syntax-path<=>) |
| 83 | + |
| 84 | + ; racket/base |
| 85 | + (syntax-path (list 2)) |
| 86 | + (sorted-set (syntax-path (list 2)) #:comparator syntax-path<=>) |
| 87 | + |
| 88 | + ; (void) |
| 89 | + (syntax-path (list 3)) |
| 90 | + (sorted-set (syntax-path (list 3 2)) #:comparator syntax-path<=>) |
| 91 | + |
| 92 | + ; void |
| 93 | + (syntax-path (list 3 0)) |
| 94 | + (sorted-set (syntax-path (list 3 2 (tail-syntax 1) 0)) #:comparator syntax-path<=>))) |
| 95 | + |
| 96 | + (check-equal? table expected-table))) |
0 commit comments