use crate::grammar::repr::*;
use crate::lr1::core::*;
use crate::lr1::lookahead::Lookahead;
use petgraph::graph::NodeIndex;
use petgraph::prelude::*;
use petgraph::{EdgeDirection, Graph};
pub struct StateGraph {
graph: Graph<(), Symbol>,
}
impl StateGraph {
pub fn new<L>(states: &[State<L>]) -> StateGraph
where
L: Lookahead,
{
let nodes = states.len();
let edges = states.iter().map(|s| s.shifts.len() + s.gotos.len()).sum();
let mut graph = Graph::with_capacity(nodes, edges);
for i in 0..states.len() {
let j = graph.add_node(());
assert_eq!(i, j.index());
}
for (i, state) in states.iter().enumerate() {
let shifts = state
.shifts
.iter()
.map(|(terminal, &state)| (Symbol::Terminal(terminal.clone()), state));
let gotos = state
.gotos
.iter()
.map(|(nt, &state)| (Symbol::Nonterminal(nt.clone()), state));
let it = shifts.chain(gotos).map(|(symbol, successor)| {
(NodeIndex::new(i), NodeIndex::new(successor.0), symbol)
});
graph.extend_with_edges(it);
}
StateGraph { graph }
}
pub fn trace_back(
&self,
initial_state_index: StateIndex,
initial_symbols: &[Symbol],
) -> Vec<StateIndex> {
let mut stack = vec![(initial_state_index, initial_symbols)];
let mut result = vec![];
while let Some((state_index, symbols)) = stack.pop() {
if let Some((head, tail)) = symbols.split_last() {
stack.extend(
self.graph
.edges_directed(NodeIndex::new(state_index.0), EdgeDirection::Incoming)
.filter(|edge| edge.weight() == head)
.map(|edge| (StateIndex(edge.source().index()), tail)),
);
} else {
result.push(state_index);
}
}
result.sort();
result.dedup();
result
}
pub fn successors(&self, state_index: StateIndex) -> impl Iterator<Item = StateIndex> + '_ {
self.graph
.edges_directed(NodeIndex::new(state_index.0), EdgeDirection::Outgoing)
.map(|edge| StateIndex(edge.target().index()))
}
pub fn predecessors<'a>(
&'a self,
state_index: StateIndex,
symbol: &'a Symbol,
) -> impl Iterator<Item = StateIndex> + 'a {
self.graph
.edges_directed(NodeIndex::new(state_index.0), EdgeDirection::Incoming)
.filter(move |edge| edge.weight() == symbol)
.map(|edge| StateIndex(edge.source().index()))
}
}