use collections::Set;
use grammar::repr::*;
use lr1::core::*;
use lr1::first::FirstSets;
use lr1::lookahead::*;
use lr1::state_graph::StateGraph;
use super::table::{ConflictIndex, LaneTable};
pub struct LaneTracer<'trace, 'grammar: 'trace> {
states: &'trace [LR0State<'grammar>],
first_sets: FirstSets,
state_graph: StateGraph,
table: LaneTable<'grammar>,
}
impl<'trace, 'grammar> LaneTracer<'trace, 'grammar> {
pub fn new(grammar: &'grammar Grammar,
states: &'trace [LR0State<'grammar>],
conflicts: usize)
-> Self {
LaneTracer {
states: states,
first_sets: FirstSets::new(grammar),
state_graph: StateGraph::new(states),
table: LaneTable::new(grammar, conflicts),
}
}
pub fn into_table(self) -> LaneTable<'grammar> {
self.table
}
pub fn start_trace(&mut self,
state: StateIndex,
conflict: ConflictIndex,
item: LR0Item<'grammar>) {
let mut visited_set = Set::default();
match item.shift_symbol() {
Some((Symbol::Terminal(term), _)) => {
let mut token_set = TokenSet::new();
token_set.insert(Token::Terminal(term));
self.table.add_lookahead(state, conflict, &token_set);
}
Some((Symbol::Nonterminal(_), _)) => {
panic!("invalid conflict item `{:?}`: shifts nonterminal",
item);
}
None => {
self.continue_trace(state, conflict, item, &mut visited_set);
}
}
}
fn continue_trace(&mut self,
state: StateIndex,
conflict: ConflictIndex,
item: LR0Item<'grammar>,
visited: &mut Set<(StateIndex, LR0Item<'grammar>)>) {
if !visited.insert((state, item)) {
return;
}
if item.index > 0 {
let shifted_symbol = item.production.symbols[item.index - 1];
let unshifted_item = Item { index: item.index - 1, ..item };
let predecessors = self.state_graph.predecessors(state, shifted_symbol);
for predecessor in predecessors {
self.table.add_successor(predecessor, state);
self.continue_trace(predecessor, conflict, unshifted_item, visited);
}
return;
}
let state_items = &self.states[state.0].items.vec;
let nonterminal = item.production.nonterminal;
for &pred_item in state_items.iter()
.filter(|i| i.can_shift_nonterminal(nonterminal)) {
let symbol_sets = pred_item.symbol_sets();
let mut first = self.first_sets.first0(symbol_sets.suffix);
let derives_epsilon = first.take_eof();
self.table.add_lookahead(state, conflict, &first);
if derives_epsilon {
self.continue_trace(state, conflict, pred_item, visited);
}
}
}
}