| 1 | // Copyright 2004-5 Trustees of Indiana University |
|---|
| 2 | |
|---|
| 3 | // Distributed under the Boost Software License, Version 1.0. |
|---|
| 4 | // (See accompanying file LICENSE_1_0.txt or copy at |
|---|
| 5 | // http://www.boost.org/LICENSE_1_0.txt) |
|---|
| 6 | |
|---|
| 7 | // |
|---|
| 8 | // read_graphviz_spirit.hpp - |
|---|
| 9 | // Initialize a model of the BGL's MutableGraph concept and an associated |
|---|
| 10 | // collection of property maps using a graph expressed in the GraphViz |
|---|
| 11 | // DOT Language. |
|---|
| 12 | // |
|---|
| 13 | // Based on the grammar found at: |
|---|
| 14 | // http://www.graphviz.org/cvs/doc/info/lang.html |
|---|
| 15 | // |
|---|
| 16 | // See documentation for this code at: |
|---|
| 17 | // http://www.boost.org/libs/graph/doc/read-graphviz.html |
|---|
| 18 | // |
|---|
| 19 | |
|---|
| 20 | // Author: Ronald Garcia |
|---|
| 21 | // |
|---|
| 22 | |
|---|
| 23 | #ifndef BOOST_READ_GRAPHVIZ_SPIRIT_HPP |
|---|
| 24 | #define BOOST_READ_GRAPHVIZ_SPIRIT_HPP |
|---|
| 25 | |
|---|
| 26 | // Phoenix/Spirit set these limits to 3, but I need more. |
|---|
| 27 | #define PHOENIX_LIMIT 6 |
|---|
| 28 | #define BOOST_SPIRIT_CLOSURE_LIMIT 6 |
|---|
| 29 | |
|---|
| 30 | |
|---|
| 31 | #include <boost/spirit/iterator/multi_pass.hpp> |
|---|
| 32 | #include <boost/spirit/core.hpp> |
|---|
| 33 | #include <boost/spirit/utility/confix.hpp> |
|---|
| 34 | #include <boost/spirit/utility/distinct.hpp> |
|---|
| 35 | #include <boost/spirit/utility/lists.hpp> |
|---|
| 36 | #include <boost/spirit/utility/escape_char.hpp> |
|---|
| 37 | #include <boost/spirit/attribute.hpp> |
|---|
| 38 | #include <boost/spirit/dynamic.hpp> |
|---|
| 39 | #include <boost/spirit/actor.hpp> |
|---|
| 40 | #include <boost/spirit/phoenix.hpp> |
|---|
| 41 | #include <boost/spirit/phoenix/binders.hpp> |
|---|
| 42 | #include <boost/ref.hpp> |
|---|
| 43 | #include <boost/function/function2.hpp> |
|---|
| 44 | #include <boost/type_traits/is_same.hpp> |
|---|
| 45 | #include <boost/dynamic_property_map.hpp> |
|---|
| 46 | #include <boost/graph/graph_traits.hpp> |
|---|
| 47 | #include <boost/detail/workaround.hpp> |
|---|
| 48 | #include <algorithm> |
|---|
| 49 | #include <exception> // for std::exception |
|---|
| 50 | #include <string> |
|---|
| 51 | #include <vector> |
|---|
| 52 | #include <set> |
|---|
| 53 | #include <utility> |
|---|
| 54 | #include <map> |
|---|
| 55 | #include <boost/graph/graphviz.hpp> |
|---|
| 56 | |
|---|
| 57 | namespace phoenix { |
|---|
| 58 | // Workaround: std::map::operator[] uses a different return type than all |
|---|
| 59 | // other standard containers. Phoenix doesn't account for that. |
|---|
| 60 | template <typename TK, typename T0, typename T1> |
|---|
| 61 | struct binary_operator<index_op, std::map<TK,T0>, T1> |
|---|
| 62 | { |
|---|
| 63 | typedef typename std::map<TK,T0>::mapped_type& result_type; |
|---|
| 64 | static result_type eval(std::map<TK,T0>& container, T1 const& index) |
|---|
| 65 | { return container[index]; } |
|---|
| 66 | }; |
|---|
| 67 | } // namespace phoenix |
|---|
| 68 | |
|---|
| 69 | namespace boost { |
|---|
| 70 | namespace detail { |
|---|
| 71 | namespace graph { |
|---|
| 72 | |
|---|
| 73 | |
|---|
| 74 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 75 | // Application-specific type definitions |
|---|
| 76 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 77 | |
|---|
| 78 | typedef std::set<edge_t> edges_t; |
|---|
| 79 | typedef std::set<node_t> nodes_t; |
|---|
| 80 | typedef std::set<id_t> ids_t; |
|---|
| 81 | typedef std::map<edge_t,ids_t> edge_map_t; |
|---|
| 82 | typedef std::map<node_t,ids_t> node_map_t; |
|---|
| 83 | typedef std::map<id_t,id_t> props_t; |
|---|
| 84 | typedef std::map<id_t,props_t> subgraph_props_t; |
|---|
| 85 | typedef boost::function2<void, id_t const&, id_t const&> actor_t; |
|---|
| 86 | typedef std::vector<edge_t> edge_stack_t; |
|---|
| 87 | typedef std::map<id_t,nodes_t> subgraph_nodes_t; |
|---|
| 88 | typedef std::map<id_t,edges_t> subgraph_edges_t; |
|---|
| 89 | |
|---|
| 90 | |
|---|
| 91 | |
|---|
| 92 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 93 | // Stack frames used by semantic actions |
|---|
| 94 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 95 | struct id_closure : boost::spirit::closure<id_closure, node_t> { |
|---|
| 96 | member1 name; |
|---|
| 97 | }; |
|---|
| 98 | |
|---|
| 99 | |
|---|
| 100 | struct node_id_closure : boost::spirit::closure<node_id_closure, node_t> { |
|---|
| 101 | member1 name; |
|---|
| 102 | }; |
|---|
| 103 | |
|---|
| 104 | struct attr_list_closure : boost::spirit::closure<attr_list_closure, actor_t> { |
|---|
| 105 | member1 prop_actor; |
|---|
| 106 | }; |
|---|
| 107 | |
|---|
| 108 | struct property_closure : boost::spirit::closure<property_closure, id_t, id_t> { |
|---|
| 109 | member1 key; |
|---|
| 110 | member2 value; |
|---|
| 111 | }; |
|---|
| 112 | |
|---|
| 113 | struct data_stmt_closure : boost::spirit::closure<data_stmt_closure, |
|---|
| 114 | nodes_t,nodes_t,edge_stack_t,bool,node_t> { |
|---|
| 115 | member1 sources; |
|---|
| 116 | member2 dests; |
|---|
| 117 | member3 edge_stack; |
|---|
| 118 | member4 saw_node; |
|---|
| 119 | member5 active_node; |
|---|
| 120 | }; |
|---|
| 121 | |
|---|
| 122 | struct subgraph_closure : boost::spirit::closure<subgraph_closure, |
|---|
| 123 | nodes_t, edges_t, node_t> { |
|---|
| 124 | member1 nodes; |
|---|
| 125 | member2 edges; |
|---|
| 126 | member3 name; |
|---|
| 127 | }; |
|---|
| 128 | |
|---|
| 129 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 130 | // Grammar and Actions for the DOT Language |
|---|
| 131 | ///////////////////////////////////////////////////////////////////////////// |
|---|
| 132 | |
|---|
| 133 | // Grammar for a dot file. |
|---|
| 134 | struct dot_grammar : public boost::spirit::grammar<dot_grammar> { |
|---|
| 135 | mutate_graph& graph_; |
|---|
| 136 | explicit dot_grammar(mutate_graph& graph) : graph_(graph) { } |
|---|
| 137 | |
|---|
| 138 | template <class ScannerT> |
|---|
| 139 | struct definition { |
|---|
| 140 | |
|---|
| 141 | definition(dot_grammar const& self) : self(self), subgraph_depth(0), |
|---|
| 142 | keyword_p("0-9a-zA-Z_") { |
|---|
| 143 | using namespace boost::spirit; |
|---|
| 144 | using namespace phoenix; |
|---|
| 145 | |
|---|
| 146 | // RG - Future Work |
|---|
| 147 | // - Handle multi-line strings using \ line continuation |
|---|
| 148 | // - Make keywords case insensitive |
|---|
| 149 | ID |
|---|
| 150 | = ( lexeme_d[((alpha_p | ch_p('_')) >> *(alnum_p | ch_p('_')))] |
|---|
| 151 | | real_p |
|---|
| 152 | | confix_p('"', *c_escape_ch_p, '"') |
|---|
| 153 | | comment_nest_p('<', '>') |
|---|
| 154 | )[ID.name = construct_<std::string>(arg1,arg2)] |
|---|
| 155 | ; |
|---|
| 156 | |
|---|
| 157 | a_list |
|---|
| 158 | = list_p( ID[(a_list.key = arg1), |
|---|
| 159 | (a_list.value = "true") |
|---|
| 160 | ] |
|---|
| 161 | >> !( ch_p('=') |
|---|
| 162 | >> ID[a_list.value = arg1]) |
|---|
| 163 | [phoenix::bind(&definition::call_prop_actor) |
|---|
| 164 | (var(*this),a_list.key,a_list.value)],ch_p(',')); |
|---|
| 165 | |
|---|
| 166 | attr_list = +(ch_p('[') >> !a_list >> ch_p(']')); |
|---|
| 167 | |
|---|
| 168 | // RG - disregard port id's for now. |
|---|
| 169 | port_location |
|---|
| 170 | = (ch_p(':') >> ID) |
|---|
| 171 | | (ch_p(':') >> ch_p('(') >> ID >> ch_p(',') >> ID >> ch_p(')')) |
|---|
| 172 | ; |
|---|
| 173 | |
|---|
| 174 | port_angle = ch_p('@') >> ID; |
|---|
| 175 | |
|---|
| 176 | port |
|---|
| 177 | = port_location >> (!port_angle) |
|---|
| 178 | | port_angle >> (!port_location); |
|---|
| 179 | |
|---|
| 180 | |
|---|
| 181 | node_id |
|---|
| 182 | = ( ID[node_id.name = arg1] >> (!port) ) |
|---|
| 183 | [phoenix::bind(&definition::memoize_node)(var(*this))]; |
|---|
| 184 | |
|---|
| 185 | attr_stmt |
|---|
| 186 | = (as_lower_d[keyword_p("graph")] |
|---|
| 187 | >> attr_list(actor_t(phoenix::bind(&definition::default_graph_prop) |
|---|
| 188 | (var(*this),arg1,arg2)))) |
|---|
| 189 | | (as_lower_d[keyword_p("node")] |
|---|
| 190 | >> attr_list(actor_t(phoenix::bind(&definition::default_node_prop) |
|---|
| 191 | (var(*this),arg1,arg2)))) |
|---|
| 192 | | (as_lower_d[keyword_p("edge")] |
|---|
| 193 | >> attr_list(actor_t(phoenix::bind(&definition::default_edge_prop) |
|---|
| 194 | (var(*this),arg1,arg2)))) |
|---|
| 195 | ; |
|---|
| 196 | |
|---|
| 197 | // edge_head is set depending on the graph type (directed/undirected) |
|---|
| 198 | edgeop = ch_p('-') >> ch_p(boost::ref(edge_head)); |
|---|
| 199 | |
|---|
| 200 | edgeRHS |
|---|
| 201 | = +( edgeop[(data_stmt.sources = data_stmt.dests), |
|---|
| 202 | (data_stmt.dests = construct_<nodes_t>())] |
|---|
| 203 | >> ( subgraph[data_stmt.dests = arg1] |
|---|
| 204 | | node_id[phoenix::bind(&definition::insert_node) |
|---|
| 205 | (var(*this),data_stmt.dests,arg1)] |
|---|
| 206 | ) |
|---|
| 207 | [phoenix::bind(&definition::activate_edge) |
|---|
| 208 | (var(*this),data_stmt.sources,data_stmt.dests, |
|---|
| 209 | var(edges), var(default_edge_props))] |
|---|
| 210 | ); |
|---|
| 211 | |
|---|
| 212 | |
|---|
| 213 | // To avoid backtracking, edge, node, and subgraph statements are |
|---|
| 214 | // processed as one nonterminal. |
|---|
| 215 | data_stmt |
|---|
| 216 | = ( subgraph[(data_stmt.dests = arg1),// will get moved in rhs |
|---|
| 217 | (data_stmt.saw_node = false)] |
|---|
| 218 | | node_id[(phoenix::bind(&definition::insert_node) |
|---|
| 219 | (var(*this),data_stmt.dests,arg1)), |
|---|
| 220 | (data_stmt.saw_node = true), |
|---|
| 221 | #ifdef BOOST_GRAPH_DEBUG |
|---|
| 222 | (std::cout << val("AcTive Node: ") << arg1 << "\n"), |
|---|
| 223 | #endif // BOOST_GRAPH_DEBUG |
|---|
| 224 | (data_stmt.active_node = arg1)] |
|---|
| 225 | ) >> if_p(edgeRHS)[ |
|---|
| 226 | !attr_list( |
|---|
| 227 | actor_t(phoenix::bind(&definition::edge_prop) |
|---|
| 228 | (var(*this),arg1,arg2))) |
|---|
| 229 | ].else_p[ |
|---|
| 230 | if_p(data_stmt.saw_node)[ |
|---|
| 231 | !attr_list( |
|---|
| 232 | actor_t(phoenix::bind(&definition::node_prop) |
|---|
| 233 | (var(*this),arg1,arg2))) |
|---|
| 234 | ] // otherwise it's a subgraph, nothing more to do. |
|---|
| 235 | ]; |
|---|
| 236 | |
|---|
| 237 | |
|---|
| 238 | stmt |
|---|
| 239 | = (ID >> ch_p('=') >> ID) // Graph property -- ignore. |
|---|
| 240 | | attr_stmt |
|---|
| 241 | | data_stmt |
|---|
| 242 | ; |
|---|
| 243 | |
|---|
| 244 | stmt_list = *( stmt >> !ch_p(';') ); |
|---|
| 245 | |
|---|
| 246 | subgraph |
|---|
| 247 | = !( as_lower_d[keyword_p("subgraph")] |
|---|
| 248 | >> (!ID[(subgraph.name = arg1), |
|---|
| 249 | (subgraph.nodes = (var(subgraph_nodes))[arg1]), |
|---|
| 250 | (subgraph.edges = (var(subgraph_edges))[arg1])]) |
|---|
| 251 | ) |
|---|
| 252 | >> ch_p('{')[++var(subgraph_depth)] |
|---|
| 253 | >> stmt_list |
|---|
| 254 | >> ch_p('}')[--var(subgraph_depth)] |
|---|
| 255 | [(var(subgraph_nodes))[subgraph.name] = subgraph.nodes] |
|---|
| 256 | [(var(subgraph_edges))[subgraph.name] = subgraph.edges] |
|---|
| 257 | |
|---|
| 258 | | as_lower_d[keyword_p("subgraph")] |
|---|
| 259 | >> ID[(subgraph.nodes = (var(subgraph_nodes))[arg1]), |
|---|
| 260 | (subgraph.edges = (var(subgraph_edges))[arg1])] |
|---|
| 261 | ; |
|---|
| 262 | |
|---|
| 263 | the_grammar |
|---|
| 264 | = (!as_lower_d[keyword_p("strict")]) |
|---|
| 265 | >> ( as_lower_d[keyword_p("graph")][ |
|---|
| 266 | (var(edge_head) = '-'), |
|---|
| 267 | (phoenix::bind(&definition::check_undirected)(var(*this)))] |
|---|
| 268 | | as_lower_d[keyword_p("digraph")][ |
|---|
| 269 | (var(edge_head) = '>'), |
|---|
| 270 | (phoenix::bind(&definition::check_directed)(var(*this)))] |
|---|
| 271 | ) |
|---|
| 272 | >> (!ID) >> ch_p('{') >> stmt_list >> ch_p('}'); |
|---|
| 273 | |
|---|
| 274 | } // definition() |
|---|
| 275 | |
|---|
| 276 | typedef boost::spirit::rule<ScannerT> rule_t; |
|---|
| 277 | |
|---|
| 278 | rule_t const& start() const { return the_grammar; } |
|---|
| 279 | |
|---|
| 280 | |
|---|
| 281 | // |
|---|
| 282 | // Semantic actions |
|---|
| 283 | // |
|---|
| 284 | |
|---|
| 285 | void check_undirected() { |
|---|
| 286 | if(self.graph_.is_directed()) |
|---|
| 287 | throw boost::undirected_graph_error(); |
|---|
| 288 | } |
|---|
| 289 | |
|---|
| 290 | void check_directed() { |
|---|
| 291 | if(!self.graph_.is_directed()) |
|---|
| 292 | throw boost::directed_graph_error(); |
|---|
| 293 | } |
|---|
| 294 | |
|---|
| 295 | void memoize_node() { |
|---|
| 296 | id_t const& node = node_id.name(); |
|---|
| 297 | props_t& node_props = default_node_props; |
|---|
| 298 | |
|---|
| 299 | if(nodes.find(node) == nodes.end()) { |
|---|
| 300 | nodes.insert(node); |
|---|
| 301 | self.graph_.do_add_vertex(node); |
|---|
| 302 | |
|---|
| 303 | node_map.insert(std::make_pair(node,ids_t())); |
|---|
| 304 | |
|---|
| 305 | #ifdef BOOST_GRAPH_DEBUG |
|---|
| 306 | std::cout << "Add new node " << node << std::endl; |
|---|
| 307 | #endif // BOOST_GRAPH_DEBUG |
|---|
| 308 | // Set the default properties for this edge |
|---|
| 309 | // RG: Here I would actually set the properties |
|---|
| 310 | for(props_t::iterator i = node_props.begin(); |
|---|
| 311 | i != node_props.end(); ++i) { |
|---|
| 312 | set_node_property(node,i->first,i->second); |
|---|
| 313 | } |
|---|
| 314 | if(subgraph_depth > 0) { |
|---|
| 315 | subgraph.nodes().insert(node); |
|---|
| 316 | // Set the subgraph's default properties as well |
|---|
| 317 | props_t& props = subgraph_node_props[subgraph.name()]; |
|---|
| 318 | for(props_t::iterator i = props.begin(); i != props.end(); ++i) { |
|---|
| 319 | set_node_property(node,i->first,i->second); |
|---|
| 320 | } |
|---|
| 321 | } |
|---|
| 322 | } else { |
|---|
| 323 | #ifdef BOOST_GRAPH_DEBUG |
|---|
| 324 | std::cout << "See node " << node << std::endl; |
|---|
| 325 | #endif // BOOST_GRAPH_DEBUG |
|---|
| 326 | } |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | void activate_edge(nodes_t& sources, nodes_t& dests, edges_t& edges, |
|---|
| 330 | props_t& edge_props) { |
|---|
| 331 | edge_stack_t& edge_stack = data_stmt.edge_stack(); |
|---|
| 332 | for(nodes_t::iterator i = sources.begin(); i != sources.end(); ++i) { |
|---|
| 333 | for(nodes_t::iterator j = dests.begin(); j != dests.end(); ++j) { |
|---|
| 334 | // Create the edge and and push onto the edge stack. |
|---|
| 335 | #ifdef BOOST_GRAPH_DEBUG |
|---|
| 336 | std::cout << "Edge " << *i << " to " << *j << std::endl; |
|---|
| 337 | #endif // BOOST_GRAPH_DEBUG |
|---|
| 338 | |
|---|
| 339 | edge_t edge = edge_t::new_edge(); |
|---|
| 340 | edge_stack.push_back(edge); |
|---|
| 341 | edges.insert(edge); |
|---|
| 342 | edge_map.insert(std::make_pair(edge,ids_t())); |
|---|
| 343 | |
|---|
| 344 | // Add the real edge. |
|---|
| 345 | self.graph_.do_add_edge(edge, *i, *j); |
|---|
| 346 | |
|---|
| 347 | // Set the default properties for this edge |
|---|
| 348 | for(props_t::iterator k = edge_props.begin(); |
|---|
| 349 | k != edge_props.end(); ++k) { |
|---|
| 350 | set_edge_property(edge,k->first,k->second); |
|---|
| 351 | } |
|---|
| 352 | if(subgraph_depth > 0) { |
|---|
| 353 | subgraph.edges().insert(edge); |
|---|
| 354 | // Set the subgraph's default properties as well |
|---|
| 355 | props_t& props = subgraph_edge_props[subgraph.name()]; |
|---|
| 356 | for(props_t::iterator k = props.begin(); k != props.end(); ++k) { |
|---|
| 357 | set_edge_property(edge,k->first,k->second); |
|---|
| 358 | } |
|---|
| 359 | } |
|---|
| 360 | } |
|---|
| 361 | } |
|---|
| 362 | } |
|---|
| 363 | |
|---|
| 364 | // node_prop - Assign the property for the current active node. |
|---|
| 365 | void node_prop(id_t const& key, id_t const& value) { |
|---|
| 366 | node_t& active_object = data_stmt.active_node(); |
|---|
| 367 | set_node_property(active_object, key, value); |
|---|
| 368 | } |
|---|
| 369 | |
|---|
| 370 | // edge_prop - Assign the property for the current active edges. |
|---|
| 371 | void edge_prop(id_t const& key, id_t const& value) { |
|---|
| 372 | edge_stack_t const& active_edges_ = data_stmt.edge_stack(); |
|---|
| 373 | for (edge_stack_t::const_iterator i = active_edges_.begin(); |
|---|
| 374 | i != active_edges_.end(); ++i) { |
|---|
| 375 | set_edge_property(*i,key,value); |
|---|
| 376 | } |
|---|
| 377 | } |
|---|
| 378 | |
|---|
| 379 | // default_graph_prop - Just ignore graph properties. |
|---|
| 380 | void default_graph_prop(id_t const&, id_t const&) { } |
|---|
| 381 | |
|---|
| 382 | // default_node_prop - declare default properties for any future new nodes |
|---|
| 383 | void default_node_prop(id_t const& key, id_t const& value) { |
|---|
| 384 | nodes_t& nodes_ = |
|---|
| 385 | subgraph_depth == 0 ? nodes : subgraph.nodes(); |
|---|
| 386 | props_t& node_props_ = |
|---|
| 387 | subgraph_depth == 0 ? |
|---|
| 388 | default_node_props : |
|---|
| 389 | subgraph_node_props[subgraph.name()]; |
|---|
| 390 | |
|---|
| 391 | // add this to the selected list of default node properties. |
|---|
| 392 | node_props_[key] = value; |
|---|
| 393 | // for each node, set its property to default-constructed value |
|---|
| 394 | // if it hasn't been set already. |
|---|
| 395 | // set the dynamic property map value |
|---|
| 396 | for(nodes_t::iterator i = nodes_.begin(); i != nodes_.end(); ++i) |
|---|
| 397 | if(node_map[*i].find(key) == node_map[*i].end()) { |
|---|
| 398 | set_node_property(*i,key,id_t()); |
|---|
| 399 | } |
|---|
| 400 | } |
|---|
| 401 | |
|---|
| 402 | // default_edge_prop - declare default properties for any future new edges |
|---|
| 403 | void default_edge_prop(id_t const& key, id_t const& value) { |
|---|
| 404 | edges_t& edges_ = |
|---|
| 405 | subgraph_depth == 0 ? edges : subgraph.edges(); |
|---|
| 406 | props_t& edge_props_ = |
|---|
| 407 | subgraph_depth == 0 ? |
|---|
| 408 | default_edge_props : |
|---|
| 409 | subgraph_edge_props[subgraph.name()]; |
|---|
| 410 | |
|---|
| 411 | // add this to the list of default edge properties. |
|---|
| 412 | edge_props_[key] = value; |
|---|
| 413 | // for each edge, set its property to be empty string |
|---|
| 414 | // set the dynamic property map value |
|---|
| 415 | for(edges_t::iterator i = edges_.begin(); i != edges_.end(); ++i) |
|---|
| 416 | if(edge_map[*i].find(key) == edge_map[*i].end()) |
|---|
| 417 | set_edge_property(*i,key,id_t()); |
|---|
| 418 | } |
|---|
| 419 | |
|---|
| 420 | // helper function |
|---|
| 421 | void insert_node(nodes_t& nodes, id_t const& name) { |
|---|
| 422 | nodes.insert(name); |
|---|
| 423 | } |
|---|
| 424 | |
|---|
| 425 | void call_prop_actor(std::string const& lhs, std::string const& rhs) { |
|---|
| 426 | actor_t& actor = attr_list.prop_actor(); |
|---|
| 427 | // If first and last characters of the rhs are double-quotes, |
|---|
| 428 | // remove them. |
|---|
| 429 | if (!rhs.empty() && rhs[0] == '"' && rhs[rhs.size() - 1] == '"') |
|---|
| 430 | actor(lhs, rhs.substr(1, rhs.size()-2)); |
|---|
| 431 | else |
|---|
| 432 | actor(lhs,rhs); |
|---|
| 433 | } |
|---|
| 434 | |
|---|
| 435 | void set_node_property(node_t const& node, id_t const& key, |
|---|
| 436 | id_t const& value) { |
|---|
| 437 | |
|---|
| 438 | // Add the property key to the "set" table to avoid default overwrite |
|---|
| 439 | node_map[node].insert(key); |
|---|
| 440 | // Set the user's property map |
|---|
| 441 | self.graph_.set_node_property(key, node, value); |
|---|
| 442 | #ifdef BOOST_GRAPH_DEBUG |
|---|
| 443 | // Tell the world |
|---|
| 444 | std::cout << node << ": " << key << " = " << value << std::endl; |
|---|
| 445 | #endif // BOOST_GRAPH_DEBUG |
|---|
| 446 | } |
|---|
| 447 | |
|---|
| 448 | void set_edge_property(edge_t const& edge, id_t const& key, |
|---|
| 449 | id_t const& value) { |
|---|
| 450 | |
|---|
| 451 | // Add the property key to the "set" table to avoid default overwrite |
|---|
| 452 | edge_map[edge].insert(key); |
|---|
| 453 | // Set the user's property map |
|---|
| 454 | self.graph_.set_edge_property(key, edge, value); |
|---|
| 455 | #ifdef BOOST_GRAPH_DEBUG |
|---|
| 456 | // Tell the world |
|---|
| 457 | std::cout << "(" << edge.first << "," << edge.second << "): " |
|---|
| 458 | << key << " = " << value << std::endl; |
|---|
| 459 | #endif // BOOST_GRAPH_DEBUG |
|---|
| 460 | } |
|---|
| 461 | |
|---|
| 462 | // Variables explicitly initialized |
|---|
| 463 | dot_grammar const& self; |
|---|
| 464 | // if subgraph_depth > 0, then we're processing a subgraph. |
|---|
| 465 | int subgraph_depth; |
|---|
| 466 | |
|---|
| 467 | // Keywords; |
|---|
| 468 | const boost::spirit::distinct_parser<> keyword_p; |
|---|
| 469 | // |
|---|
| 470 | // rules that make up the grammar |
|---|
| 471 | // |
|---|
| 472 | boost::spirit::rule<ScannerT,id_closure::context_t> ID; |
|---|
| 473 | boost::spirit::rule<ScannerT,property_closure::context_t> a_list; |
|---|
| 474 | boost::spirit::rule<ScannerT,attr_list_closure::context_t> attr_list; |
|---|
| 475 | rule_t port_location; |
|---|
| 476 | rule_t port_angle; |
|---|
| 477 | rule_t port; |
|---|
| 478 | boost::spirit::rule<ScannerT,node_id_closure::context_t> node_id; |
|---|
| 479 | rule_t attr_stmt; |
|---|
| 480 | boost::spirit::rule<ScannerT,data_stmt_closure::context_t> data_stmt; |
|---|
| 481 | boost::spirit::rule<ScannerT,subgraph_closure::context_t> subgraph; |
|---|
| 482 | rule_t edgeop; |
|---|
| 483 | rule_t edgeRHS; |
|---|
| 484 | rule_t stmt; |
|---|
| 485 | rule_t stmt_list; |
|---|
| 486 | rule_t the_grammar; |
|---|
| 487 | |
|---|
| 488 | |
|---|
| 489 | // The grammar uses edge_head to dynamically set the syntax for edges |
|---|
| 490 | // directed graphs: edge_head = '>', and so edgeop = "->" |
|---|
| 491 | // undirected graphs: edge_head = '-', and so edgeop = "--" |
|---|
| 492 | char edge_head; |
|---|
| 493 | |
|---|
| 494 | |
|---|
| 495 | // |
|---|
| 496 | // Support data structures |
|---|
| 497 | // |
|---|
| 498 | |
|---|
| 499 | nodes_t nodes; // list of node names seen |
|---|
| 500 | edges_t edges; // list of edges seen |
|---|
| 501 | node_map_t node_map; // remember the properties set for each node |
|---|
| 502 | edge_map_t edge_map; // remember the properties set for each edge |
|---|
| 503 | |
|---|
| 504 | subgraph_nodes_t subgraph_nodes; // per-subgraph lists of nodes |
|---|
| 505 | subgraph_edges_t subgraph_edges; // per-subgraph lists of edges |
|---|
| 506 | |
|---|
| 507 | props_t default_node_props; // global default node properties |
|---|
| 508 | props_t default_edge_props; // global default edge properties |
|---|
| 509 | subgraph_props_t subgraph_node_props; // per-subgraph default node properties |
|---|
| 510 | subgraph_props_t subgraph_edge_props; // per-subgraph default edge properties |
|---|
| 511 | }; // struct definition |
|---|
| 512 | }; // struct dot_grammar |
|---|
| 513 | |
|---|
| 514 | |
|---|
| 515 | |
|---|
| 516 | // |
|---|
| 517 | // dot_skipper - GraphViz whitespace and comment skipper |
|---|
| 518 | // |
|---|
| 519 | struct dot_skipper : public boost::spirit::grammar<dot_skipper> |
|---|
| 520 | { |
|---|
| 521 | dot_skipper() {} |
|---|
| 522 | |
|---|
| 523 | template <typename ScannerT> |
|---|
| 524 | struct definition |
|---|
| 525 | { |
|---|
| 526 | definition(dot_skipper const& /*self*/) { |
|---|
| 527 | using namespace boost::spirit; |
|---|
| 528 | using namespace phoenix; |
|---|
| 529 | // comment forms |
|---|
| 530 | skip = space_p |
|---|
| 531 | | comment_p("//") |
|---|
| 532 | | comment_p("#") |
|---|
| 533 | #if BOOST_WORKAROUND(BOOST_MSVC, <= 1400) |
|---|
| 534 | | confix_p(str_p("/*") ,*anychar_p, str_p("*/")) |
|---|
| 535 | #else |
|---|
| 536 | | confix_p("/*" ,*anychar_p, "*/") |
|---|
| 537 | #endif |
|---|
| 538 | ; |
|---|
| 539 | |
|---|
| 540 | #ifdef BOOST_SPIRIT_DEBUG |
|---|
| 541 | BOOST_SPIRIT_DEBUG_RULE(skip); |
|---|
| 542 | #endif |
|---|
| 543 | } |
|---|
| 544 | |
|---|
| 545 | boost::spirit::rule<ScannerT> skip; |
|---|
| 546 | boost::spirit::rule<ScannerT> const& |
|---|
| 547 | start() const { return skip; } |
|---|
| 548 | }; // definition |
|---|
| 549 | }; // dot_skipper |
|---|
| 550 | |
|---|
| 551 | } // namespace graph |
|---|
| 552 | } // namespace detail |
|---|
| 553 | |
|---|
| 554 | template <typename MultiPassIterator, typename MutableGraph> |
|---|
| 555 | bool read_graphviz(MultiPassIterator begin, MultiPassIterator end, |
|---|
| 556 | MutableGraph& graph, dynamic_properties& dp, |
|---|
| 557 | std::string const& node_id = "node_id") { |
|---|
| 558 | using namespace boost; |
|---|
| 559 | using namespace boost::spirit; |
|---|
| 560 | |
|---|
| 561 | typedef MultiPassIterator iterator_t; |
|---|
| 562 | typedef skip_parser_iteration_policy< boost::detail::graph::dot_skipper> |
|---|
| 563 | iter_policy_t; |
|---|
| 564 | typedef scanner_policies<iter_policy_t> scanner_policies_t; |
|---|
| 565 | typedef scanner<iterator_t, scanner_policies_t> scanner_t; |
|---|
| 566 | |
|---|
| 567 | ::boost::detail::graph::mutate_graph_impl<MutableGraph> |
|---|
| 568 | m_graph(graph, dp, node_id); |
|---|
| 569 | |
|---|
| 570 | ::boost::detail::graph::dot_grammar p(m_graph); |
|---|
| 571 | ::boost::detail::graph::dot_skipper skip_p; |
|---|
| 572 | |
|---|
| 573 | iter_policy_t iter_policy(skip_p); |
|---|
| 574 | scanner_policies_t policies(iter_policy); |
|---|
| 575 | |
|---|
| 576 | scanner_t scan(begin, end, policies); |
|---|
| 577 | |
|---|
| 578 | return p.parse(scan); |
|---|
| 579 | } |
|---|
| 580 | |
|---|
| 581 | } // namespace boost |
|---|
| 582 | |
|---|
| 583 | #endif // BOOST_READ_GRAPHVIZ_SPIRIT_HPP |
|---|