Force recursive wrapper parser

The code below (adapted from the qi mini_xml example) does not compile. There is an error related to the brac rule, which has a recursive boost::variant attribute.
However, all commented versions of brac are compiled.

I am very curious to find out what makes a simple parser so special in this case:

 #include <boost/config/warning_disable.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix_core.hpp> #include <boost/spirit/include/phoenix_operator.hpp> #include <boost/spirit/include/phoenix_fusion.hpp> #include <boost/spirit/include/phoenix_stl.hpp> #include <boost/spirit/include/phoenix_object.hpp> #include <boost/fusion/include/adapt_struct.hpp> #include <boost/variant/recursive_variant.hpp> #include <string> #include <vector> namespace client { namespace fusion = boost::fusion; namespace phoenix = boost::phoenix; namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; struct ast_node; typedef boost::variant< boost::recursive_wrapper<ast_node>, std::string > ast_branch; struct ast_node { std::string text; std::vector<ast_branch> children; }; } BOOST_FUSION_ADAPT_STRUCT( client::ast_node, (std::string, text) (std::vector<client::ast_branch>, children) ) namespace client { template <typename Iterator> struct ast_node_grammar : qi::grammar<Iterator, ast_branch(), ascii::space_type> { ast_node_grammar() : ast_node_grammar::base_type(brac) { using qi::_1; using qi::_val; using ascii::char_; using ascii::string; name %= *char_; brac %= string("no way") ; // brac = string("works")[_val = _1] ; // brac %= string("this") | string("works"); // brac %= name ; // works // brac %= *char_ ; // works } qi::rule<Iterator, std::string()> name; qi::rule<Iterator, ast_branch(), ascii::space_type> brac; }; } int main(int argc, char **argv) { typedef client::ast_node_grammar<std::string::const_iterator> ast_node_grammar; ast_node_grammar gram; client::ast_branch ast; std::string text("dummy"); using boost::spirit::ascii::space; std::string::const_iterator iter = text.begin(); std::string::const_iterator end = text.end(); bool r = phrase_parse(iter, end, gram, space, ast); return r ? 0 : 1; } 

Part of the error message:

 /usr/include/boost/spirit/home/qi/detail/assign_to.hpp:38:17: error: No match for 'boost::variant< boost::recursive_wrapper<client::ast_node>, basic_string<char> >::variant( const __normal_iterator<const char *, basic_string<char> > &, const __normal_iterator< const char *, basic_string<char> > &)' 

Thanks in advance.

+3
source share
1 answer

I would suggest that the problem is attribute compatibility. Unlike documentation , the ascii :: string parser can expose an iterator range instead of a string.

 name = string("no way"); 

not a problem, because an attribute opened with ascii :: string can be forced into the attribute type of a rule without any difficulty.

However, the attribute type of the brac rule is ast_branch , which is only an option with one of the possible types. Therefore, the ast_branch type has several constructors, and it is not clear to the Spirit that it is suitable for this particular transformation.

There are several ways (in addition to the approaches that you have already indicated):

  • use attr_cast

     brac = qi::attr_cast( string("no way") ); 
  • use as_string

     brac = qi::as_string[ string("no way") ]; 
  • use setting points

     namespace boost { namespace spirit { namespace traits { template <typename It> struct assign_to_attribute_from_iterators<client::ast_branch, It> { static void call(It const& f, It const& l, client::ast_branch& val) { val = std::string(f, l); } }; }}} 

Each of them has the same effect: make Spirit understands which attribute transformation to use.

Here is a complete working example showing all three:

 // #define BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT #include <boost/config/warning_disable.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/fusion/adapted.hpp> #include <boost/spirit/include/phoenix.hpp> #include <boost/variant/recursive_variant.hpp> #include <string> #include <vector> namespace client { namespace fusion = boost::fusion; namespace phoenix = boost::phoenix; namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; struct ast_node; typedef boost::variant< boost::recursive_wrapper<ast_node>, std::string > ast_branch; struct ast_node { std::string text; std::vector<ast_branch> children; }; } namespace boost { namespace spirit { namespace traits { template <typename It> struct assign_to_attribute_from_iterators<client::ast_branch, It> { static void call(It const& f, It const& l, client::ast_branch& val) { val = std::string(f, l); } }; }}} BOOST_FUSION_ADAPT_STRUCT( client::ast_node, (std::string, text) (std::vector<client::ast_branch>, children) ) namespace client { template <typename Iterator> struct ast_node_grammar : qi::grammar<Iterator, ast_branch(), ascii::space_type> { ast_node_grammar() : ast_node_grammar::base_type(brac) { using qi::_1; using qi::_val; using ascii::char_; using ascii::string; name %= *char_; brac = string("works"); brac = string("works")[_val = _1] ; brac %= string("this") | string("works"); brac %= name ; // works brac %= *char_ ; // works brac = qi::as_string[ string("no way") ]; brac = qi::attr_cast( string("no way") ); } qi::rule<Iterator, std::string()> name; qi::rule<Iterator, ast_branch(), ascii::space_type> brac; }; } int main(int argc, char **argv) { typedef client::ast_node_grammar<std::string::const_iterator> ast_node_grammar; ast_node_grammar gram; client::ast_branch ast; std::string text("dummy"); using boost::spirit::ascii::space; std::string::const_iterator iter = text.begin(); std::string::const_iterator end = text.end(); bool r = phrase_parse(iter, end, gram, space, ast); return r ? 0 : 1; } 
+5
source

All Articles