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