diff options
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander.rs')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 152 |
1 files changed, 123 insertions, 29 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index d5189b537..4b007647c 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -81,9 +81,29 @@ struct Bindings { | |||
81 | enum Binding { | 81 | enum Binding { |
82 | Simple(tt::TokenTree), | 82 | Simple(tt::TokenTree), |
83 | Nested(Vec<Binding>), | 83 | Nested(Vec<Binding>), |
84 | Empty, | ||
84 | } | 85 | } |
85 | 86 | ||
86 | impl Bindings { | 87 | impl Bindings { |
88 | fn push_optional(&mut self, name: &SmolStr) { | ||
89 | // FIXME: Do we have a better way to represent an empty token ? | ||
90 | // Insert an empty subtree for empty token | ||
91 | self.inner.insert( | ||
92 | name.clone(), | ||
93 | Binding::Simple( | ||
94 | tt::Subtree { delimiter: tt::Delimiter::None, token_trees: vec![] }.into(), | ||
95 | ), | ||
96 | ); | ||
97 | } | ||
98 | |||
99 | fn push_empty(&mut self, name: &SmolStr) { | ||
100 | self.inner.insert(name.clone(), Binding::Empty); | ||
101 | } | ||
102 | |||
103 | fn contains(&self, name: &SmolStr) -> bool { | ||
104 | self.inner.contains_key(name) | ||
105 | } | ||
106 | |||
87 | fn get(&self, name: &SmolStr, nesting: &[usize]) -> Result<&tt::TokenTree, ExpandError> { | 107 | fn get(&self, name: &SmolStr, nesting: &[usize]) -> Result<&tt::TokenTree, ExpandError> { |
88 | let mut b = self | 108 | let mut b = self |
89 | .inner | 109 | .inner |
@@ -96,6 +116,12 @@ impl Bindings { | |||
96 | "could not find nested binding `{}`", | 116 | "could not find nested binding `{}`", |
97 | name | 117 | name |
98 | )))?, | 118 | )))?, |
119 | Binding::Empty => { | ||
120 | return Err(ExpandError::BindingError(format!( | ||
121 | "could not find empty binding `{}`", | ||
122 | name | ||
123 | ))) | ||
124 | } | ||
99 | }; | 125 | }; |
100 | } | 126 | } |
101 | match b { | 127 | match b { |
@@ -104,16 +130,26 @@ impl Bindings { | |||
104 | "expected simple binding, found nested binding `{}`", | 130 | "expected simple binding, found nested binding `{}`", |
105 | name | 131 | name |
106 | ))), | 132 | ))), |
133 | Binding::Empty => Err(ExpandError::BindingError(format!( | ||
134 | "expected simple binding, found empty binding `{}`", | ||
135 | name | ||
136 | ))), | ||
107 | } | 137 | } |
108 | } | 138 | } |
109 | 139 | ||
110 | fn push_nested(&mut self, nested: Bindings) -> Result<(), ExpandError> { | 140 | fn push_nested(&mut self, idx: usize, nested: Bindings) -> Result<(), ExpandError> { |
111 | for (key, value) in nested.inner { | 141 | for (key, value) in nested.inner { |
112 | if !self.inner.contains_key(&key) { | 142 | if !self.inner.contains_key(&key) { |
113 | self.inner.insert(key.clone(), Binding::Nested(Vec::new())); | 143 | self.inner.insert(key.clone(), Binding::Nested(Vec::new())); |
114 | } | 144 | } |
115 | match self.inner.get_mut(&key) { | 145 | match self.inner.get_mut(&key) { |
116 | Some(Binding::Nested(it)) => it.push(value), | 146 | Some(Binding::Nested(it)) => { |
147 | // insert empty nested bindings before this one | ||
148 | while it.len() < idx { | ||
149 | it.push(Binding::Nested(vec![])); | ||
150 | } | ||
151 | it.push(value); | ||
152 | } | ||
117 | _ => { | 153 | _ => { |
118 | return Err(ExpandError::BindingError(format!( | 154 | return Err(ExpandError::BindingError(format!( |
119 | "could not find binding `{}`", | 155 | "could not find binding `{}`", |
@@ -130,6 +166,24 @@ impl Bindings { | |||
130 | } | 166 | } |
131 | } | 167 | } |
132 | 168 | ||
169 | fn collect_vars(subtree: &crate::Subtree) -> Vec<SmolStr> { | ||
170 | let mut res = vec![]; | ||
171 | |||
172 | for tkn in subtree.token_trees.iter() { | ||
173 | match tkn { | ||
174 | crate::TokenTree::Leaf(crate::Leaf::Var(crate::Var { text, .. })) => { | ||
175 | res.push(text.clone()); | ||
176 | } | ||
177 | crate::TokenTree::Subtree(subtree) => { | ||
178 | res.extend(collect_vars(subtree)); | ||
179 | } | ||
180 | _ => {} | ||
181 | } | ||
182 | } | ||
183 | |||
184 | res | ||
185 | } | ||
186 | |||
133 | fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, ExpandError> { | 187 | fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, ExpandError> { |
134 | let mut res = Bindings::default(); | 188 | let mut res = Bindings::default(); |
135 | for pat in pattern.token_trees.iter() { | 189 | for pat in pattern.token_trees.iter() { |
@@ -178,10 +232,6 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
178 | input.eat_meta().ok_or(ExpandError::UnexpectedToken)?.clone(); | 232 | input.eat_meta().ok_or(ExpandError::UnexpectedToken)?.clone(); |
179 | res.inner.insert(text.clone(), Binding::Simple(meta.into())); | 233 | res.inner.insert(text.clone(), Binding::Simple(meta.into())); |
180 | } | 234 | } |
181 | // FIXME: | ||
182 | // Enable followiing code when everything is fixed | ||
183 | // At least we can dogfood itself to not stackoverflow | ||
184 | // | ||
185 | "tt" => { | 235 | "tt" => { |
186 | let token = input.eat().ok_or(ExpandError::UnexpectedToken)?.clone(); | 236 | let token = input.eat().ok_or(ExpandError::UnexpectedToken)?.clone(); |
187 | res.inner.insert(text.clone(), Binding::Simple(token.into())); | 237 | res.inner.insert(text.clone(), Binding::Simple(token.into())); |
@@ -206,8 +256,13 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
206 | ); | 256 | ); |
207 | } | 257 | } |
208 | "vis" => { | 258 | "vis" => { |
209 | let vis = input.eat_vis().ok_or(ExpandError::UnexpectedToken)?.clone(); | 259 | // `vis` is optional |
210 | res.inner.insert(text.clone(), Binding::Simple(vis.into())); | 260 | if let Some(vis) = input.try_eat_vis() { |
261 | let vis = vis.clone(); | ||
262 | res.inner.insert(text.clone(), Binding::Simple(vis.into())); | ||
263 | } else { | ||
264 | res.push_optional(&text); | ||
265 | } | ||
211 | } | 266 | } |
212 | 267 | ||
213 | _ => return Err(ExpandError::UnexpectedToken), | 268 | _ => return Err(ExpandError::UnexpectedToken), |
@@ -236,7 +291,6 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
236 | loop { | 291 | loop { |
237 | match match_lhs(subtree, input) { | 292 | match match_lhs(subtree, input) { |
238 | Ok(nested) => { | 293 | Ok(nested) => { |
239 | counter += 1; | ||
240 | limit -= 1; | 294 | limit -= 1; |
241 | if limit == 0 { | 295 | if limit == 0 { |
242 | log::warn!("match_lhs excced in repeat pattern exceed limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", subtree, input, kind, separator); | 296 | log::warn!("match_lhs excced in repeat pattern exceed limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", subtree, input, kind, separator); |
@@ -244,7 +298,8 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
244 | } | 298 | } |
245 | 299 | ||
246 | memento = input.save(); | 300 | memento = input.save(); |
247 | res.push_nested(nested)?; | 301 | res.push_nested(counter, nested)?; |
302 | counter += 1; | ||
248 | if counter == 1 { | 303 | if counter == 1 { |
249 | if let crate::RepeatKind::ZeroOrOne = kind { | 304 | if let crate::RepeatKind::ZeroOrOne = kind { |
250 | break; | 305 | break; |
@@ -252,20 +307,9 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
252 | } | 307 | } |
253 | 308 | ||
254 | if let Some(separator) = separator { | 309 | if let Some(separator) = separator { |
255 | use crate::Separator::*; | ||
256 | |||
257 | if !input | 310 | if !input |
258 | .eat_seperator() | 311 | .eat_seperator() |
259 | .map(|sep| match (sep, separator) { | 312 | .map(|sep| sep == *separator) |
260 | (Ident(ref a), Ident(ref b)) => a.text == b.text, | ||
261 | (Literal(ref a), Literal(ref b)) => a.text == b.text, | ||
262 | (Puncts(ref a), Puncts(ref b)) if a.len() == b.len() => { | ||
263 | let a_iter = a.iter().map(|a| a.char); | ||
264 | let b_iter = b.iter().map(|b| b.char); | ||
265 | a_iter.eq(b_iter) | ||
266 | } | ||
267 | _ => false, | ||
268 | }) | ||
269 | .unwrap_or(false) | 313 | .unwrap_or(false) |
270 | { | 314 | { |
271 | input.rollback(memento); | 315 | input.rollback(memento); |
@@ -284,6 +328,10 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
284 | crate::RepeatKind::OneOrMore if counter == 0 => { | 328 | crate::RepeatKind::OneOrMore if counter == 0 => { |
285 | return Err(ExpandError::UnexpectedToken); | 329 | return Err(ExpandError::UnexpectedToken); |
286 | } | 330 | } |
331 | _ if counter == 0 => { | ||
332 | // Collect all empty variables in subtrees | ||
333 | collect_vars(subtree).iter().for_each(|s| res.push_empty(s)); | ||
334 | } | ||
287 | _ => {} | 335 | _ => {} |
288 | } | 336 | } |
289 | } | 337 | } |
@@ -322,6 +370,14 @@ fn expand_subtree( | |||
322 | .token_trees | 370 | .token_trees |
323 | .iter() | 371 | .iter() |
324 | .map(|it| expand_tt(it, ctx)) | 372 | .map(|it| expand_tt(it, ctx)) |
373 | .filter(|it| { | ||
374 | // Filter empty subtree | ||
375 | if let Ok(tt::TokenTree::Subtree(subtree)) = it { | ||
376 | subtree.delimiter != tt::Delimiter::None || !subtree.token_trees.is_empty() | ||
377 | } else { | ||
378 | true | ||
379 | } | ||
380 | }) | ||
325 | .collect::<Result<Vec<_>, ExpandError>>()?; | 381 | .collect::<Result<Vec<_>, ExpandError>>()?; |
326 | 382 | ||
327 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) | 383 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) |
@@ -356,14 +412,23 @@ fn expand_tt( | |||
356 | let mut has_seps = 0; | 412 | let mut has_seps = 0; |
357 | let mut counter = 0; | 413 | let mut counter = 0; |
358 | 414 | ||
415 | // We store the old var expanded value, and restore it later | ||
416 | // It is because before this `$repeat`, | ||
417 | // it is possible some variables already expanad in the same subtree | ||
418 | // | ||
419 | // `some_var_expanded` keep check if the deeper subtree has expanded variables | ||
359 | let mut some_var_expanded = false; | 420 | let mut some_var_expanded = false; |
421 | let old_var_expanded = ctx.var_expanded; | ||
360 | ctx.var_expanded = false; | 422 | ctx.var_expanded = false; |
361 | 423 | ||
362 | while let Ok(t) = expand_subtree(&repeat.subtree, ctx) { | 424 | while let Ok(t) = expand_subtree(&repeat.subtree, ctx) { |
363 | // if no var expaned in the child, we count it as a fail | 425 | // if no var expanded in the child, we count it as a fail |
364 | if !ctx.var_expanded { | 426 | if !ctx.var_expanded { |
365 | break; | 427 | break; |
366 | } | 428 | } |
429 | |||
430 | // Reset `ctx.var_expandeded` to see if there is other expanded variable | ||
431 | // in the next matching | ||
367 | some_var_expanded = true; | 432 | some_var_expanded = true; |
368 | ctx.var_expanded = false; | 433 | ctx.var_expanded = false; |
369 | 434 | ||
@@ -407,7 +472,8 @@ fn expand_tt( | |||
407 | } | 472 | } |
408 | } | 473 | } |
409 | 474 | ||
410 | ctx.var_expanded = some_var_expanded; | 475 | // Restore the `var_expanded` by combining old one and the new one |
476 | ctx.var_expanded = some_var_expanded || old_var_expanded; | ||
411 | 477 | ||
412 | ctx.nesting.pop().unwrap(); | 478 | ctx.nesting.pop().unwrap(); |
413 | for _ in 0..has_seps { | 479 | for _ in 0..has_seps { |
@@ -433,6 +499,33 @@ fn expand_tt( | |||
433 | // FIXME: Properly handle $crate token | 499 | // FIXME: Properly handle $crate token |
434 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) | 500 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) |
435 | .into() | 501 | .into() |
502 | } else if !ctx.bindings.contains(&v.text) { | ||
503 | // Note that it is possible to have a `$var` inside a macro which is not bound. | ||
504 | // For example: | ||
505 | // ``` | ||
506 | // macro_rules! foo { | ||
507 | // ($a:ident, $b:ident, $c:tt) => { | ||
508 | // macro_rules! bar { | ||
509 | // ($bi:ident) => { | ||
510 | // fn $bi() -> u8 {$c} | ||
511 | // } | ||
512 | // } | ||
513 | // } | ||
514 | // ``` | ||
515 | // We just treat it a normal tokens | ||
516 | tt::Subtree { | ||
517 | delimiter: tt::Delimiter::None, | ||
518 | token_trees: vec![ | ||
519 | tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone }) | ||
520 | .into(), | ||
521 | tt::Leaf::from(tt::Ident { | ||
522 | text: v.text.clone(), | ||
523 | id: TokenId::unspecified(), | ||
524 | }) | ||
525 | .into(), | ||
526 | ], | ||
527 | } | ||
528 | .into() | ||
436 | } else { | 529 | } else { |
437 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); | 530 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); |
438 | ctx.var_expanded = true; | 531 | ctx.var_expanded = true; |
@@ -459,11 +552,12 @@ mod tests { | |||
459 | 552 | ||
460 | #[test] | 553 | #[test] |
461 | fn test_expand_rule() { | 554 | fn test_expand_rule() { |
462 | assert_err( | 555 | // FIXME: The missing $var check should be in parsing phase |
463 | "($i:ident) => ($j)", | 556 | // assert_err( |
464 | "foo!{a}", | 557 | // "($i:ident) => ($j)", |
465 | ExpandError::BindingError(String::from("could not find binding `j`")), | 558 | // "foo!{a}", |
466 | ); | 559 | // ExpandError::BindingError(String::from("could not find binding `j`")), |
560 | // ); | ||
467 | 561 | ||
468 | assert_err( | 562 | assert_err( |
469 | "($($i:ident);*) => ($i)", | 563 | "($($i:ident);*) => ($i)", |