Fix more bugs

This commit is contained in:
Edwin Cheng
2019-04-25 23:12:57 +08:00
parent 299d97b6d9
commit c55a2dbc1d
8 changed files with 205 additions and 26 deletions

View File

@@ -21,7 +21,10 @@ fn expand_rule(rule: &crate::Rule, input: &tt::Subtree) -> Result<tt::Subtree, E
if !input.is_eof() {
return Err(ExpandError::UnexpectedToken);
}
expand_subtree(&rule.rhs, &bindings, &mut Vec::new())
let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new(), var_expanded: false };
expand_subtree(&rule.rhs, &mut ctx)
}
/// The actual algorithm for expansion is not too hard, but is pretty tricky.
@@ -225,7 +228,7 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
crate::TokenTree::Repeat(crate::Repeat { subtree, kind, separator }) => {
// Dirty hack to make macro-expansion terminate.
// This should be replaced by a propper macro-by-example implementation
let mut limit = 128;
let mut limit = 65536;
let mut counter = 0;
let mut memento = input.save();
@@ -236,6 +239,7 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
counter += 1;
limit -= 1;
if limit == 0 {
log::warn!("match_lhs excced in repeat pattern exceed limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", subtree, input, kind, separator);
break;
}
@@ -303,15 +307,21 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
Ok(res)
}
#[derive(Debug)]
struct ExpandCtx<'a> {
bindings: &'a Bindings,
nesting: Vec<usize>,
var_expanded: bool,
}
fn expand_subtree(
template: &crate::Subtree,
bindings: &Bindings,
nesting: &mut Vec<usize>,
ctx: &mut ExpandCtx,
) -> Result<tt::Subtree, ExpandError> {
let token_trees = template
.token_trees
.iter()
.map(|it| expand_tt(it, bindings, nesting))
.map(|it| expand_tt(it, ctx))
.collect::<Result<Vec<_>, ExpandError>>()?;
Ok(tt::Subtree { token_trees, delimiter: template.delimiter })
@@ -333,26 +343,43 @@ fn reduce_single_token(mut subtree: tt::Subtree) -> tt::TokenTree {
fn expand_tt(
template: &crate::TokenTree,
bindings: &Bindings,
nesting: &mut Vec<usize>,
ctx: &mut ExpandCtx,
) -> Result<tt::TokenTree, ExpandError> {
let res: tt::TokenTree = match template {
crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, bindings, nesting)?.into(),
crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, ctx)?.into(),
crate::TokenTree::Repeat(repeat) => {
let mut token_trees: Vec<tt::TokenTree> = Vec::new();
nesting.push(0);
ctx.nesting.push(0);
// Dirty hack to make macro-expansion terminate.
// This should be replaced by a propper macro-by-example implementation
let mut limit = 128;
let mut limit = 65536;
let mut has_seps = 0;
let mut counter = 0;
while let Ok(t) = expand_subtree(&repeat.subtree, bindings, nesting) {
limit -= 1;
if limit == 0 {
let mut some_var_expanded = false;
ctx.var_expanded = false;
while let Ok(t) = expand_subtree(&repeat.subtree, ctx) {
// if no var expaned in the child, we count it as a fail
if !ctx.var_expanded {
break;
}
let idx = nesting.pop().unwrap();
nesting.push(idx + 1);
some_var_expanded = true;
ctx.var_expanded = false;
counter += 1;
limit -= 1;
if limit == 0 {
log::warn!(
"expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
template,
ctx
);
break;
}
let idx = ctx.nesting.pop().unwrap();
ctx.nesting.push(idx + 1);
token_trees.push(reduce_single_token(t).into());
if let Some(ref sep) = repeat.separator {
@@ -374,12 +401,23 @@ fn expand_tt(
}
}
}
if let crate::RepeatKind::ZeroOrOne = repeat.kind {
break;
}
}
nesting.pop().unwrap();
ctx.var_expanded = some_var_expanded;
ctx.nesting.pop().unwrap();
for _ in 0..has_seps {
token_trees.pop();
}
if crate::RepeatKind::OneOrMore == repeat.kind && counter == 0 {
return Err(ExpandError::UnexpectedToken);
}
// Check if it is a singel token subtree without any delimiter
// e.g {Delimiter:None> ['>'] /Delimiter:None>}
reduce_single_token(tt::Subtree { token_trees, delimiter: tt::Delimiter::None })
@@ -396,7 +434,8 @@ fn expand_tt(
tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() })
.into()
} else {
let tkn = bindings.get(&v.text, nesting)?.clone();
let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone();
ctx.var_expanded = true;
if let tt::TokenTree::Subtree(subtree) = tkn {
reduce_single_token(subtree)