#![allow(dead_code)]
use intern::intern;
use normalize::{NormError, NormResult};
use petgraph::graph::{Graph, NodeIndex};
use grammar::consts::INLINE;
use grammar::repr::*;
use collections::{map, Map};
#[cfg(test)]
mod test;
pub fn inline_order(grammar: &Grammar) -> NormResult<Vec<NonterminalString>> {
let mut graph = NonterminalGraph::new(grammar);
graph.create_nodes();
graph.add_edges();
graph.inline_order()
}
struct NonterminalGraph<'grammar> {
grammar: &'grammar Grammar,
graph: Graph<NonterminalString, ()>,
nonterminal_map: Map<NonterminalString, NodeIndex>,
}
#[derive(Copy, Clone)]
enum WalkState {
NotVisited,
Visiting,
Visited,
}
impl<'grammar> NonterminalGraph<'grammar> {
fn new(grammar: &'grammar Grammar) -> NonterminalGraph<'grammar> {
NonterminalGraph {
grammar: grammar,
graph: Graph::new(),
nonterminal_map: map(),
}
}
fn create_nodes(&mut self) {
let inline = intern(INLINE);
for (&name, data) in &self.grammar.nonterminals {
if data.annotations.iter().any(|a| a.id == inline) {
let index = self.graph.add_node(name);
self.nonterminal_map.insert(name, index);
}
}
}
fn add_edges(&mut self) {
for production in self.grammar.nonterminals.values().flat_map(|d| &d.productions) {
let from_index = match self.nonterminal_map.get(&production.nonterminal) {
Some(&index) => index,
None => continue, };
for &symbol in &production.symbols {
match symbol {
Symbol::Nonterminal(to) => {
if let Some(&to_index) = self.nonterminal_map.get(&to) {
self.graph.add_edge(from_index, to_index, ());
}
}
Symbol::Terminal(_) => { }
}
}
}
}
fn inline_order(&self) -> NormResult<Vec<NonterminalString>> {
let mut states = vec![WalkState::NotVisited; self.graph.node_count()];
let mut result = vec![];
for node in self.nonterminal_map.values().cloned() {
try!(self.walk(&mut states, &mut result, node));
}
Ok(result)
}
fn walk(&self,
states: &mut Vec<WalkState>,
result: &mut Vec<NonterminalString>,
source: NodeIndex)
-> NormResult<()>
{
let nt = *self.graph.node_weight(source).unwrap();
match states[source.index()] {
WalkState::NotVisited => {
states[source.index()] = WalkState::Visiting;
for target in self.graph.neighbors(source) {
try!(self.walk(states, result, target));
}
states[source.index()] = WalkState::Visited;
result.push(nt);
Ok(())
}
WalkState::Visited => {
Ok(())
}
WalkState::Visiting => {
return_err!(
self.grammar.nonterminals[&nt].span,
"cyclic inline directive: `{}` would have to be inlined into itself", nt);
}
}
}
}