|
8 | 8 | expression-directly-enclosing |
9 | 9 | syntax-find-first |
10 | 10 | syntax-search |
| 11 | + syntax-search-everything |
11 | 12 | syntax-traverse) |
12 | 13 |
|
13 | 14 |
|
14 | 15 | (require (for-syntax racket/base |
15 | | - resyntax/private/more-syntax-parse-classes |
16 | | - syntax/parse) |
17 | | - racket/match |
| 16 | + resyntax/private/more-syntax-parse-classes) |
18 | 17 | racket/sequence |
19 | 18 | racket/stream |
20 | 19 | syntax/parse |
|
55 | 54 |
|
56 | 55 |
|
57 | 56 | (define-syntax-parse-rule |
58 | | - (syntax-search stx-expr option:syntax-parse-option ... clause:syntax-search-clause ...) |
| 57 | + (syntax-search stx-expr |
| 58 | + (~optional (~seq #:skip-root? skip-root?) #:defaults ([skip-root? #'#false])) |
| 59 | + option:syntax-parse-option ... |
| 60 | + clause:syntax-search-clause ...) |
59 | 61 | #:declare stx-expr (expr/c #'syntax?) |
60 | | - (let () |
| 62 | + (let ([skip-root-id skip-root?]) |
61 | 63 | (define-syntax-class search-case |
62 | 64 | #:attributes (output-stream) |
63 | 65 | (~@ . option) ... |
64 | 66 | (pattern clause.syntax-pattern (~@ . clause.directive) ... |
65 | 67 | #:attr output-stream clause.output-stream) |
66 | 68 | ...) |
67 | | - (let loop ([stx stx-expr.c]) |
| 69 | + (let loop ([stx stx-expr.c] [root? #true]) |
68 | 70 | (stream-lazy |
69 | 71 | (syntax-parse stx |
70 | | - [(~var matched search-case) (attribute matched.output-stream)] |
| 72 | + [child |
| 73 | + #:when (not (and skip-root-id root?)) |
| 74 | + #:with (~var matched search-case) (attribute child) |
| 75 | + (attribute matched.output-stream)] |
71 | 76 | [(part (... ...)) |
72 | 77 | #:cut |
73 | 78 | (apply stream-append |
74 | 79 | (for/list ([part-stx (in-list (attribute part))]) |
75 | | - (loop part-stx)))] |
| 80 | + (loop part-stx #false)))] |
76 | 81 | [(part (... ...+) . tail-part) |
77 | 82 | #:cut |
78 | 83 | (stream-append (apply stream-append |
79 | 84 | (for/list ([part-stx (in-list (attribute part))]) |
80 | | - (loop part-stx))) |
81 | | - (loop #'tail-part))] |
| 85 | + (loop part-stx #false))) |
| 86 | + (loop #'tail-part #false))] |
82 | 87 | [_ (stream)]))))) |
83 | 88 |
|
84 | 89 |
|
| 90 | +(define (syntax-search-everything stx) |
| 91 | + (stream-cons stx (syntax-search stx #:skip-root? #true [_ (syntax-search-everything this-syntax)]))) |
| 92 | + |
| 93 | + |
85 | 94 | (module+ test |
86 | 95 | (test-case "syntax-search" |
87 | 96 | (define stx |
|
100 | 109 |
|
101 | 110 |
|
102 | 111 | (define-syntax-parse-rule (syntax-find-first stx-expr |
103 | | - option:syntax-parse-option ... |
104 | | - syntax-pattern |
105 | | - directive:syntax-parse-pattern-directive ...) |
| 112 | + option:syntax-parse-option ... |
| 113 | + syntax-pattern |
| 114 | + directive:syntax-parse-pattern-directive ...) |
106 | 115 | (let () |
107 | 116 | (define results (syntax-search stx-expr (~@ . option) ... [syntax-pattern (~@ . directive) ...])) |
108 | 117 | (and (not (stream-empty? results)) (stream-first results)))) |
|
123 | 132 |
|
124 | 133 | (define-syntax-parse-rule |
125 | 134 | (syntax-traverse (~var stx-expr (expr/c #'syntax?)) |
| 135 | + (~optional (~seq #:skip-root? skip-root?) #:defaults ([skip-root? #'#false])) |
126 | 136 | option:syntax-parse-option ... |
127 | 137 | [clause-pattern directive:syntax-parse-pattern-directive ... clause-body:expr ...+] ...) |
128 | | - (let () |
| 138 | + (let ([skip-root-id skip-root?]) |
129 | 139 | (define-syntax-class traversal-case |
130 | 140 | #:attributes (traversed) |
131 | 141 | (~@ . option) ... |
132 | 142 | (pattern clause-pattern (~@ . directive) ... |
133 | 143 | #:attr traversed (let () clause-body ...)) ...) |
134 | | - (let loop ([stx stx-expr.c]) |
| 144 | + (let loop ([stx stx-expr.c] [root? #true]) |
| 145 | + |
| 146 | + (define (rewrap-datum datum) |
| 147 | + (datum->syntax stx datum stx stx)) |
| 148 | + |
135 | 149 | (syntax-parse stx |
136 | | - [(~var matched traversal-case) (attribute matched.traversed)] |
| 150 | + |
| 151 | + [child |
| 152 | + #:when (not (and skip-root-id root?)) |
| 153 | + #:with (~var matched traversal-case) (attribute child) |
| 154 | + (define case-scope (make-syntax-introducer)) |
| 155 | + (case-scope (attribute matched.traversed) 'add)] |
137 | 156 |
|
138 | 157 | [(part (... ...)) |
139 | 158 | #:cut |
140 | | - #:with (traversed-part (... ...)) (map loop (attribute part)) |
141 | | - #'(traversed-part (... ...))] |
| 159 | + (rewrap-datum |
| 160 | + (for/list ([child (in-list (attribute part))]) |
| 161 | + (loop child #false)))] |
142 | 162 | [(part (... ...+) . tail-part) |
143 | 163 | #:cut |
144 | | - #:with (traversed-part (... ...)) (map loop (attribute part)) |
145 | | - #:with traversed-tail (loop #'tail-part) |
146 | | - #'(traversed-part (... ...) . traversed-tail)] |
| 164 | + (define traversed-children |
| 165 | + (for/list ([child (in-list (attribute part))]) |
| 166 | + (loop child #false))) |
| 167 | + (define traversed-tail (loop #'tail-part #false)) |
| 168 | + (rewrap-datum (append traversed-children traversed-tail))] |
147 | 169 | [_ stx])))) |
148 | 170 |
|
149 | 171 |
|
|
156 | 178 | (cons a b)) |
157 | 179 | (cons c d))) |
158 | 180 | (define actual |
159 | | - (syntax->datum |
160 | | - (syntax-traverse stx |
161 | | - #:literals (cons) |
162 | | - [(cons _ _) #'CONS-EXPRESSION]))) |
| 181 | + (syntax-traverse stx |
| 182 | + #:literals (cons) |
| 183 | + [(cons _ _) #'CONS-EXPRESSION])) |
163 | 184 | (define expected |
164 | 185 | '(define (foo) |
165 | 186 | CONS-EXPRESSION |
166 | 187 | (define (bar) |
167 | 188 | CONS-EXPRESSION) |
168 | 189 | CONS-EXPRESSION)) |
169 | | - (check-equal? actual expected))) |
| 190 | + (check-equal? (syntax->datum actual) expected)) |
| 191 | + |
| 192 | + (test-case "syntax-traverse #:skip-root? true" |
| 193 | + (define stx #'(a b (c d) e)) |
| 194 | + (define actual |
| 195 | + (syntax-traverse stx |
| 196 | + #:skip-root? #true |
| 197 | + [(_ ...) #'LIST])) |
| 198 | + (check-equal? (syntax->datum actual) '(a b LIST e))) |
| 199 | + |
| 200 | + (test-case "syntax-traverse #:skip-root? true doesn't execute directives on root" |
| 201 | + (define stx #'(a b (c d) e)) |
| 202 | + (define execution-count 0) |
| 203 | + (syntax-traverse stx |
| 204 | + #:skip-root? #true |
| 205 | + [(_ ...) |
| 206 | + #:do [(set! execution-count (add1 execution-count))] |
| 207 | + #'LIST]) |
| 208 | + (check-equal? execution-count 1)) |
| 209 | + |
| 210 | + (test-case "syntax-traverse #:skip-root? false" |
| 211 | + (define stx #'(a b (c d) e)) |
| 212 | + (define actual |
| 213 | + (syntax-traverse stx |
| 214 | + #:skip-root? #false |
| 215 | + [(_ ...) #'LIST])) |
| 216 | + (check-equal? (syntax->datum actual) 'LIST)) |
| 217 | + |
| 218 | + (test-case "syntax-traverse originality" |
| 219 | + (define stx (read-syntax #false (open-input-string "(1 2 (a b) 3 4)"))) |
| 220 | + (check-true (syntax-original? stx)) |
| 221 | + (define traversed-stx |
| 222 | + (syntax-traverse stx |
| 223 | + [(_ id:id) (attribute id)])) |
| 224 | + (check-equal? (syntax->datum traversed-stx) '(1 2 b 3 4)) |
| 225 | + (check-true (syntax-original? traversed-stx)) |
| 226 | + (define/syntax-parse (1* 2* b* 3* 4*) traversed-stx) |
| 227 | + (check-true (syntax-original? #'1*)) |
| 228 | + (check-true (syntax-original? #'2*)) |
| 229 | + (check-false (syntax-original? #'b*)) |
| 230 | + (check-true (syntax-original? #'3*)) |
| 231 | + (check-true (syntax-original? #'4*)))) |
0 commit comments