use std::env::args;
use abnf::{
rulelist,
types::{Node, Rule},
};
trait Dependencies {
fn calc_dependencies(&self) -> Vec<String>;
}
impl Dependencies for Rule {
fn calc_dependencies(&self) -> Vec<String> {
self.node().calc_dependencies()
}
}
impl Dependencies for Node {
fn calc_dependencies(&self) -> Vec<String> {
match self {
Node::Alternatives(nodes) | Node::Concatenation(nodes) => {
let mut ret_val = Vec::new();
for node in nodes {
for dep in node.calc_dependencies() {
if !ret_val.contains(&dep) {
ret_val.push(dep);
}
}
}
ret_val
}
Node::Group(node) | Node::Optional(node) => node.calc_dependencies(),
Node::Repetition { node, .. } => node.calc_dependencies(),
Node::Rulename(name) => vec![name.to_owned()],
Node::TerminalValues(_) | Node::String(_) | Node::Prose(_) => vec![],
}
}
}
fn print_gml(rules: Vec<Rule>) {
println!("graph [");
for rule in rules.iter() {
println!("\tnode [id \"{}\" label \"{}\"]", rule.name(), rule.name());
}
for rule in rules.iter() {
for dep in rule.calc_dependencies() {
if [
"ALPHA", "BIT", "CHAR", "CR", "CRLF", "CTL", "DIGIT", "DQUOTE", "HEXDIG", "HTAB",
"LF", "LWSP", "OCTET", "SP", "VCHAR", "WSP",
]
.contains(&&dep[..])
{
continue;
}
println!("\tedge [source \"{}\" target \"{}\"]", rule.name(), dep);
}
}
println!("]");
}
fn print_gv(rules: Vec<Rule>) {
println!("digraph {{");
println!("\tcompound=true;");
println!("\toverlap=scalexy;");
println!("\tsplines=true;");
println!("\tlayout=neato;\n");
for rule in rules.iter() {
let name = rule.name().to_owned().replace("-", "_");
let deps = rule
.calc_dependencies()
.iter()
.map(|name| name.replace("-", "_"))
.collect::<Vec<_>>();
println!("\t{} -> {{{}}}", name, deps.join(" "));
}
println!("}}");
}
fn main() -> std::io::Result<()> {
let rules = {
let data =
std::fs::read_to_string(args().nth(1).expect("USAGE: dependencies file [gml|gv]"))?;
rulelist(&data).unwrap_or_else(|e| {
println!("{}", e);
std::process::exit(1);
})
};
let format = args().nth(2).unwrap_or(String::from("gml"));
match format.as_ref() {
"gml" => print_gml(rules),
"gv" => print_gv(rules),
other => {
eprintln!("Unknown format \"{}\". Try \"gml\" or \"gv\".", other);
std::process::exit(1);
}
}
Ok(())
}