diff options
Diffstat (limited to 'crates/parser/src/grammar/patterns.rs')
-rw-r--r-- | crates/parser/src/grammar/patterns.rs | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs new file mode 100644 index 000000000..07b1d6dd5 --- /dev/null +++ b/crates/parser/src/grammar/patterns.rs | |||
@@ -0,0 +1,379 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use super::*; | ||
4 | |||
5 | pub(super) const PATTERN_FIRST: TokenSet = expressions::LITERAL_FIRST | ||
6 | .union(paths::PATH_FIRST) | ||
7 | .union(token_set![T![box], T![ref], T![mut], T!['('], T!['['], T![&], T![_], T![-], T![.]]); | ||
8 | |||
9 | pub(crate) fn pattern(p: &mut Parser) { | ||
10 | pattern_r(p, PAT_RECOVERY_SET); | ||
11 | } | ||
12 | |||
13 | /// Parses a pattern list separated by pipes `|` | ||
14 | pub(super) fn pattern_top(p: &mut Parser) { | ||
15 | pattern_top_r(p, PAT_RECOVERY_SET) | ||
16 | } | ||
17 | |||
18 | pub(crate) fn pattern_single(p: &mut Parser) { | ||
19 | pattern_single_r(p, PAT_RECOVERY_SET); | ||
20 | } | ||
21 | |||
22 | /// Parses a pattern list separated by pipes `|` | ||
23 | /// using the given `recovery_set` | ||
24 | pub(super) fn pattern_top_r(p: &mut Parser, recovery_set: TokenSet) { | ||
25 | p.eat(T![|]); | ||
26 | pattern_r(p, recovery_set); | ||
27 | } | ||
28 | |||
29 | /// Parses a pattern list separated by pipes `|`, with no leading `|`,using the | ||
30 | /// given `recovery_set` | ||
31 | // test or_pattern | ||
32 | // fn main() { | ||
33 | // match () { | ||
34 | // (_ | _) => (), | ||
35 | // &(_ | _) => (), | ||
36 | // (_ | _,) => (), | ||
37 | // [_ | _,] => (), | ||
38 | // } | ||
39 | // } | ||
40 | fn pattern_r(p: &mut Parser, recovery_set: TokenSet) { | ||
41 | let m = p.start(); | ||
42 | pattern_single_r(p, recovery_set); | ||
43 | |||
44 | if !p.at(T![|]) { | ||
45 | m.abandon(p); | ||
46 | return; | ||
47 | } | ||
48 | while p.eat(T![|]) { | ||
49 | pattern_single_r(p, recovery_set); | ||
50 | } | ||
51 | m.complete(p, OR_PAT); | ||
52 | } | ||
53 | |||
54 | fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) { | ||
55 | if let Some(lhs) = atom_pat(p, recovery_set) { | ||
56 | // test range_pat | ||
57 | // fn main() { | ||
58 | // match 92 { | ||
59 | // 0 ... 100 => (), | ||
60 | // 101 ..= 200 => (), | ||
61 | // 200 .. 301=> (), | ||
62 | // } | ||
63 | // } | ||
64 | for &range_op in [T![...], T![..=], T![..]].iter() { | ||
65 | if p.at(range_op) { | ||
66 | let m = lhs.precede(p); | ||
67 | p.bump(range_op); | ||
68 | atom_pat(p, recovery_set); | ||
69 | m.complete(p, RANGE_PAT); | ||
70 | return; | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | |||
76 | const PAT_RECOVERY_SET: TokenSet = | ||
77 | token_set![LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA]; | ||
78 | |||
79 | fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { | ||
80 | let m = match p.nth(0) { | ||
81 | T![box] => box_pat(p), | ||
82 | T![ref] | T![mut] => ident_pat(p, true), | ||
83 | IDENT => match p.nth(1) { | ||
84 | // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro | ||
85 | // (T![x]). | ||
86 | T!['('] | T!['{'] | T![!] => path_or_macro_pat(p), | ||
87 | T![:] if p.nth_at(1, T![::]) => path_or_macro_pat(p), | ||
88 | _ => ident_pat(p, true), | ||
89 | }, | ||
90 | |||
91 | // test type_path_in_pattern | ||
92 | // fn main() { let <_>::Foo = (); } | ||
93 | _ if paths::is_path_start(p) => path_or_macro_pat(p), | ||
94 | _ if is_literal_pat_start(p) => literal_pat(p), | ||
95 | |||
96 | T![.] if p.at(T![..]) => rest_pat(p), | ||
97 | T![_] => wildcard_pat(p), | ||
98 | T![&] => ref_pat(p), | ||
99 | T!['('] => tuple_pat(p), | ||
100 | T!['['] => slice_pat(p), | ||
101 | |||
102 | _ => { | ||
103 | p.err_recover("expected pattern", recovery_set); | ||
104 | return None; | ||
105 | } | ||
106 | }; | ||
107 | |||
108 | Some(m) | ||
109 | } | ||
110 | |||
111 | fn is_literal_pat_start(p: &Parser) -> bool { | ||
112 | p.at(T![-]) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER) | ||
113 | || p.at_ts(expressions::LITERAL_FIRST) | ||
114 | } | ||
115 | |||
116 | // test literal_pattern | ||
117 | // fn main() { | ||
118 | // match () { | ||
119 | // -1 => (), | ||
120 | // 92 => (), | ||
121 | // 'c' => (), | ||
122 | // "hello" => (), | ||
123 | // } | ||
124 | // } | ||
125 | fn literal_pat(p: &mut Parser) -> CompletedMarker { | ||
126 | assert!(is_literal_pat_start(p)); | ||
127 | let m = p.start(); | ||
128 | if p.at(T![-]) { | ||
129 | p.bump(T![-]); | ||
130 | } | ||
131 | expressions::literal(p); | ||
132 | m.complete(p, LITERAL_PAT) | ||
133 | } | ||
134 | |||
135 | // test path_part | ||
136 | // fn foo() { | ||
137 | // let foo::Bar = (); | ||
138 | // let ::Bar = (); | ||
139 | // let Bar { .. } = (); | ||
140 | // let Bar(..) = (); | ||
141 | // } | ||
142 | fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker { | ||
143 | assert!(paths::is_path_start(p)); | ||
144 | let m = p.start(); | ||
145 | paths::expr_path(p); | ||
146 | let kind = match p.current() { | ||
147 | T!['('] => { | ||
148 | tuple_pat_fields(p); | ||
149 | TUPLE_STRUCT_PAT | ||
150 | } | ||
151 | T!['{'] => { | ||
152 | record_pat_field_list(p); | ||
153 | RECORD_PAT | ||
154 | } | ||
155 | // test marco_pat | ||
156 | // fn main() { | ||
157 | // let m!(x) = 0; | ||
158 | // } | ||
159 | T![!] => { | ||
160 | items::macro_call_after_excl(p); | ||
161 | return m.complete(p, MACRO_CALL).precede(p).complete(p, MACRO_PAT); | ||
162 | } | ||
163 | _ => PATH_PAT, | ||
164 | }; | ||
165 | m.complete(p, kind) | ||
166 | } | ||
167 | |||
168 | // test tuple_pat_fields | ||
169 | // fn foo() { | ||
170 | // let S() = (); | ||
171 | // let S(_) = (); | ||
172 | // let S(_,) = (); | ||
173 | // let S(_, .. , x) = (); | ||
174 | // } | ||
175 | fn tuple_pat_fields(p: &mut Parser) { | ||
176 | assert!(p.at(T!['('])); | ||
177 | p.bump(T!['(']); | ||
178 | pat_list(p, T![')']); | ||
179 | p.expect(T![')']); | ||
180 | } | ||
181 | |||
182 | // test record_field_pat_list | ||
183 | // fn foo() { | ||
184 | // let S {} = (); | ||
185 | // let S { f, ref mut g } = (); | ||
186 | // let S { h: _, ..} = (); | ||
187 | // let S { h: _, } = (); | ||
188 | // } | ||
189 | fn record_pat_field_list(p: &mut Parser) { | ||
190 | assert!(p.at(T!['{'])); | ||
191 | let m = p.start(); | ||
192 | p.bump(T!['{']); | ||
193 | while !p.at(EOF) && !p.at(T!['}']) { | ||
194 | match p.current() { | ||
195 | // A trailing `..` is *not* treated as a REST_PAT. | ||
196 | T![.] if p.at(T![..]) => p.bump(T![..]), | ||
197 | T!['{'] => error_block(p, "expected ident"), | ||
198 | |||
199 | c => { | ||
200 | let m = p.start(); | ||
201 | match c { | ||
202 | // test record_field_pat | ||
203 | // fn foo() { | ||
204 | // let S { 0: 1 } = (); | ||
205 | // let S { x: 1 } = (); | ||
206 | // } | ||
207 | IDENT | INT_NUMBER if p.nth(1) == T![:] => { | ||
208 | name_ref_or_index(p); | ||
209 | p.bump(T![:]); | ||
210 | pattern(p); | ||
211 | } | ||
212 | T![box] => { | ||
213 | // FIXME: not all box patterns should be allowed | ||
214 | box_pat(p); | ||
215 | } | ||
216 | _ => { | ||
217 | ident_pat(p, false); | ||
218 | } | ||
219 | } | ||
220 | m.complete(p, RECORD_PAT_FIELD); | ||
221 | } | ||
222 | } | ||
223 | if !p.at(T!['}']) { | ||
224 | p.expect(T![,]); | ||
225 | } | ||
226 | } | ||
227 | p.expect(T!['}']); | ||
228 | m.complete(p, RECORD_PAT_FIELD_LIST); | ||
229 | } | ||
230 | |||
231 | // test placeholder_pat | ||
232 | // fn main() { let _ = (); } | ||
233 | fn wildcard_pat(p: &mut Parser) -> CompletedMarker { | ||
234 | assert!(p.at(T![_])); | ||
235 | let m = p.start(); | ||
236 | p.bump(T![_]); | ||
237 | m.complete(p, WILDCARD_PAT) | ||
238 | } | ||
239 | |||
240 | // test dot_dot_pat | ||
241 | // fn main() { | ||
242 | // let .. = (); | ||
243 | // // | ||
244 | // // Tuples | ||
245 | // // | ||
246 | // let (a, ..) = (); | ||
247 | // let (a, ..,) = (); | ||
248 | // let Tuple(a, ..) = (); | ||
249 | // let Tuple(a, ..,) = (); | ||
250 | // let (.., ..) = (); | ||
251 | // let Tuple(.., ..) = (); | ||
252 | // let (.., a, ..) = (); | ||
253 | // let Tuple(.., a, ..) = (); | ||
254 | // // | ||
255 | // // Slices | ||
256 | // // | ||
257 | // let [..] = (); | ||
258 | // let [head, ..] = (); | ||
259 | // let [head, tail @ ..] = (); | ||
260 | // let [head, .., cons] = (); | ||
261 | // let [head, mid @ .., cons] = (); | ||
262 | // let [head, .., .., cons] = (); | ||
263 | // let [head, .., mid, tail @ ..] = (); | ||
264 | // let [head, .., mid, .., cons] = (); | ||
265 | // } | ||
266 | fn rest_pat(p: &mut Parser) -> CompletedMarker { | ||
267 | assert!(p.at(T![..])); | ||
268 | let m = p.start(); | ||
269 | p.bump(T![..]); | ||
270 | m.complete(p, REST_PAT) | ||
271 | } | ||
272 | |||
273 | // test ref_pat | ||
274 | // fn main() { | ||
275 | // let &a = (); | ||
276 | // let &mut b = (); | ||
277 | // } | ||
278 | fn ref_pat(p: &mut Parser) -> CompletedMarker { | ||
279 | assert!(p.at(T![&])); | ||
280 | let m = p.start(); | ||
281 | p.bump(T![&]); | ||
282 | p.eat(T![mut]); | ||
283 | pattern_single(p); | ||
284 | m.complete(p, REF_PAT) | ||
285 | } | ||
286 | |||
287 | // test tuple_pat | ||
288 | // fn main() { | ||
289 | // let (a, b, ..) = (); | ||
290 | // let (a,) = (); | ||
291 | // let (..) = (); | ||
292 | // let () = (); | ||
293 | // } | ||
294 | fn tuple_pat(p: &mut Parser) -> CompletedMarker { | ||
295 | assert!(p.at(T!['('])); | ||
296 | let m = p.start(); | ||
297 | p.bump(T!['(']); | ||
298 | let mut has_comma = false; | ||
299 | let mut has_pat = false; | ||
300 | let mut has_rest = false; | ||
301 | while !p.at(EOF) && !p.at(T![')']) { | ||
302 | has_pat = true; | ||
303 | if !p.at_ts(PATTERN_FIRST) { | ||
304 | p.error("expected a pattern"); | ||
305 | break; | ||
306 | } | ||
307 | has_rest |= p.at(T![..]); | ||
308 | |||
309 | pattern(p); | ||
310 | if !p.at(T![')']) { | ||
311 | has_comma = true; | ||
312 | p.expect(T![,]); | ||
313 | } | ||
314 | } | ||
315 | p.expect(T![')']); | ||
316 | |||
317 | m.complete(p, if !has_comma && !has_rest && has_pat { PAREN_PAT } else { TUPLE_PAT }) | ||
318 | } | ||
319 | |||
320 | // test slice_pat | ||
321 | // fn main() { | ||
322 | // let [a, b, ..] = []; | ||
323 | // } | ||
324 | fn slice_pat(p: &mut Parser) -> CompletedMarker { | ||
325 | assert!(p.at(T!['['])); | ||
326 | let m = p.start(); | ||
327 | p.bump(T!['[']); | ||
328 | pat_list(p, T![']']); | ||
329 | p.expect(T![']']); | ||
330 | m.complete(p, SLICE_PAT) | ||
331 | } | ||
332 | |||
333 | fn pat_list(p: &mut Parser, ket: SyntaxKind) { | ||
334 | while !p.at(EOF) && !p.at(ket) { | ||
335 | if !p.at_ts(PATTERN_FIRST) { | ||
336 | p.error("expected a pattern"); | ||
337 | break; | ||
338 | } | ||
339 | |||
340 | pattern(p); | ||
341 | if !p.at(ket) { | ||
342 | p.expect(T![,]); | ||
343 | } | ||
344 | } | ||
345 | } | ||
346 | |||
347 | // test bind_pat | ||
348 | // fn main() { | ||
349 | // let a = (); | ||
350 | // let mut b = (); | ||
351 | // let ref c = (); | ||
352 | // let ref mut d = (); | ||
353 | // let e @ _ = (); | ||
354 | // let ref mut f @ g @ _ = (); | ||
355 | // } | ||
356 | fn ident_pat(p: &mut Parser, with_at: bool) -> CompletedMarker { | ||
357 | let m = p.start(); | ||
358 | p.eat(T![ref]); | ||
359 | p.eat(T![mut]); | ||
360 | name(p); | ||
361 | if with_at && p.eat(T![@]) { | ||
362 | pattern_single(p); | ||
363 | } | ||
364 | m.complete(p, IDENT_PAT) | ||
365 | } | ||
366 | |||
367 | // test box_pat | ||
368 | // fn main() { | ||
369 | // let box i = (); | ||
370 | // let box Outer { box i, j: box Inner(box &x) } = (); | ||
371 | // let box ref mut i = (); | ||
372 | // } | ||
373 | fn box_pat(p: &mut Parser) -> CompletedMarker { | ||
374 | assert!(p.at(T![box])); | ||
375 | let m = p.start(); | ||
376 | p.bump(T![box]); | ||
377 | pattern_single(p); | ||
378 | m.complete(p, BOX_PAT) | ||
379 | } | ||