diff options
author | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
---|---|---|
committer | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
commit | 178c3e135a2a249692f7784712492e7884ae0c00 (patch) | |
tree | ac6b769dbf7162150caa0c1624786a4dd79ff3be /crates/parser/src/grammar/expressions/atom.rs | |
parent | 06ff8e6c760ff05f10e868b5d1f9d79e42fbb49c (diff) | |
parent | c2594daf2974dbd4ce3d9b7ec72481764abaceb5 (diff) |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'crates/parser/src/grammar/expressions/atom.rs')
-rw-r--r-- | crates/parser/src/grammar/expressions/atom.rs | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs new file mode 100644 index 000000000..ba6dd2fbc --- /dev/null +++ b/crates/parser/src/grammar/expressions/atom.rs | |||
@@ -0,0 +1,611 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use super::*; | ||
4 | |||
5 | // test expr_literals | ||
6 | // fn foo() { | ||
7 | // let _ = true; | ||
8 | // let _ = false; | ||
9 | // let _ = 1; | ||
10 | // let _ = 2.0; | ||
11 | // let _ = b'a'; | ||
12 | // let _ = 'b'; | ||
13 | // let _ = "c"; | ||
14 | // let _ = r"d"; | ||
15 | // let _ = b"e"; | ||
16 | // let _ = br"f"; | ||
17 | // } | ||
18 | pub(crate) const LITERAL_FIRST: TokenSet = token_set![ | ||
19 | TRUE_KW, | ||
20 | FALSE_KW, | ||
21 | INT_NUMBER, | ||
22 | FLOAT_NUMBER, | ||
23 | BYTE, | ||
24 | CHAR, | ||
25 | STRING, | ||
26 | RAW_STRING, | ||
27 | BYTE_STRING, | ||
28 | RAW_BYTE_STRING | ||
29 | ]; | ||
30 | |||
31 | pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> { | ||
32 | if !p.at_ts(LITERAL_FIRST) { | ||
33 | return None; | ||
34 | } | ||
35 | let m = p.start(); | ||
36 | p.bump_any(); | ||
37 | Some(m.complete(p, LITERAL)) | ||
38 | } | ||
39 | |||
40 | // E.g. for after the break in `if break {}`, this should not match | ||
41 | pub(super) const ATOM_EXPR_FIRST: TokenSet = | ||
42 | LITERAL_FIRST.union(paths::PATH_FIRST).union(token_set![ | ||
43 | T!['('], | ||
44 | T!['{'], | ||
45 | T!['['], | ||
46 | L_DOLLAR, | ||
47 | T![|], | ||
48 | T![move], | ||
49 | T![box], | ||
50 | T![if], | ||
51 | T![while], | ||
52 | T![match], | ||
53 | T![unsafe], | ||
54 | T![return], | ||
55 | T![break], | ||
56 | T![continue], | ||
57 | T![async], | ||
58 | T![try], | ||
59 | T![loop], | ||
60 | T![for], | ||
61 | LIFETIME, | ||
62 | ]); | ||
63 | |||
64 | const EXPR_RECOVERY_SET: TokenSet = token_set![LET_KW, R_DOLLAR]; | ||
65 | |||
66 | pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> { | ||
67 | if let Some(m) = literal(p) { | ||
68 | return Some((m, BlockLike::NotBlock)); | ||
69 | } | ||
70 | if paths::is_path_start(p) { | ||
71 | return Some(path_expr(p, r)); | ||
72 | } | ||
73 | let la = p.nth(1); | ||
74 | let done = match p.current() { | ||
75 | T!['('] => tuple_expr(p), | ||
76 | T!['['] => array_expr(p), | ||
77 | L_DOLLAR => meta_var_expr(p), | ||
78 | T![|] => closure_expr(p), | ||
79 | T![move] if la == T![|] => closure_expr(p), | ||
80 | T![async] if la == T![|] || (la == T![move] && p.nth(2) == T![|]) => closure_expr(p), | ||
81 | T![if] => if_expr(p), | ||
82 | |||
83 | T![loop] => loop_expr(p, None), | ||
84 | T![box] => box_expr(p, None), | ||
85 | T![for] => for_expr(p, None), | ||
86 | T![while] => while_expr(p, None), | ||
87 | T![try] => try_block_expr(p, None), | ||
88 | LIFETIME if la == T![:] => { | ||
89 | let m = p.start(); | ||
90 | label(p); | ||
91 | match p.current() { | ||
92 | T![loop] => loop_expr(p, Some(m)), | ||
93 | T![for] => for_expr(p, Some(m)), | ||
94 | T![while] => while_expr(p, Some(m)), | ||
95 | // test labeled_block | ||
96 | // fn f() { 'label: {}; } | ||
97 | T!['{'] => { | ||
98 | block_expr(p); | ||
99 | m.complete(p, EFFECT_EXPR) | ||
100 | } | ||
101 | _ => { | ||
102 | // test_err misplaced_label_err | ||
103 | // fn main() { | ||
104 | // 'loop: impl | ||
105 | // } | ||
106 | p.error("expected a loop"); | ||
107 | m.complete(p, ERROR); | ||
108 | return None; | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | T![async] if la == T!['{'] || (la == T![move] && p.nth(2) == T!['{']) => { | ||
113 | let m = p.start(); | ||
114 | p.bump(T![async]); | ||
115 | p.eat(T![move]); | ||
116 | block_expr(p); | ||
117 | m.complete(p, EFFECT_EXPR) | ||
118 | } | ||
119 | T![match] => match_expr(p), | ||
120 | // test unsafe_block | ||
121 | // fn f() { unsafe { } } | ||
122 | T![unsafe] if la == T!['{'] => { | ||
123 | let m = p.start(); | ||
124 | p.bump(T![unsafe]); | ||
125 | block_expr(p); | ||
126 | m.complete(p, EFFECT_EXPR) | ||
127 | } | ||
128 | T!['{'] => { | ||
129 | // test for_range_from | ||
130 | // fn foo() { | ||
131 | // for x in 0 .. { | ||
132 | // break; | ||
133 | // } | ||
134 | // } | ||
135 | block_expr_unchecked(p) | ||
136 | } | ||
137 | T![return] => return_expr(p), | ||
138 | T![continue] => continue_expr(p), | ||
139 | T![break] => break_expr(p, r), | ||
140 | _ => { | ||
141 | p.err_recover("expected expression", EXPR_RECOVERY_SET); | ||
142 | return None; | ||
143 | } | ||
144 | }; | ||
145 | let blocklike = match done.kind() { | ||
146 | IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR | EFFECT_EXPR => { | ||
147 | BlockLike::Block | ||
148 | } | ||
149 | _ => BlockLike::NotBlock, | ||
150 | }; | ||
151 | Some((done, blocklike)) | ||
152 | } | ||
153 | |||
154 | // test tuple_expr | ||
155 | // fn foo() { | ||
156 | // (); | ||
157 | // (1); | ||
158 | // (1,); | ||
159 | // } | ||
160 | fn tuple_expr(p: &mut Parser) -> CompletedMarker { | ||
161 | assert!(p.at(T!['('])); | ||
162 | let m = p.start(); | ||
163 | p.expect(T!['(']); | ||
164 | |||
165 | let mut saw_comma = false; | ||
166 | let mut saw_expr = false; | ||
167 | while !p.at(EOF) && !p.at(T![')']) { | ||
168 | saw_expr = true; | ||
169 | if !p.at_ts(EXPR_FIRST) { | ||
170 | p.error("expected expression"); | ||
171 | break; | ||
172 | } | ||
173 | expr(p); | ||
174 | if !p.at(T![')']) { | ||
175 | saw_comma = true; | ||
176 | p.expect(T![,]); | ||
177 | } | ||
178 | } | ||
179 | p.expect(T![')']); | ||
180 | m.complete(p, if saw_expr && !saw_comma { PAREN_EXPR } else { TUPLE_EXPR }) | ||
181 | } | ||
182 | |||
183 | // test array_expr | ||
184 | // fn foo() { | ||
185 | // []; | ||
186 | // [1]; | ||
187 | // [1, 2,]; | ||
188 | // [1; 2]; | ||
189 | // } | ||
190 | fn array_expr(p: &mut Parser) -> CompletedMarker { | ||
191 | assert!(p.at(T!['['])); | ||
192 | let m = p.start(); | ||
193 | |||
194 | let mut n_exprs = 0u32; | ||
195 | let mut has_semi = false; | ||
196 | |||
197 | p.bump(T!['[']); | ||
198 | while !p.at(EOF) && !p.at(T![']']) { | ||
199 | n_exprs += 1; | ||
200 | |||
201 | // test array_attrs | ||
202 | // const A: &[i64] = &[1, #[cfg(test)] 2]; | ||
203 | if !expr_with_attrs(p) { | ||
204 | break; | ||
205 | } | ||
206 | |||
207 | if n_exprs == 1 && p.eat(T![;]) { | ||
208 | has_semi = true; | ||
209 | continue; | ||
210 | } | ||
211 | |||
212 | if has_semi || !p.at(T![']']) && !p.expect(T![,]) { | ||
213 | break; | ||
214 | } | ||
215 | } | ||
216 | p.expect(T![']']); | ||
217 | |||
218 | m.complete(p, ARRAY_EXPR) | ||
219 | } | ||
220 | |||
221 | // test lambda_expr | ||
222 | // fn foo() { | ||
223 | // || (); | ||
224 | // || -> i32 { 92 }; | ||
225 | // |x| x; | ||
226 | // move |x: i32,| x; | ||
227 | // async || {}; | ||
228 | // move || {}; | ||
229 | // async move || {}; | ||
230 | // } | ||
231 | fn closure_expr(p: &mut Parser) -> CompletedMarker { | ||
232 | assert!( | ||
233 | p.at(T![|]) | ||
234 | || (p.at(T![move]) && p.nth(1) == T![|]) | ||
235 | || (p.at(T![async]) && p.nth(1) == T![|]) | ||
236 | || (p.at(T![async]) && p.nth(1) == T![move] && p.nth(2) == T![|]) | ||
237 | ); | ||
238 | let m = p.start(); | ||
239 | p.eat(T![async]); | ||
240 | p.eat(T![move]); | ||
241 | params::param_list_closure(p); | ||
242 | if opt_ret_type(p) { | ||
243 | // test lambda_ret_block | ||
244 | // fn main() { || -> i32 { 92 }(); } | ||
245 | block_expr(p); | ||
246 | } else { | ||
247 | if p.at_ts(EXPR_FIRST) { | ||
248 | expr(p); | ||
249 | } else { | ||
250 | p.error("expected expression"); | ||
251 | } | ||
252 | } | ||
253 | m.complete(p, CLOSURE_EXPR) | ||
254 | } | ||
255 | |||
256 | // test if_expr | ||
257 | // fn foo() { | ||
258 | // if true {}; | ||
259 | // if true {} else {}; | ||
260 | // if true {} else if false {} else {}; | ||
261 | // if S {}; | ||
262 | // if { true } { } else { }; | ||
263 | // } | ||
264 | fn if_expr(p: &mut Parser) -> CompletedMarker { | ||
265 | assert!(p.at(T![if])); | ||
266 | let m = p.start(); | ||
267 | p.bump(T![if]); | ||
268 | condition(p); | ||
269 | block_expr(p); | ||
270 | if p.at(T![else]) { | ||
271 | p.bump(T![else]); | ||
272 | if p.at(T![if]) { | ||
273 | if_expr(p); | ||
274 | } else { | ||
275 | block_expr(p); | ||
276 | } | ||
277 | } | ||
278 | m.complete(p, IF_EXPR) | ||
279 | } | ||
280 | |||
281 | // test label | ||
282 | // fn foo() { | ||
283 | // 'a: loop {} | ||
284 | // 'b: while true {} | ||
285 | // 'c: for x in () {} | ||
286 | // } | ||
287 | fn label(p: &mut Parser) { | ||
288 | assert!(p.at(LIFETIME) && p.nth(1) == T![:]); | ||
289 | let m = p.start(); | ||
290 | p.bump(LIFETIME); | ||
291 | p.bump_any(); | ||
292 | m.complete(p, LABEL); | ||
293 | } | ||
294 | |||
295 | // test loop_expr | ||
296 | // fn foo() { | ||
297 | // loop {}; | ||
298 | // } | ||
299 | fn loop_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | ||
300 | assert!(p.at(T![loop])); | ||
301 | let m = m.unwrap_or_else(|| p.start()); | ||
302 | p.bump(T![loop]); | ||
303 | block_expr(p); | ||
304 | m.complete(p, LOOP_EXPR) | ||
305 | } | ||
306 | |||
307 | // test while_expr | ||
308 | // fn foo() { | ||
309 | // while true {}; | ||
310 | // while let Some(x) = it.next() {}; | ||
311 | // while { true } {}; | ||
312 | // } | ||
313 | fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | ||
314 | assert!(p.at(T![while])); | ||
315 | let m = m.unwrap_or_else(|| p.start()); | ||
316 | p.bump(T![while]); | ||
317 | condition(p); | ||
318 | block_expr(p); | ||
319 | m.complete(p, WHILE_EXPR) | ||
320 | } | ||
321 | |||
322 | // test for_expr | ||
323 | // fn foo() { | ||
324 | // for x in [] {}; | ||
325 | // } | ||
326 | fn for_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | ||
327 | assert!(p.at(T![for])); | ||
328 | let m = m.unwrap_or_else(|| p.start()); | ||
329 | p.bump(T![for]); | ||
330 | patterns::pattern(p); | ||
331 | p.expect(T![in]); | ||
332 | expr_no_struct(p); | ||
333 | block_expr(p); | ||
334 | m.complete(p, FOR_EXPR) | ||
335 | } | ||
336 | |||
337 | // test cond | ||
338 | // fn foo() { if let Some(_) = None {} } | ||
339 | // fn bar() { | ||
340 | // if let Some(_) | Some(_) = None {} | ||
341 | // if let | Some(_) = None {} | ||
342 | // while let Some(_) | Some(_) = None {} | ||
343 | // while let | Some(_) = None {} | ||
344 | // } | ||
345 | fn condition(p: &mut Parser) { | ||
346 | let m = p.start(); | ||
347 | if p.eat(T![let]) { | ||
348 | patterns::pattern_top(p); | ||
349 | p.expect(T![=]); | ||
350 | } | ||
351 | expr_no_struct(p); | ||
352 | m.complete(p, CONDITION); | ||
353 | } | ||
354 | |||
355 | // test match_expr | ||
356 | // fn foo() { | ||
357 | // match () { }; | ||
358 | // match S {}; | ||
359 | // match { } { _ => () }; | ||
360 | // match { S {} } {}; | ||
361 | // } | ||
362 | fn match_expr(p: &mut Parser) -> CompletedMarker { | ||
363 | assert!(p.at(T![match])); | ||
364 | let m = p.start(); | ||
365 | p.bump(T![match]); | ||
366 | expr_no_struct(p); | ||
367 | if p.at(T!['{']) { | ||
368 | match_arm_list(p); | ||
369 | } else { | ||
370 | p.error("expected `{`") | ||
371 | } | ||
372 | m.complete(p, MATCH_EXPR) | ||
373 | } | ||
374 | |||
375 | pub(crate) fn match_arm_list(p: &mut Parser) { | ||
376 | assert!(p.at(T!['{'])); | ||
377 | let m = p.start(); | ||
378 | p.eat(T!['{']); | ||
379 | |||
380 | // test match_arms_inner_attribute | ||
381 | // fn foo() { | ||
382 | // match () { | ||
383 | // #![doc("Inner attribute")] | ||
384 | // #![doc("Can be")] | ||
385 | // #![doc("Stacked")] | ||
386 | // _ => (), | ||
387 | // } | ||
388 | // } | ||
389 | attributes::inner_attrs(p); | ||
390 | |||
391 | while !p.at(EOF) && !p.at(T!['}']) { | ||
392 | if p.at(T!['{']) { | ||
393 | error_block(p, "expected match arm"); | ||
394 | continue; | ||
395 | } | ||
396 | |||
397 | // test match_arms_commas | ||
398 | // fn foo() { | ||
399 | // match () { | ||
400 | // _ => (), | ||
401 | // _ => {} | ||
402 | // _ => () | ||
403 | // } | ||
404 | // } | ||
405 | if match_arm(p).is_block() { | ||
406 | p.eat(T![,]); | ||
407 | } else if !p.at(T!['}']) { | ||
408 | p.expect(T![,]); | ||
409 | } | ||
410 | } | ||
411 | p.expect(T!['}']); | ||
412 | m.complete(p, MATCH_ARM_LIST); | ||
413 | } | ||
414 | |||
415 | // test match_arm | ||
416 | // fn foo() { | ||
417 | // match () { | ||
418 | // _ => (), | ||
419 | // _ if Test > Test{field: 0} => (), | ||
420 | // X | Y if Z => (), | ||
421 | // | X | Y if Z => (), | ||
422 | // | X => (), | ||
423 | // }; | ||
424 | // } | ||
425 | fn match_arm(p: &mut Parser) -> BlockLike { | ||
426 | let m = p.start(); | ||
427 | // test match_arms_outer_attributes | ||
428 | // fn foo() { | ||
429 | // match () { | ||
430 | // #[cfg(feature = "some")] | ||
431 | // _ => (), | ||
432 | // #[cfg(feature = "other")] | ||
433 | // _ => (), | ||
434 | // #[cfg(feature = "many")] | ||
435 | // #[cfg(feature = "attributes")] | ||
436 | // #[cfg(feature = "before")] | ||
437 | // _ => (), | ||
438 | // } | ||
439 | // } | ||
440 | attributes::outer_attrs(p); | ||
441 | |||
442 | patterns::pattern_top_r(p, TokenSet::EMPTY); | ||
443 | if p.at(T![if]) { | ||
444 | match_guard(p); | ||
445 | } | ||
446 | p.expect(T![=>]); | ||
447 | let blocklike = expr_stmt(p).1; | ||
448 | m.complete(p, MATCH_ARM); | ||
449 | blocklike | ||
450 | } | ||
451 | |||
452 | // test match_guard | ||
453 | // fn foo() { | ||
454 | // match () { | ||
455 | // _ if foo => (), | ||
456 | // } | ||
457 | // } | ||
458 | fn match_guard(p: &mut Parser) -> CompletedMarker { | ||
459 | assert!(p.at(T![if])); | ||
460 | let m = p.start(); | ||
461 | p.bump(T![if]); | ||
462 | expr(p); | ||
463 | m.complete(p, MATCH_GUARD) | ||
464 | } | ||
465 | |||
466 | // test block | ||
467 | // fn a() {} | ||
468 | // fn b() { let _ = 1; } | ||
469 | // fn c() { 1; 2; } | ||
470 | // fn d() { 1; 2 } | ||
471 | pub(crate) fn block_expr(p: &mut Parser) { | ||
472 | if !p.at(T!['{']) { | ||
473 | p.error("expected a block"); | ||
474 | return; | ||
475 | } | ||
476 | block_expr_unchecked(p); | ||
477 | } | ||
478 | |||
479 | fn block_expr_unchecked(p: &mut Parser) -> CompletedMarker { | ||
480 | assert!(p.at(T!['{'])); | ||
481 | let m = p.start(); | ||
482 | p.bump(T!['{']); | ||
483 | expr_block_contents(p); | ||
484 | p.expect(T!['}']); | ||
485 | m.complete(p, BLOCK_EXPR) | ||
486 | } | ||
487 | |||
488 | // test return_expr | ||
489 | // fn foo() { | ||
490 | // return; | ||
491 | // return 92; | ||
492 | // } | ||
493 | fn return_expr(p: &mut Parser) -> CompletedMarker { | ||
494 | assert!(p.at(T![return])); | ||
495 | let m = p.start(); | ||
496 | p.bump(T![return]); | ||
497 | if p.at_ts(EXPR_FIRST) { | ||
498 | expr(p); | ||
499 | } | ||
500 | m.complete(p, RETURN_EXPR) | ||
501 | } | ||
502 | |||
503 | // test continue_expr | ||
504 | // fn foo() { | ||
505 | // loop { | ||
506 | // continue; | ||
507 | // continue 'l; | ||
508 | // } | ||
509 | // } | ||
510 | fn continue_expr(p: &mut Parser) -> CompletedMarker { | ||
511 | assert!(p.at(T![continue])); | ||
512 | let m = p.start(); | ||
513 | p.bump(T![continue]); | ||
514 | p.eat(LIFETIME); | ||
515 | m.complete(p, CONTINUE_EXPR) | ||
516 | } | ||
517 | |||
518 | // test break_expr | ||
519 | // fn foo() { | ||
520 | // loop { | ||
521 | // break; | ||
522 | // break 'l; | ||
523 | // break 92; | ||
524 | // break 'l 92; | ||
525 | // } | ||
526 | // } | ||
527 | fn break_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker { | ||
528 | assert!(p.at(T![break])); | ||
529 | let m = p.start(); | ||
530 | p.bump(T![break]); | ||
531 | p.eat(LIFETIME); | ||
532 | // test break_ambiguity | ||
533 | // fn foo(){ | ||
534 | // if break {} | ||
535 | // while break {} | ||
536 | // for i in break {} | ||
537 | // match break {} | ||
538 | // } | ||
539 | if p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])) { | ||
540 | expr(p); | ||
541 | } | ||
542 | m.complete(p, BREAK_EXPR) | ||
543 | } | ||
544 | |||
545 | // test try_block_expr | ||
546 | // fn foo() { | ||
547 | // let _ = try {}; | ||
548 | // } | ||
549 | fn try_block_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | ||
550 | assert!(p.at(T![try])); | ||
551 | let m = m.unwrap_or_else(|| p.start()); | ||
552 | // Special-case `try!` as macro. | ||
553 | // This is a hack until we do proper edition support | ||
554 | if p.nth_at(1, T![!]) { | ||
555 | // test try_macro_fallback | ||
556 | // fn foo() { try!(Ok(())); } | ||
557 | let path = p.start(); | ||
558 | let path_segment = p.start(); | ||
559 | let name_ref = p.start(); | ||
560 | p.bump_remap(IDENT); | ||
561 | name_ref.complete(p, NAME_REF); | ||
562 | path_segment.complete(p, PATH_SEGMENT); | ||
563 | path.complete(p, PATH); | ||
564 | let _block_like = items::macro_call_after_excl(p); | ||
565 | return m.complete(p, MACRO_CALL); | ||
566 | } | ||
567 | |||
568 | p.bump(T![try]); | ||
569 | block_expr(p); | ||
570 | m.complete(p, EFFECT_EXPR) | ||
571 | } | ||
572 | |||
573 | // test box_expr | ||
574 | // fn foo() { | ||
575 | // let x = box 1i32; | ||
576 | // let y = (box 1i32, box 2i32); | ||
577 | // let z = Foo(box 1i32, box 2i32); | ||
578 | // } | ||
579 | fn box_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker { | ||
580 | assert!(p.at(T![box])); | ||
581 | let m = m.unwrap_or_else(|| p.start()); | ||
582 | p.bump(T![box]); | ||
583 | if p.at_ts(EXPR_FIRST) { | ||
584 | expr(p); | ||
585 | } | ||
586 | m.complete(p, BOX_EXPR) | ||
587 | } | ||
588 | |||
589 | /// Expression from `$var` macro expansion, wrapped in dollars | ||
590 | fn meta_var_expr(p: &mut Parser) -> CompletedMarker { | ||
591 | assert!(p.at(L_DOLLAR)); | ||
592 | let m = p.start(); | ||
593 | p.bump(L_DOLLAR); | ||
594 | let (completed, _is_block) = | ||
595 | expr_bp(p, Restrictions { forbid_structs: false, prefer_stmt: false }, 1); | ||
596 | |||
597 | match (completed, p.current()) { | ||
598 | (Some(it), R_DOLLAR) => { | ||
599 | p.bump(R_DOLLAR); | ||
600 | m.abandon(p); | ||
601 | it | ||
602 | } | ||
603 | _ => { | ||
604 | while !p.at(R_DOLLAR) { | ||
605 | p.bump_any() | ||
606 | } | ||
607 | p.bump(R_DOLLAR); | ||
608 | m.complete(p, ERROR) | ||
609 | } | ||
610 | } | ||
611 | } | ||