1
// Copyright 2004-5 Trustees of Indiana University
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)
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
13
// Based on the grammar found at:
14
// http://www.graphviz.org/cvs/doc/info/lang.html
16
// See documentation for this code at:
17
// http://www.boost.org/libs/graph/doc/read-graphviz.html
20
// Author: Ronald Garcia
23
#ifndef BOOST_READ_GRAPHVIZ_SPIRIT_HPP
24
#define BOOST_READ_GRAPHVIZ_SPIRIT_HPP
26
// Phoenix/Spirit set these limits to 3, but I need more.
27
#define PHOENIX_LIMIT 6
28
#define BOOST_SPIRIT_CLOSURE_LIMIT 6
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>
49
#include <exception> // for std::exception
55
#include <boost/graph/graphviz.hpp>
56
#include <boost/throw_exception.hpp>
59
// Workaround: std::map::operator[] uses a different return type than all
60
// other standard containers. Phoenix doesn't account for that.
61
template <typename TK, typename T0, typename T1>
62
struct binary_operator<index_op, std::map<TK,T0>, T1>
64
typedef typename std::map<TK,T0>::mapped_type& result_type;
65
static result_type eval(std::map<TK,T0>& container, T1 const& index)
66
{ return container[index]; }
68
} // namespace phoenix
75
/////////////////////////////////////////////////////////////////////////////
76
// Application-specific type definitions
77
/////////////////////////////////////////////////////////////////////////////
79
typedef std::set<edge_t> edges_t;
80
typedef std::set<node_t> nodes_t;
81
typedef std::set<id_t> ids_t;
82
typedef std::map<edge_t,ids_t> edge_map_t;
83
typedef std::map<node_t,ids_t> node_map_t;
84
typedef std::map<id_t,id_t> props_t;
85
typedef std::map<id_t,props_t> subgraph_props_t;
86
typedef boost::function2<void, id_t const&, id_t const&> actor_t;
87
typedef std::vector<edge_t> edge_stack_t;
88
typedef std::map<id_t,nodes_t> subgraph_nodes_t;
89
typedef std::map<id_t,edges_t> subgraph_edges_t;
93
/////////////////////////////////////////////////////////////////////////////
94
// Stack frames used by semantic actions
95
/////////////////////////////////////////////////////////////////////////////
96
struct id_closure : boost::spirit::closure<id_closure, node_t> {
101
struct node_id_closure : boost::spirit::closure<node_id_closure, node_t> {
105
struct attr_list_closure : boost::spirit::closure<attr_list_closure, actor_t> {
109
struct property_closure : boost::spirit::closure<property_closure, id_t, id_t> {
114
struct data_stmt_closure : boost::spirit::closure<data_stmt_closure,
115
nodes_t,nodes_t,edge_stack_t,bool,node_t> {
123
struct subgraph_closure : boost::spirit::closure<subgraph_closure,
124
nodes_t, edges_t, node_t> {
130
/////////////////////////////////////////////////////////////////////////////
131
// Grammar and Actions for the DOT Language
132
/////////////////////////////////////////////////////////////////////////////
134
// Grammar for a dot file.
135
struct dot_grammar : public boost::spirit::grammar<dot_grammar> {
136
mutate_graph& graph_;
137
explicit dot_grammar(mutate_graph& graph) : graph_(graph) { }
139
template <class ScannerT>
142
definition(dot_grammar const& self) : self(self), subgraph_depth(0),
143
keyword_p("0-9a-zA-Z_") {
144
using namespace boost::spirit;
145
using namespace phoenix;
148
// - Handle multi-line strings using \ line continuation
149
// - Make keywords case insensitive
151
= ( lexeme_d[((alpha_p | ch_p('_')) >> *(alnum_p | ch_p('_')))]
153
| lexeme_d[confix_p('"', *c_escape_ch_p, '"')]
154
| comment_nest_p('<', '>')
155
)[ID.name = construct_<std::string>(arg1,arg2)]
159
= list_p( ID[(a_list.key = arg1),
160
(a_list.value = "true")
163
>> ID[a_list.value = arg1])
164
[phoenix::bind(&definition::call_prop_actor)
165
(var(*this),a_list.key,a_list.value)],!ch_p(','));
167
attr_list = +(ch_p('[') >> !a_list >> ch_p(']'));
169
// RG - disregard port id's for now.
172
| (ch_p(':') >> ch_p('(') >> ID >> ch_p(',') >> ID >> ch_p(')'))
175
port_angle = ch_p('@') >> ID;
178
= port_location >> (!port_angle)
179
| port_angle >> (!port_location);
183
= ( ID[node_id.name = arg1] >> (!port) )
184
[phoenix::bind(&definition::memoize_node)(var(*this))];
187
= (ID[graph_stmt.key = arg1] >>
189
ID[graph_stmt.value = arg1])
190
[phoenix::bind(&definition::call_graph_prop)
191
(var(*this),graph_stmt.key,graph_stmt.value)]
195
= (as_lower_d[keyword_p("graph")]
196
>> attr_list(actor_t(phoenix::bind(&definition::default_graph_prop)
197
(var(*this),arg1,arg2))))
198
| (as_lower_d[keyword_p("node")]
199
>> attr_list(actor_t(phoenix::bind(&definition::default_node_prop)
200
(var(*this),arg1,arg2))))
201
| (as_lower_d[keyword_p("edge")]
202
>> attr_list(actor_t(phoenix::bind(&definition::default_edge_prop)
203
(var(*this),arg1,arg2))))
206
// edge_head is set depending on the graph type (directed/undirected)
207
edgeop = ch_p('-') >> ch_p(boost::ref(edge_head));
210
= +( edgeop[(data_stmt.sources = data_stmt.dests),
211
(data_stmt.dests = construct_<nodes_t>())]
212
>> ( subgraph[data_stmt.dests = arg1]
213
| node_id[phoenix::bind(&definition::insert_node)
214
(var(*this),data_stmt.dests,arg1)]
216
[phoenix::bind(&definition::activate_edge)
217
(var(*this),data_stmt.sources,data_stmt.dests,
218
var(edges), var(default_edge_props))]
222
// To avoid backtracking, edge, node, and subgraph statements are
223
// processed as one nonterminal.
225
= ( subgraph[(data_stmt.dests = arg1),// will get moved in rhs
226
(data_stmt.saw_node = false)]
227
| node_id[(phoenix::bind(&definition::insert_node)
228
(var(*this),data_stmt.dests,arg1)),
229
(data_stmt.saw_node = true),
230
#ifdef BOOST_GRAPH_DEBUG
231
(std::cout << val("AcTive Node: ") << arg1 << "\n"),
232
#endif // BOOST_GRAPH_DEBUG
233
(data_stmt.active_node = arg1)]
236
actor_t(phoenix::bind(&definition::edge_prop)
237
(var(*this),arg1,arg2)))
239
if_p(data_stmt.saw_node)[
241
actor_t(phoenix::bind(&definition::node_prop)
242
(var(*this),arg1,arg2)))
243
] // otherwise it's a subgraph, nothing more to do.
253
stmt_list = *( stmt >> !ch_p(';') );
256
= !( as_lower_d[keyword_p("subgraph")]
257
>> (!ID[(subgraph.name = arg1),
258
(subgraph.nodes = (var(subgraph_nodes))[arg1]),
259
(subgraph.edges = (var(subgraph_edges))[arg1])])
261
>> ch_p('{')[++var(subgraph_depth)]
263
>> ch_p('}')[--var(subgraph_depth)]
264
[(var(subgraph_nodes))[subgraph.name] = subgraph.nodes]
265
[(var(subgraph_edges))[subgraph.name] = subgraph.edges]
267
| as_lower_d[keyword_p("subgraph")]
268
>> ID[(subgraph.nodes = (var(subgraph_nodes))[arg1]),
269
(subgraph.edges = (var(subgraph_edges))[arg1])]
273
= (!as_lower_d[keyword_p("strict")])
274
>> ( as_lower_d[keyword_p("graph")][
275
(var(edge_head) = '-'),
276
(phoenix::bind(&definition::check_undirected)(var(*this)))]
277
| as_lower_d[keyword_p("digraph")][
278
(var(edge_head) = '>'),
279
(phoenix::bind(&definition::check_directed)(var(*this)))]
281
>> (!ID) >> ch_p('{') >> stmt_list >> ch_p('}');
285
typedef boost::spirit::rule<ScannerT> rule_t;
287
rule_t const& start() const { return the_grammar; }
294
void check_undirected() {
295
if(self.graph_.is_directed())
296
boost::throw_exception(boost::undirected_graph_error());
299
void check_directed() {
300
if(!self.graph_.is_directed())
301
boost::throw_exception(boost::directed_graph_error());
304
void memoize_node() {
305
id_t const& node = node_id.name();
306
props_t& node_props = default_node_props;
308
if(nodes.find(node) == nodes.end()) {
310
self.graph_.do_add_vertex(node);
312
node_map.insert(std::make_pair(node,ids_t()));
314
#ifdef BOOST_GRAPH_DEBUG
315
std::cout << "Add new node " << node << std::endl;
316
#endif // BOOST_GRAPH_DEBUG
317
// Set the default properties for this edge
318
// RG: Here I would actually set the properties
319
for(props_t::iterator i = node_props.begin();
320
i != node_props.end(); ++i) {
321
set_node_property(node,i->first,i->second);
323
if(subgraph_depth > 0) {
324
subgraph.nodes().insert(node);
325
// Set the subgraph's default properties as well
326
props_t& props = subgraph_node_props[subgraph.name()];
327
for(props_t::iterator i = props.begin(); i != props.end(); ++i) {
328
set_node_property(node,i->first,i->second);
332
#ifdef BOOST_GRAPH_DEBUG
333
std::cout << "See node " << node << std::endl;
334
#endif // BOOST_GRAPH_DEBUG
338
void activate_edge(nodes_t& sources, nodes_t& dests, edges_t& edges,
339
props_t& edge_props) {
340
edge_stack_t& edge_stack = data_stmt.edge_stack();
341
for(nodes_t::iterator i = sources.begin(); i != sources.end(); ++i) {
342
for(nodes_t::iterator j = dests.begin(); j != dests.end(); ++j) {
343
// Create the edge and push onto the edge stack.
344
#ifdef BOOST_GRAPH_DEBUG
345
std::cout << "Edge " << *i << " to " << *j << std::endl;
346
#endif // BOOST_GRAPH_DEBUG
348
edge_t edge = edge_t::new_edge();
349
edge_stack.push_back(edge);
351
edge_map.insert(std::make_pair(edge,ids_t()));
353
// Add the real edge.
354
self.graph_.do_add_edge(edge, *i, *j);
356
// Set the default properties for this edge
357
for(props_t::iterator k = edge_props.begin();
358
k != edge_props.end(); ++k) {
359
set_edge_property(edge,k->first,k->second);
361
if(subgraph_depth > 0) {
362
subgraph.edges().insert(edge);
363
// Set the subgraph's default properties as well
364
props_t& props = subgraph_edge_props[subgraph.name()];
365
for(props_t::iterator k = props.begin(); k != props.end(); ++k) {
366
set_edge_property(edge,k->first,k->second);
373
// node_prop - Assign the property for the current active node.
374
void node_prop(id_t const& key, id_t const& value) {
375
node_t& active_object = data_stmt.active_node();
376
set_node_property(active_object, key, value);
379
// edge_prop - Assign the property for the current active edges.
380
void edge_prop(id_t const& key, id_t const& value) {
381
edge_stack_t const& active_edges_ = data_stmt.edge_stack();
382
for (edge_stack_t::const_iterator i = active_edges_.begin();
383
i != active_edges_.end(); ++i) {
384
set_edge_property(*i,key,value);
388
// default_graph_prop - Store as a graph property.
389
void default_graph_prop(id_t const& key, id_t const& value) {
390
#ifdef BOOST_GRAPH_DEBUG
391
std::cout << key << " = " << value << std::endl;
392
#endif // BOOST_GRAPH_DEBUG
393
self.graph_.set_graph_property(key, value);
396
// default_node_prop - declare default properties for any future new nodes
397
void default_node_prop(id_t const& key, id_t const& value) {
399
subgraph_depth == 0 ? nodes : subgraph.nodes();
400
props_t& node_props_ =
401
subgraph_depth == 0 ?
403
subgraph_node_props[subgraph.name()];
405
// add this to the selected list of default node properties.
406
node_props_[key] = value;
407
// for each node, set its property to default-constructed value
408
// if it hasn't been set already.
409
// set the dynamic property map value
410
for(nodes_t::iterator i = nodes_.begin(); i != nodes_.end(); ++i)
411
if(node_map[*i].find(key) == node_map[*i].end()) {
412
set_node_property(*i,key,id_t());
416
// default_edge_prop - declare default properties for any future new edges
417
void default_edge_prop(id_t const& key, id_t const& value) {
419
subgraph_depth == 0 ? edges : subgraph.edges();
420
props_t& edge_props_ =
421
subgraph_depth == 0 ?
423
subgraph_edge_props[subgraph.name()];
425
// add this to the list of default edge properties.
426
edge_props_[key] = value;
427
// for each edge, set its property to be empty string
428
// set the dynamic property map value
429
for(edges_t::iterator i = edges_.begin(); i != edges_.end(); ++i)
430
if(edge_map[*i].find(key) == edge_map[*i].end())
431
set_edge_property(*i,key,id_t());
435
void insert_node(nodes_t& nodes, id_t const& name) {
439
void call_prop_actor(std::string const& lhs, std::string const& rhs) {
440
actor_t& actor = attr_list.prop_actor();
441
// If first and last characters of the rhs are double-quotes,
443
if (!rhs.empty() && rhs[0] == '"' && rhs[rhs.size() - 1] == '"')
444
actor(lhs, rhs.substr(1, rhs.size()-2));
449
void call_graph_prop(std::string const& lhs, std::string const& rhs) {
450
// If first and last characters of the rhs are double-quotes,
452
if (!rhs.empty() && rhs[0] == '"' && rhs[rhs.size() - 1] == '"')
453
this->default_graph_prop(lhs, rhs.substr(1, rhs.size()-2));
455
this->default_graph_prop(lhs,rhs);
458
void set_node_property(node_t const& node, id_t const& key,
461
// Add the property key to the "set" table to avoid default overwrite
462
node_map[node].insert(key);
463
// Set the user's property map
464
self.graph_.set_node_property(key, node, value);
465
#ifdef BOOST_GRAPH_DEBUG
467
std::cout << node << ": " << key << " = " << value << std::endl;
468
#endif // BOOST_GRAPH_DEBUG
471
void set_edge_property(edge_t const& edge, id_t const& key,
474
// Add the property key to the "set" table to avoid default overwrite
475
edge_map[edge].insert(key);
476
// Set the user's property map
477
self.graph_.set_edge_property(key, edge, value);
478
#ifdef BOOST_GRAPH_DEBUG
480
#if 0 // RG - edge representation changed,
481
std::cout << "(" << edge.first << "," << edge.second << "): "
483
std::cout << "an edge: "
485
<< key << " = " << value << std::endl;
486
#endif // BOOST_GRAPH_DEBUG
489
// Variables explicitly initialized
490
dot_grammar const& self;
491
// if subgraph_depth > 0, then we're processing a subgraph.
495
const boost::spirit::distinct_parser<> keyword_p;
497
// rules that make up the grammar
499
boost::spirit::rule<ScannerT,id_closure::context_t> ID;
500
boost::spirit::rule<ScannerT,property_closure::context_t> a_list;
501
boost::spirit::rule<ScannerT,attr_list_closure::context_t> attr_list;
502
rule_t port_location;
505
boost::spirit::rule<ScannerT,node_id_closure::context_t> node_id;
506
boost::spirit::rule<ScannerT,property_closure::context_t> graph_stmt;
508
boost::spirit::rule<ScannerT,data_stmt_closure::context_t> data_stmt;
509
boost::spirit::rule<ScannerT,subgraph_closure::context_t> subgraph;
517
// The grammar uses edge_head to dynamically set the syntax for edges
518
// directed graphs: edge_head = '>', and so edgeop = "->"
519
// undirected graphs: edge_head = '-', and so edgeop = "--"
524
// Support data structures
527
nodes_t nodes; // list of node names seen
528
edges_t edges; // list of edges seen
529
node_map_t node_map; // remember the properties set for each node
530
edge_map_t edge_map; // remember the properties set for each edge
532
subgraph_nodes_t subgraph_nodes; // per-subgraph lists of nodes
533
subgraph_edges_t subgraph_edges; // per-subgraph lists of edges
535
props_t default_node_props; // global default node properties
536
props_t default_edge_props; // global default edge properties
537
subgraph_props_t subgraph_node_props; // per-subgraph default node properties
538
subgraph_props_t subgraph_edge_props; // per-subgraph default edge properties
539
}; // struct definition
540
}; // struct dot_grammar
545
// dot_skipper - GraphViz whitespace and comment skipper
547
struct dot_skipper : public boost::spirit::grammar<dot_skipper>
551
template <typename ScannerT>
554
definition(dot_skipper const& /*self*/) {
555
using namespace boost::spirit;
556
using namespace phoenix;
558
skip = eol_p >> comment_p("#")
561
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1400)
562
| confix_p(str_p("/*") ,*anychar_p, str_p("*/"))
564
| confix_p("/*" ,*anychar_p, "*/")
568
#ifdef BOOST_SPIRIT_DEBUG
569
BOOST_SPIRIT_DEBUG_RULE(skip);
573
boost::spirit::rule<ScannerT> skip;
574
boost::spirit::rule<ScannerT> const&
575
start() const { return skip; }
580
} // namespace detail
582
template <typename MultiPassIterator, typename MutableGraph>
583
bool read_graphviz(MultiPassIterator begin, MultiPassIterator end,
584
MutableGraph& graph, dynamic_properties& dp,
585
std::string const& node_id = "node_id") {
586
using namespace boost;
587
using namespace boost::spirit;
589
typedef MultiPassIterator iterator_t;
590
typedef skip_parser_iteration_policy< boost::detail::graph::dot_skipper>
592
typedef scanner_policies<iter_policy_t> scanner_policies_t;
593
typedef scanner<iterator_t, scanner_policies_t> scanner_t;
595
::boost::detail::graph::mutate_graph_impl<MutableGraph>
596
m_graph(graph, dp, node_id);
598
::boost::detail::graph::dot_grammar p(m_graph);
599
::boost::detail::graph::dot_skipper skip_p;
601
iter_policy_t iter_policy(skip_p);
602
scanner_policies_t policies(iter_policy);
604
scanner_t scan(begin, end, policies);
606
return p.parse(scan);
611
#endif // BOOST_READ_GRAPHVIZ_SPIRIT_HPP