use crate::grammar::repr::*;
use crate::normalize::NormResult;
mod graph;
#[cfg(test)]
mod test;
pub fn inline(mut grammar: Grammar) -> NormResult<Grammar> {
let order = graph::inline_order(&grammar)?;
for nt in order {
inline_nt(&mut grammar, &nt);
}
Ok(grammar)
}
fn inline_nt(grammar: &mut Grammar, inline_nt: &NonterminalString) {
let inline_productions = grammar.productions_for(inline_nt).to_vec();
for data in grammar.nonterminals.values_mut() {
let mut new_productions = vec![];
let mut new_action_fn_defns = vec![];
for into_production in &data.productions {
if !into_production
.symbols
.contains(&Symbol::Nonterminal(inline_nt.clone()))
{
new_productions.push(into_production.clone());
continue;
}
let mut inliner = Inliner {
action_fn_defns: &grammar.action_fn_defns,
inline_nonterminal: inline_nt.clone(),
into_production,
inline_fallible: 0,
inline_productions: &inline_productions,
new_symbols: vec![],
new_productions: &mut new_productions,
new_action_fn_defns: &mut new_action_fn_defns,
};
inliner.inline(&into_production.symbols);
}
data.productions = new_productions;
grammar.action_fn_defns.extend(new_action_fn_defns);
}
}
struct Inliner<'a> {
action_fn_defns: &'a [ActionFnDefn],
inline_nonterminal: NonterminalString,
inline_productions: &'a [Production],
inline_fallible: u32,
into_production: &'a Production,
new_symbols: Vec<InlinedSymbol>,
new_productions: &'a mut Vec<Production>,
new_action_fn_defns: &'a mut Vec<ActionFnDefn>,
}
impl<'a> Inliner<'a> {
fn inline(&mut self, into_symbols: &[Symbol]) {
if into_symbols.is_empty() {
let into_action = self.into_production.action;
let into_fallible = self.action_fn_defns[into_action.index()].fallible;
let into_ret_type = self.action_fn_defns[into_action.index()].ret_type.clone();
let inline_fallible = self.inline_fallible != 0;
let index = self.action_fn_defns.len() + self.new_action_fn_defns.len();
let action_fn = ActionFn::new(index);
let inline_defn = InlineActionFnDefn {
action: into_action,
symbols: self.new_symbols.clone(),
};
self.new_action_fn_defns.push(ActionFnDefn {
fallible: into_fallible || inline_fallible,
ret_type: into_ret_type,
kind: ActionFnDefnKind::Inline(inline_defn),
});
let prod_symbols: Vec<Symbol> = self
.new_symbols
.iter()
.flat_map(|sym| match *sym {
InlinedSymbol::Original(ref s) => vec![s.clone()],
InlinedSymbol::Inlined(_, ref s) => s.clone(),
})
.collect();
self.new_productions.push(Production {
nonterminal: self.into_production.nonterminal.clone(),
span: self.into_production.span,
symbols: prod_symbols,
action: action_fn,
});
} else {
let next_symbol = &into_symbols[0];
match *next_symbol {
Symbol::Nonterminal(ref n) if *n == self.inline_nonterminal => {
for inline_production in self.inline_productions {
let inline_action = inline_production.action;
let fallible = self.action_fn_defns[inline_action.index()].fallible;
self.inline_fallible += fallible as u32;
self.new_symbols.push(InlinedSymbol::Inlined(
inline_production.action,
inline_production.symbols.clone(),
));
self.inline(&into_symbols[1..]);
self.new_symbols.pop();
self.inline_fallible -= fallible as u32;
}
}
_ => {
self.new_symbols
.push(InlinedSymbol::Original(next_symbol.clone()));
self.inline(&into_symbols[1..]);
self.new_symbols.pop();
}
}
}
}
}