From 41824dbab205e878efa2b173ef0b4c1acbf57644 Mon Sep 17 00:00:00 2001 From: Arnaud Becheler <8360330+Becheler@users.noreply.github.com> Date: Fri, 17 Jul 2026 11:47:26 +0200 Subject: [PATCH] refactor: remove boost.spirit graphviz parser --- CMakeLists.txt | 1 - build.jam | 1 - .../graph/detail/read_graphviz_spirit.hpp | 673 ------------------ include/boost/graph/graphviz.hpp | 25 +- test/isomorphism.cpp | 1 - 5 files changed, 5 insertions(+), 696 deletions(-) delete mode 100644 include/boost/graph/detail/read_graphviz_spirit.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cf2e99fbc..2931ece2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,6 @@ target_link_libraries(boost_graph Boost::range Boost::serialization Boost::smart_ptr - Boost::spirit Boost::throw_exception Boost::tti Boost::tuple diff --git a/build.jam b/build.jam index c8f1b19af..db217e98a 100644 --- a/build.jam +++ b/build.jam @@ -33,7 +33,6 @@ constant boost_dependencies : /boost/range//boost_range /boost/serialization//boost_serialization /boost/smart_ptr//boost_smart_ptr - /boost/spirit//boost_spirit /boost/throw_exception//boost_throw_exception /boost/tti//boost_tti /boost/tuple//boost_tuple diff --git a/include/boost/graph/detail/read_graphviz_spirit.hpp b/include/boost/graph/detail/read_graphviz_spirit.hpp deleted file mode 100644 index 165007766..000000000 --- a/include/boost/graph/detail/read_graphviz_spirit.hpp +++ /dev/null @@ -1,673 +0,0 @@ -// 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_spirit.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: -// https://web.archive.org/web/20041213234742/http://www.graphviz.org/cvs/doc/info/lang.html -// -// See documentation for this code at: -// http://www.boost.org/libs/graph/doc/read_graphviz.html -// - -// Author: Ronald Garcia -// - -#ifndef BOOST_READ_GRAPHVIZ_SPIRIT_HPP -#define BOOST_READ_GRAPHVIZ_SPIRIT_HPP - -// Phoenix/Spirit set these limits to 3, but I need more. -#define PHOENIX_LIMIT 6 -#define BOOST_SPIRIT_CLOSURE_LIMIT 6 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // for std::exception -#include -#include -#include -#include -#include -#include -#include - -namespace phoenix -{ -// Workaround: std::map::operator[] uses a different return type than all -// other standard containers. Phoenix doesn't account for that. -template < typename TK, typename T0, typename T1 > -struct binary_operator< index_op, std::map< TK, T0 >, T1 > -{ - typedef typename std::map< TK, T0 >::mapped_type& result_type; - static result_type eval(std::map< TK, T0 >& container, T1 const& index) - { - return container[index]; - } -}; -} // namespace phoenix - -namespace boost -{ -namespace detail -{ - namespace graph - { - - ///////////////////////////////////////////////////////////////////////////// - // Application-specific type definitions - ///////////////////////////////////////////////////////////////////////////// - - typedef std::set< edge_t > edges_t; - typedef std::set< node_t > nodes_t; - typedef std::set< id_t > ids_t; - typedef std::map< edge_t, ids_t > edge_map_t; - typedef std::map< node_t, ids_t > node_map_t; - typedef std::map< id_t, id_t > props_t; - typedef std::map< id_t, props_t > subgraph_props_t; - typedef boost::function2< void, id_t const&, id_t const& > actor_t; - typedef std::vector< edge_t > edge_stack_t; - typedef std::map< id_t, nodes_t > subgraph_nodes_t; - typedef std::map< id_t, edges_t > subgraph_edges_t; - - ///////////////////////////////////////////////////////////////////////////// - // Stack frames used by semantic actions - ///////////////////////////////////////////////////////////////////////////// - struct id_closure - : boost::spirit::classic::closure< id_closure, node_t > - { - member1 name; - }; - - struct node_id_closure - : boost::spirit::classic::closure< node_id_closure, node_t > - { - member1 name; - }; - - struct attr_list_closure - : boost::spirit::classic::closure< attr_list_closure, actor_t > - { - member1 prop_actor; - }; - - struct property_closure - : boost::spirit::classic::closure< property_closure, id_t, id_t > - { - member1 key; - member2 value; - }; - - struct data_stmt_closure - : boost::spirit::classic::closure< data_stmt_closure, nodes_t, nodes_t, - edge_stack_t, bool, node_t > - { - member1 sources; - member2 dests; - member3 edge_stack; - member4 saw_node; - member5 active_node; - }; - - struct subgraph_closure - : boost::spirit::classic::closure< subgraph_closure, nodes_t, edges_t, - node_t > - { - member1 nodes; - member2 edges; - member3 name; - }; - - ///////////////////////////////////////////////////////////////////////////// - // Grammar and Actions for the DOT Language - ///////////////////////////////////////////////////////////////////////////// - - // Grammar for a dot file. - struct dot_grammar - : public boost::spirit::classic::grammar< dot_grammar > - { - mutate_graph& graph_; - explicit dot_grammar(mutate_graph& graph) : graph_(graph) {} - - template < class ScannerT > struct definition - { - - definition(dot_grammar const& self) - : self(self), subgraph_depth(0), keyword_p("0-9a-zA-Z_") - { - using namespace boost::spirit::classic; - using namespace phoenix; - - // RG - Future Work - // - Handle multi-line strings using \ line continuation - // - Make keywords case insensitive - ID = (lexeme_d[( - (alpha_p | ch_p('_')) >> *(alnum_p | ch_p('_')))] - | real_p | lexeme_d[confix_p('"', *c_escape_ch_p, '"')] - | comment_nest_p('<', '>'))[ID.name - = construct_< std::string >(arg1, arg2)]; - - a_list = list_p( - ID[((a_list.key = arg1), (a_list.value = "true"))] >> !( - ch_p('=') >> ID[a_list.value = arg1])[phoenix::bind( - &definition::call_prop_actor)( - var(*this), a_list.key, a_list.value)], - !ch_p(',')); - - attr_list = +(ch_p('[') >> !a_list >> ch_p(']')); - - // RG - disregard port id's for now. - port_location = (ch_p(':') >> ID) - | (ch_p(':') >> ch_p('(') >> ID >> ch_p(',') >> ID - >> ch_p(')')); - - port_angle = ch_p('@') >> ID; - - port = port_location >> (!port_angle) - | port_angle >> (!port_location); - - node_id - = (ID[node_id.name = arg1] >> (!port))[phoenix::bind( - &definition::memoize_node)(var(*this))]; - - graph_stmt = (ID[graph_stmt.key = arg1] >> ch_p('=') - >> ID[graph_stmt.value = arg1])[phoenix::bind( - &definition::call_graph_prop)(var(*this), - graph_stmt.key, graph_stmt.value)]; // Graph property. - - attr_stmt - = (as_lower_d[keyword_p("graph")] >> attr_list(actor_t( - phoenix::bind(&definition::default_graph_prop)( - var(*this), arg1, arg2)))) - | (as_lower_d[keyword_p("node")] >> attr_list(actor_t( - phoenix::bind(&definition::default_node_prop)( - var(*this), arg1, arg2)))) - | (as_lower_d[keyword_p("edge")] >> attr_list(actor_t( - phoenix::bind(&definition::default_edge_prop)( - var(*this), arg1, arg2)))); - - // edge_head is set depending on the graph type - // (directed/undirected) - edgeop = ch_p('-') >> ch_p(boost::ref(edge_head)); - - edgeRHS = +(edgeop[((data_stmt.sources = data_stmt.dests), - (data_stmt.dests = construct_< nodes_t >()))] - >> (subgraph[data_stmt.dests = arg1] - | node_id[phoenix::bind(&definition::insert_node)( - var(*this), data_stmt.dests, arg1)]) - [phoenix::bind(&definition::activate_edge)( - var(*this), data_stmt.sources, data_stmt.dests, - var(edges), var(default_edge_props))]); - - // To avoid backtracking, edge, node, and subgraph - // statements are processed as one nonterminal. - data_stmt - = (subgraph[((data_stmt.dests - = arg1), // will get moved in rhs - (data_stmt.saw_node = false))] - | node_id[((phoenix::bind( - &definition::insert_node)( - var(*this), data_stmt.dests, arg1)), - (data_stmt.saw_node = true), -#ifdef BOOST_GRAPH_DEBUG - (std::cout << val("AcTive Node: ") << arg1 - << "\n"), -#endif // BOOST_GRAPH_DEBUG - (data_stmt.active_node = arg1))]) - >> if_p(edgeRHS)[!attr_list(actor_t(phoenix::bind( - &definition::edge_prop)( - var(*this), arg1, arg2)))] - .else_p[if_p(data_stmt.saw_node)[!attr_list( - actor_t(phoenix::bind( - &definition::node_prop)(var(*this), arg1, - arg2)))] // otherwise it's a subgraph, - // nothing more to do. - ]; - - stmt = graph_stmt | attr_stmt | data_stmt; - - stmt_list = *(stmt >> !ch_p(';')); - - subgraph = !(as_lower_d[keyword_p("subgraph")] - >> (!ID[((subgraph.name = arg1), - (subgraph.nodes - = (var(subgraph_nodes))[arg1]), - (subgraph.edges - = (var(subgraph_edges))[arg1]))])) - >> ch_p('{')[++var(subgraph_depth)] >> stmt_list - >> ch_p('}')[--var(subgraph_depth)] - [(var(subgraph_nodes))[subgraph.name] - = subgraph.nodes] - [(var(subgraph_edges))[subgraph.name] - = subgraph.edges] - - | as_lower_d[keyword_p("subgraph")] - >> ID[((subgraph.nodes - = (var(subgraph_nodes))[arg1]), - (subgraph.edges = (var(subgraph_edges))[arg1]))]; - - the_grammar = (!as_lower_d[keyword_p("strict")]) - >> (as_lower_d[keyword_p( - "graph")][((var(edge_head) = '-'), - (phoenix::bind(&definition::check_undirected)( - var(*this))))] - | as_lower_d[keyword_p( - "digraph")][((var(edge_head) = '>'), - (phoenix::bind(&definition::check_directed)( - var(*this))))]) - >> (!ID) >> ch_p('{') >> stmt_list >> ch_p('}'); - - } // definition() - - typedef boost::spirit::classic::rule< ScannerT > rule_t; - - rule_t const& start() const { return the_grammar; } - - // - // Semantic actions - // - - void check_undirected() - { - if (self.graph_.is_directed()) - boost::throw_exception(boost::undirected_graph_error()); - } - - void check_directed() - { - if (!self.graph_.is_directed()) - boost::throw_exception(boost::directed_graph_error()); - } - - void memoize_node() - { - id_t const& node = node_id.name(); - props_t& node_props = default_node_props; - - if (nodes.find(node) == nodes.end()) - { - nodes.insert(node); - self.graph_.do_add_vertex(node); - - node_map.insert(std::make_pair(node, ids_t())); - -#ifdef BOOST_GRAPH_DEBUG - std::cout << "Add new node " << node << std::endl; -#endif // BOOST_GRAPH_DEBUG - // Set the default properties for this edge - // RG: Here I would actually set the properties - for (props_t::iterator i = node_props.begin(); - i != node_props.end(); ++i) - { - set_node_property(node, i->first, i->second); - } - if (subgraph_depth > 0) - { - subgraph.nodes().insert(node); - // Set the subgraph's default properties as well - props_t& props - = subgraph_node_props[subgraph.name()]; - for (props_t::iterator i = props.begin(); - i != props.end(); ++i) - { - set_node_property(node, i->first, i->second); - } - } - } - else - { -#ifdef BOOST_GRAPH_DEBUG - std::cout << "See node " << node << std::endl; -#endif // BOOST_GRAPH_DEBUG - } - } - - void activate_edge(nodes_t& sources, nodes_t& dests, - edges_t& edges, props_t& edge_props) - { - edge_stack_t& edge_stack = data_stmt.edge_stack(); - for (nodes_t::iterator i = sources.begin(); - i != sources.end(); ++i) - { - for (nodes_t::iterator j = dests.begin(); - j != dests.end(); ++j) - { - // Create the edge and push onto the edge stack. -#ifdef BOOST_GRAPH_DEBUG - std::cout << "Edge " << *i << " to " << *j - << std::endl; -#endif // BOOST_GRAPH_DEBUG - - edge_t edge = edge_t::new_edge(); - edge_stack.push_back(edge); - edges.insert(edge); - edge_map.insert(std::make_pair(edge, ids_t())); - - // Add the real edge. - self.graph_.do_add_edge(edge, *i, *j); - - // Set the default properties for this edge - for (props_t::iterator k = edge_props.begin(); - k != edge_props.end(); ++k) - { - set_edge_property(edge, k->first, k->second); - } - if (subgraph_depth > 0) - { - subgraph.edges().insert(edge); - // Set the subgraph's default properties as well - props_t& props - = subgraph_edge_props[subgraph.name()]; - for (props_t::iterator k = props.begin(); - k != props.end(); ++k) - { - set_edge_property( - edge, k->first, k->second); - } - } - } - } - } - - // node_prop - Assign the property for the current active node. - void node_prop(id_t const& key, id_t const& value) - { - node_t& active_object = data_stmt.active_node(); - set_node_property(active_object, key, value); - } - - // edge_prop - Assign the property for the current active edges. - void edge_prop(id_t const& key, id_t const& value) - { - edge_stack_t const& active_edges_ = data_stmt.edge_stack(); - for (edge_stack_t::const_iterator i = active_edges_.begin(); - i != active_edges_.end(); ++i) - { - set_edge_property(*i, key, value); - } - } - - // default_graph_prop - Store as a graph property. - void default_graph_prop(id_t const& key, id_t const& value) - { -#ifdef BOOST_GRAPH_DEBUG - std::cout << key << " = " << value << std::endl; -#endif // BOOST_GRAPH_DEBUG - self.graph_.set_graph_property(key, value); - } - - // default_node_prop - declare default properties for any future - // new nodes - void default_node_prop(id_t const& key, id_t const& value) - { - nodes_t& nodes_ - = subgraph_depth == 0 ? nodes : subgraph.nodes(); - props_t& node_props_ = subgraph_depth == 0 - ? default_node_props - : subgraph_node_props[subgraph.name()]; - - // add this to the selected list of default node properties. - node_props_[key] = value; - // for each node, set its property to default-constructed - // value - // if it hasn't been set already. - // set the dynamic property map value - for (nodes_t::iterator i = nodes_.begin(); - i != nodes_.end(); ++i) - if (node_map[*i].find(key) == node_map[*i].end()) - { - set_node_property(*i, key, id_t()); - } - } - - // default_edge_prop - declare default properties for any future - // new edges - void default_edge_prop(id_t const& key, id_t const& value) - { - edges_t& edges_ - = subgraph_depth == 0 ? edges : subgraph.edges(); - props_t& edge_props_ = subgraph_depth == 0 - ? default_edge_props - : subgraph_edge_props[subgraph.name()]; - - // add this to the list of default edge properties. - edge_props_[key] = value; - // for each edge, set its property to be empty string - // set the dynamic property map value - for (edges_t::iterator i = edges_.begin(); - i != edges_.end(); ++i) - if (edge_map[*i].find(key) == edge_map[*i].end()) - set_edge_property(*i, key, id_t()); - } - - // helper function - void insert_node(nodes_t& nodes, id_t const& name) - { - nodes.insert(name); - } - - void call_prop_actor( - std::string const& lhs, std::string const& rhs) - { - actor_t& actor = attr_list.prop_actor(); - // If first and last characters of the rhs are - // double-quotes, remove them. - if (!rhs.empty() && rhs[0] == '"' - && rhs[rhs.size() - 1] == '"') - actor(lhs, rhs.substr(1, rhs.size() - 2)); - else - actor(lhs, rhs); - } - - void call_graph_prop( - std::string const& lhs, std::string const& rhs) - { - // If first and last characters of the rhs are - // double-quotes, remove them. - if (!rhs.empty() && rhs[0] == '"' - && rhs[rhs.size() - 1] == '"') - this->default_graph_prop( - lhs, rhs.substr(1, rhs.size() - 2)); - else - this->default_graph_prop(lhs, rhs); - } - - void set_node_property( - node_t const& node, id_t const& key, id_t const& value) - { - - // Add the property key to the "set" table to avoid default - // overwrite - node_map[node].insert(key); - // Set the user's property map - self.graph_.set_node_property(key, node, value); -#ifdef BOOST_GRAPH_DEBUG - // Tell the world - std::cout << node << ": " << key << " = " << value - << std::endl; -#endif // BOOST_GRAPH_DEBUG - } - - void set_edge_property( - edge_t const& edge, id_t const& key, id_t const& value) - { - - // Add the property key to the "set" table to avoid default - // overwrite - edge_map[edge].insert(key); - // Set the user's property map - self.graph_.set_edge_property(key, edge, value); -#ifdef BOOST_GRAPH_DEBUG - // Tell the world -#if 0 // RG - edge representation changed, - std::cout << "(" << edge.first << "," << edge.second << "): " -#else - std::cout << "an edge: " -#endif // 0 - << key << " = " << value << std::endl; -#endif // BOOST_GRAPH_DEBUG - } - - // Variables explicitly initialized - dot_grammar const& self; - // if subgraph_depth > 0, then we're processing a subgraph. - int subgraph_depth; - - // Keywords; - const boost::spirit::classic::distinct_parser<> keyword_p; - // - // rules that make up the grammar - // - boost::spirit::classic::rule< ScannerT, id_closure::context_t > - ID; - boost::spirit::classic::rule< ScannerT, - property_closure::context_t > - a_list; - boost::spirit::classic::rule< ScannerT, - attr_list_closure::context_t > - attr_list; - rule_t port_location; - rule_t port_angle; - rule_t port; - boost::spirit::classic::rule< ScannerT, - node_id_closure::context_t > - node_id; - boost::spirit::classic::rule< ScannerT, - property_closure::context_t > - graph_stmt; - rule_t attr_stmt; - boost::spirit::classic::rule< ScannerT, - data_stmt_closure::context_t > - data_stmt; - boost::spirit::classic::rule< ScannerT, - subgraph_closure::context_t > - subgraph; - rule_t edgeop; - rule_t edgeRHS; - rule_t stmt; - rule_t stmt_list; - rule_t the_grammar; - - // The grammar uses edge_head to dynamically set the syntax for - // edges directed graphs: edge_head = '>', and so edgeop = "->" - // undirected graphs: edge_head = '-', and so edgeop = "--" - char edge_head; - - // - // Support data structures - // - - nodes_t nodes; // list of node names seen - edges_t edges; // list of edges seen - node_map_t - node_map; // remember the properties set for each node - edge_map_t - edge_map; // remember the properties set for each edge - - subgraph_nodes_t subgraph_nodes; // per-subgraph lists of nodes - subgraph_edges_t subgraph_edges; // per-subgraph lists of edges - - props_t default_node_props; // global default node properties - props_t default_edge_props; // global default edge properties - subgraph_props_t - subgraph_node_props; // per-subgraph default node properties - subgraph_props_t - subgraph_edge_props; // per-subgraph default edge properties - }; // struct definition - }; // struct dot_grammar - - // - // dot_skipper - GraphViz whitespace and comment skipper - // - struct dot_skipper - : public boost::spirit::classic::grammar< dot_skipper > - { - dot_skipper() {} - - template < typename ScannerT > struct definition - { - definition(dot_skipper const& /*self*/) - { - using namespace boost::spirit::classic; - using namespace phoenix; - // comment forms - skip = eol_p >> comment_p("#") | space_p | comment_p("//") -#if BOOST_WORKAROUND(BOOST_MSVC, <= 1400) - | confix_p(str_p("/*"), *anychar_p, str_p("*/")) -#else - | confix_p("/*", *anychar_p, "*/") -#endif - ; - -#ifdef BOOST_SPIRIT_DEBUG - BOOST_SPIRIT_DEBUG_RULE(skip); -#endif - } - - boost::spirit::classic::rule< ScannerT > skip; - boost::spirit::classic::rule< ScannerT > const& start() const - { - return skip; - } - }; // definition - }; // dot_skipper - - } // namespace graph -} // namespace detail - -template < typename MultiPassIterator, typename MutableGraph > -bool read_graphviz_spirit(MultiPassIterator begin, MultiPassIterator end, - MutableGraph& graph, dynamic_properties& dp, - std::string const& node_id = "node_id") -{ - using namespace boost; - using namespace boost::spirit::classic; - - typedef MultiPassIterator iterator_t; - 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::mutate_graph_impl< MutableGraph > m_graph( - graph, dp, node_id); - - ::boost::detail::graph::dot_grammar p(m_graph); - ::boost::detail::graph::dot_skipper skip_p; - - iter_policy_t iter_policy(skip_p); - scanner_policies_t policies(iter_policy); - - scanner_t scan(begin, end, policies); - - bool ok = p.parse(scan); - m_graph.finish_building_graph(); - return ok; -} - -} // namespace boost - -#endif // BOOST_READ_GRAPHVIZ_SPIRIT_HPP diff --git a/include/boost/graph/graphviz.hpp b/include/boost/graph/graphviz.hpp index 62e58cde3..e798017fc 100644 --- a/include/boost/graph/graphviz.hpp +++ b/include/boost/graph/graphviz.hpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -925,13 +925,12 @@ namespace detail } // end namespace boost::detail::graph #ifdef BOOST_GRAPH_USE_SPIRIT_PARSER -#ifndef BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS -#define BOOST_GRAPH_READ_GRAPHVIZ_ITERATORS +BOOST_PRAGMA_MESSAGE( + "BOOST_GRAPH_USE_SPIRIT_PARSER is deprecated and no longer has any effect: " + "the Boost.Spirit read_graphviz parser has been removed" + "read_graphviz now always uses the default parser.") #endif -#include -#else // New default parser #include -#endif // BOOST_GRAPH_USE_SPIRIT_PARSER namespace boost { @@ -941,11 +940,7 @@ template < typename MutableGraph > bool read_graphviz(const std::string& data, MutableGraph& graph, dynamic_properties& dp, std::string const& node_id = "node_id") { -#ifdef BOOST_GRAPH_USE_SPIRIT_PARSER - return read_graphviz_spirit(data.begin(), data.end(), graph, dp, node_id); -#else // Non-Spirit parser return read_graphviz_new(data, graph, dp, node_id); -#endif } // Parse the passed iterator range as a GraphViz dot file. @@ -954,18 +949,8 @@ bool read_graphviz(InputIterator user_first, InputIterator user_last, MutableGraph& graph, dynamic_properties& dp, std::string const& node_id = "node_id") { -#ifdef BOOST_GRAPH_USE_SPIRIT_PARSER - typedef InputIterator is_t; - typedef boost::spirit::classic::multi_pass< is_t > iterator_t; - - iterator_t first(boost::spirit::classic::make_multi_pass(user_first)); - iterator_t last(boost::spirit::classic::make_multi_pass(user_last)); - - return read_graphviz_spirit(first, last, graph, dp, node_id); -#else // Non-Spirit parser return read_graphviz_new( std::string(user_first, user_last), graph, dp, node_id); -#endif } // Parse the passed stream as a GraphViz dot file. diff --git a/test/isomorphism.cpp b/test/isomorphism.cpp index d5d2ec914..3f3df34d2 100644 --- a/test/isomorphism.cpp +++ b/test/isomorphism.cpp @@ -31,7 +31,6 @@ #include #include -#define BOOST_GRAPH_USE_SPIRIT_PARSER #include #include