Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r53092 - in trunk: boost/graph boost/graph/detail libs/graph/doc libs/graph/src libs/graph/test
From: jewillco_at_[hidden]
Date: 2009-05-18 13:27:16


Author: jewillco
Date: 2009-05-18 13:27:15 EDT (Mon, 18 May 2009)
New Revision: 53092
URL: http://svn.boost.org/trac/boost/changeset/53092

Log:
Added new Graphviz parser (recursive descent); does not support subgraphs in edges yet
Added:
   trunk/boost/graph/detail/read_graphviz_new.hpp (contents, props changed)
Text files modified:
   trunk/boost/graph/graphviz.hpp | 17 +
   trunk/libs/graph/doc/read_graphviz.html | 420 ++++++++++++++++++++++++++++++++-------
   trunk/libs/graph/doc/read_graphviz.rst | 98 ++++----
   trunk/libs/graph/src/read_graphviz_spirit.cpp | 26 --
   trunk/libs/graph/test/Jamfile.v2 | 5
   trunk/libs/graph/test/graphviz_test.cpp | 56 +++--
   6 files changed, 447 insertions(+), 175 deletions(-)

Added: trunk/boost/graph/detail/read_graphviz_new.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/graph/detail/read_graphviz_new.hpp 2009-05-18 13:27:15 EDT (Mon, 18 May 2009)
@@ -0,0 +1,699 @@
+// Copyright 2004-9 Trustees of Indiana University
+
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+//
+// read_graphviz_new.hpp -
+// Initialize a model of the BGL's MutableGraph concept and an associated
+// collection of property maps using a graph expressed in the GraphViz
+// DOT Language.
+//
+// Based on the grammar found at:
+// http://www.graphviz.org/cvs/doc/info/lang.html
+//
+// Jeremiah rewrite used grammar found at:
+// http://www.graphviz.org/doc/info/lang.html
+// and page 34 or http://www.graphviz.org/pdf/dotguide.pdf
+//
+// See documentation for this code at:
+// http://www.boost.org/libs/graph/doc/read-graphviz.html
+//
+
+// Author: Jeremiah Willcock
+// Ronald Garcia
+//
+
+#ifndef BOOST_READ_GRAPHVIZ_NEW_HPP
+#define BOOST_READ_GRAPHVIZ_NEW_HPP
+
+#include <boost/ref.hpp>
+#include <boost/function/function2.hpp>
+#include <boost/property_map/dynamic_property_map.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/detail/workaround.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <algorithm>
+#include <exception> // for std::exception
+#include <string>
+#include <vector>
+#include <set>
+#include <utility>
+#include <map>
+#include <iostream>
+#include <cstdlib>
+#include <boost/graph/graphviz.hpp>
+#include <boost/throw_exception.hpp>
+#include <boost/regex.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+namespace boost {
+
+namespace read_graphviz_detail {
+ struct token {
+ enum token_type {
+ kw_strict,
+ kw_graph,
+ kw_digraph,
+ kw_node,
+ kw_edge,
+ kw_subgraph,
+ left_brace,
+ right_brace,
+ semicolon,
+ equal,
+ left_bracket,
+ right_bracket,
+ comma,
+ colon,
+ dash_greater,
+ dash_dash,
+ plus,
+ left_paren,
+ right_paren,
+ at,
+ identifier,
+ quoted_string, // Only used internally in tokenizer
+ eof,
+ invalid
+ } type;
+ std::string normalized_value; // May have double-quotes removed and/or some escapes replaced
+ token(token_type type, const std::string& normalized_value)
+ : type(type), normalized_value(normalized_value) {}
+ token(): type(invalid), normalized_value("") {}
+ friend std::ostream& operator<<(std::ostream& o, const token& t) {
+ switch (t.type) {
+ case token::kw_strict: o << "<strict>"; break;
+ case token::kw_graph: o << "<graph>"; break;
+ case token::kw_digraph: o << "<digraph>"; break;
+ case token::kw_node: o << "<node>"; break;
+ case token::kw_edge: o << "<edge>"; break;
+ case token::kw_subgraph: o << "<subgraph>"; break;
+ case token::left_brace: o << "<left_brace>"; break;
+ case token::right_brace: o << "<right_brace>"; break;
+ case token::semicolon: o << "<semicolon>"; break;
+ case token::equal: o << "<equal>"; break;
+ case token::left_bracket: o << "<left_bracket>"; break;
+ case token::right_bracket: o << "<right_bracket>"; break;
+ case token::comma: o << "<comma>"; break;
+ case token::colon: o << "<colon>"; break;
+ case token::dash_greater: o << "<dash-greater>"; break;
+ case token::dash_dash: o << "<dash-dash>"; break;
+ case token::plus: o << "<plus>"; break;
+ case token::left_paren: o << "<left_paren>"; break;
+ case token::right_paren: o << "<right_paren>"; break;
+ case token::at: o << "<at>"; break;
+ case token::identifier: o << "<identifier>"; break;
+ case token::quoted_string: o << "<quoted_string>"; break;
+ case token::eof: o << "<eof>"; break;
+ default: o << "<invalid type>"; break;
+ }
+ o << " '" << t.normalized_value << "'";
+ return o;
+ }
+ };
+
+ struct lex_error: public std::exception {
+ std::string errmsg;
+ char bad_char;
+ lex_error(const std::string& errmsg, char bad_char): errmsg(errmsg), bad_char(bad_char) {
+ this->errmsg += std::string(" (char is '") + bad_char + "')";
+ }
+ const char* what() const throw () {return errmsg.c_str();}
+ ~lex_error() throw () {};
+ };
+
+ struct parse_error: public std::exception {
+ std::string errmsg;
+ token bad_token;
+ parse_error(const std::string& errmsg, const token& bad_token): errmsg(errmsg), bad_token(bad_token) {
+ this->errmsg += std::string(" (token is \"") + boost::lexical_cast<std::string>(bad_token) + "\")";
+ }
+ const char* what() const throw () {return errmsg.c_str();}
+ ~parse_error() throw () {};
+ };
+
+ template <typename BidirectionalIterator>
+ struct tokenizer {
+ BidirectionalIterator begin, end;
+ std::vector<token> lookahead;
+ // Precomputed regexes
+ boost::regex stuff_to_skip;
+ boost::regex basic_id_token;
+ boost::regex punctuation_token;
+ boost::regex number_token;
+ boost::regex quoted_string_token;
+
+ tokenizer(BidirectionalIterator begin, BidirectionalIterator end)
+ : begin(begin), end(end)
+ {
+ std::string end_of_token = "(?=(?:\\W))";
+ std::string whitespace = "(?:\\s+)";
+ std::string slash_slash_comment = "(?://.*$)";
+ std::string slash_star_comment = "(?:/\\*.*?\\*/)";
+ std::string hash_comment = "(?:^#.*?$)";
+ stuff_to_skip = "\\A(?:" + whitespace + "|" + slash_slash_comment + "|" + slash_star_comment + "|" + hash_comment + ")*";
+ basic_id_token = "\\A([[:alpha:]_](?:\\w*))";
+ punctuation_token = "\\A([][{};=,:+()@]|[-][>-])";
+ number_token = "\\A([-]?(?:(?:\\.\\d+)|(?:\\d+(?:\\.\\d*)?)))";
+ quoted_string_token = "\\A(\"(?:[^\"\\\\]|(?:[\\\\].))*\")";
+ }
+
+ void skip() {
+ boost::match_results<BidirectionalIterator> results;
+ bool found = boost::regex_search(begin, end, results, stuff_to_skip);
+ assert (found);
+ boost::sub_match<BidirectionalIterator> sm1 = results.suffix();
+ assert (sm1.second == end);
+ begin = sm1.first;
+ }
+
+ token get_token_raw() {
+ if (!lookahead.empty()) {
+ token t = lookahead.front();
+ lookahead.erase(lookahead.begin());
+ return t;
+ }
+ skip();
+ if (begin == end) return token(token::eof, "");
+ // Look for keywords first
+ bool found;
+ boost::match_results<BidirectionalIterator> results;
+ found = boost::regex_search(begin, end, results, basic_id_token);
+ if (found) {
+ std::string str = results[1].str();
+ std::string str_lower = boost::algorithm::to_lower_copy(str);
+ begin = results.suffix().first;
+ if (str_lower == "strict") {
+ return token(token::kw_strict, str);
+ } else if (str_lower == "graph") {
+ return token(token::kw_graph, str);
+ } else if (str_lower == "digraph") {
+ return token(token::kw_digraph, str);
+ } else if (str_lower == "node") {
+ return token(token::kw_node, str);
+ } else if (str_lower == "edge") {
+ return token(token::kw_edge, str);
+ } else if (str_lower == "subgraph") {
+ return token(token::kw_subgraph, str);
+ } else {
+ return token(token::identifier, str);
+ }
+ }
+ found = boost::regex_search(begin, end, results, punctuation_token);
+ if (found) {
+ std::string str = results[1].str();
+ begin = results.suffix().first;
+ switch (str[0]) {
+ case '[': return token(token::left_bracket, str);
+ case ']': return token(token::right_bracket, str);
+ case '{': return token(token::left_brace, str);
+ case '}': return token(token::right_brace, str);
+ case ';': return token(token::semicolon, str);
+ case '=': return token(token::equal, str);
+ case ',': return token(token::comma, str);
+ case ':': return token(token::colon, str);
+ case '+': return token(token::plus, str);
+ case '(': return token(token::left_paren, str);
+ case ')': return token(token::right_paren, str);
+ case '@': return token(token::at, str);
+ case '-': {
+ switch (str[1]) {
+ case '-': return token(token::dash_dash, str);
+ case '>': return token(token::dash_greater, str);
+ default: assert (!"Definition of punctuation_token does not match switch statement");
+ }
+ }
+ default: assert (!"Definition of punctuation_token does not match switch statement"); std::abort();
+ }
+ }
+ found = boost::regex_search(begin, end, results, number_token);
+ if (found) {
+ std::string str = results[1].str();
+ begin = results.suffix().first;
+ return token(token::identifier, str);
+ }
+ found = boost::regex_search(begin, end, results, quoted_string_token);
+ if (found) {
+ std::string str = results[1].str();
+ begin = results.suffix().first;
+ // Remove the beginning and ending quotes
+ assert (str.size() >= 2);
+ str.erase(str.begin());
+ str.erase(str.end() - 1);
+ // Unescape quotes in the middle, but nothing else (see format spec)
+ for (size_t i = 0; i + 1 < str.size() /* May change */; ++i) {
+ if (str[i] == '\\' && str[i + 1] == '"') {
+ str.erase(str.begin() + i);
+ // Don't need to adjust i
+ } else if (str[i] == '\\' && str[i + 1] == '\n') {
+ str.erase(str.begin() + i);
+ str.erase(str.begin() + i);
+ --i; // Invert ++ that will be applied
+ }
+ }
+ return token(token::quoted_string, str);
+ }
+ if (*begin == '<') {
+ throw_lex_error("HTML strings not supported");
+ return token();
+ } else {
+ throw_lex_error("Invalid character");
+ return token();
+ }
+ }
+
+ token peek_token_raw() {
+ if (lookahead.empty()) {
+ token t = get_token_raw();
+ lookahead.push_back(t);
+ }
+ return lookahead.front();
+ }
+
+ token get_token() { // Handle string concatenation
+ token t = get_token_raw();
+ if (t.type != token::quoted_string) return t;
+ std::string str = t.normalized_value;
+ while (peek_token_raw().type == token::plus) {
+ get_token_raw();
+ token t2 = get_token_raw();
+ if (t2.type != token::quoted_string) {
+ throw_lex_error("Must have quoted string after string concatenation");
+ }
+ str += t2.normalized_value;
+ }
+ return token(token::identifier, str); // Note that quoted_string does not get passed to the parser
+ }
+
+ void throw_lex_error(const std::string& errmsg) {
+ boost::throw_exception(lex_error(errmsg, *begin));
+ }
+ };
+
+ typedef std::map<std::string, std::string> properties;
+
+ struct node_id {
+ std::string name;
+ std::string angle; // Or empty if no angle
+ std::vector<std::string> location; // Up to two identifiers
+ };
+
+ // Parser policy object should have the following methods:
+ // struct parser_policy {
+ // void do_node(const node_id& n, const properties& default_props, const properties& custom_props);
+ // void do_edge(const node_id& a, const node_id& b, const properties& props);
+ // void do_begin_graph(bool is_strict, bool is_directed, const std::string& name);
+ // void do_end_graph(const properties& props);
+ // void do_begin_subgraph(const std::string& name);
+ // void do_end_subgraph(const properties& props);
+ // };
+
+ template <typename BidirectionalIterator, typename Policy>
+ struct parser {
+ tokenizer<BidirectionalIterator> tokenizer;
+ std::vector<token> lookahead;
+ bool graph_is_directed;
+ properties graph_props;
+ properties node_props;
+ properties edge_props;
+ Policy hooks;
+
+ parser(BidirectionalIterator begin, BidirectionalIterator end, Policy hooks)
+ : tokenizer(begin, end), lookahead(), hooks(hooks) {}
+
+ token get() {
+ if (lookahead.empty()) {
+ token t = tokenizer.get_token();
+ return t;
+ } else {
+ token t = lookahead.front();
+ lookahead.erase(lookahead.begin());
+ return t;
+ }
+ }
+
+ token peek() {
+ if (lookahead.empty()) {
+ lookahead.push_back(tokenizer.get_token());
+ }
+ return lookahead.front();
+ }
+
+ void error(const std::string& str) {
+ boost::throw_exception(parse_error(str, peek()));
+ }
+
+ void parse_graph(bool want_directed) {
+ bool is_strict = false;
+ bool is_directed;
+ std::string name;
+ if (peek().type == token::kw_strict) {get(); is_strict = true;}
+ switch (peek().type) {
+ case token::kw_graph: is_directed = false; break;
+ case token::kw_digraph: is_directed = true; break;
+ default: error("Wanted \"graph\" or \"digraph\"");
+ }
+ graph_is_directed = is_directed; // Used to check edges
+ if (want_directed != graph_is_directed) {
+ if (want_directed) {
+ boost::throw_exception(boost::undirected_graph_error());
+ } else {
+ boost::throw_exception(boost::directed_graph_error());
+ }
+ }
+ hooks.do_begin_graph(is_strict, is_directed, name);
+ get();
+ switch (peek().type) {
+ case token::identifier: name = peek().normalized_value; get(); break;
+ case token::left_brace: break;
+ default: error("Wanted a graph name or left brace");
+ }
+ if (peek().type == token::left_brace) get(); else error("Wanted a left brace to start the graph");
+ parse_stmt_list();
+ if (peek().type == token::right_brace) get(); else error("Wanted a right brace to end the graph");
+ hooks.do_end_graph(graph_props);
+ if (peek().type == token::eof) {} else error("Wanted end of file");
+ }
+
+ void parse_stmt_list() {
+ while (true) {
+ if (peek().type == token::right_brace) return;
+ parse_stmt();
+ if (peek().type == token::semicolon) get();
+ }
+ }
+
+ void parse_stmt() {
+ switch (peek().type) {
+ case token::kw_node:
+ case token::kw_edge:
+ case token::kw_graph: parse_attr_stmt(); break;
+ case token::kw_subgraph:
+ case token::left_brace: parse_subgraph(); break;
+ case token::identifier: {
+ token id = get();
+ switch (peek().type) {
+ case token::dash_dash:
+ case token::dash_greater: {
+ node_id n = parse_node_id_rest(id);
+ parse_edge_stmt(n);
+ break;
+ }
+ case token::equal: {
+ get();
+ if (peek().type != token::identifier) error("Wanted identifier as right side of =");
+ token id2 = get();
+ graph_props[id.normalized_value] = id2.normalized_value;
+ break;
+ }
+ default: {
+ node_id n = parse_node_id_rest(id);
+ parse_node_stmt(n);
+ break;
+ }
+ }
+ break;
+ }
+ default: error("Invalid start token for statement");
+ }
+ }
+
+ void parse_attr_stmt() {
+ switch (get().type) {
+ case token::kw_graph: parse_attr_list(graph_props); break;
+ case token::kw_node: parse_attr_list(node_props); break;
+ case token::kw_edge: parse_attr_list(edge_props); break;
+ default: assert (!"Bad attr_stmt case"); std::abort();
+ }
+ }
+
+ void parse_subgraph() {
+ std::string name;
+ if (peek().type == token::kw_subgraph) {
+ get();
+ if (peek().type == token::identifier) {
+ name = get().normalized_value;
+ }
+ }
+ properties saved_node_props = node_props;
+ properties saved_edge_props = edge_props;
+ properties saved_graph_props = graph_props;
+ if (peek().type != token::left_brace) {
+ if (name.empty()) error("Subgraph reference needs a name");
+ hooks.do_subgraph_reference(name);
+ return;
+ }
+ hooks.do_begin_subgraph(name);
+ if (peek().type == token::left_brace) get(); else error("Wanted left brace to start subgraph");
+ parse_stmt_list();
+ if (peek().type == token::right_brace) get(); else error("Wanted right brace to end subgraph");
+ hooks.do_end_subgraph(graph_props);
+ node_props = saved_node_props;
+ edge_props = saved_edge_props;
+ graph_props = saved_graph_props;
+ if (peek().type == token::dash_greater ||
+ peek().type == token::dash_dash) { // FIXME
+ std::cerr << "FIXME: Subgraphs in edges are not supported" << std::endl;
+ error("Unsupported subgraph edge case");
+ }
+ }
+
+ node_id parse_node_id_rest(const token& name) {
+ // A node ID is a node name, followed optionally by a port angle and a
+ // port location (in either order); a port location is either :id,
+ // :id:id, or :(id,id); the last two forms are treated as equivalent
+ // although I am not sure about that.
+ node_id id;
+ id.name = name.normalized_value;
+ parse_more:
+ switch (peek().type) {
+ case token::at: {
+ get();
+ if (peek().type != token::identifier) error("Wanted identifier as port angle");
+ if (id.angle != "") error("Duplicate port angle");
+ id.angle = get().normalized_value;
+ goto parse_more;
+ }
+ case token::colon: {
+ get();
+ if (!id.location.empty()) error("Duplicate port location");
+ switch (peek().type) {
+ case token::identifier: {
+ id.location.push_back(get().normalized_value);
+ switch (peek().type) {
+ case token::colon: {
+ get();
+ if (peek().type != token::identifier) error("Wanted identifier as port location");
+ id.location.push_back(get().normalized_value);
+ goto parse_more;
+ }
+ default: goto parse_more;
+ }
+ }
+ case token::left_paren: {
+ get();
+ if (peek().type != token::identifier) error("Wanted identifier as first element of port location");
+ id.location.push_back(get().normalized_value);
+ if (peek().type != token::comma) error("Wanted comma between parts of port location");
+ get();
+ if (peek().type != token::identifier) error("Wanted identifier as second element of port location");
+ id.location.push_back(get().normalized_value);
+ if (peek().type != token::right_paren) error("Wanted right parenthesis to close port location");
+ get();
+ goto parse_more;
+ }
+ default: error("Wanted identifier or left parenthesis as start of port location");
+ }
+ }
+ default: break;
+ }
+ return id;
+ }
+
+ node_id parse_node_id() {
+ if (peek().type != token::identifier) error("Wanted identifier as node name (subgraphs not supported yet)");
+ token name = get();
+ return parse_node_id_rest(name);
+ }
+
+ void parse_node_stmt(const node_id& n) {
+ properties this_node_props;
+ if (peek().type == token::left_bracket) parse_attr_list(this_node_props);
+ hooks.do_node(n, node_props, this_node_props);
+ }
+
+ void parse_edge_stmt(const node_id& lhs) {
+ std::vector<node_id> nodes_in_chain(1, lhs);
+ while (true) {
+ bool leave_loop = true;
+ switch (peek().type) {
+ case token::dash_dash: {
+ if (graph_is_directed) error("Using -- in directed graph");
+ get();
+ nodes_in_chain.push_back(parse_node_id());
+ leave_loop = false;
+ break;
+ }
+ case token::dash_greater: {
+ if (!graph_is_directed) error("Using -> in undirected graph");
+ get();
+ nodes_in_chain.push_back(parse_node_id());
+ leave_loop = false;
+ break;
+ }
+ default: leave_loop = true; break;
+ }
+ if (leave_loop) break;
+ }
+ properties this_edge_props = edge_props;
+ if (peek().type == token::left_bracket) parse_attr_list(this_edge_props);
+ assert (nodes_in_chain.size() >= 2); // Should be in node parser otherwise
+ for (size_t i = 0; i + 1 < nodes_in_chain.size(); ++i) {
+ hooks.do_edge(nodes_in_chain[i], nodes_in_chain[i + 1], this_edge_props);
+ }
+ }
+
+ void parse_attr_list(properties& props) {
+ while (true) {
+ if (peek().type == token::left_bracket) get(); else error("Wanted left bracket to start attribute list");
+ while (true) {
+ switch (peek().type) {
+ case token::right_bracket: break;
+ case token::identifier: {
+ std::string lhs = get().normalized_value;
+ std::string rhs = "true";
+ if (peek().type == token::equal) {
+ get();
+ if (peek().type != token::identifier) error("Wanted identifier as value of attributed");
+ rhs = get().normalized_value;
+ }
+ props[lhs] = rhs;
+ break;
+ }
+ default: error("Wanted identifier as name of attribute");
+ }
+ if (peek().type == token::comma) {get(); continue;}
+ break;
+ }
+ if (peek().type == token::right_bracket) get(); else error("Wanted right bracket to end attribute list");
+ if (peek().type != token::left_bracket) break;
+ }
+ }
+ };
+
+ struct graph_parser_policy {
+ ::boost::detail::graph::mutate_graph* mg;
+
+ typedef boost::detail::graph::node_t vertex;
+ typedef boost::detail::graph::edge_t edge;
+
+ std::map<std::string, vertex> vertex_map;
+ std::map<std::string, edge> edge_map;
+
+ graph_parser_policy(::boost::detail::graph::mutate_graph* mg)
+ : mg(mg),
+ vertex_map(),
+ edge_map()
+ {}
+
+ bool node_exists(const node_id& n) const {
+ std::map<std::string, vertex>::const_iterator i = vertex_map.find(n.name);
+ return (i != vertex_map.end());
+ }
+
+ vertex get_or_build_node(const node_id& n) {
+ // FIXME: use ports
+ std::map<std::string, vertex>::const_iterator i = vertex_map.find(n.name);
+ if (i == vertex_map.end()) {
+ vertex v = n.name;
+ mg->do_add_vertex(v);
+ vertex_map.insert(std::make_pair(v, v));
+ return v;
+ } else {
+ return i->second;
+ }
+ }
+
+ static std::string node_id_to_string(const node_id& n) {
+ std::string result = n.name;
+ for (size_t i = 0; i < n.location.size(); ++i) {
+ result += ":" + n.location[i];
+ }
+ if (!n.angle.empty()) result += "@" + n.angle;
+ return result;
+ }
+
+ static std::string props_to_string(const properties& props) {
+ std::string result = "[";
+ for (properties::const_iterator i = props.begin(); i != props.end(); ++i) {
+ if (i != props.begin()) result += ", ";
+ result += i->first + "=" + i->second;
+ }
+ result += "]";
+ return result;
+ }
+
+ void do_node(const node_id& n, const properties& def_props, const properties& new_props) {
+ std::cerr << node_id_to_string(n) << " " << props_to_string(def_props) << " " << props_to_string(new_props) << std::endl;
+ properties props_to_set = new_props;
+ if (!node_exists(n)) { // Only use defaults if node is new
+ props_to_set.insert(def_props.begin(), def_props.end()); // This keeps old properties in preference to ones from def_props
+ }
+ vertex v = get_or_build_node(n);
+ for (properties::const_iterator i = props_to_set.begin(); i != props_to_set.end(); ++i) {
+ mg->set_node_property(i->first, v, i->second);
+ }
+ }
+
+ void do_edge(const node_id& a, const node_id& b, const properties& props) {
+ std::cerr << node_id_to_string(a) << " -> " << node_id_to_string(b) << " " << props_to_string(props) << std::endl;
+ edge e = edge::new_edge();
+ mg->do_add_edge(e, get_or_build_node(a), get_or_build_node(b));
+ for (properties::const_iterator i = props.begin(); i != props.end(); ++i) {
+ mg->set_edge_property(i->first, e, i->second);
+ }
+ }
+
+ void do_begin_graph(bool is_strict, bool is_directed, const std::string& name) {
+ // std::cerr << "starting" << (is_strict ? " strict" : "") << " " << (is_directed ? "directed" : "undirected") << " graph named " << name << std::endl;
+ }
+
+ void do_end_graph(const properties& props) {
+ std::cerr << "ending graph " << props_to_string(props) << std::endl;
+ for (properties::const_iterator i = props.begin(); i != props.end(); ++i) {
+ mg->set_graph_property(i->first, i->second);
+ }
+ }
+
+ void do_subgraph_reference(const std::string& name) {
+ // std::cerr << "subgraph reference to " << name << std::endl;
+ }
+
+ void do_begin_subgraph(const std::string& name) {
+ // std::cerr << "starting subgraph named " << name << std::endl;
+ }
+
+ void do_end_subgraph(const properties& props) {
+ // std::cerr << "ending subgraph " << props_to_string(props) << std::endl;
+ }
+ };
+
+} // namespace read_graphviz_detail
+
+template <typename BidirectionalIterator, typename MutableGraph>
+bool read_graphviz(BidirectionalIterator begin, BidirectionalIterator end,
+ MutableGraph& graph, boost::dynamic_properties& dp,
+ std::string const& node_id = "node_id") {
+ boost::detail::graph::mutate_graph_impl<MutableGraph> mg(graph, dp, node_id);
+ read_graphviz_detail::graph_parser_policy policy(&mg);
+ read_graphviz_detail::parser<BidirectionalIterator, read_graphviz_detail::graph_parser_policy> p(begin, end, policy);
+ p.parse_graph(mg.is_directed());
+ return true;
+}
+
+} // namespace boost
+
+#endif // BOOST_READ_GRAPHVIZ_NEW_HPP

Modified: trunk/boost/graph/graphviz.hpp
==============================================================================
--- trunk/boost/graph/graphviz.hpp (original)
+++ trunk/boost/graph/graphviz.hpp 2009-05-18 13:27:15 EDT (Mon, 18 May 2009)
@@ -784,15 +784,24 @@
                    dynamic_properties& dp,
                    std::string const& node_id = "node_id")
 {
- detail::graph::mutate_graph_impl<MutableGraph> m_graph(graph, dp, node_id);
- return detail::graph::read_graphviz(in, m_graph);
+ std::string data;
+ in >> std::noskipws;
+ std::copy(std::istream_iterator<char>(in),
+ std::istream_iterator<char>(),
+ std::back_inserter(data));
+ return read_graphviz(data.begin(),data.end(),graph,dp,node_id);
 }
 
 } // namespace boost
 
-#ifdef BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS
+#ifdef BOOST_GRAPH_USE_SPIRIT_PARSER
+# ifndef BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS
+# define BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS
+# endif
 # include <boost/graph/detail/read_graphviz_spirit.hpp>
-#endif // BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS
+#else // New default parser
+# include <boost/graph/detail/read_graphviz_new.hpp>
+#endif // BOOST_GRAPH_USE_SPIRIT_PARSER
 
 #ifdef BOOST_GRAPH_USE_MPI
 # include <boost/graph/distributed/graphviz.hpp>

Modified: trunk/libs/graph/doc/read_graphviz.html
==============================================================================
--- trunk/libs/graph/doc/read_graphviz.html (original)
+++ trunk/libs/graph/doc/read_graphviz.html 2009-05-18 13:27:15 EDT (Mon, 18 May 2009)
@@ -1,48 +1,319 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<!--
- Copyright (c) 2005 Trustees of Indiana University
-
- Distributed under the Boost Software License, Version 1.0.
- (See accompanying file LICENSE_1_0.txt or copy at
- http://www.boost.org/LICENSE_1_0.txt)
- -->
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<meta name="generator" content="Docutils 0.3.8: http://docutils.sourceforge.net/" />
+<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
 <title>Boost read_graphviz</title>
-<link rel="stylesheet" href="default.css" type="text/css" />
+<style type="text/css">
+
+/*
+:Author: David Goodger (goodger_at_[hidden])
+:Id: $Id$
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+ border: 0 }
+
+table.borderless td, table.borderless th {
+ /* Override padding for "table.docutils td" with "! important".
+ The right padding separates the table cells. */
+ padding: 0 0.5em 0 0 ! important }
+
+.first {
+ /* Override more specific margin styles with "! important". */
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+ font-weight: bold }
+*/
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin: 0 0 0.5em 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1.title {
+ text-align: center }
+
+h2.subtitle {
+ text-align: center }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left {
+ clear: left }
+
+img.align-right {
+ clear: right }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font: inherit }
+
+pre.literal-block, pre.doctest-block {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+ul.auto-toc {
+ list-style-type: none }
+
+</style>
 </head>
 <body>
 <div class="document" id="logo-read-graphviz">
-<h1 class="title"><a class="reference" href="../../../index.htm"><img align="middle" alt="Boost" src="../../../boost.png" /></a> <tt class="docutils literal"><span class="pre">read_graphviz</span></tt></h1>
+<h1 class="title"><a class="reference external" href="../../../index.htm"><img align="middle" alt="Boost" class="align-middle" src="../../../boost.png" /></a> <tt class="docutils literal"><span class="pre">read_graphviz</span></tt></h1>
+
+<!-- Copyright (c) 2005 Trustees of Indiana University
+Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt) -->
 <pre class="literal-block">
-template &lt;typename MutableGraph&gt;
-bool read_graphviz(std::istream&amp; in, MutableGraph&amp; graph,
- dynamic_properties&amp; dp,
- const std::string&amp; node_id = &quot;node_id&quot;);
-
-// Only available if BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS is defined
-template &lt;typename MultiPassIterator, typename MutableGraph&gt;
-bool read_graphviz(MultiPassIterator begin, MultiPassIterator end,
- MutableGraph&amp; graph, dynamic_properties&amp; dp,
- const std::string&amp; node_id = &quot;node_id&quot;);
-
-// Deprecated GraphViz readers
-void read_graphviz(const std::string&amp; file, GraphvizDigraph&amp; g);
-void read_graphviz(FILE* file, GraphvizDigraph&amp; g);
-void read_graphviz(const std::string&amp; file, GraphvizGraph&amp; g);
-void read_graphviz(FILE* file, GraphvizGraph&amp; g);
+namespace boost {
+
+ template &lt;typename MutableGraph&gt;
+ bool read_graphviz(std::istream&amp; in, MutableGraph&amp; graph,
+ dynamic_properties&amp; dp,
+ const std::string&amp; node_id = &quot;node_id&quot;);
+
+ template &lt;typename BidirectionalIterator, typename MutableGraph&gt;
+ bool read_graphviz(BidirectionalIterator begin, BidirectionalIterator end,
+ MutableGraph&amp; graph, dynamic_properties&amp; dp,
+ const std::string&amp; node_id = &quot;node_id&quot;);
+
+}
 </pre>
 <p>The <tt class="docutils literal"><span class="pre">read_graphviz</span></tt> function interprets a graph described using the
-<a class="reference" href="http://graphviz.org/">GraphViz</a> DOT language and builds a BGL graph that captures that
-description. Using this function, you can initialize a graph using
-data stored as text. To use the iterator version of <tt class="docutils literal"><span class="pre">read_graphviz</span></tt>,
-you will need to define the macro BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS
-before including the header <tt class="docutils literal"><span class="pre">&lt;boost/graph/graphviz.hpp&gt;</span></tt>. Doing so
-may greatly increase the amount of time required to compile the
-GraphViz reader.</p>
+<a class="reference external" href="http://graphviz.org/">GraphViz</a> DOT language and builds a BGL graph that captures that
+description. Using these functions, you can initialize a graph using
+data stored as text.</p>
+<p>The <tt class="docutils literal"><span class="pre">BOOST_GRAPH_USE_SPIRIT_PARSER</span></tt> macro may be defined before including
+<tt class="docutils literal"><span class="pre">&lt;boost/graph/graphviz.hpp&gt;</span></tt> to include a previous parser implementation,
+with the same interface, that uses Spirit for its implementation. The main
+advantage of that version is that it supports subgraphs to a limited extent,
+while the new parser does not support them at all.</p>
 <p>The DOT language can specify both directed and undirected graphs, and
 <tt class="docutils literal"><span class="pre">read_graphviz</span></tt> differentiates between the two. One must pass
 <tt class="docutils literal"><span class="pre">read_graphviz</span></tt> an undirected graph when reading an undirected graph;
@@ -50,7 +321,7 @@
 will throw an exception if it encounters parallel edges and cannot add
 them to the graph.</p>
 <p>To handle properties expressed in the DOT language, <tt class="docutils literal"><span class="pre">read_graphviz</span></tt>
-takes a <a class="reference" href="../../property_map/doc/dynamic_property_map.html">dynamic_properties</a> object and operates on its collection of
+takes a <a class="reference external" href="../../property_map/doc/dynamic_property_map.html">dynamic_properties</a> object and operates on its collection of
 property maps. The reader passes all the properties encountered to
 this object, using the GraphViz string keys as the property keys.
 Furthermore, <tt class="docutils literal"><span class="pre">read_graphviz</span></tt> stores node identifier names under the
@@ -58,32 +329,31 @@
 <dl class="docutils">
 <dt>Requirements:</dt>
 <dd><ul class="first last simple">
-<li>The type of the graph must model the <a class="reference" href="MutableGraph.html">Mutable Graph</a> concept.</li>
-<li>The type of the iterator must model the <a class="reference" href="../../iterator/index.html">Multi-Pass Iterator</a>
+<li>The type of the graph must model the <a class="reference external" href="MutableGraph.html">Mutable Graph</a> concept.</li>
+<li>The type of the iterator must model the <a class="reference external" href="http://www.sgi.com/tech/stl/BidirectionalIterator.html">Bidirectional Iterator</a>
 concept.</li>
 <li>The property map value types must be default-constructible.</li>
 </ul>
 </dd>
 </dl>
 <div class="contents topic" id="contents">
-<p class="topic-title first"><a name="contents">Contents</a></p>
+<p class="topic-title first">Contents</p>
 <ul class="simple">
-<li><a class="reference" href="#where-defined" id="id2" name="id2">Where Defined</a></li>
-<li><a class="reference" href="#exceptions" id="id3" name="id3">Exceptions</a></li>
-<li><a class="reference" href="#example" id="id4" name="id4">Example</a></li>
-<li><a class="reference" href="#building-the-graphviz-readers" id="id5" name="id5">Building the GraphViz Readers</a></li>
-<li><a class="reference" href="#deprecated-readers" id="id6" name="id6">Deprecated Readers</a></li>
-<li><a class="reference" href="#notes" id="id7" name="id7">Notes</a></li>
-<li><a class="reference" href="#see-also" id="id8" name="id8">See Also</a></li>
-<li><a class="reference" href="#future-work" id="id9" name="id9">Future Work</a></li>
+<li><a class="reference internal" href="#where-defined" id="id2">Where Defined</a></li>
+<li><a class="reference internal" href="#exceptions" id="id3">Exceptions</a></li>
+<li><a class="reference internal" href="#example" id="id4">Example</a></li>
+<li><a class="reference internal" href="#building-the-graphviz-readers" id="id5">Building the GraphViz Readers</a></li>
+<li><a class="reference internal" href="#notes" id="id6">Notes</a></li>
+<li><a class="reference internal" href="#see-also" id="id7">See Also</a></li>
+<li><a class="reference internal" href="#future-work" id="id8">Future Work</a></li>
 </ul>
 </div>
 <div class="section" id="where-defined">
-<h1><a class="toc-backref" href="#id2" name="where-defined">Where Defined</a></h1>
+<h1><a class="toc-backref" href="#id2">Where Defined</a></h1>
 <p><tt class="docutils literal"><span class="pre">&lt;boost/graph/graphviz.hpp&gt;</span></tt></p>
 </div>
 <div class="section" id="exceptions">
-<h1><a class="toc-backref" href="#id3" name="exceptions">Exceptions</a></h1>
+<h1><a class="toc-backref" href="#id3">Exceptions</a></h1>
 <pre class="literal-block">
 struct graph_exception : public std::exception {
   virtual ~graph_exception() throw();
@@ -130,13 +400,13 @@
 language.</p>
 </div>
 <div class="section" id="example">
-<h1><a class="toc-backref" href="#id4" name="example">Example</a></h1>
+<h1><a class="toc-backref" href="#id4">Example</a></h1>
 <p>The following example illustrates a relatively simple use of the
 GraphViz reader to populate an <tt class="docutils literal"><span class="pre">adjacency_list</span></tt> graph</p>
 <pre class="literal-block">
 // Vertex properties
 typedef property &lt; vertex_name_t, std::string,
- property &lt; vertex_color_t, float &gt; &gt; vertex_p;
+ property &lt; vertex_color_t, float &gt; &gt; vertex_p;
 // Edge properties
 typedef property &lt; edge_weight_t, double &gt; edge_p;
 // Graph properties
@@ -162,7 +432,7 @@
 dp.property(&quot;weight&quot;,weight);
 
 // Use ref_property_map to turn a graph property into a property map
-boost::ref_property_map&lt;graph_t*,std::string&gt;
+boost::ref_property_map&lt;graph_t*,std::string&gt;
   gname(get_property(graph,graph_name));
 dp.property(&quot;name&quot;,gname);
 
@@ -174,20 +444,13 @@
 </pre>
 </div>
 <div class="section" id="building-the-graphviz-readers">
-<h1><a class="toc-backref" href="#id5" name="building-the-graphviz-readers">Building the GraphViz Readers</a></h1>
+<h1><a class="toc-backref" href="#id5">Building the GraphViz Readers</a></h1>
 <p>To use the GraphViz readers, you will need to build and link against
-the &quot;boost_graph&quot; library. The library can be built by following the
-<a class="reference" href="../../../doc/html/bbv2/installation.html">Boost Jam Build Instructions</a> for the subdirectory <tt class="docutils literal"><span class="pre">libs/graph/build</span></tt>.</p>
-</div>
-<div class="section" id="deprecated-readers">
-<h1><a class="toc-backref" href="#id6" name="deprecated-readers">Deprecated Readers</a></h1>
-<p>The deprecated readers do not provide exceptions on error (they
-abort), they require the use of one of the predefined graph types
-(<tt class="docutils literal"><span class="pre">GraphvizDigraph</span></tt> or <tt class="docutils literal"><span class="pre">GraphvizGraph</span></tt>), and they do not support
-arbitrary properties. They will be removed in a future Boost version.</p>
+the &quot;boost_regex&quot; library. The library can be built by following the
+<a class="reference external" href="../../../more/getting_started.html#Build_Install">Boost Jam Build Instructions</a> for the subdirectory <tt class="docutils literal"><span class="pre">libs/regex/build</span></tt>.</p>
 </div>
 <div class="section" id="notes">
-<h1><a class="toc-backref" href="#id7" name="notes">Notes</a></h1>
+<h1><a class="toc-backref" href="#id6">Notes</a></h1>
 <blockquote>
 <ul class="simple">
 <li>The <tt class="docutils literal"><span class="pre">read_graphviz</span></tt> function does not use any code from the
@@ -196,15 +459,6 @@
 site, as well as experiments run using the dot application. The
 resulting interpretation may be subtly different from dot for some
 corner cases that are not well specified.</li>
-<li><tt class="docutils literal"><span class="pre">read_graphviz</span></tt> treats subgraphs as syntactic sugar. It does not
-reflect subgraphs as actual entities in the BGL. Rather, they are
-used to shorten some edge definitions as well as to give a subset
-of all nodes or edges certain properties. For example, the
-DOT graphs <tt class="docutils literal"><span class="pre">digraph</span> <span class="pre">{</span> <span class="pre">a</span> <span class="pre">-&gt;</span> <span class="pre">subgraph</span> <span class="pre">{b</span> <span class="pre">-&gt;</span> <span class="pre">c}</span> <span class="pre">-&gt;</span> <span class="pre">e</span> <span class="pre">}</span></tt> and
-<tt class="docutils literal"><span class="pre">digraph</span> <span class="pre">{</span> <span class="pre">a</span> <span class="pre">-&gt;</span> <span class="pre">b</span> <span class="pre">-&gt;</span> <span class="pre">e</span> <span class="pre">;</span> <span class="pre">a</span> <span class="pre">-&gt;</span> <span class="pre">c</span> <span class="pre">-&gt;</span> <span class="pre">e</span> <span class="pre">;</span> <span class="pre">b</span> <span class="pre">-&gt;</span> <span class="pre">c}</span></tt> are equivalent.</li>
-<li>Subgraph IDs refer to subgraphs defined earlier in the graph
-description. Undefined subgraphs behave as empty subgraphs
-(<tt class="docutils literal"><span class="pre">{}</span></tt>).</li>
 <li>On successful reading of a graph, every vertex and edge will have
 an associated value for every respective edge and vertex property
 encountered while interpreting the graph. These values will be set
@@ -217,21 +471,33 @@
 that property map value types are default constructible.</strong></li>
 </ul>
 </blockquote>
+<!-- - ``read_graphviz`` treats subgraphs as syntactic sugar. It does not
+ reflect subgraphs as actual entities in the BGL. Rather, they are
+ used to shorten some edge definitions as well as to give a subset
+ of all nodes or edges certain properties. For example, the
+ DOT graphs ``digraph { a -> subgraph {b -> c} -> e }`` and
+ ``digraph { a -> b -> e ; a -> c -> e ; b -> c}`` are equivalent. -->
+<!-- - Subgraph IDs refer to subgraphs defined earlier in the graph
+ description. Undefined subgraphs behave as empty subgraphs
+ (``{}``). -->
 </div>
 <div class="section" id="see-also">
-<h1><a class="toc-backref" href="#id8" name="see-also">See Also</a></h1>
-<p><a class="reference" href="write-graphviz.html">write_graphviz</a></p>
+<h1><a class="toc-backref" href="#id7">See Also</a></h1>
+<p><a class="reference external" href="write-graphviz.html">write_graphviz</a></p>
 </div>
 <div class="section" id="future-work">
-<h1><a class="toc-backref" href="#id9" name="future-work">Future Work</a></h1>
+<h1><a class="toc-backref" href="#id8">Future Work</a></h1>
 <blockquote>
 <ul class="simple">
-<li>The parser currently does not handle continuation lines as defined
-in the DOT Language. Some more sophisticated parsing of
-identifier(so-called &quot;ID&quot; in the source) is required to support this.</li>
-<li>Support for optional recognition of subgraphs as distinct entities.</li>
+<li>Support for subgraphs (currently parsed but ignored).</li>
+<li>Support for HTML strings.</li>
+<li>Passing port information to BGL.</li>
+<li>Expanding escape codes in the same way GraphViz does.</li>
+<li>Enforcement of the <tt class="docutils literal"><span class="pre">strict</span></tt> keyword (ignoring self-loops and parallel
+edges).</li>
 </ul>
 </blockquote>
+<!-- - Support for optional recognition of subgraphs as distinct entities. -->
 </div>
 </div>
 </body>

Modified: trunk/libs/graph/doc/read_graphviz.rst
==============================================================================
--- trunk/libs/graph/doc/read_graphviz.rst (original)
+++ trunk/libs/graph/doc/read_graphviz.rst 2009-05-18 13:27:15 EDT (Mon, 18 May 2009)
@@ -14,32 +14,31 @@
 
 ::
 
- template <typename MutableGraph>
- bool read_graphviz(std::istream& in, MutableGraph& graph,
- dynamic_properties& dp,
- const std::string& node_id = "node_id");
-
- // Only available if BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS is defined
- template <typename MultiPassIterator, typename MutableGraph>
- bool read_graphviz(MultiPassIterator begin, MultiPassIterator end,
- MutableGraph& graph, dynamic_properties& dp,
- const std::string& node_id = "node_id");
-
- // Deprecated GraphViz readers
- void read_graphviz(const std::string& file, GraphvizDigraph& g);
- void read_graphviz(FILE* file, GraphvizDigraph& g);
- void read_graphviz(const std::string& file, GraphvizGraph& g);
- void read_graphviz(FILE* file, GraphvizGraph& g);
+ namespace boost {
+
+ template <typename MutableGraph>
+ bool read_graphviz(std::istream& in, MutableGraph& graph,
+ dynamic_properties& dp,
+ const std::string& node_id = "node_id");
+
+ template <typename BidirectionalIterator, typename MutableGraph>
+ bool read_graphviz(BidirectionalIterator begin, BidirectionalIterator end,
+ MutableGraph& graph, dynamic_properties& dp,
+ const std::string& node_id = "node_id");
+
+ }
 
  
 The ``read_graphviz`` function interprets a graph described using the
 GraphViz_ DOT language and builds a BGL graph that captures that
-description. Using this function, you can initialize a graph using
-data stored as text. To use the iterator version of ``read_graphviz``,
-you will need to define the macro BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS
-before including the header ``<boost/graph/graphviz.hpp>``. Doing so
-may greatly increase the amount of time required to compile the
-GraphViz reader.
+description. Using these functions, you can initialize a graph using
+data stored as text.
+
+The ``BOOST_GRAPH_USE_SPIRIT_PARSER`` macro may be defined before including
+``<boost/graph/graphviz.hpp>`` to include a previous parser implementation,
+with the same interface, that uses Spirit for its implementation. The main
+advantage of that version is that it supports subgraphs to a limited extent,
+while the new parser does not support them at all.
 
 The DOT language can specify both directed and undirected graphs, and
 ``read_graphviz`` differentiates between the two. One must pass
@@ -57,7 +56,7 @@
 
 Requirements:
  - The type of the graph must model the `Mutable Graph`_ concept.
- - The type of the iterator must model the `Multi-Pass Iterator`_
+ - The type of the iterator must model the `Bidirectional Iterator`_
    concept.
  - The property map value types must be default-constructible.
 
@@ -172,15 +171,8 @@
 Building the GraphViz Readers
 -----------------------------
 To use the GraphViz readers, you will need to build and link against
-the "boost_graph" library. The library can be built by following the
-`Boost Jam Build Instructions`_ for the subdirectory ``libs/graph/build``.
-
-Deprecated Readers
-------------------
-The deprecated readers do not provide exceptions on error (they
-abort), they require the use of one of the predefined graph types
-(``GraphvizDigraph`` or ``GraphvizGraph``), and they do not support
-arbitrary properties. They will be removed in a future Boost version.
+the "boost_regex" library. The library can be built by following the
+`Boost Jam Build Instructions`_ for the subdirectory ``libs/regex/build``.
 
 
 Notes
@@ -193,17 +185,6 @@
    resulting interpretation may be subtly different from dot for some
    corner cases that are not well specified.
 
- - ``read_graphviz`` treats subgraphs as syntactic sugar. It does not
- reflect subgraphs as actual entities in the BGL. Rather, they are
- used to shorten some edge definitions as well as to give a subset
- of all nodes or edges certain properties. For example, the
- DOT graphs ``digraph { a -> subgraph {b -> c} -> e }`` and
- ``digraph { a -> b -> e ; a -> c -> e ; b -> c}`` are equivalent.
-
- - Subgraph IDs refer to subgraphs defined earlier in the graph
- description. Undefined subgraphs behave as empty subgraphs
- (``{}``).
-
  - On successful reading of a graph, every vertex and edge will have
    an associated value for every respective edge and vertex property
    encountered while interpreting the graph. These values will be set
@@ -215,6 +196,19 @@
    given the default constructed value of the value type. **Be sure
    that property map value types are default constructible.**
 
+..
+ - ``read_graphviz`` treats subgraphs as syntactic sugar. It does not
+ reflect subgraphs as actual entities in the BGL. Rather, they are
+ used to shorten some edge definitions as well as to give a subset
+ of all nodes or edges certain properties. For example, the
+ DOT graphs ``digraph { a -> subgraph {b -> c} -> e }`` and
+ ``digraph { a -> b -> e ; a -> c -> e ; b -> c}`` are equivalent.
+
+..
+ - Subgraph IDs refer to subgraphs defined earlier in the graph
+ description. Undefined subgraphs behave as empty subgraphs
+ (``{}``).
+
 See Also
 --------
 
@@ -224,16 +218,24 @@
 Future Work
 -----------
 
- - The parser currently does not handle continuation lines as defined
- in the DOT Language. Some more sophisticated parsing of
- identifier(so-called "ID" in the source) is required to support this.
+ - Support for subgraphs (currently parsed but ignored).
 
- - Support for optional recognition of subgraphs as distinct entities.
+ - Support for HTML strings.
+
+ - Passing port information to BGL.
+
+ - Expanding escape codes in the same way GraphViz does.
+
+ - Enforcement of the ``strict`` keyword (ignoring self-loops and parallel
+ edges).
     
+..
+ - Support for optional recognition of subgraphs as distinct entities.
+
 
 .. _GraphViz: http://graphviz.org/
 .. _`Mutable Graph`: MutableGraph.html
-.. _`Multi-Pass Iterator`: ../../iterator/index.html
+.. _`Bidirectional Iterator`: http://www.sgi.com/tech/stl/BidirectionalIterator.html
 .. _dynamic_properties: ../../property_map/doc/dynamic_property_map.html
 .. _write_graphviz: write-graphviz.html
 .. _Boost Jam Build Instructions: ../../../more/getting_started.html#Build_Install

Modified: trunk/libs/graph/src/read_graphviz_spirit.cpp
==============================================================================
--- trunk/libs/graph/src/read_graphviz_spirit.cpp (original)
+++ trunk/libs/graph/src/read_graphviz_spirit.cpp 2009-05-18 13:27:15 EDT (Mon, 18 May 2009)
@@ -26,38 +26,22 @@
 # define BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS
 #endif
 #include <boost/graph/graphviz.hpp>
+#include <iostream>
 
 namespace boost { namespace detail { namespace graph {
 
+#if 0
 BOOST_GRAPH_DECL
 bool read_graphviz(std::istream& in, mutate_graph& graph)
 {
   using namespace boost;
- using namespace boost::spirit::classic;
 
   typedef std::istream_iterator<char> is_t;
- typedef multi_pass<is_t> iterator_t;
 
- iterator_t first(make_multi_pass(is_t(in)));
- iterator_t last(make_multi_pass(is_t()));
+ std::string str((is_t(in)), is_t());
 
- // Turn off white space skipping on the stream
- in.unsetf(std::ios::skipws);
-
- typedef skip_parser_iteration_policy< boost::detail::graph::dot_skipper>
- iter_policy_t;
- typedef scanner_policies<iter_policy_t> scanner_policies_t;
- typedef scanner<iterator_t, scanner_policies_t> scanner_t;
-
- boost::detail::graph::dot_grammar p(graph);
- boost::detail::graph::dot_skipper skip_p;
-
- iter_policy_t iter_policy(skip_p);
- scanner_policies_t policies(iter_policy);
-
- scanner_t scan(first, last, policies);
-
- return p.parse(scan);
+ return read_graphviz(str.begin(), str.end());
 }
+#endif
 
 } } } // end namespace boost::detail::graph

Modified: trunk/libs/graph/test/Jamfile.v2
==============================================================================
--- trunk/libs/graph/test/Jamfile.v2 (original)
+++ trunk/libs/graph/test/Jamfile.v2 2009-05-18 13:27:15 EDT (Mon, 18 May 2009)
@@ -58,7 +58,8 @@
     [ compile graph_concepts.cpp ]
     [ run graphviz_test.cpp
             /boost/test//boost_test_exec_monitor/<link>static
- ../build//boost_graph ]
+ ../build//boost_graph
+ ../../regex/build//boost_regex ]
     [ run metis_test.cpp : $(METIS_INPUT_FILE) ]
     [ run gursoy_atun_layout_test.cpp ]
     [ run layout_test.cpp : : : <test-info>always_show_run_output <toolset>intel:<debug-symbols>off ]
@@ -85,7 +86,7 @@
     [ run matching_test.cpp ]
     [ run max_flow_test.cpp ]
     [ run kolmogorov_max_flow_test.cpp ]
- [ run cycle_ratio_tests.cpp ../build//boost_graph : $(CYCLE_RATIO_INPUT_FILE) ]
+ [ run cycle_ratio_tests.cpp ../build//boost_graph ../../regex/build//boost_regex : $(CYCLE_RATIO_INPUT_FILE) ]
     [ run basic_planarity_test.cpp ]
     [ run make_connected_test.cpp ]
     [ run make_bicon_planar_test.cpp ]

Modified: trunk/libs/graph/test/graphviz_test.cpp
==============================================================================
--- trunk/libs/graph/test/graphviz_test.cpp (original)
+++ trunk/libs/graph/test/graphviz_test.cpp 2009-05-18 13:27:15 EDT (Mon, 18 May 2009)
@@ -11,8 +11,8 @@
 
 // Author: Ronald Garcia
 
-//#define BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS
 #define BOOST_GRAPHVIZ_USE_ISTREAM
+#include <boost/regex.hpp>
 #include <boost/graph/graphviz.hpp>
 #include <boost/assign/std/map.hpp>
 #include <boost/graph/adjacency_list.hpp>
@@ -31,9 +31,6 @@
 using namespace std;
 using namespace boost;
 
-#ifndef BOOST_GRAPHVIZ_USE_ISTREAM
-using namespace boost::spirit;
-#endif
 using namespace boost::assign;
 
 typedef std::string node_t;
@@ -43,7 +40,8 @@
 typedef std::map<edge_t,double> weight_map_t;
 
 template <typename Directedness, typename OutEdgeList>
-bool test_graph(std::istream& dotfile, mass_map_t const& masses,
+bool test_graph(std::istream& dotfile, std::size_t correct_num_vertices,
+ mass_map_t const& masses,
                 weight_map_t const& weights,
                 std::string const& node_id = "node_id",
                 std::string const& g_name = std::string()) {
@@ -79,11 +77,14 @@
   if(read_graphviz(dotfile,graph,dp,node_id)) {
 #else
   std::string data;
+ dotfile >> std::noskipws;
   std::copy(std::istream_iterator<char>(dotfile),
             std::istream_iterator<char>(),
             std::back_inserter(data));
   if(read_graphviz(data.begin(),data.end(),graph,dp,node_id)) {
 #endif
+ // check correct vertex count
+ BOOST_CHECK_EQUAL(num_vertices(graph), correct_num_vertices);
     // check masses
     if(!masses.empty()) {
       // assume that all the masses have been set
@@ -139,16 +140,25 @@
     mass_map_t masses;
     insert ( masses ) ("a",0.0f) ("c",7.7f) ("e", 6.66f);
     gs_t gs("digraph { a node [mass = 7.7] c e [mass = 6.66] }");
- BOOST_CHECK((test_graph<directedS,vecS>(gs,masses,weight_map_t())));
+ BOOST_CHECK((test_graph<directedS,vecS>(gs,3,masses,weight_map_t())));
+ }
+
+ {
+ mass_map_t masses;
+ insert ( masses ) ("a",0.0f) ("e", 6.66f);
+ gs_t gs("digraph { a node [mass = 7.7] \"a\" e [mass = 6.66] }");
+ BOOST_CHECK((test_graph<directedS,vecS>(gs,2,masses,weight_map_t())));
   }
 
   {
     weight_map_t weights;
     insert( weights )(make_pair("a","b"),0.0)
- (make_pair("c","d"),7.7)(make_pair("e","f"),6.66);
- gs_t gs("digraph { a -> b edge [weight = 7.7] "
- "c -> d e-> f [weight = 6.66] }");
- BOOST_CHECK((test_graph<directedS,vecS>(gs,mass_map_t(),weights)));
+ (make_pair("c","d"),7.7)(make_pair("e","f"),6.66)
+ (make_pair("d","e"),0.5)(make_pair("e","a"),0.5);
+ gs_t gs("digraph { a -> b eDge [weight = 7.7] "
+ "c -> d e-> f [weight = 6.66] "
+ "d ->e->a [weight=.5]}");
+ BOOST_CHECK((test_graph<directedS,vecS>(gs,6,mass_map_t(),weights)));
   }
 
   // undirected graph with alternate node_id property name
@@ -156,7 +166,7 @@
     mass_map_t masses;
     insert ( masses ) ("a",0.0f) ("c",7.7f) ("e", 6.66f);
     gs_t gs("graph { a node [mass = 7.7] c e [mass = 6.66] }");
- BOOST_CHECK((test_graph<undirectedS,vecS>(gs,masses,weight_map_t(),
+ BOOST_CHECK((test_graph<undirectedS,vecS>(gs,3,masses,weight_map_t(),
                                              "nodenames")));
   }
 
@@ -165,7 +175,7 @@
     mass_map_t masses;
     insert ( masses ) ("a",0.0f) ("c",7.7f) ("e", 6.66f);
     gs_t gs("graph { a node [mass = 7.7] c e [mass = 6.66] }");
- BOOST_CHECK((test_graph<undirectedS,vecS>(gs,masses,weight_map_t())));
+ BOOST_CHECK((test_graph<undirectedS,vecS>(gs,3,masses,weight_map_t())));
   }
 
   {
@@ -174,7 +184,7 @@
       (make_pair("c","d"),7.7)(make_pair("e","f"),6.66);
     gs_t gs("graph { a -- b eDge [weight = 7.7] "
             "c -- d e -- f [weight = 6.66] }");
- BOOST_CHECK((test_graph<undirectedS,vecS>(gs,mass_map_t(),weights)));
+ BOOST_CHECK((test_graph<undirectedS,vecS>(gs,6,mass_map_t(),weights)));
   }
 
   // Mismatch directed graph test
@@ -183,7 +193,7 @@
     insert ( masses ) ("a",0.0f) ("c",7.7f) ("e", 6.66f);
     gs_t gs("graph { a nodE [mass = 7.7] c e [mass = 6.66] }");
     try {
- test_graph<directedS,vecS>(gs,masses,weight_map_t());
+ test_graph<directedS,vecS>(gs,3,masses,weight_map_t());
       BOOST_ERROR("Failed to throw boost::directed_graph_error.");
     } catch (boost::undirected_graph_error&) {}
   }
@@ -194,7 +204,7 @@
     insert ( masses ) ("a",0.0f) ("c",7.7f) ("e", 6.66f);
     gs_t gs("digraph { a node [mass = 7.7] c e [mass = 6.66] }");
     try {
- test_graph<undirectedS,vecS>(gs,masses,weight_map_t());
+ test_graph<undirectedS,vecS>(gs,3,masses,weight_map_t());
       BOOST_ERROR("Failed to throw boost::directed_graph_error.");
     } catch (boost::directed_graph_error&) {}
   }
@@ -205,7 +215,7 @@
     insert( weights )(make_pair("a","b"),7.7);
     gs_t gs("diGraph { a -> b [weight = 7.7] a -> b [weight = 7.7] }");
     try {
- test_graph<directedS,setS>(gs,mass_map_t(),weights);
+ test_graph<directedS,setS>(gs,2,mass_map_t(),weights);
       BOOST_ERROR("Failed to throw boost::bad_parallel_edge.");
     } catch (boost::bad_parallel_edge&) {}
   }
@@ -215,16 +225,16 @@
     weight_map_t weights;
     insert( weights )(make_pair("a","b"),7.7);
     gs_t gs("digraph { a -> b [weight = 7.7] a -> b [weight = 7.7] }");
- BOOST_CHECK((test_graph<directedS,vecS>(gs,mass_map_t(),weights)));
+ BOOST_CHECK((test_graph<directedS,vecS>(gs,2,mass_map_t(),weights)));
   }
 
   // Graph Property Test 1
   {
     mass_map_t masses;
     insert ( masses ) ("a",0.0f) ("c",0.0f) ("e", 6.66f);
- gs_t gs("digraph { graph [name=\"foo\"] a c e [mass = 6.66] }");
- std::string graph_name("foo");
- BOOST_CHECK((test_graph<directedS,vecS>(gs,masses,weight_map_t(),"",
+ gs_t gs("digraph { graph [name=\"foo \\\"escaped\\\"\"] a c e [mass = 6.66] }");
+ std::string graph_name("foo \"escaped\"");
+ BOOST_CHECK((test_graph<directedS,vecS>(gs,3,masses,weight_map_t(),"",
                                             graph_name)));
   }
 
@@ -232,9 +242,9 @@
   {
     mass_map_t masses;
     insert ( masses ) ("a",0.0f) ("c",0.0f) ("e", 6.66f);
- gs_t gs("digraph { name=\"foo\" a c e [mass = 6.66] }");
+ gs_t gs("digraph { name=\"fo\"+ \"\\\no\" a c e [mass = 6.66] }");
     std::string graph_name("foo");
- BOOST_CHECK((test_graph<directedS,vecS>(gs,masses,weight_map_t(),"",
+ BOOST_CHECK((test_graph<directedS,vecS>(gs,3,masses,weight_map_t(),"",
                                             graph_name)));
   }
 
@@ -246,7 +256,7 @@
       "a1 [ label = \"//depot/path/to/file_29#9\" ];"
       "a0 -> a1 [ color=gray ];"
       "}");
- BOOST_CHECK((test_graph<directedS,vecS>(gs,mass_map_t(),weight_map_t())));
+ BOOST_CHECK((test_graph<directedS,vecS>(gs,2,mass_map_t(),weight_map_t())));
   }
   return 0;
 }


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk