extern crate asexp;
extern crate petgraph;
use asexp::Sexp;
use asexp::atom::Atom;
use asexp::token::{Token, Tokenizer};
use petgraph::graph::NodeIndex;
use petgraph::{Directed, Graph};
use std::collections::BTreeMap;
pub fn parse_gml<NodeWeightFn, EdgeWeightFn, N, E>(
s: &str,
node_weight_fn: &NodeWeightFn,
edge_weight_fn: &EdgeWeightFn,
) -> Result<Graph<N, E, Directed>, &'static str>
where
NodeWeightFn: Fn(Option<&Sexp>) -> Option<N>,
EdgeWeightFn: Fn(Option<&Sexp>) -> Option<E>,
{
match parse_gml_to_sexp(s) {
Ok(sexp) => sexp_to_graph(sexp, node_weight_fn, edge_weight_fn),
Err(_) => Err("Invalid GML"),
}
}
fn parse_gml_to_sexp(s: &str) -> Result<Sexp, ()> {
let iter = Tokenizer::new(s, true).with_curly_around();
let iter = iter.map(|t| match t {
Token::OpenBracket => Token::OpenCurly,
Token::CloseBracket => Token::CloseCurly,
a => a,
});
Sexp::parse_iter(iter)
}
fn sexp_to_graph<NodeWeightFn, EdgeWeightFn, N, E>(
sexp: Sexp,
node_weight_fn: &NodeWeightFn,
edge_weight_fn: &EdgeWeightFn,
) -> Result<Graph<N, E, Directed>, &'static str>
where
NodeWeightFn: Fn(Option<&Sexp>) -> Option<N>,
EdgeWeightFn: Fn(Option<&Sexp>) -> Option<E>,
{
let mut map = try!(sexp.into_map());
if let Some(Sexp::Map(v)) = map.remove("graph") {
let mut node_map: BTreeMap<u64, NodeIndex> = BTreeMap::new();
let mut graph = Graph::new();
let mut edges = Vec::new();
for (k, v) in v {
match k.get_str() {
Some("directed") => {
match v.get_uint() {
Some(1) => {}
_ => {
return Err("only directed graph supported");
}
}
}
Some("node") => {
let node_info = try!(v.into_map());
if let Some(&Sexp::Atom(Atom::UInt(node_id))) = node_info.get("id") {
match node_weight_fn(node_info.get("weight")) {
Some(weight) => {
let idx = graph.add_node(weight);
if let Some(_) = node_map.insert(node_id, idx) {
return Err("duplicate node-id");
}
}
None => {
return Err("invalid node weight");
}
}
} else {
return Err("Invalid id");
}
}
Some("edge") => {
let edge_info = try!(v.into_map());
let source =
if let Some(&Sexp::Atom(Atom::UInt(source))) = edge_info.get("source") {
source
} else {
return Err("Invalid source id");
};
let target =
if let Some(&Sexp::Atom(Atom::UInt(target))) = edge_info.get("target") {
target
} else {
return Err("Invalid target id");
};
match edge_weight_fn(edge_info.get("weight")) {
Some(weight) => {
edges.push((source, target, weight));
}
None => {
return Err("invalid edge weight");
}
}
}
_ => {
return Err("invalid item");
}
}
}
for (source, target, weight) in edges {
let source_idx = node_map[&source];
let target_idx = node_map[&target];
graph.add_edge(source_idx, target_idx, weight);
}
Ok(graph)
} else {
return Err("no graph given or invalid");
}
}
#[test]
fn test_parse_gml() {
let gml = "
# comment
graph
[
directed 1
node
[
id 1
\
weight 1.0
]
node
[
id 2
]
edge
\
[
source 1
target 2
weight 1.1000
]
\
edge
[
source 2
target 1
]
]
";
let g = parse_gml(gml,
&|s| -> Option<f64> { Some(s.and_then(Sexp::get_float).unwrap_or(0.0)) },
&|_| -> Option<()> { Some(()) });
assert!(g.is_ok());
let g = g.unwrap();
assert_eq!(true, g.is_directed());
assert_eq!(
true,
g.find_edge(NodeIndex::new(0), NodeIndex::new(1)).is_some()
);
assert_eq!(
true,
g.find_edge(NodeIndex::new(1), NodeIndex::new(0)).is_some()
);
assert_eq!(Some(&1.0), g.node_weight(NodeIndex::new(0)));
assert_eq!(Some(&0.0), g.node_weight(NodeIndex::new(1)));
}