123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- // Copyright 2004-9 Trustees of Indiana University
- // Distributed under the Boost Software License, Version 1.0.
- // (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- //
- // read_graphviz_spirit.hpp -
- // Initialize a model of the BGL's MutableGraph concept and an associated
- // collection of property maps using a graph expressed in the GraphViz
- // DOT Language.
- //
- // Based on the grammar found at:
- // https://web.archive.org/web/20041213234742/http://www.graphviz.org/cvs/doc/info/lang.html
- //
- // See documentation for this code at:
- // http://www.boost.org/libs/graph/doc/read_graphviz.html
- //
- // Author: Ronald Garcia
- //
- #ifndef BOOST_READ_GRAPHVIZ_SPIRIT_HPP
- #define BOOST_READ_GRAPHVIZ_SPIRIT_HPP
- // Phoenix/Spirit set these limits to 3, but I need more.
- #define PHOENIX_LIMIT 6
- #define BOOST_SPIRIT_CLOSURE_LIMIT 6
- #include <boost/spirit/include/classic_multi_pass.hpp>
- #include <boost/spirit/include/classic_core.hpp>
- #include <boost/spirit/include/classic_confix.hpp>
- #include <boost/spirit/include/classic_distinct.hpp>
- #include <boost/spirit/include/classic_lists.hpp>
- #include <boost/spirit/include/classic_escape_char.hpp>
- #include <boost/spirit/include/classic_attribute.hpp>
- #include <boost/spirit/include/classic_dynamic.hpp>
- #include <boost/spirit/include/classic_actor.hpp>
- #include <boost/spirit/include/classic_closure.hpp>
- #include <boost/spirit/include/phoenix1.hpp>
- #include <boost/spirit/include/phoenix1_binders.hpp>
- #include <boost/ref.hpp>
- #include <boost/function/function2.hpp>
- #include <boost/type_traits/is_same.hpp>
- #include <boost/property_map/dynamic_property_map.hpp>
- #include <boost/graph/graph_traits.hpp>
- #include <boost/detail/workaround.hpp>
- #include <algorithm>
- #include <exception> // for std::exception
- #include <string>
- #include <vector>
- #include <set>
- #include <utility>
- #include <map>
- #include <boost/graph/graphviz.hpp>
- #include <boost/throw_exception.hpp>
- namespace phoenix
- {
- // Workaround: std::map::operator[] uses a different return type than all
- // other standard containers. Phoenix doesn't account for that.
- template < typename TK, typename T0, typename T1 >
- struct binary_operator< index_op, std::map< TK, T0 >, T1 >
- {
- typedef typename std::map< TK, T0 >::mapped_type& result_type;
- static result_type eval(std::map< TK, T0 >& container, T1 const& index)
- {
- return container[index];
- }
- };
- } // namespace phoenix
- namespace boost
- {
- namespace detail
- {
- namespace graph
- {
- /////////////////////////////////////////////////////////////////////////////
- // Application-specific type definitions
- /////////////////////////////////////////////////////////////////////////////
- typedef std::set< edge_t > edges_t;
- typedef std::set< node_t > nodes_t;
- typedef std::set< id_t > ids_t;
- typedef std::map< edge_t, ids_t > edge_map_t;
- typedef std::map< node_t, ids_t > node_map_t;
- typedef std::map< id_t, id_t > props_t;
- typedef std::map< id_t, props_t > subgraph_props_t;
- typedef boost::function2< void, id_t const&, id_t const& > actor_t;
- typedef std::vector< edge_t > edge_stack_t;
- typedef std::map< id_t, nodes_t > subgraph_nodes_t;
- typedef std::map< id_t, edges_t > subgraph_edges_t;
- /////////////////////////////////////////////////////////////////////////////
- // Stack frames used by semantic actions
- /////////////////////////////////////////////////////////////////////////////
- struct id_closure
- : boost::spirit::classic::closure< id_closure, node_t >
- {
- member1 name;
- };
- struct node_id_closure
- : boost::spirit::classic::closure< node_id_closure, node_t >
- {
- member1 name;
- };
- struct attr_list_closure
- : boost::spirit::classic::closure< attr_list_closure, actor_t >
- {
- member1 prop_actor;
- };
- struct property_closure
- : boost::spirit::classic::closure< property_closure, id_t, id_t >
- {
- member1 key;
- member2 value;
- };
- struct data_stmt_closure
- : boost::spirit::classic::closure< data_stmt_closure, nodes_t, nodes_t,
- edge_stack_t, bool, node_t >
- {
- member1 sources;
- member2 dests;
- member3 edge_stack;
- member4 saw_node;
- member5 active_node;
- };
- struct subgraph_closure
- : boost::spirit::classic::closure< subgraph_closure, nodes_t, edges_t,
- node_t >
- {
- member1 nodes;
- member2 edges;
- member3 name;
- };
- /////////////////////////////////////////////////////////////////////////////
- // Grammar and Actions for the DOT Language
- /////////////////////////////////////////////////////////////////////////////
- // Grammar for a dot file.
- struct dot_grammar
- : public boost::spirit::classic::grammar< dot_grammar >
- {
- mutate_graph& graph_;
- explicit dot_grammar(mutate_graph& graph) : graph_(graph) {}
- template < class ScannerT > struct definition
- {
- definition(dot_grammar const& self)
- : self(self), subgraph_depth(0), keyword_p("0-9a-zA-Z_")
- {
- using namespace boost::spirit::classic;
- using namespace phoenix;
- // RG - Future Work
- // - Handle multi-line strings using \ line continuation
- // - Make keywords case insensitive
- ID = (lexeme_d[(
- (alpha_p | ch_p('_')) >> *(alnum_p | ch_p('_')))]
- | real_p | lexeme_d[confix_p('"', *c_escape_ch_p, '"')]
- | comment_nest_p('<', '>'))[ID.name
- = construct_< std::string >(arg1, arg2)];
- a_list = list_p(
- ID[(a_list.key = arg1), (a_list.value = "true")] >> !(
- ch_p('=') >> ID[a_list.value = arg1])[phoenix::bind(
- &definition::call_prop_actor)(
- var(*this), a_list.key, a_list.value)],
- !ch_p(','));
- attr_list = +(ch_p('[') >> !a_list >> ch_p(']'));
- // RG - disregard port id's for now.
- port_location = (ch_p(':') >> ID)
- | (ch_p(':') >> ch_p('(') >> ID >> ch_p(',') >> ID
- >> ch_p(')'));
- port_angle = ch_p('@') >> ID;
- port = port_location >> (!port_angle)
- | port_angle >> (!port_location);
- node_id
- = (ID[node_id.name = arg1] >> (!port))[phoenix::bind(
- &definition::memoize_node)(var(*this))];
- graph_stmt = (ID[graph_stmt.key = arg1] >> ch_p('=')
- >> ID[graph_stmt.value = arg1])[phoenix::bind(
- &definition::call_graph_prop)(var(*this),
- graph_stmt.key, graph_stmt.value)]; // Graph property.
- attr_stmt
- = (as_lower_d[keyword_p("graph")] >> attr_list(actor_t(
- phoenix::bind(&definition::default_graph_prop)(
- var(*this), arg1, arg2))))
- | (as_lower_d[keyword_p("node")] >> attr_list(actor_t(
- phoenix::bind(&definition::default_node_prop)(
- var(*this), arg1, arg2))))
- | (as_lower_d[keyword_p("edge")] >> attr_list(actor_t(
- phoenix::bind(&definition::default_edge_prop)(
- var(*this), arg1, arg2))));
- // edge_head is set depending on the graph type
- // (directed/undirected)
- edgeop = ch_p('-') >> ch_p(boost::ref(edge_head));
- edgeRHS = +(edgeop[(data_stmt.sources = data_stmt.dests),
- (data_stmt.dests = construct_< nodes_t >())]
- >> (subgraph[data_stmt.dests = arg1]
- | node_id[phoenix::bind(&definition::insert_node)(
- var(*this), data_stmt.dests, arg1)])
- [phoenix::bind(&definition::activate_edge)(
- var(*this), data_stmt.sources, data_stmt.dests,
- var(edges), var(default_edge_props))]);
- // To avoid backtracking, edge, node, and subgraph
- // statements are processed as one nonterminal.
- data_stmt
- = (subgraph[(data_stmt.dests
- = arg1), // will get moved in rhs
- (data_stmt.saw_node = false)]
- | node_id[(phoenix::bind(
- &definition::insert_node)(
- var(*this), data_stmt.dests, arg1)),
- (data_stmt.saw_node = true),
- #ifdef BOOST_GRAPH_DEBUG
- (std::cout << val("AcTive Node: ") << arg1
- << "\n"),
- #endif // BOOST_GRAPH_DEBUG
- (data_stmt.active_node = arg1)])
- >> if_p(edgeRHS)[!attr_list(actor_t(phoenix::bind(
- &definition::edge_prop)(
- var(*this), arg1, arg2)))]
- .else_p[if_p(data_stmt.saw_node)[!attr_list(
- actor_t(phoenix::bind(
- &definition::node_prop)(var(*this), arg1,
- arg2)))] // otherwise it's a subgraph,
- // nothing more to do.
- ];
- stmt = graph_stmt | attr_stmt | data_stmt;
- stmt_list = *(stmt >> !ch_p(';'));
- subgraph = !(as_lower_d[keyword_p("subgraph")]
- >> (!ID[(subgraph.name = arg1),
- (subgraph.nodes
- = (var(subgraph_nodes))[arg1]),
- (subgraph.edges
- = (var(subgraph_edges))[arg1])]))
- >> ch_p('{')[++var(subgraph_depth)] >> stmt_list
- >> ch_p('}')[--var(subgraph_depth)]
- [(var(subgraph_nodes))[subgraph.name]
- = subgraph.nodes]
- [(var(subgraph_edges))[subgraph.name]
- = subgraph.edges]
- | as_lower_d[keyword_p("subgraph")]
- >> ID[(subgraph.nodes
- = (var(subgraph_nodes))[arg1]),
- (subgraph.edges = (var(subgraph_edges))[arg1])];
- the_grammar = (!as_lower_d[keyword_p("strict")])
- >> (as_lower_d[keyword_p(
- "graph")][(var(edge_head) = '-'),
- (phoenix::bind(&definition::check_undirected)(
- var(*this)))]
- | as_lower_d[keyword_p(
- "digraph")][(var(edge_head) = '>'),
- (phoenix::bind(&definition::check_directed)(
- var(*this)))])
- >> (!ID) >> ch_p('{') >> stmt_list >> ch_p('}');
- } // definition()
- typedef boost::spirit::classic::rule< ScannerT > rule_t;
- rule_t const& start() const { return the_grammar; }
- //
- // Semantic actions
- //
- void check_undirected()
- {
- if (self.graph_.is_directed())
- boost::throw_exception(boost::undirected_graph_error());
- }
- void check_directed()
- {
- if (!self.graph_.is_directed())
- boost::throw_exception(boost::directed_graph_error());
- }
- void memoize_node()
- {
- id_t const& node = node_id.name();
- props_t& node_props = default_node_props;
- if (nodes.find(node) == nodes.end())
- {
- nodes.insert(node);
- self.graph_.do_add_vertex(node);
- node_map.insert(std::make_pair(node, ids_t()));
- #ifdef BOOST_GRAPH_DEBUG
- std::cout << "Add new node " << node << std::endl;
- #endif // BOOST_GRAPH_DEBUG
- // Set the default properties for this edge
- // RG: Here I would actually set the properties
- for (props_t::iterator i = node_props.begin();
- i != node_props.end(); ++i)
- {
- set_node_property(node, i->first, i->second);
- }
- if (subgraph_depth > 0)
- {
- subgraph.nodes().insert(node);
- // Set the subgraph's default properties as well
- props_t& props
- = subgraph_node_props[subgraph.name()];
- for (props_t::iterator i = props.begin();
- i != props.end(); ++i)
- {
- set_node_property(node, i->first, i->second);
- }
- }
- }
- else
- {
- #ifdef BOOST_GRAPH_DEBUG
- std::cout << "See node " << node << std::endl;
- #endif // BOOST_GRAPH_DEBUG
- }
- }
- void activate_edge(nodes_t& sources, nodes_t& dests,
- edges_t& edges, props_t& edge_props)
- {
- edge_stack_t& edge_stack = data_stmt.edge_stack();
- for (nodes_t::iterator i = sources.begin();
- i != sources.end(); ++i)
- {
- for (nodes_t::iterator j = dests.begin();
- j != dests.end(); ++j)
- {
- // Create the edge and push onto the edge stack.
- #ifdef BOOST_GRAPH_DEBUG
- std::cout << "Edge " << *i << " to " << *j
- << std::endl;
- #endif // BOOST_GRAPH_DEBUG
- edge_t edge = edge_t::new_edge();
- edge_stack.push_back(edge);
- edges.insert(edge);
- edge_map.insert(std::make_pair(edge, ids_t()));
- // Add the real edge.
- self.graph_.do_add_edge(edge, *i, *j);
- // Set the default properties for this edge
- for (props_t::iterator k = edge_props.begin();
- k != edge_props.end(); ++k)
- {
- set_edge_property(edge, k->first, k->second);
- }
- if (subgraph_depth > 0)
- {
- subgraph.edges().insert(edge);
- // Set the subgraph's default properties as well
- props_t& props
- = subgraph_edge_props[subgraph.name()];
- for (props_t::iterator k = props.begin();
- k != props.end(); ++k)
- {
- set_edge_property(
- edge, k->first, k->second);
- }
- }
- }
- }
- }
- // node_prop - Assign the property for the current active node.
- void node_prop(id_t const& key, id_t const& value)
- {
- node_t& active_object = data_stmt.active_node();
- set_node_property(active_object, key, value);
- }
- // edge_prop - Assign the property for the current active edges.
- void edge_prop(id_t const& key, id_t const& value)
- {
- edge_stack_t const& active_edges_ = data_stmt.edge_stack();
- for (edge_stack_t::const_iterator i = active_edges_.begin();
- i != active_edges_.end(); ++i)
- {
- set_edge_property(*i, key, value);
- }
- }
- // default_graph_prop - Store as a graph property.
- void default_graph_prop(id_t const& key, id_t const& value)
- {
- #ifdef BOOST_GRAPH_DEBUG
- std::cout << key << " = " << value << std::endl;
- #endif // BOOST_GRAPH_DEBUG
- self.graph_.set_graph_property(key, value);
- }
- // default_node_prop - declare default properties for any future
- // new nodes
- void default_node_prop(id_t const& key, id_t const& value)
- {
- nodes_t& nodes_
- = subgraph_depth == 0 ? nodes : subgraph.nodes();
- props_t& node_props_ = subgraph_depth == 0
- ? default_node_props
- : subgraph_node_props[subgraph.name()];
- // add this to the selected list of default node properties.
- node_props_[key] = value;
- // for each node, set its property to default-constructed
- // value
- // if it hasn't been set already.
- // set the dynamic property map value
- for (nodes_t::iterator i = nodes_.begin();
- i != nodes_.end(); ++i)
- if (node_map[*i].find(key) == node_map[*i].end())
- {
- set_node_property(*i, key, id_t());
- }
- }
- // default_edge_prop - declare default properties for any future
- // new edges
- void default_edge_prop(id_t const& key, id_t const& value)
- {
- edges_t& edges_
- = subgraph_depth == 0 ? edges : subgraph.edges();
- props_t& edge_props_ = subgraph_depth == 0
- ? default_edge_props
- : subgraph_edge_props[subgraph.name()];
- // add this to the list of default edge properties.
- edge_props_[key] = value;
- // for each edge, set its property to be empty string
- // set the dynamic property map value
- for (edges_t::iterator i = edges_.begin();
- i != edges_.end(); ++i)
- if (edge_map[*i].find(key) == edge_map[*i].end())
- set_edge_property(*i, key, id_t());
- }
- // helper function
- void insert_node(nodes_t& nodes, id_t const& name)
- {
- nodes.insert(name);
- }
- void call_prop_actor(
- std::string const& lhs, std::string const& rhs)
- {
- actor_t& actor = attr_list.prop_actor();
- // If first and last characters of the rhs are
- // double-quotes, remove them.
- if (!rhs.empty() && rhs[0] == '"'
- && rhs[rhs.size() - 1] == '"')
- actor(lhs, rhs.substr(1, rhs.size() - 2));
- else
- actor(lhs, rhs);
- }
- void call_graph_prop(
- std::string const& lhs, std::string const& rhs)
- {
- // If first and last characters of the rhs are
- // double-quotes, remove them.
- if (!rhs.empty() && rhs[0] == '"'
- && rhs[rhs.size() - 1] == '"')
- this->default_graph_prop(
- lhs, rhs.substr(1, rhs.size() - 2));
- else
- this->default_graph_prop(lhs, rhs);
- }
- void set_node_property(
- node_t const& node, id_t const& key, id_t const& value)
- {
- // Add the property key to the "set" table to avoid default
- // overwrite
- node_map[node].insert(key);
- // Set the user's property map
- self.graph_.set_node_property(key, node, value);
- #ifdef BOOST_GRAPH_DEBUG
- // Tell the world
- std::cout << node << ": " << key << " = " << value
- << std::endl;
- #endif // BOOST_GRAPH_DEBUG
- }
- void set_edge_property(
- edge_t const& edge, id_t const& key, id_t const& value)
- {
- // Add the property key to the "set" table to avoid default
- // overwrite
- edge_map[edge].insert(key);
- // Set the user's property map
- self.graph_.set_edge_property(key, edge, value);
- #ifdef BOOST_GRAPH_DEBUG
- // Tell the world
- #if 0 // RG - edge representation changed,
- std::cout << "(" << edge.first << "," << edge.second << "): "
- #else
- std::cout << "an edge: "
- #endif // 0
- << key << " = " << value << std::endl;
- #endif // BOOST_GRAPH_DEBUG
- }
- // Variables explicitly initialized
- dot_grammar const& self;
- // if subgraph_depth > 0, then we're processing a subgraph.
- int subgraph_depth;
- // Keywords;
- const boost::spirit::classic::distinct_parser<> keyword_p;
- //
- // rules that make up the grammar
- //
- boost::spirit::classic::rule< ScannerT, id_closure::context_t >
- ID;
- boost::spirit::classic::rule< ScannerT,
- property_closure::context_t >
- a_list;
- boost::spirit::classic::rule< ScannerT,
- attr_list_closure::context_t >
- attr_list;
- rule_t port_location;
- rule_t port_angle;
- rule_t port;
- boost::spirit::classic::rule< ScannerT,
- node_id_closure::context_t >
- node_id;
- boost::spirit::classic::rule< ScannerT,
- property_closure::context_t >
- graph_stmt;
- rule_t attr_stmt;
- boost::spirit::classic::rule< ScannerT,
- data_stmt_closure::context_t >
- data_stmt;
- boost::spirit::classic::rule< ScannerT,
- subgraph_closure::context_t >
- subgraph;
- rule_t edgeop;
- rule_t edgeRHS;
- rule_t stmt;
- rule_t stmt_list;
- rule_t the_grammar;
- // The grammar uses edge_head to dynamically set the syntax for
- // edges directed graphs: edge_head = '>', and so edgeop = "->"
- // undirected graphs: edge_head = '-', and so edgeop = "--"
- char edge_head;
- //
- // Support data structures
- //
- nodes_t nodes; // list of node names seen
- edges_t edges; // list of edges seen
- node_map_t
- node_map; // remember the properties set for each node
- edge_map_t
- edge_map; // remember the properties set for each edge
- subgraph_nodes_t subgraph_nodes; // per-subgraph lists of nodes
- subgraph_edges_t subgraph_edges; // per-subgraph lists of edges
- props_t default_node_props; // global default node properties
- props_t default_edge_props; // global default edge properties
- subgraph_props_t
- subgraph_node_props; // per-subgraph default node properties
- subgraph_props_t
- subgraph_edge_props; // per-subgraph default edge properties
- }; // struct definition
- }; // struct dot_grammar
- //
- // dot_skipper - GraphViz whitespace and comment skipper
- //
- struct dot_skipper
- : public boost::spirit::classic::grammar< dot_skipper >
- {
- dot_skipper() {}
- template < typename ScannerT > struct definition
- {
- definition(dot_skipper const& /*self*/)
- {
- using namespace boost::spirit::classic;
- using namespace phoenix;
- // comment forms
- skip = eol_p >> comment_p("#") | space_p | comment_p("//")
- #if BOOST_WORKAROUND(BOOST_MSVC, <= 1400)
- | confix_p(str_p("/*"), *anychar_p, str_p("*/"))
- #else
- | confix_p("/*", *anychar_p, "*/")
- #endif
- ;
- #ifdef BOOST_SPIRIT_DEBUG
- BOOST_SPIRIT_DEBUG_RULE(skip);
- #endif
- }
- boost::spirit::classic::rule< ScannerT > skip;
- boost::spirit::classic::rule< ScannerT > const& start() const
- {
- return skip;
- }
- }; // definition
- }; // dot_skipper
- } // namespace graph
- } // namespace detail
- template < typename MultiPassIterator, typename MutableGraph >
- bool read_graphviz_spirit(MultiPassIterator begin, MultiPassIterator end,
- MutableGraph& graph, dynamic_properties& dp,
- std::string const& node_id = "node_id")
- {
- using namespace boost;
- using namespace boost::spirit::classic;
- typedef MultiPassIterator iterator_t;
- typedef skip_parser_iteration_policy< boost::detail::graph::dot_skipper >
- iter_policy_t;
- typedef scanner_policies< iter_policy_t > scanner_policies_t;
- typedef scanner< iterator_t, scanner_policies_t > scanner_t;
- ::boost::detail::graph::mutate_graph_impl< MutableGraph > m_graph(
- graph, dp, node_id);
- ::boost::detail::graph::dot_grammar p(m_graph);
- ::boost::detail::graph::dot_skipper skip_p;
- iter_policy_t iter_policy(skip_p);
- scanner_policies_t policies(iter_policy);
- scanner_t scan(begin, end, policies);
- bool ok = p.parse(scan);
- m_graph.finish_building_graph();
- return ok;
- }
- } // namespace boost
- #endif // BOOST_READ_GRAPHVIZ_SPIRIT_HPP
|