read_graphviz_spirit.hpp 27 KB

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