aboutsummaryrefslogtreecommitdiff
path: root/crates/parser/src/grammar/patterns.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/parser/src/grammar/patterns.rs')
-rw-r--r--crates/parser/src/grammar/patterns.rs379
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..716bdc978
--- /dev/null
+++ b/crates/parser/src/grammar/patterns.rs
@@ -0,0 +1,379 @@
1//! FIXME: write short doc here
2
3use super::*;
4
5pub(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
9pub(crate) fn pattern(p: &mut Parser) {
10 pattern_r(p, PAT_RECOVERY_SET);
11}
12
13/// Parses a pattern list separated by pipes `|`
14pub(super) fn pattern_top(p: &mut Parser) {
15 pattern_top_r(p, PAT_RECOVERY_SET)
16}
17
18pub(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`
24pub(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// }
40fn 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
54fn 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
76const PAT_RECOVERY_SET: TokenSet =
77 token_set![LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA];
78
79fn 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] => bind_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 _ => bind_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![..]) => dot_dot_pat(p),
97 T![_] => placeholder_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
111fn 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// }
125fn 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// }
142fn 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_field_pat_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// }
175fn 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// }
189fn record_field_pat_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 bind_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 _ = (); }
233fn placeholder_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// }
266fn dot_dot_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// }
278fn 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// }
294fn 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// }
324fn 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
333fn 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// }
356fn bind_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// }
373fn 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}