diff options
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander.rs')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 167 |
1 files changed, 123 insertions, 44 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index 00fb09a3b..d5189b537 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -21,7 +21,10 @@ fn expand_rule(rule: &crate::Rule, input: &tt::Subtree) -> Result<tt::Subtree, E | |||
21 | if !input.is_eof() { | 21 | if !input.is_eof() { |
22 | return Err(ExpandError::UnexpectedToken); | 22 | return Err(ExpandError::UnexpectedToken); |
23 | } | 23 | } |
24 | expand_subtree(&rule.rhs, &bindings, &mut Vec::new()) | 24 | |
25 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new(), var_expanded: false }; | ||
26 | |||
27 | expand_subtree(&rule.rhs, &mut ctx) | ||
25 | } | 28 | } |
26 | 29 | ||
27 | /// The actual algorithm for expansion is not too hard, but is pretty tricky. | 30 | /// The actual algorithm for expansion is not too hard, but is pretty tricky. |
@@ -179,10 +182,10 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
179 | // Enable followiing code when everything is fixed | 182 | // Enable followiing code when everything is fixed |
180 | // At least we can dogfood itself to not stackoverflow | 183 | // At least we can dogfood itself to not stackoverflow |
181 | // | 184 | // |
182 | // "tt" => { | 185 | "tt" => { |
183 | // let token = input.eat().ok_or(ExpandError::UnexpectedToken)?.clone(); | 186 | let token = input.eat().ok_or(ExpandError::UnexpectedToken)?.clone(); |
184 | // res.inner.insert(text.clone(), Binding::Simple(token.into())); | 187 | res.inner.insert(text.clone(), Binding::Simple(token.into())); |
185 | // } | 188 | } |
186 | "item" => { | 189 | "item" => { |
187 | let item = | 190 | let item = |
188 | input.eat_item().ok_or(ExpandError::UnexpectedToken)?.clone(); | 191 | input.eat_item().ok_or(ExpandError::UnexpectedToken)?.clone(); |
@@ -196,6 +199,7 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
196 | "literal" => { | 199 | "literal" => { |
197 | let literal = | 200 | let literal = |
198 | input.eat_literal().ok_or(ExpandError::UnexpectedToken)?.clone(); | 201 | input.eat_literal().ok_or(ExpandError::UnexpectedToken)?.clone(); |
202 | |||
199 | res.inner.insert( | 203 | res.inner.insert( |
200 | text.clone(), | 204 | text.clone(), |
201 | Binding::Simple(tt::Leaf::from(literal).into()), | 205 | Binding::Simple(tt::Leaf::from(literal).into()), |
@@ -210,7 +214,7 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
210 | } | 214 | } |
211 | } | 215 | } |
212 | crate::Leaf::Punct(punct) => { | 216 | crate::Leaf::Punct(punct) => { |
213 | if input.eat_punct() != Some(punct) { | 217 | if !input.eat_punct().map(|p| p.char == punct.char).unwrap_or(false) { |
214 | return Err(ExpandError::UnexpectedToken); | 218 | return Err(ExpandError::UnexpectedToken); |
215 | } | 219 | } |
216 | } | 220 | } |
@@ -224,20 +228,54 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
224 | crate::TokenTree::Repeat(crate::Repeat { subtree, kind, separator }) => { | 228 | crate::TokenTree::Repeat(crate::Repeat { subtree, kind, separator }) => { |
225 | // Dirty hack to make macro-expansion terminate. | 229 | // Dirty hack to make macro-expansion terminate. |
226 | // This should be replaced by a propper macro-by-example implementation | 230 | // This should be replaced by a propper macro-by-example implementation |
227 | let mut limit = 128; | 231 | let mut limit = 65536; |
228 | let mut counter = 0; | 232 | let mut counter = 0; |
229 | while let Ok(nested) = match_lhs(subtree, input) { | 233 | |
230 | counter += 1; | 234 | let mut memento = input.save(); |
231 | limit -= 1; | 235 | |
232 | if limit == 0 { | 236 | loop { |
233 | break; | 237 | match match_lhs(subtree, input) { |
234 | } | 238 | Ok(nested) => { |
235 | res.push_nested(nested)?; | 239 | counter += 1; |
236 | if let Some(separator) = *separator { | 240 | limit -= 1; |
237 | if !input.is_eof() { | 241 | if limit == 0 { |
238 | if input.eat_punct().map(|p| p.char) != Some(separator) { | 242 | log::warn!("match_lhs excced in repeat pattern exceed limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", subtree, input, kind, separator); |
239 | return Err(ExpandError::UnexpectedToken); | 243 | break; |
244 | } | ||
245 | |||
246 | memento = input.save(); | ||
247 | res.push_nested(nested)?; | ||
248 | if counter == 1 { | ||
249 | if let crate::RepeatKind::ZeroOrOne = kind { | ||
250 | break; | ||
251 | } | ||
240 | } | 252 | } |
253 | |||
254 | if let Some(separator) = separator { | ||
255 | use crate::Separator::*; | ||
256 | |||
257 | if !input | ||
258 | .eat_seperator() | ||
259 | .map(|sep| match (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) | ||
270 | { | ||
271 | input.rollback(memento); | ||
272 | break; | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | Err(_) => { | ||
277 | input.rollback(memento); | ||
278 | break; | ||
241 | } | 279 | } |
242 | } | 280 | } |
243 | } | 281 | } |
@@ -246,10 +284,6 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
246 | crate::RepeatKind::OneOrMore if counter == 0 => { | 284 | crate::RepeatKind::OneOrMore if counter == 0 => { |
247 | return Err(ExpandError::UnexpectedToken); | 285 | return Err(ExpandError::UnexpectedToken); |
248 | } | 286 | } |
249 | crate::RepeatKind::ZeroOrOne if counter > 1 => { | ||
250 | return Err(ExpandError::UnexpectedToken); | ||
251 | } | ||
252 | |||
253 | _ => {} | 287 | _ => {} |
254 | } | 288 | } |
255 | } | 289 | } |
@@ -273,15 +307,21 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
273 | Ok(res) | 307 | Ok(res) |
274 | } | 308 | } |
275 | 309 | ||
310 | #[derive(Debug)] | ||
311 | struct ExpandCtx<'a> { | ||
312 | bindings: &'a Bindings, | ||
313 | nesting: Vec<usize>, | ||
314 | var_expanded: bool, | ||
315 | } | ||
316 | |||
276 | fn expand_subtree( | 317 | fn expand_subtree( |
277 | template: &crate::Subtree, | 318 | template: &crate::Subtree, |
278 | bindings: &Bindings, | 319 | ctx: &mut ExpandCtx, |
279 | nesting: &mut Vec<usize>, | ||
280 | ) -> Result<tt::Subtree, ExpandError> { | 320 | ) -> Result<tt::Subtree, ExpandError> { |
281 | let token_trees = template | 321 | let token_trees = template |
282 | .token_trees | 322 | .token_trees |
283 | .iter() | 323 | .iter() |
284 | .map(|it| expand_tt(it, bindings, nesting)) | 324 | .map(|it| expand_tt(it, ctx)) |
285 | .collect::<Result<Vec<_>, ExpandError>>()?; | 325 | .collect::<Result<Vec<_>, ExpandError>>()?; |
286 | 326 | ||
287 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) | 327 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) |
@@ -303,43 +343,81 @@ fn reduce_single_token(mut subtree: tt::Subtree) -> tt::TokenTree { | |||
303 | 343 | ||
304 | fn expand_tt( | 344 | fn expand_tt( |
305 | template: &crate::TokenTree, | 345 | template: &crate::TokenTree, |
306 | bindings: &Bindings, | 346 | ctx: &mut ExpandCtx, |
307 | nesting: &mut Vec<usize>, | ||
308 | ) -> Result<tt::TokenTree, ExpandError> { | 347 | ) -> Result<tt::TokenTree, ExpandError> { |
309 | let res: tt::TokenTree = match template { | 348 | let res: tt::TokenTree = match template { |
310 | crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, bindings, nesting)?.into(), | 349 | crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, ctx)?.into(), |
311 | crate::TokenTree::Repeat(repeat) => { | 350 | crate::TokenTree::Repeat(repeat) => { |
312 | let mut token_trees: Vec<tt::TokenTree> = Vec::new(); | 351 | let mut token_trees: Vec<tt::TokenTree> = Vec::new(); |
313 | nesting.push(0); | 352 | ctx.nesting.push(0); |
314 | // Dirty hack to make macro-expansion terminate. | 353 | // Dirty hack to make macro-expansion terminate. |
315 | // This should be replaced by a propper macro-by-example implementation | 354 | // This should be replaced by a propper macro-by-example implementation |
316 | let mut limit = 128; | 355 | let mut limit = 65536; |
317 | let mut has_sep = false; | 356 | let mut has_seps = 0; |
357 | let mut counter = 0; | ||
318 | 358 | ||
319 | while let Ok(t) = expand_subtree(&repeat.subtree, bindings, nesting) { | 359 | let mut some_var_expanded = false; |
360 | ctx.var_expanded = false; | ||
361 | |||
362 | while let Ok(t) = expand_subtree(&repeat.subtree, ctx) { | ||
363 | // if no var expaned in the child, we count it as a fail | ||
364 | if !ctx.var_expanded { | ||
365 | break; | ||
366 | } | ||
367 | some_var_expanded = true; | ||
368 | ctx.var_expanded = false; | ||
369 | |||
370 | counter += 1; | ||
320 | limit -= 1; | 371 | limit -= 1; |
321 | if limit == 0 { | 372 | if limit == 0 { |
373 | log::warn!( | ||
374 | "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}", | ||
375 | template, | ||
376 | ctx | ||
377 | ); | ||
322 | break; | 378 | break; |
323 | } | 379 | } |
324 | let idx = nesting.pop().unwrap(); | 380 | |
325 | nesting.push(idx + 1); | 381 | let idx = ctx.nesting.pop().unwrap(); |
382 | ctx.nesting.push(idx + 1); | ||
326 | token_trees.push(reduce_single_token(t).into()); | 383 | token_trees.push(reduce_single_token(t).into()); |
327 | 384 | ||
328 | if let Some(sep) = repeat.separator { | 385 | if let Some(ref sep) = repeat.separator { |
329 | let punct = | 386 | match sep { |
330 | tt::Leaf::from(tt::Punct { char: sep, spacing: tt::Spacing::Alone }); | 387 | crate::Separator::Ident(ident) => { |
331 | token_trees.push(punct.into()); | 388 | has_seps = 1; |
332 | has_sep = true; | 389 | token_trees.push(tt::Leaf::from(ident.clone()).into()); |
390 | } | ||
391 | crate::Separator::Literal(lit) => { | ||
392 | has_seps = 1; | ||
393 | token_trees.push(tt::Leaf::from(lit.clone()).into()); | ||
394 | } | ||
395 | |||
396 | crate::Separator::Puncts(puncts) => { | ||
397 | has_seps = puncts.len(); | ||
398 | for punct in puncts { | ||
399 | token_trees.push(tt::Leaf::from(*punct).into()); | ||
400 | } | ||
401 | } | ||
402 | } | ||
403 | } | ||
404 | |||
405 | if let crate::RepeatKind::ZeroOrOne = repeat.kind { | ||
406 | break; | ||
333 | } | 407 | } |
334 | } | 408 | } |
335 | nesting.pop().unwrap(); | ||
336 | 409 | ||
337 | // Dirty hack for remove the last sep | 410 | ctx.var_expanded = some_var_expanded; |
338 | // if it is a "," undo the push | 411 | |
339 | if has_sep && repeat.separator.unwrap() == ',' { | 412 | ctx.nesting.pop().unwrap(); |
413 | for _ in 0..has_seps { | ||
340 | token_trees.pop(); | 414 | token_trees.pop(); |
341 | } | 415 | } |
342 | 416 | ||
417 | if crate::RepeatKind::OneOrMore == repeat.kind && counter == 0 { | ||
418 | return Err(ExpandError::UnexpectedToken); | ||
419 | } | ||
420 | |||
343 | // Check if it is a singel token subtree without any delimiter | 421 | // Check if it is a singel token subtree without any delimiter |
344 | // e.g {Delimiter:None> ['>'] /Delimiter:None>} | 422 | // e.g {Delimiter:None> ['>'] /Delimiter:None>} |
345 | reduce_single_token(tt::Subtree { token_trees, delimiter: tt::Delimiter::None }) | 423 | reduce_single_token(tt::Subtree { token_trees, delimiter: tt::Delimiter::None }) |
@@ -356,7 +434,8 @@ fn expand_tt( | |||
356 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) | 434 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) |
357 | .into() | 435 | .into() |
358 | } else { | 436 | } else { |
359 | let tkn = bindings.get(&v.text, nesting)?.clone(); | 437 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); |
438 | ctx.var_expanded = true; | ||
360 | 439 | ||
361 | if let tt::TokenTree::Subtree(subtree) = tkn { | 440 | if let tt::TokenTree::Subtree(subtree) = tkn { |
362 | reduce_single_token(subtree) | 441 | reduce_single_token(subtree) |