]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/libs/graph/src/read_graphviz_new.cpp
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / boost / libs / graph / src / read_graphviz_new.cpp
index 3c9a7091567af61da195a730f00bab5c4f03a653..f4162eae5f7ae122f3d20462a8b1062680b40951 100644 (file)
@@ -17,7 +17,7 @@
 //   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: 
+//   See documentation for this code at:
 //     http://www.boost.org/libs/graph/doc/read_graphviz.html
 //
 
 #include <boost/graph/dll_import_export.hpp>
 #include <boost/graph/graphviz.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
+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
+        };
+        token_type 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;
+        }
     };
-    token_type 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;
-    }
-  };
 
-  bad_graphviz_syntax lex_error(const std::string& errmsg, char bad_char) {
-    if (bad_char == '\0') {
-      return bad_graphviz_syntax(errmsg + " (at end of input)");
-    } else {
-      return bad_graphviz_syntax(errmsg + " (char is '" + bad_char + "')");
+    bad_graphviz_syntax lex_error(const std::string& errmsg, char bad_char)
+    {
+        if (bad_char == '\0')
+        {
+            return bad_graphviz_syntax(errmsg + " (at end of input)");
+        }
+        else
+        {
+            return bad_graphviz_syntax(
+                errmsg + " (char is '" + bad_char + "')");
+        }
     }
-  }
-
-  bad_graphviz_syntax parse_error(const std::string& errmsg, const token& bad_token) {
-    return bad_graphviz_syntax(errmsg + " (token is \"" + boost::lexical_cast<std::string>(bad_token) + "\")");
-  }
-
-  struct tokenizer {
-    std::string::const_iterator 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;
-    boost::regex xml_tag_token;
-    boost::regex cdata;
-
-    tokenizer(const std::string& str) : begin(str.begin()), end(str.end())
+
+    bad_graphviz_syntax parse_error(
+        const std::string& errmsg, const token& bad_token)
     {
-      std::string end_of_token = "(?=(?:\\W))";
-      std::string whitespace = "(?:\\s+)";
-      std::string slash_slash_comment = "(?://.*?$)";
-      std::string slash_star_comment = "(?:/\\*.*?\\*/)";
-      std::string hash_comment = "(?:^#.*?$)";
-      std::string backslash_newline = "(?:[\\\\][\\n])";
-      stuff_to_skip = "\\A(?:" + whitespace + "|" +
-                                 slash_slash_comment + "|" +
-                                 slash_star_comment + "|" +
-                                 hash_comment + "|" +
-                                 backslash_newline + ")*";
-      basic_id_token = "\\A([[:alpha:]_](?:\\w*))";
-      punctuation_token = "\\A([][{};=,:+()@]|[-][>-])";
-      number_token = "\\A([-]?(?:(?:\\.\\d+)|(?:\\d+(?:\\.\\d*)?)))";
-      quoted_string_token = "\\A(\"(?:[^\"\\\\]|(?:[\\\\].))*\")";
-      xml_tag_token = "\\A<(/?)(?:[^!?'\"]|(?:'[^']*?')|(?:\"[^\"]*?\"))*?(/?)>";
-      cdata = "\\A\\Q<![CDATA[\\E.*?\\Q]]>\\E";
+        return bad_graphviz_syntax(errmsg + " (token is \""
+            + boost::lexical_cast< std::string >(bad_token) + "\")");
     }
 
-    void skip() {
-      boost::match_results<std::string::const_iterator> results;
+    struct tokenizer
+    {
+        std::string::const_iterator 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;
+        boost::regex xml_tag_token;
+        boost::regex cdata;
+
+        tokenizer(const std::string& str) : begin(str.begin()), end(str.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 = "(?:^#.*?$)";
+            std::string backslash_newline = "(?:[\\\\][\\n])";
+            stuff_to_skip = "\\A(?:" + whitespace + "|" + slash_slash_comment
+                + "|" + slash_star_comment + "|" + hash_comment + "|"
+                + backslash_newline + ")*";
+            basic_id_token = "\\A([[:alpha:]_](?:\\w*))";
+            punctuation_token = "\\A([][{};=,:+()@]|[-][>-])";
+            number_token = "\\A([-]?(?:(?:\\.\\d+)|(?:\\d+(?:\\.\\d*)?)))";
+            quoted_string_token = "\\A(\"(?:[^\"\\\\]|(?:[\\\\].))*\")";
+            xml_tag_token
+                = "\\A<(/?)(?:[^!?'\"]|(?:'[^']*?')|(?:\"[^\"]*?\"))*?(/?)>";
+            cdata = "\\A\\Q<![CDATA[\\E.*?\\Q]]>\\E";
+        }
+
+        void skip()
+        {
+            boost::match_results< std::string::const_iterator > results;
 #ifndef NDEBUG
-      bool found =
+            bool found =
 #endif
-        boost::regex_search(begin, end, results, stuff_to_skip);
+                boost::regex_search(begin, end, results, stuff_to_skip);
 #ifndef NDEBUG
-      BOOST_ASSERT (found);
+            BOOST_ASSERT(found);
 #endif
-      boost::sub_match<std::string::const_iterator> sm1 = results.suffix();
-      BOOST_ASSERT (sm1.second == end);
-      begin = sm1.first;
-    }
+            boost::sub_match< std::string::const_iterator > sm1
+                = results.suffix();
+            BOOST_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<std::string::const_iterator> 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: BOOST_ASSERT (!"Definition of punctuation_token does not match switch statement");
-            }
-          }
-          default: BOOST_ASSERT (!"Definition of punctuation_token does not match switch statement");
-        }
-      }
-      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
-        BOOST_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 == '<') {
-        std::string::const_iterator saved_begin = begin;
-        int counter = 0;
-        do {
-          if (begin == end) throw_lex_error("Unclosed HTML string");
-          if (*begin != '<') {
-            ++begin;
-            continue;
-          }
-          found = boost::regex_search(begin, end, results, xml_tag_token);
-          if (found) {
-            begin = results.suffix().first;
-            if (results[1].str() == "/") { // Close tag
-              --counter;
-            } else if (results[2].str() == "/") { // Empty tag
-            } else { // Open tag
-              ++counter;
-            }
-            continue;
-          }
-          found = boost::regex_search(begin, end, results, cdata);
-          if (found) {
-            begin = results.suffix().first;
-            continue;
-          }
-          throw_lex_error("Invalid contents in HTML string");
-        } while (counter > 0);
-        return token(token::identifier, std::string(saved_begin, begin));
-      } else {
-        throw_lex_error("Invalid character");
-        return token();
-      }
-    }
+        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< std::string::const_iterator > 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:
+                        BOOST_ASSERT(!"Definition of punctuation_token does "
+                                      "not match switch statement");
+                    }
+                }
+                default:
+                    BOOST_ASSERT(!"Definition of punctuation_token does not "
+                                  "match switch statement");
+                }
+            }
+            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
+                BOOST_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 == '<')
+            {
+                std::string::const_iterator saved_begin = begin;
+                int counter = 0;
+                do
+                {
+                    if (begin == end)
+                        throw_lex_error("Unclosed HTML string");
+                    if (*begin != '<')
+                    {
+                        ++begin;
+                        continue;
+                    }
+                    found = boost::regex_search(
+                        begin, end, results, xml_tag_token);
+                    if (found)
+                    {
+                        begin = results.suffix().first;
+                        if (results[1].str() == "/")
+                        { // Close tag
+                            --counter;
+                        }
+                        else if (results[2].str() == "/")
+                        { // Empty tag
+                        }
+                        else
+                        { // Open tag
+                            ++counter;
+                        }
+                        continue;
+                    }
+                    found = boost::regex_search(begin, end, results, cdata);
+                    if (found)
+                    {
+                        begin = results.suffix().first;
+                        continue;
+                    }
+                    throw_lex_error("Invalid contents in HTML string");
+                } while (counter > 0);
+                return token(
+                    token::identifier, std::string(saved_begin, begin));
+            }
+            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 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
-    }
+        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 == end ? '\0' : *begin)));
-    }
-  };
-
-  struct edge_endpoint {
-    bool is_subgraph;
-    node_and_port node_ep;
-    subgraph_name subgraph_ep;
-
-    static edge_endpoint node(const node_and_port& ep) {
-      edge_endpoint r;
-      r.is_subgraph = false;
-      r.node_ep = ep;
-      return r;
-    }
+        void throw_lex_error(const std::string& errmsg)
+        {
+            boost::throw_exception(
+                lex_error(errmsg, (begin == end ? '\0' : *begin)));
+        }
+    };
 
-    static edge_endpoint subgraph(const subgraph_name& ep) {
-      edge_endpoint r;
-      r.is_subgraph = true;
-      r.subgraph_ep = ep;
-      return r;
-    }
-  };
-
-  struct node_or_subgraph_ref {
-    bool is_subgraph;
-    std::string name; // Name for subgraphs or nodes, "___root___" for root graph
-  };
-
-  static node_or_subgraph_ref noderef(const node_name& n) {
-    node_or_subgraph_ref r;
-    r.is_subgraph = false;
-    r.name = n;
-    return r;
-  }
-
-  static node_or_subgraph_ref subgraphref(const subgraph_name& n) {
-    node_or_subgraph_ref r;
-    r.is_subgraph = true;
-    r.name = n;
-    return r;
-  }
-
-  typedef std::vector<node_or_subgraph_ref> subgraph_member_list;
-
-  struct subgraph_info {
-    properties def_node_props;
-    properties def_edge_props;
-    subgraph_member_list members;
-  };
-
-  struct parser {
-    tokenizer the_tokenizer;
-    std::vector<token> lookahead;
-    parser_result& r;
-    std::map<subgraph_name, subgraph_info> subgraphs;
-    std::string current_subgraph_name;
-    int sgcounter; // Counter for anonymous subgraphs
-    std::set<std::pair<node_name, node_name> > existing_edges; // Used for checking in strict graphs
-
-    subgraph_info& current() {return subgraphs[current_subgraph_name];}
-    properties& current_graph_props() {return r.graph_props[current_subgraph_name];}
-    subgraph_member_list& current_members() {return current().members;}
-
-    parser(const std::string& gr, parser_result& result)
-        : the_tokenizer(gr), lookahead(), r(result), sgcounter(0) {
-      current_subgraph_name = "___root___";
-      current() = subgraph_info(); // Initialize root graph
-      current_graph_props().clear();
-      current_members().clear();
-    }
+    struct edge_endpoint
+    {
+        bool is_subgraph;
+        node_and_port node_ep;
+        subgraph_name subgraph_ep;
 
-    token get() {
-      if (lookahead.empty()) {
-        token t = the_tokenizer.get_token();
-        return t;
-      } else {
-        token t = lookahead.front();
-        lookahead.erase(lookahead.begin());
-        return t;
-      }
-    }
+        static edge_endpoint node(const node_and_port& ep)
+        {
+            edge_endpoint r;
+            r.is_subgraph = false;
+            r.node_ep = ep;
+            return r;
+        }
 
-    token peek() {
-      if (lookahead.empty()) {
-        lookahead.push_back(the_tokenizer.get_token());
-      }
-      return lookahead.front();
-    }
+        static edge_endpoint subgraph(const subgraph_name& ep)
+        {
+            edge_endpoint r;
+            r.is_subgraph = true;
+            r.subgraph_ep = ep;
+            return r;
+        }
+    };
 
-    void error(const std::string& str) {
-      boost::throw_exception(parse_error(str, peek()));
-    }
+    struct node_or_subgraph_ref
+    {
+        bool is_subgraph;
+        std::string
+            name; // Name for subgraphs or nodes, "___root___" for root graph
+    };
 
-    void parse_graph(bool want_directed) {
-      bool is_strict = false;
-      bool is_directed = false;
-      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\"");
-      }
-      r.graph_is_directed = is_directed; // Used to check edges
-      r.graph_is_strict = is_strict;
-      if (want_directed != r.graph_is_directed) {
-        if (want_directed) {
-          boost::throw_exception(boost::undirected_graph_error());
-        } else {
-          boost::throw_exception(boost::directed_graph_error());
-        }
-      }
-      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");
-      if (peek().type == token::eof) {} else error("Wanted end of file");
+    static node_or_subgraph_ref noderef(const node_name& n)
+    {
+        node_or_subgraph_ref r;
+        r.is_subgraph = false;
+        r.name = n;
+        return r;
     }
 
-    void parse_stmt_list() {
-      while (true) {
-        if (peek().type == token::right_brace) return;
-        parse_stmt();
-        if (peek().type == token::semicolon) get();
-      }
+    static node_or_subgraph_ref subgraphref(const subgraph_name& n)
+    {
+        node_or_subgraph_ref r;
+        r.is_subgraph = true;
+        r.name = n;
+        return r;
     }
 
-    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:
-        case token::identifier: {
-          token id = get();
-          if (id.type == token::identifier && peek().type == token::equal) { // Graph property
+    typedef std::vector< node_or_subgraph_ref > subgraph_member_list;
+
+    struct subgraph_info
+    {
+        properties def_node_props;
+        properties def_edge_props;
+        subgraph_member_list members;
+    };
+
+    struct parser
+    {
+        tokenizer the_tokenizer;
+        std::vector< token > lookahead;
+        parser_result& r;
+        std::map< subgraph_name, subgraph_info > subgraphs;
+        std::string current_subgraph_name;
+        int sgcounter; // Counter for anonymous subgraphs
+        std::set< std::pair< node_name, node_name > >
+            existing_edges; // Used for checking in strict graphs
+
+        subgraph_info& current() { return subgraphs[current_subgraph_name]; }
+        properties& current_graph_props()
+        {
+            return r.graph_props[current_subgraph_name];
+        }
+        subgraph_member_list& current_members() { return current().members; }
+
+        parser(const std::string& gr, parser_result& result)
+        : the_tokenizer(gr), lookahead(), r(result), sgcounter(0)
+        {
+            current_subgraph_name = "___root___";
+            current() = subgraph_info(); // Initialize root graph
+            current_graph_props().clear();
+            current_members().clear();
+        }
+
+        token get()
+        {
+            if (lookahead.empty())
+            {
+                token t = the_tokenizer.get_token();
+                return t;
+            }
+            else
+            {
+                token t = lookahead.front();
+                lookahead.erase(lookahead.begin());
+                return t;
+            }
+        }
+
+        token peek()
+        {
+            if (lookahead.empty())
+            {
+                lookahead.push_back(the_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 = false;
+            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\"");
+            }
+            r.graph_is_directed = is_directed; // Used to check edges
+            r.graph_is_strict = is_strict;
+            if (want_directed != r.graph_is_directed)
+            {
+                if (want_directed)
+                {
+                    boost::throw_exception(boost::undirected_graph_error());
+                }
+                else
+                {
+                    boost::throw_exception(boost::directed_graph_error());
+                }
+            }
             get();
-            if (peek().type != token::identifier) error("Wanted identifier as right side of =");
-            token id2 = get();
-            current_graph_props()[id.normalized_value] = id2.normalized_value;
-          } else {
-            edge_endpoint ep = parse_endpoint_rest(id);
-            if (peek().type == token::dash_dash || peek().type == token::dash_greater) { // Edge
-              parse_edge_stmt(ep);
-            } else {
-              if (!ep.is_subgraph) { // Only nodes can have attribute lists
-                // This node already exists because of its first mention
-                // (properties set to defaults by parse_node_and_port, called
-                // by parse_endpoint_rest)
-                properties this_node_props;
-                if (peek().type == token::left_bracket) {
-                  parse_attr_list(this_node_props);
+            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");
+            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:
+            case token::identifier:
+            {
+                token id = get();
+                if (id.type == token::identifier && peek().type == token::equal)
+                { // Graph property
+                    get();
+                    if (peek().type != token::identifier)
+                        error("Wanted identifier as right side of =");
+                    token id2 = get();
+                    current_graph_props()[id.normalized_value]
+                        = id2.normalized_value;
                 }
-                for (properties::const_iterator i = this_node_props.begin();
-                     i != this_node_props.end(); ++i) {
-                  // Override old properties with same names
-                  r.nodes[ep.node_ep.name][i->first] = i->second;
+                else
+                {
+                    edge_endpoint ep = parse_endpoint_rest(id);
+                    if (peek().type == token::dash_dash
+                        || peek().type == token::dash_greater)
+                    { // Edge
+                        parse_edge_stmt(ep);
+                    }
+                    else
+                    {
+                        if (!ep.is_subgraph)
+                        { // Only nodes can have attribute lists
+                            // This node already exists because of its first
+                            // mention (properties set to defaults by
+                            // parse_node_and_port, called by
+                            // parse_endpoint_rest)
+                            properties this_node_props;
+                            if (peek().type == token::left_bracket)
+                            {
+                                parse_attr_list(this_node_props);
+                            }
+                            for (properties::const_iterator i
+                                 = this_node_props.begin();
+                                 i != this_node_props.end(); ++i)
+                            {
+                                // Override old properties with same names
+                                r.nodes[ep.node_ep.name][i->first] = i->second;
+                            }
+                            current_members().push_back(
+                                noderef(ep.node_ep.name));
+                        }
+                        else
+                        {
+                            current_members().push_back(
+                                subgraphref(ep.subgraph_ep));
+                        }
+                    }
                 }
-                current_members().push_back(noderef(ep.node_ep.name));
-              } else {
-                current_members().push_back(subgraphref(ep.subgraph_ep));
-              }
+                break;
+            }
+            default:
+                error("Invalid start token for statement");
             }
-          }
-          break;
         }
-        default: error("Invalid start token for statement");
-      }
-    }
 
-    void parse_attr_stmt() {
-      switch (get().type) {
-        case token::kw_graph: parse_attr_list(current_graph_props()); break;
-        case token::kw_node: parse_attr_list(current().def_node_props); break;
-        case token::kw_edge: parse_attr_list(current().def_edge_props); break;
-        default: BOOST_ASSERT (!"Bad attr_stmt case");
-      }
-    }
+        void parse_attr_stmt()
+        {
+            switch (get().type)
+            {
+            case token::kw_graph:
+                parse_attr_list(current_graph_props());
+                break;
+            case token::kw_node:
+                parse_attr_list(current().def_node_props);
+                break;
+            case token::kw_edge:
+                parse_attr_list(current().def_edge_props);
+                break;
+            default:
+                BOOST_ASSERT(!"Bad attr_stmt case");
+            }
+        }
 
-    edge_endpoint parse_endpoint() {
-      switch (peek().type) {
-        case token::kw_subgraph:
-        case token::left_brace:
-        case token::identifier: {
-          token first = get();
-          return parse_endpoint_rest(first);
+        edge_endpoint parse_endpoint()
+        {
+            switch (peek().type)
+            {
+            case token::kw_subgraph:
+            case token::left_brace:
+            case token::identifier:
+            {
+                token first = get();
+                return parse_endpoint_rest(first);
+            }
+            default:
+            {
+                error("Wanted \"subgraph\", \"{\", or identifier to start node "
+                      "or subgraph");
+                return edge_endpoint();
+            }
+            }
         }
-        default: {
-          error("Wanted \"subgraph\", \"{\", or identifier to start node or subgraph");
-          return edge_endpoint();
+
+        edge_endpoint parse_endpoint_rest(const token& first_token)
+        {
+            switch (first_token.type)
+            {
+            case token::kw_subgraph:
+            case token::left_brace:
+                return edge_endpoint::subgraph(parse_subgraph(first_token));
+            default:
+                return edge_endpoint::node(parse_node_and_port(first_token));
+            }
         }
-      }
-    }
 
-    edge_endpoint parse_endpoint_rest(const token& first_token) {
-      switch (first_token.type) {
-        case token::kw_subgraph:
-        case token::left_brace: return edge_endpoint::subgraph(parse_subgraph(first_token));
-        default: return edge_endpoint::node(parse_node_and_port(first_token));
-      }
-    }
+        subgraph_name parse_subgraph(const token& first_token)
+        {
+            std::string name;
+            bool is_anonymous = true;
+            if (first_token.type == token::kw_subgraph)
+            {
+                if (peek().type == token::identifier)
+                {
+                    name = get().normalized_value;
+                    is_anonymous = false;
+                }
+            }
+            if (is_anonymous)
+            {
+                name = "___subgraph_"
+                    + boost::lexical_cast< std::string >(++sgcounter);
+            }
+            if (subgraphs.find(name) == subgraphs.end())
+            {
+                subgraphs[name]
+                    = current(); // Initialize properties and defaults
+                subgraphs[name].members.clear(); // Except member list
+            }
+            if (first_token.type == token::kw_subgraph
+                && peek().type != token::left_brace)
+            {
+                if (is_anonymous)
+                    error("Subgraph reference needs a name");
+                return name;
+            }
+            subgraph_name old_sg = current_subgraph_name;
+            current_subgraph_name = 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");
+            current_subgraph_name = old_sg;
+            return name;
+        }
 
-    subgraph_name parse_subgraph(const token& first_token) {
-      std::string name;
-      bool is_anonymous = true;
-      if (first_token.type == token::kw_subgraph) {
-        if (peek().type == token::identifier) {
-          name = get().normalized_value;
-          is_anonymous = false;
-        }
-      }
-      if (is_anonymous) {
-        name = "___subgraph_" +
-                 boost::lexical_cast<std::string>(++sgcounter);
-      }
-      if (subgraphs.find(name) == subgraphs.end()) {
-        subgraphs[name] = current(); // Initialize properties and defaults
-        subgraphs[name].members.clear(); // Except member list
-      }
-      if (first_token.type == token::kw_subgraph && peek().type != token::left_brace) {
-        if (is_anonymous) error("Subgraph reference needs a name");
-        return name;
-      }
-      subgraph_name old_sg = current_subgraph_name;
-      current_subgraph_name = 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");
-      current_subgraph_name = old_sg;
-      return name;
-    }
+        node_and_port parse_node_and_port(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_and_port 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;
+            }
+            if (r.nodes.find(id.name) == r.nodes.end())
+            { // First mention
+                r.nodes[id.name] = current().def_node_props;
+            }
+            return id;
+        }
 
-    node_and_port parse_node_and_port(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_and_port 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;
+        void parse_edge_stmt(const edge_endpoint& lhs)
+        {
+            std::vector< edge_endpoint > nodes_in_chain(1, lhs);
+            while (true)
+            {
+                bool leave_loop = true;
+                switch (peek().type)
+                {
+                case token::dash_dash:
+                {
+                    if (r.graph_is_directed)
+                        error("Using -- in directed graph");
+                    get();
+                    nodes_in_chain.push_back(parse_endpoint());
+                    leave_loop = false;
+                    break;
                 }
-                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;
-      }
-      if (r.nodes.find(id.name) == r.nodes.end()) { // First mention
-        r.nodes[id.name] = current().def_node_props;
-      }
-      return id;
-    }
+                case token::dash_greater:
+                {
+                    if (!r.graph_is_directed)
+                        error("Using -> in undirected graph");
+                    get();
+                    nodes_in_chain.push_back(parse_endpoint());
+                    leave_loop = false;
+                    break;
+                }
+                default:
+                    leave_loop = true;
+                    break;
+                }
+                if (leave_loop)
+                    break;
+            }
+            properties this_edge_props = current().def_edge_props;
+            if (peek().type == token::left_bracket)
+                parse_attr_list(this_edge_props);
+            BOOST_ASSERT(nodes_in_chain.size()
+                >= 2); // Should be in node parser otherwise
+            for (size_t i = 0; i + 1 < nodes_in_chain.size(); ++i)
+            {
+                do_orig_edge(
+                    nodes_in_chain[i], nodes_in_chain[i + 1], this_edge_props);
+            }
+        }
 
-    void parse_edge_stmt(const edge_endpoint& lhs) {
-      std::vector<edge_endpoint> nodes_in_chain(1, lhs);
-      while (true) {
-        bool leave_loop = true;
-        switch (peek().type) {
-          case token::dash_dash: {
-            if (r.graph_is_directed) error("Using -- in directed graph");
-            get();
-            nodes_in_chain.push_back(parse_endpoint());
-            leave_loop = false;
-            break;
-          }
-          case token::dash_greater: {
-            if (!r.graph_is_directed) error("Using -> in undirected graph");
-            get();
-            nodes_in_chain.push_back(parse_endpoint());
-            leave_loop = false;
-            break;
-          }
-          default: leave_loop = true; break;
-        }
-        if (leave_loop) break;
-      }
-      properties this_edge_props = current().def_edge_props;
-      if (peek().type == token::left_bracket) parse_attr_list(this_edge_props);
-      BOOST_ASSERT (nodes_in_chain.size() >= 2); // Should be in node parser otherwise
-      for (size_t i = 0; i + 1 < nodes_in_chain.size(); ++i) {
-        do_orig_edge(nodes_in_chain[i], nodes_in_chain[i + 1], this_edge_props);
-      }
-    }
+        // Do an edge from the file, the edge may need to be expanded if it
+        // connects to a subgraph
+        void do_orig_edge(const edge_endpoint& src, const edge_endpoint& tgt,
+            const properties& props)
+        {
+            std::set< node_and_port > sources = get_recursive_members(src);
+            std::set< node_and_port > targets = get_recursive_members(tgt);
+            for (std::set< node_and_port >::const_iterator i = sources.begin();
+                 i != sources.end(); ++i)
+            {
+                for (std::set< node_and_port >::const_iterator j
+                     = targets.begin();
+                     j != targets.end(); ++j)
+                {
+                    do_edge(*i, *j, props);
+                }
+            }
+        }
 
-    // Do an edge from the file, the edge may need to be expanded if it connects to a subgraph
-    void do_orig_edge(const edge_endpoint& src, const edge_endpoint& tgt, const properties& props) {
-      std::set<node_and_port> sources = get_recursive_members(src);
-      std::set<node_and_port> targets = get_recursive_members(tgt);
-      for (std::set<node_and_port>::const_iterator i = sources.begin(); i != sources.end(); ++i) {
-        for (std::set<node_and_port>::const_iterator j = targets.begin(); j != targets.end(); ++j) {
-          do_edge(*i, *j, props);
+        // Get nodes in an edge_endpoint, recursively
+        std::set< node_and_port > get_recursive_members(
+            const edge_endpoint& orig_ep)
+        {
+            std::set< node_and_port > result;
+            std::vector< edge_endpoint > worklist(1, orig_ep);
+            std::set< subgraph_name > done;
+            while (!worklist.empty())
+            {
+                edge_endpoint ep = worklist.back();
+                worklist.pop_back();
+                if (ep.is_subgraph)
+                {
+                    if (done.find(ep.subgraph_ep) == done.end())
+                    {
+                        done.insert(ep.subgraph_ep);
+                        std::map< subgraph_name, subgraph_info >::const_iterator
+                            info_i
+                            = subgraphs.find(ep.subgraph_ep);
+                        if (info_i != subgraphs.end())
+                        {
+                            const subgraph_member_list& members
+                                = info_i->second.members;
+                            for (subgraph_member_list::const_iterator i
+                                 = members.begin();
+                                 i != members.end(); ++i)
+                            {
+                                node_or_subgraph_ref ref = *i;
+                                if (ref.is_subgraph)
+                                {
+                                    worklist.push_back(
+                                        edge_endpoint::subgraph(ref.name));
+                                }
+                                else
+                                {
+                                    node_and_port np;
+                                    np.name = ref.name;
+                                    worklist.push_back(edge_endpoint::node(np));
+                                }
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    result.insert(ep.node_ep);
+                }
+            }
+            return result;
         }
-      }
-    }
 
-    // Get nodes in an edge_endpoint, recursively
-    std::set<node_and_port> get_recursive_members(const edge_endpoint& orig_ep) {
-      std::set<node_and_port> result;
-      std::vector<edge_endpoint> worklist(1, orig_ep);
-      std::set<subgraph_name> done;
-      while (!worklist.empty()) {
-        edge_endpoint ep = worklist.back();
-        worklist.pop_back();
-        if (ep.is_subgraph) {
-          if (done.find(ep.subgraph_ep) == done.end()) {
-            done.insert(ep.subgraph_ep);
-            std::map<subgraph_name, subgraph_info>::const_iterator
-              info_i = subgraphs.find(ep.subgraph_ep);
-            if (info_i != subgraphs.end()) {
-              const subgraph_member_list& members = info_i->second.members;
-              for (subgraph_member_list::const_iterator i = members.begin();
-                   i != members.end(); ++i) {
-                node_or_subgraph_ref ref = *i;
-                if (ref.is_subgraph) {
-                  worklist.push_back(edge_endpoint::subgraph(ref.name));
-                } else {
-                  node_and_port np;
-                  np.name = ref.name;
-                  worklist.push_back(edge_endpoint::node(np));
+        // Do a fixed-up edge, with only nodes as endpoints
+        void do_edge(const node_and_port& src, const node_and_port& tgt,
+            const properties& props)
+        {
+            if (r.graph_is_strict)
+            {
+                if (src.name == tgt.name)
+                    return;
+                std::pair< node_name, node_name > tag(src.name, tgt.name);
+                if (existing_edges.find(tag) != existing_edges.end())
+                {
+                    return; // Parallel edge
                 }
-              }
+                existing_edges.insert(tag);
             }
-          }
-        } else {
-          result.insert(ep.node_ep);
+            edge_info e;
+            e.source = src;
+            e.target = tgt;
+            e.props = props;
+            r.edges.push_back(e);
         }
-      }
-      return result;
-    }
 
-    // Do a fixed-up edge, with only nodes as endpoints
-    void do_edge(const node_and_port& src, const node_and_port& tgt, const properties& props) {
-      if (r.graph_is_strict) {
-        if (src.name == tgt.name) return;
-        std::pair<node_name, node_name> tag(src.name, tgt.name);
-        if (existing_edges.find(tag) != existing_edges.end()) {
-          return; // Parallel edge
-        }
-        existing_edges.insert(tag);
-      }
-      edge_info e;
-      e.source = src;
-      e.target = tgt;
-      e.props = props;
-      r.edges.push_back(e);
-    }
+        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 attribute");
+                            rhs = get().normalized_value;
+                        }
+                        props[lhs] = rhs;
+                        break;
+                    }
+                    default:
+                        error("Wanted identifier as name of attribute");
+                    }
+                    if (peek().type == token::comma
+                        || peek().type == token::semicolon)
+                        get();
+                    else if (peek().type == token::right_bracket)
+                        break;
+                }
+                if (peek().type == token::right_bracket)
+                    get();
+                else
+                    error("Wanted right bracket to end attribute list");
+                if (peek().type != token::left_bracket)
+                    break;
+            }
+        }
+    };
 
-    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 attribute");
-                rhs = get().normalized_value;
-              }
-              props[lhs] = rhs;
-              break;
-            }
-            default: error("Wanted identifier as name of attribute");
-          }
-          if (peek().type == token::comma || peek().type == token::semicolon) get();
-          else if(peek().type == token::right_bracket) break;
-        }
-        if (peek().type == token::right_bracket) get(); else error("Wanted right bracket to end attribute list");
-        if (peek().type != token::left_bracket) break;
-      }
-    }
-  };
-
-  void parse_graphviz_from_string(const std::string& str, parser_result& result, bool want_directed) {
-    parser p(str, result);
-    p.parse_graph(want_directed);
-  }
-
-  // Some debugging stuff
-  std::ostream& operator<<(std::ostream& o, const node_and_port& n) {
-    o << n.name;
-    for (size_t i = 0; i < n.location.size(); ++i) {
-      o << ":" << n.location[i];
-    }
-    if (!n.angle.empty()) o << "@" << n.angle;
-    return o;
-  }
-
-  // Can't be operator<< because properties is just an std::map
-  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;
+    void parse_graphviz_from_string(
+        const std::string& str, parser_result& result, bool want_directed)
+    {
+        parser p(str, result);
+        p.parse_graph(want_directed);
     }
-    result += "]";
-    return result;
-  }
-
-  void translate_results_to_graph(const parser_result& r, ::boost::detail::graph::mutate_graph* mg) {
-    typedef boost::detail::graph::edge_t edge;
-    for (std::map<node_name, properties>::const_iterator i = r.nodes.begin(); i != r.nodes.end(); ++i) {
-      // std::cerr << i->first << " " << props_to_string(i->second) << std::endl;
-      mg->do_add_vertex(i->first);
-      for (properties::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
-        mg->set_node_property(j->first, i->first, j->second);
-      }
+
+    // Some debugging stuff
+    std::ostream& operator<<(std::ostream& o, const node_and_port& n)
+    {
+        o << n.name;
+        for (size_t i = 0; i < n.location.size(); ++i)
+        {
+            o << ":" << n.location[i];
+        }
+        if (!n.angle.empty())
+            o << "@" << n.angle;
+        return o;
     }
-    for (std::vector<edge_info>::const_iterator i = r.edges.begin(); i != r.edges.end(); ++i) {
-      const edge_info& ei = *i;
-      // std::cerr << ei.source << " -> " << ei.target << " " << props_to_string(ei.props) << std::endl;
-      edge e = edge::new_edge();
-      mg->do_add_edge(e, ei.source.name, ei.target.name);
-      for (properties::const_iterator j = ei.props.begin(); j != ei.props.end(); ++j) {
-        mg->set_edge_property(j->first, e, j->second);
-      }
+
+    // Can't be operator<< because properties is just an std::map
+    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;
     }
-    std::map<subgraph_name, properties>::const_iterator root_graph_props_i = r.graph_props.find("___root___");
-    BOOST_ASSERT (root_graph_props_i != r.graph_props.end()); // Should not happen
-    const properties& root_graph_props = root_graph_props_i->second;
-    // std::cerr << "ending graph " << props_to_string(root_graph_props) << std::endl;
-    for (properties::const_iterator i = root_graph_props.begin(); i != root_graph_props.end(); ++i) {
-      mg->set_graph_property(i->first, i->second);
+
+    void translate_results_to_graph(
+        const parser_result& r, ::boost::detail::graph::mutate_graph* mg)
+    {
+        typedef boost::detail::graph::edge_t edge;
+        for (std::map< node_name, properties >::const_iterator i
+             = r.nodes.begin();
+             i != r.nodes.end(); ++i)
+        {
+            // std::cerr << i->first << " " << props_to_string(i->second) <<
+            // std::endl;
+            mg->do_add_vertex(i->first);
+            for (properties::const_iterator j = i->second.begin();
+                 j != i->second.end(); ++j)
+            {
+                mg->set_node_property(j->first, i->first, j->second);
+            }
+        }
+        for (std::vector< edge_info >::const_iterator i = r.edges.begin();
+             i != r.edges.end(); ++i)
+        {
+            const edge_info& ei = *i;
+            // std::cerr << ei.source << " -> " << ei.target << " " <<
+            // props_to_string(ei.props) << std::endl;
+            edge e = edge::new_edge();
+            mg->do_add_edge(e, ei.source.name, ei.target.name);
+            for (properties::const_iterator j = ei.props.begin();
+                 j != ei.props.end(); ++j)
+            {
+                mg->set_edge_property(j->first, e, j->second);
+            }
+        }
+        std::map< subgraph_name, properties >::const_iterator root_graph_props_i
+            = r.graph_props.find("___root___");
+        BOOST_ASSERT(
+            root_graph_props_i != r.graph_props.end()); // Should not happen
+        const properties& root_graph_props = root_graph_props_i->second;
+        // std::cerr << "ending graph " << props_to_string(root_graph_props) <<
+        // std::endl;
+        for (properties::const_iterator i = root_graph_props.begin();
+             i != root_graph_props.end(); ++i)
+        {
+            mg->set_graph_property(i->first, i->second);
+        }
+        mg->finish_building_graph();
     }
-    mg->finish_building_graph();
-  }
 
 } // end namespace read_graphviz_detail
 
-namespace detail {
-  namespace graph {
+namespace detail
+{
+    namespace graph
+    {
 
-    BOOST_GRAPH_DECL bool read_graphviz_new(const std::string& str, boost::detail::graph::mutate_graph* mg) {
-      read_graphviz_detail::parser_result parsed_file;
-      read_graphviz_detail::parse_graphviz_from_string(str, parsed_file, mg->is_directed());
-      read_graphviz_detail::translate_results_to_graph(parsed_file, mg);
-      return true;
-    }
+        BOOST_GRAPH_DECL bool read_graphviz_new(
+            const std::string& str, boost::detail::graph::mutate_graph* mg)
+        {
+            read_graphviz_detail::parser_result parsed_file;
+            read_graphviz_detail::parse_graphviz_from_string(
+                str, parsed_file, mg->is_directed());
+            read_graphviz_detail::translate_results_to_graph(parsed_file, mg);
+            return true;
+        }
 
-  } // end namespace graph
+    } // end namespace graph
 } // end namespace detail
 
 } // end namespace boost