diff options
author | Edwin Cheng <[email protected]> | 2021-01-29 16:21:43 +0000 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2021-01-29 16:21:43 +0000 |
commit | 706ac8256d878626126756969b48b262d2e187b5 (patch) | |
tree | 8adb77484d4563ad61fc76ed9796d38a42a161e8 /crates/mbe | |
parent | 3ecd5112bbc2cc1a45f423e0256230507f159162 (diff) |
Simplify mbe match error.
Handle parse error in rule parsing instead of match in mbe
Diffstat (limited to 'crates/mbe')
-rw-r--r-- | crates/mbe/src/expander.rs | 9 | ||||
-rw-r--r-- | crates/mbe/src/expander/matcher.rs | 286 | ||||
-rw-r--r-- | crates/mbe/src/expander/transcriber.rs | 7 | ||||
-rw-r--r-- | crates/mbe/src/lib.rs | 31 | ||||
-rw-r--r-- | crates/mbe/src/parser.rs | 40 | ||||
-rw-r--r-- | crates/mbe/src/tests.rs | 17 |
6 files changed, 180 insertions, 210 deletions
diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs index 802c8fb0f..e7e14b3cc 100644 --- a/crates/mbe/src/expander.rs +++ b/crates/mbe/src/expander.rs | |||
@@ -16,13 +16,8 @@ pub(crate) fn expand_rules( | |||
16 | ) -> ExpandResult<tt::Subtree> { | 16 | ) -> ExpandResult<tt::Subtree> { |
17 | let mut match_: Option<(matcher::Match, &crate::Rule)> = None; | 17 | let mut match_: Option<(matcher::Match, &crate::Rule)> = None; |
18 | for rule in rules { | 18 | for rule in rules { |
19 | let new_match = match matcher::match_(&rule.lhs, input) { | 19 | let new_match = matcher::match_(&rule.lhs, input); |
20 | Ok(m) => m, | 20 | |
21 | Err(_e) => { | ||
22 | // error in pattern parsing | ||
23 | continue; | ||
24 | } | ||
25 | }; | ||
26 | if new_match.err.is_none() { | 21 | if new_match.err.is_none() { |
27 | // If we find a rule that applies without errors, we're done. | 22 | // If we find a rule that applies without errors, we're done. |
28 | // Unconditionally returning the transcription here makes the | 23 | // Unconditionally returning the transcription here makes the |
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 5b5845850..987a4f676 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs | |||
@@ -77,35 +77,26 @@ impl Match { | |||
77 | } | 77 | } |
78 | } | 78 | } |
79 | 79 | ||
80 | // General note: These functions have two channels to return errors, a `Result` | 80 | /// Matching errors are added to the `Match`. |
81 | // return value and the `&mut Match`. The returned Result is for pattern parsing | 81 | pub(super) fn match_(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { |
82 | // errors; if a branch of the macro definition doesn't parse, it doesn't make | ||
83 | // sense to try using it. Matching errors are added to the `Match`. It might | ||
84 | // make sense to make pattern parsing a separate step? | ||
85 | |||
86 | pub(super) fn match_(pattern: &MetaTemplate, src: &tt::Subtree) -> Result<Match, ExpandError> { | ||
87 | assert!(pattern.delimiter == None); | 82 | assert!(pattern.delimiter == None); |
88 | 83 | ||
89 | let mut res = Match::default(); | 84 | let mut res = Match::default(); |
90 | let mut src = TtIter::new(src); | 85 | let mut src = TtIter::new(src); |
91 | 86 | ||
92 | match_subtree(&mut res, pattern, &mut src)?; | 87 | match_subtree(&mut res, pattern, &mut src); |
93 | 88 | ||
94 | if src.len() > 0 { | 89 | if src.len() > 0 { |
95 | res.unmatched_tts += src.len(); | 90 | res.unmatched_tts += src.len(); |
96 | res.add_err(err!("leftover tokens")); | 91 | res.add_err(err!("leftover tokens")); |
97 | } | 92 | } |
98 | 93 | ||
99 | Ok(res) | 94 | res |
100 | } | 95 | } |
101 | 96 | ||
102 | fn match_subtree( | 97 | fn match_subtree(res: &mut Match, pattern: &MetaTemplate, src: &mut TtIter) { |
103 | res: &mut Match, | ||
104 | pattern: &MetaTemplate, | ||
105 | src: &mut TtIter, | ||
106 | ) -> Result<(), ExpandError> { | ||
107 | for op in pattern.iter() { | 98 | for op in pattern.iter() { |
108 | match op.as_ref().map_err(|err| err.clone())? { | 99 | match op { |
109 | Op::Leaf(lhs) => { | 100 | Op::Leaf(lhs) => { |
110 | let rhs = match src.expect_leaf() { | 101 | let rhs = match src.expect_leaf() { |
111 | Ok(l) => l, | 102 | Ok(l) => l, |
@@ -145,7 +136,7 @@ fn match_subtree( | |||
145 | continue; | 136 | continue; |
146 | } | 137 | } |
147 | let mut src = TtIter::new(rhs); | 138 | let mut src = TtIter::new(rhs); |
148 | match_subtree(res, lhs, &mut src)?; | 139 | match_subtree(res, lhs, &mut src); |
149 | if src.len() > 0 { | 140 | if src.len() > 0 { |
150 | res.add_err(err!("leftover tokens")); | 141 | res.add_err(err!("leftover tokens")); |
151 | } | 142 | } |
@@ -172,11 +163,139 @@ fn match_subtree( | |||
172 | } | 163 | } |
173 | } | 164 | } |
174 | Op::Repeat { subtree, kind, separator } => { | 165 | Op::Repeat { subtree, kind, separator } => { |
175 | match_repeat(res, subtree, *kind, separator, src)?; | 166 | match_repeat(res, subtree, *kind, separator, src); |
167 | } | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | |||
172 | pub(super) fn match_repeat( | ||
173 | res: &mut Match, | ||
174 | pattern: &MetaTemplate, | ||
175 | kind: RepeatKind, | ||
176 | separator: &Option<Separator>, | ||
177 | src: &mut TtIter, | ||
178 | ) { | ||
179 | // Dirty hack to make macro-expansion terminate. | ||
180 | // This should be replaced by a proper macro-by-example implementation | ||
181 | let mut limit = 65536; | ||
182 | let mut counter = 0; | ||
183 | |||
184 | for i in 0.. { | ||
185 | let mut fork = src.clone(); | ||
186 | |||
187 | if let Some(separator) = &separator { | ||
188 | if i != 0 && !fork.eat_separator(separator) { | ||
189 | break; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | let mut nested = Match::default(); | ||
194 | match_subtree(&mut nested, pattern, &mut fork); | ||
195 | if nested.err.is_none() { | ||
196 | limit -= 1; | ||
197 | if limit == 0 { | ||
198 | log::warn!( | ||
199 | "match_lhs exceeded repeat pattern limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", | ||
200 | pattern, | ||
201 | src, | ||
202 | kind, | ||
203 | separator | ||
204 | ); | ||
205 | break; | ||
206 | } | ||
207 | *src = fork; | ||
208 | |||
209 | if let Err(err) = res.bindings.push_nested(counter, nested.bindings) { | ||
210 | res.add_err(err); | ||
211 | } | ||
212 | counter += 1; | ||
213 | if counter == 1 { | ||
214 | if let RepeatKind::ZeroOrOne = kind { | ||
215 | break; | ||
216 | } | ||
217 | } | ||
218 | } else { | ||
219 | break; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | match (kind, counter) { | ||
224 | (RepeatKind::OneOrMore, 0) => { | ||
225 | res.add_err(ExpandError::UnexpectedToken); | ||
226 | } | ||
227 | (_, 0) => { | ||
228 | // Collect all empty variables in subtrees | ||
229 | let mut vars = Vec::new(); | ||
230 | collect_vars(&mut vars, pattern); | ||
231 | for var in vars { | ||
232 | res.bindings.push_empty(&var) | ||
176 | } | 233 | } |
177 | } | 234 | } |
235 | _ => (), | ||
236 | } | ||
237 | } | ||
238 | |||
239 | fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragment>> { | ||
240 | let fragment = match kind { | ||
241 | "path" => Path, | ||
242 | "expr" => Expr, | ||
243 | "ty" => Type, | ||
244 | "pat" => Pattern, | ||
245 | "stmt" => Statement, | ||
246 | "block" => Block, | ||
247 | "meta" => MetaItem, | ||
248 | "item" => Item, | ||
249 | _ => { | ||
250 | let tt_result = match kind { | ||
251 | "ident" => input | ||
252 | .expect_ident() | ||
253 | .map(|ident| Some(tt::Leaf::from(ident.clone()).into())) | ||
254 | .map_err(|()| err!("expected ident")), | ||
255 | "tt" => input.expect_tt().map(Some).map_err(|()| err!()), | ||
256 | "lifetime" => input | ||
257 | .expect_lifetime() | ||
258 | .map(|tt| Some(tt)) | ||
259 | .map_err(|()| err!("expected lifetime")), | ||
260 | "literal" => { | ||
261 | let neg = input.eat_char('-'); | ||
262 | input | ||
263 | .expect_literal() | ||
264 | .map(|literal| { | ||
265 | let lit = tt::Leaf::from(literal.clone()); | ||
266 | match neg { | ||
267 | None => Some(lit.into()), | ||
268 | Some(neg) => Some(tt::TokenTree::Subtree(tt::Subtree { | ||
269 | delimiter: None, | ||
270 | token_trees: vec![neg, lit.into()], | ||
271 | })), | ||
272 | } | ||
273 | }) | ||
274 | .map_err(|()| err!()) | ||
275 | } | ||
276 | // `vis` is optional | ||
277 | "vis" => match input.eat_vis() { | ||
278 | Some(vis) => Ok(Some(vis)), | ||
279 | None => Ok(None), | ||
280 | }, | ||
281 | _ => Err(ExpandError::UnexpectedToken), | ||
282 | }; | ||
283 | return tt_result.map(|it| it.map(Fragment::Tokens)).into(); | ||
284 | } | ||
285 | }; | ||
286 | let result = input.expect_fragment(fragment); | ||
287 | result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) }) | ||
288 | } | ||
289 | |||
290 | fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) { | ||
291 | for op in pattern.iter() { | ||
292 | match op { | ||
293 | Op::Var { name, .. } => buf.push(name.clone()), | ||
294 | Op::Leaf(_) => (), | ||
295 | Op::Subtree(subtree) => collect_vars(buf, subtree), | ||
296 | Op::Repeat { subtree, .. } => collect_vars(buf, subtree), | ||
297 | } | ||
178 | } | 298 | } |
179 | Ok(()) | ||
180 | } | 299 | } |
181 | 300 | ||
182 | impl<'a> TtIter<'a> { | 301 | impl<'a> TtIter<'a> { |
@@ -369,134 +488,3 @@ impl<'a> TtIter<'a> { | |||
369 | } | 488 | } |
370 | } | 489 | } |
371 | } | 490 | } |
372 | |||
373 | pub(super) fn match_repeat( | ||
374 | res: &mut Match, | ||
375 | pattern: &MetaTemplate, | ||
376 | kind: RepeatKind, | ||
377 | separator: &Option<Separator>, | ||
378 | src: &mut TtIter, | ||
379 | ) -> Result<(), ExpandError> { | ||
380 | // Dirty hack to make macro-expansion terminate. | ||
381 | // This should be replaced by a proper macro-by-example implementation | ||
382 | let mut limit = 65536; | ||
383 | let mut counter = 0; | ||
384 | |||
385 | for i in 0.. { | ||
386 | let mut fork = src.clone(); | ||
387 | |||
388 | if let Some(separator) = &separator { | ||
389 | if i != 0 && !fork.eat_separator(separator) { | ||
390 | break; | ||
391 | } | ||
392 | } | ||
393 | |||
394 | let mut nested = Match::default(); | ||
395 | match_subtree(&mut nested, pattern, &mut fork)?; | ||
396 | if nested.err.is_none() { | ||
397 | limit -= 1; | ||
398 | if limit == 0 { | ||
399 | log::warn!( | ||
400 | "match_lhs exceeded repeat pattern limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", | ||
401 | pattern, | ||
402 | src, | ||
403 | kind, | ||
404 | separator | ||
405 | ); | ||
406 | break; | ||
407 | } | ||
408 | *src = fork; | ||
409 | |||
410 | if let Err(err) = res.bindings.push_nested(counter, nested.bindings) { | ||
411 | res.add_err(err); | ||
412 | } | ||
413 | counter += 1; | ||
414 | if counter == 1 { | ||
415 | if let RepeatKind::ZeroOrOne = kind { | ||
416 | break; | ||
417 | } | ||
418 | } | ||
419 | } else { | ||
420 | break; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | match (kind, counter) { | ||
425 | (RepeatKind::OneOrMore, 0) => { | ||
426 | res.add_err(ExpandError::UnexpectedToken); | ||
427 | } | ||
428 | (_, 0) => { | ||
429 | // Collect all empty variables in subtrees | ||
430 | let mut vars = Vec::new(); | ||
431 | collect_vars(&mut vars, pattern)?; | ||
432 | for var in vars { | ||
433 | res.bindings.push_empty(&var) | ||
434 | } | ||
435 | } | ||
436 | _ => (), | ||
437 | } | ||
438 | Ok(()) | ||
439 | } | ||
440 | |||
441 | fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragment>> { | ||
442 | let fragment = match kind { | ||
443 | "path" => Path, | ||
444 | "expr" => Expr, | ||
445 | "ty" => Type, | ||
446 | "pat" => Pattern, | ||
447 | "stmt" => Statement, | ||
448 | "block" => Block, | ||
449 | "meta" => MetaItem, | ||
450 | "item" => Item, | ||
451 | _ => { | ||
452 | let tt_result = match kind { | ||
453 | "ident" => input | ||
454 | .expect_ident() | ||
455 | .map(|ident| Some(tt::Leaf::from(ident.clone()).into())) | ||
456 | .map_err(|()| err!("expected ident")), | ||
457 | "tt" => input.expect_tt().map(Some).map_err(|()| err!()), | ||
458 | "lifetime" => input | ||
459 | .expect_lifetime() | ||
460 | .map(|tt| Some(tt)) | ||
461 | .map_err(|()| err!("expected lifetime")), | ||
462 | "literal" => { | ||
463 | let neg = input.eat_char('-'); | ||
464 | input | ||
465 | .expect_literal() | ||
466 | .map(|literal| { | ||
467 | let lit = tt::Leaf::from(literal.clone()); | ||
468 | match neg { | ||
469 | None => Some(lit.into()), | ||
470 | Some(neg) => Some(tt::TokenTree::Subtree(tt::Subtree { | ||
471 | delimiter: None, | ||
472 | token_trees: vec![neg, lit.into()], | ||
473 | })), | ||
474 | } | ||
475 | }) | ||
476 | .map_err(|()| err!()) | ||
477 | } | ||
478 | // `vis` is optional | ||
479 | "vis" => match input.eat_vis() { | ||
480 | Some(vis) => Ok(Some(vis)), | ||
481 | None => Ok(None), | ||
482 | }, | ||
483 | _ => Err(ExpandError::UnexpectedToken), | ||
484 | }; | ||
485 | return tt_result.map(|it| it.map(Fragment::Tokens)).into(); | ||
486 | } | ||
487 | }; | ||
488 | let result = input.expect_fragment(fragment); | ||
489 | result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) }) | ||
490 | } | ||
491 | |||
492 | fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) -> Result<(), ExpandError> { | ||
493 | for op in pattern.iter() { | ||
494 | match op.as_ref().map_err(|e| e.clone())? { | ||
495 | Op::Var { name, .. } => buf.push(name.clone()), | ||
496 | Op::Leaf(_) => (), | ||
497 | Op::Subtree(subtree) => collect_vars(buf, subtree)?, | ||
498 | Op::Repeat { subtree, .. } => collect_vars(buf, subtree)?, | ||
499 | } | ||
500 | } | ||
501 | Ok(()) | ||
502 | } | ||
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index 82bace110..30c090f32 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs | |||
@@ -86,13 +86,6 @@ fn expand_subtree( | |||
86 | let start_elements = arena.len(); | 86 | let start_elements = arena.len(); |
87 | let mut err = None; | 87 | let mut err = None; |
88 | for op in template.iter() { | 88 | for op in template.iter() { |
89 | let op = match op { | ||
90 | Ok(op) => op, | ||
91 | Err(e) => { | ||
92 | err = Some(e.clone()); | ||
93 | break; | ||
94 | } | ||
95 | }; | ||
96 | match op { | 89 | match op { |
97 | Op::Leaf(tt) => arena.push(tt.clone().into()), | 90 | Op::Leaf(tt) => arena.push(tt.clone().into()), |
98 | Op::Subtree(tt) => { | 91 | Op::Subtree(tt) => { |
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index bbe71ce3e..56c632665 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -24,7 +24,9 @@ use crate::{ | |||
24 | 24 | ||
25 | #[derive(Debug, PartialEq, Eq)] | 25 | #[derive(Debug, PartialEq, Eq)] |
26 | pub enum ParseError { | 26 | pub enum ParseError { |
27 | UnexpectedToken(String), | ||
27 | Expected(String), | 28 | Expected(String), |
29 | InvalidRepeat, | ||
28 | RepetitionEmptyTokenTree, | 30 | RepetitionEmptyTokenTree, |
29 | } | 31 | } |
30 | 32 | ||
@@ -34,7 +36,6 @@ pub enum ExpandError { | |||
34 | UnexpectedToken, | 36 | UnexpectedToken, |
35 | BindingError(String), | 37 | BindingError(String), |
36 | ConversionError, | 38 | ConversionError, |
37 | InvalidRepeat, | ||
38 | ProcMacroError(tt::ExpansionError), | 39 | ProcMacroError(tt::ExpansionError), |
39 | UnresolvedProcMacro, | 40 | UnresolvedProcMacro, |
40 | Other(String), | 41 | Other(String), |
@@ -53,7 +54,6 @@ impl fmt::Display for ExpandError { | |||
53 | ExpandError::UnexpectedToken => f.write_str("unexpected token in input"), | 54 | ExpandError::UnexpectedToken => f.write_str("unexpected token in input"), |
54 | ExpandError::BindingError(e) => f.write_str(e), | 55 | ExpandError::BindingError(e) => f.write_str(e), |
55 | ExpandError::ConversionError => f.write_str("could not convert tokens"), | 56 | ExpandError::ConversionError => f.write_str("could not convert tokens"), |
56 | ExpandError::InvalidRepeat => f.write_str("invalid repeat expression"), | ||
57 | ExpandError::ProcMacroError(e) => e.fmt(f), | 57 | ExpandError::ProcMacroError(e) => e.fmt(f), |
58 | ExpandError::UnresolvedProcMacro => f.write_str("unresolved proc macro"), | 58 | ExpandError::UnresolvedProcMacro => f.write_str("unresolved proc macro"), |
59 | ExpandError::Other(e) => f.write_str(e), | 59 | ExpandError::Other(e) => f.write_str(e), |
@@ -94,11 +94,11 @@ struct Rule { | |||
94 | #[derive(Clone, Debug, PartialEq, Eq)] | 94 | #[derive(Clone, Debug, PartialEq, Eq)] |
95 | struct MetaTemplate { | 95 | struct MetaTemplate { |
96 | delimiter: Option<Delimiter>, | 96 | delimiter: Option<Delimiter>, |
97 | tokens: Vec<Result<Op, ExpandError>>, | 97 | tokens: Vec<Op>, |
98 | } | 98 | } |
99 | 99 | ||
100 | impl<'a> MetaTemplate { | 100 | impl<'a> MetaTemplate { |
101 | fn iter(&self) -> impl Iterator<Item = &Result<Op, ExpandError>> { | 101 | fn iter(&self) -> impl Iterator<Item = &Op> { |
102 | self.tokens.iter() | 102 | self.tokens.iter() |
103 | } | 103 | } |
104 | 104 | ||
@@ -288,25 +288,15 @@ impl Rule { | |||
288 | .expect_subtree() | 288 | .expect_subtree() |
289 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; | 289 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; |
290 | 290 | ||
291 | let lhs = MetaTemplate { tokens: parse_pattern(&lhs), delimiter: None }; | 291 | let lhs = MetaTemplate { tokens: parse_pattern(&lhs)?, delimiter: None }; |
292 | let rhs = MetaTemplate { tokens: parse_template(&rhs), delimiter: None }; | 292 | let rhs = MetaTemplate { tokens: parse_template(&rhs)?, delimiter: None }; |
293 | 293 | ||
294 | Ok(crate::Rule { lhs, rhs }) | 294 | Ok(crate::Rule { lhs, rhs }) |
295 | } | 295 | } |
296 | } | 296 | } |
297 | 297 | ||
298 | fn to_parse_error(e: &ExpandError) -> ParseError { | ||
299 | let msg = match e { | ||
300 | ExpandError::InvalidRepeat => "invalid repeat".to_string(), | ||
301 | _ => "invalid macro definition".to_string(), | ||
302 | }; | ||
303 | ParseError::Expected(msg) | ||
304 | } | ||
305 | |||
306 | fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { | 298 | fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { |
307 | for op in pattern.iter() { | 299 | for op in pattern.iter() { |
308 | let op = op.as_ref().map_err(|e| to_parse_error(&e))?; | ||
309 | |||
310 | match op { | 300 | match op { |
311 | Op::Subtree(subtree) => validate(&subtree)?, | 301 | Op::Subtree(subtree) => validate(&subtree)?, |
312 | Op::Repeat { subtree, separator, .. } => { | 302 | Op::Repeat { subtree, separator, .. } => { |
@@ -315,20 +305,21 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { | |||
315 | 305 | ||
316 | if separator.is_none() { | 306 | if separator.is_none() { |
317 | if subtree.iter().all(|child_op| { | 307 | if subtree.iter().all(|child_op| { |
318 | match child_op.as_ref().map_err(to_parse_error) { | 308 | match child_op { |
319 | Ok(Op::Var { kind, .. }) => { | 309 | Op::Var { kind, .. } => { |
320 | // vis is optional | 310 | // vis is optional |
321 | if kind.as_ref().map_or(false, |it| it == "vis") { | 311 | if kind.as_ref().map_or(false, |it| it == "vis") { |
322 | return true; | 312 | return true; |
323 | } | 313 | } |
324 | } | 314 | } |
325 | Ok(Op::Repeat { kind, .. }) => { | 315 | Op::Repeat { kind, .. } => { |
326 | return matches!( | 316 | return matches!( |
327 | kind, | 317 | kind, |
328 | parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne | 318 | parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne |
329 | ) | 319 | ) |
330 | } | 320 | } |
331 | _ => {} | 321 | Op::Leaf(_) => {} |
322 | Op::Subtree(_) => {} | ||
332 | } | 323 | } |
333 | false | 324 | false |
334 | }) { | 325 | }) { |
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index f3047972d..b90ae7015 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs | |||
@@ -4,7 +4,7 @@ | |||
4 | use smallvec::SmallVec; | 4 | use smallvec::SmallVec; |
5 | use syntax::SmolStr; | 5 | use syntax::SmolStr; |
6 | 6 | ||
7 | use crate::{tt_iter::TtIter, ExpandError, MetaTemplate}; | 7 | use crate::{tt_iter::TtIter, MetaTemplate, ParseError}; |
8 | 8 | ||
9 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
10 | pub(crate) enum Op { | 10 | pub(crate) enum Op { |
@@ -46,12 +46,12 @@ impl PartialEq for Separator { | |||
46 | } | 46 | } |
47 | } | 47 | } |
48 | 48 | ||
49 | pub(crate) fn parse_template(template: &tt::Subtree) -> Vec<Result<Op, ExpandError>> { | 49 | pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> { |
50 | parse_inner(&template, Mode::Template) | 50 | parse_inner(&template, Mode::Template).into_iter().collect() |
51 | } | 51 | } |
52 | 52 | ||
53 | pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Vec<Result<Op, ExpandError>> { | 53 | pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<Vec<Op>, ParseError> { |
54 | parse_inner(&pattern, Mode::Pattern) | 54 | parse_inner(&pattern, Mode::Pattern).into_iter().collect() |
55 | } | 55 | } |
56 | 56 | ||
57 | #[derive(Clone, Copy)] | 57 | #[derive(Clone, Copy)] |
@@ -60,7 +60,7 @@ enum Mode { | |||
60 | Template, | 60 | Template, |
61 | } | 61 | } |
62 | 62 | ||
63 | fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ExpandError>> { | 63 | fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ParseError>> { |
64 | let mut src = TtIter::new(&tt); | 64 | let mut src = TtIter::new(&tt); |
65 | std::iter::from_fn(move || { | 65 | std::iter::from_fn(move || { |
66 | let first = src.next()?; | 66 | let first = src.next()?; |
@@ -71,7 +71,7 @@ fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ExpandError>> { | |||
71 | 71 | ||
72 | macro_rules! err { | 72 | macro_rules! err { |
73 | ($($tt:tt)*) => { | 73 | ($($tt:tt)*) => { |
74 | ExpandError::UnexpectedToken | 74 | ParseError::UnexpectedToken(($($tt)*).to_string()) |
75 | }; | 75 | }; |
76 | } | 76 | } |
77 | 77 | ||
@@ -81,7 +81,7 @@ macro_rules! bail { | |||
81 | }; | 81 | }; |
82 | } | 82 | } |
83 | 83 | ||
84 | fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Result<Op, ExpandError> { | 84 | fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Result<Op, ParseError> { |
85 | let res = match first { | 85 | let res = match first { |
86 | tt::TokenTree::Leaf(leaf @ tt::Leaf::Punct(tt::Punct { char: '$', .. })) => { | 86 | tt::TokenTree::Leaf(leaf @ tt::Leaf::Punct(tt::Punct { char: '$', .. })) => { |
87 | // Note that the '$' itself is a valid token inside macro_rules. | 87 | // Note that the '$' itself is a valid token inside macro_rules. |
@@ -93,7 +93,9 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul | |||
93 | tt::TokenTree::Subtree(subtree) => { | 93 | tt::TokenTree::Subtree(subtree) => { |
94 | let (separator, kind) = parse_repeat(src)?; | 94 | let (separator, kind) = parse_repeat(src)?; |
95 | let delimiter = subtree.delimiter; | 95 | let delimiter = subtree.delimiter; |
96 | let tokens = parse_inner(&subtree, mode); | 96 | let tokens = parse_inner(&subtree, mode) |
97 | .into_iter() | ||
98 | .collect::<Result<Vec<Op>, ParseError>>()?; | ||
97 | let subtree = MetaTemplate { tokens, delimiter }; | 99 | let subtree = MetaTemplate { tokens, delimiter }; |
98 | Op::Repeat { subtree, separator, kind } | 100 | Op::Repeat { subtree, separator, kind } |
99 | } | 101 | } |
@@ -102,7 +104,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul | |||
102 | static UNDERSCORE: SmolStr = SmolStr::new_inline("_"); | 104 | static UNDERSCORE: SmolStr = SmolStr::new_inline("_"); |
103 | 105 | ||
104 | if punct.char != '_' { | 106 | if punct.char != '_' { |
105 | return Err(ExpandError::UnexpectedToken); | 107 | return Err(ParseError::Expected("_".to_string())); |
106 | } | 108 | } |
107 | let name = UNDERSCORE.clone(); | 109 | let name = UNDERSCORE.clone(); |
108 | let kind = eat_fragment_kind(src, mode)?; | 110 | let kind = eat_fragment_kind(src, mode)?; |
@@ -135,7 +137,9 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul | |||
135 | tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()), | 137 | tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()), |
136 | tt::TokenTree::Subtree(subtree) => { | 138 | tt::TokenTree::Subtree(subtree) => { |
137 | let delimiter = subtree.delimiter; | 139 | let delimiter = subtree.delimiter; |
138 | let tokens = parse_inner(&subtree, mode); | 140 | let tokens = |
141 | parse_inner(&subtree, mode).into_iter().collect::<Result<Vec<Op>, ParseError>>()?; | ||
142 | |||
139 | let subtree = MetaTemplate { tokens, delimiter }; | 143 | let subtree = MetaTemplate { tokens, delimiter }; |
140 | Op::Subtree(subtree) | 144 | Op::Subtree(subtree) |
141 | } | 145 | } |
@@ -143,7 +147,7 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul | |||
143 | Ok(res) | 147 | Ok(res) |
144 | } | 148 | } |
145 | 149 | ||
146 | fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ExpandError> { | 150 | fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ParseError> { |
147 | if let Mode::Pattern = mode { | 151 | if let Mode::Pattern = mode { |
148 | src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; | 152 | src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; |
149 | let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; | 153 | let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; |
@@ -156,12 +160,12 @@ fn is_boolean_literal(lit: &tt::Literal) -> bool { | |||
156 | matches!(lit.text.as_str(), "true" | "false") | 160 | matches!(lit.text.as_str(), "true" | "false") |
157 | } | 161 | } |
158 | 162 | ||
159 | fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), ExpandError> { | 163 | fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), ParseError> { |
160 | let mut separator = Separator::Puncts(SmallVec::new()); | 164 | let mut separator = Separator::Puncts(SmallVec::new()); |
161 | for tt in src { | 165 | for tt in src { |
162 | let tt = match tt { | 166 | let tt = match tt { |
163 | tt::TokenTree::Leaf(leaf) => leaf, | 167 | tt::TokenTree::Leaf(leaf) => leaf, |
164 | tt::TokenTree::Subtree(_) => return Err(ExpandError::InvalidRepeat), | 168 | tt::TokenTree::Subtree(_) => return Err(ParseError::InvalidRepeat), |
165 | }; | 169 | }; |
166 | let has_sep = match &separator { | 170 | let has_sep = match &separator { |
167 | Separator::Puncts(puncts) => !puncts.is_empty(), | 171 | Separator::Puncts(puncts) => !puncts.is_empty(), |
@@ -169,7 +173,7 @@ fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), Exp | |||
169 | }; | 173 | }; |
170 | match tt { | 174 | match tt { |
171 | tt::Leaf::Ident(_) | tt::Leaf::Literal(_) if has_sep => { | 175 | tt::Leaf::Ident(_) | tt::Leaf::Literal(_) if has_sep => { |
172 | return Err(ExpandError::InvalidRepeat) | 176 | return Err(ParseError::InvalidRepeat) |
173 | } | 177 | } |
174 | tt::Leaf::Ident(ident) => separator = Separator::Ident(ident.clone()), | 178 | tt::Leaf::Ident(ident) => separator = Separator::Ident(ident.clone()), |
175 | tt::Leaf::Literal(lit) => separator = Separator::Literal(lit.clone()), | 179 | tt::Leaf::Literal(lit) => separator = Separator::Literal(lit.clone()), |
@@ -182,11 +186,11 @@ fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), Exp | |||
182 | match &mut separator { | 186 | match &mut separator { |
183 | Separator::Puncts(puncts) => { | 187 | Separator::Puncts(puncts) => { |
184 | if puncts.len() == 3 { | 188 | if puncts.len() == 3 { |
185 | return Err(ExpandError::InvalidRepeat); | 189 | return Err(ParseError::InvalidRepeat); |
186 | } | 190 | } |
187 | puncts.push(punct.clone()) | 191 | puncts.push(punct.clone()) |
188 | } | 192 | } |
189 | _ => return Err(ExpandError::InvalidRepeat), | 193 | _ => return Err(ParseError::InvalidRepeat), |
190 | } | 194 | } |
191 | continue; | 195 | continue; |
192 | } | 196 | } |
@@ -196,5 +200,5 @@ fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), Exp | |||
196 | } | 200 | } |
197 | } | 201 | } |
198 | } | 202 | } |
199 | Err(ExpandError::InvalidRepeat) | 203 | Err(ParseError::InvalidRepeat) |
200 | } | 204 | } |
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 8d978163d..1c467facd 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -33,19 +33,18 @@ mod rule_parsing { | |||
33 | 33 | ||
34 | #[test] | 34 | #[test] |
35 | fn test_invalid_arms() { | 35 | fn test_invalid_arms() { |
36 | fn check(macro_body: &str, err: &str) { | 36 | fn check(macro_body: &str, err: ParseError) { |
37 | let m = parse_macro_arm(macro_body); | 37 | let m = parse_macro_arm(macro_body); |
38 | assert_eq!(m, Err(ParseError::Expected(String::from(err)))); | 38 | assert_eq!(m, Err(err.into())); |
39 | } | 39 | } |
40 | check("invalid", ParseError::Expected("expected subtree".into())); | ||
40 | 41 | ||
41 | check("invalid", "expected subtree"); | 42 | check("$i:ident => ()", ParseError::Expected("expected subtree".into())); |
43 | check("($i:ident) ()", ParseError::Expected("expected `=`".into())); | ||
44 | check("($($i:ident)_) => ()", ParseError::InvalidRepeat); | ||
42 | 45 | ||
43 | check("$i:ident => ()", "expected subtree"); | 46 | check("($i) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); |
44 | check("($i:ident) ()", "expected `=`"); | 47 | check("($i:) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); |
45 | check("($($i:ident)_) => ()", "invalid repeat"); | ||
46 | |||
47 | check("($i) => ($i)", "invalid macro definition"); | ||
48 | check("($i:) => ($i)", "invalid macro definition"); | ||
49 | } | 48 | } |
50 | 49 | ||
51 | fn parse_macro_arm(arm_definition: &str) -> Result<crate::MacroRules, ParseError> { | 50 | fn parse_macro_arm(arm_definition: &str) -> Result<crate::MacroRules, ParseError> { |