read_graphviz_spirit.hpp 21 KB

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