c++ - Boost.Spirit.Qi: How to return attributes with Nabialek trick -


following several tutorials (e.g. http://boost-spirit.com/home/articles/qi-example/nabialek-trick/) want use nabialek trick have dynamic parser. parsing works fine, don't attributes transported. explanations https://stackoverflow.com/a/9109972/2524462 suggest, attributes should possible not arguments.

this small example parsing string , number struct. showcasing problem; method should used in larger system later on, dynamic parser needed.

question: how transport attributes nabialek trick?

i'm not spirit expert, please bear me. i'm using gcc 4.8.1 , boost 1.54.

#define boost_spirit_debug #define boost_spirit_use_phoenix_v3 #include <boost/fusion/adapted/struct.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp>  namespace qi = boost::spirit::qi; namespace phx = boost::phoenix;  //------------------------------------------------------------------------------ // data structure struct myline {   myline()       : _n(0), _s("") {   }    myline(int n, std::string s)       : _n(n), _s(s) {   }    void set(int n, std::string s) {     _n = n;     _s = s;   }    int _n;   std::string _s; };  boost_fusion_adapt_struct(::myline, (int, _n) (std::string, _s))  //------------------------------------------------------------------------------ // parser grammar template<typename it, typename skipper = qi::space_type> struct parser: qi::grammar<it, myline(), skipper> {   parser()       : parser::base_type(start) {     using namespace qi;      start = line;      string %= qi::lexeme["'" >> *~qi::char_("'") >> "'"];      1 = (string >> "@" >> qi::int_)[_val = phx::construct<myline>(_2, _1)];     2 = (qi::int_ >> "@" >> string);      keyword.add("one", &one)("two", &two);      line = keyword[_a = _1] >> qi::lazy(*_a);      on_error<fail>(         start,         std::cout << phx::val("error! expecting ") << _4         << phx::val(" here: \"") << phx::construct<std::string>(_3, _2)         << phx::val("\"\n"));      boost_spirit_debug_nodes((start)(line)(one)(two))   }  private:   template<typename attr> using rule = qi::rule<it, attr(), skipper>;    rule<myline> start, one, two;   qi::rule<it, myline, skipper, qi::locals<rule<myline>*> > line;    rule<std::string> string;    qi::symbols<char, rule<myline>*> keyword; };  //------------------------------------------------------------------------------ int main() {   (const std::string input : std::vector<std::string> { "one 'test'@1",                                                             "two 2@'test'" }) {     auto f(std::begin(input)), l(std::end(input));     const static parser<decltype(f)> p;      myline parsed_script;     bool ok = qi::phrase_parse(f, l, p, qi::space, parsed_script);      if (!ok) {       std::cout << "invalid input\n";     }      std::cout << parsed_script._n << ": " << parsed_script._s << std::endl;      if (f != l) {       std::cout << "unparsed: '" << std::string(f, l) << "'" << std::endl;     }   } } 

parsing result:

<start>   <try>one 'test'@1</try>   <line>     <try>one 'test'@1</try>     <one>       <try> 'test'@1</try>       <success></success>       <attributes>[[1, [t, e, s, t]]]</attributes>     </one>     <success></success>     <attributes>[]</attributes><locals>(0x43b0e0)</locals>   </line>   <success></success>   <attributes>[[0, []]]</attributes> </start> <start>   <try>two 2@'test'</try>   <line>     <try>two 2@'test'</try>     <two>       <try> 2@'test'</try>       <success></success>       <attributes>[[2, [t, e, s, t]]]</attributes>     </two>     <success></success>     <attributes>[]</attributes><locals>(0x43b110)</locals>   </line>   <success></success>   <attributes>[[0, []]]</attributes> </start> 

you have been paying lot of attention in spirit class :)

there number of issues:

  1. the attribute declaration of line rule wrong:

    qi::rule<it, myline, skipper, qi::locals<rule<myline>*> > line; 

    needs

    qi::rule<it, myline(), skipper, qi::locals<rule<myline>*> > line; 
  2. automatic attribute propagation inhibited in presence of semantic actions. see recent answer more information: boost.spirit: parsing number char , string. need explicitely engage spirit's auto-rule behaviour using %=:

    line = keyword[_a = _1] >> qi::lazy(*_a); 

    nees be

    // note %= line %= omit [ keyword[_a = _1] ] >> qi::lazy(*_a); 

    notes:

    • the %= can go on string rule (no semantic actions imply automatic attribute propagation)
    • we need explicitely omit[] result of keyword match, because can't assign rule<>* our myline attribute

here's fixed version:

#define boost_spirit_debug #define boost_spirit_use_phoenix_v3 #include <boost/fusion/adapted/struct.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp>  namespace qi = boost::spirit::qi; namespace phx = boost::phoenix;  //------------------------------------------------------------------------------ // data structure struct myline {   myline()       : _n(0), _s("") {   }    myline(int n, std::string s)       : _n(n), _s(s) {   }    void set(int n, std::string s) {     _n = n;     _s = s;   }    int _n;   std::string _s; };  boost_fusion_adapt_struct(::myline, (int, _n) (std::string, _s))  //------------------------------------------------------------------------------ // parser grammar template<typename it, typename skipper = qi::space_type> struct parser: qi::grammar<it, myline(), skipper> {   parser()       : parser::base_type(start) {     using namespace qi;      start  = line;      string = qi::lexeme["'" >> *~qi::char_("'") >> "'"];      1    = (string >> "@" >> qi::int_) [_val           = phx::construct<myline>(_2, _1)];     2    = (qi::int_ >> "@" >> string);      keyword.add("one", &one)("two", &two);      // note %=     line %= omit [ keyword[_a = _1] ] >> qi::lazy(*_a);      on_error<fail>(         start,         std::cout << phx::val("error! expecting ") << _4         << phx::val(" here: \"") << phx::construct<std::string>(_3, _2)         << phx::val("\"\n"));      boost_spirit_debug_nodes((start)(line)(one)(two))   }  private:   template<typename attr> using rule = qi::rule<it, attr(), skipper>;    rule<myline> start, one, two;   qi::rule<it, myline(), skipper, qi::locals<rule<myline>* > > line;    rule<std::string> string;    qi::symbols<char, rule<myline>* > keyword; };  //------------------------------------------------------------------------------ int main() {   (const std::string input : std::vector<std::string> { "one 'test1'@1",                                                             "two 2@'test2'" }) {     auto f(std::begin(input)), l(std::end(input));     const static parser<decltype(f)> p;      myline parsed_script;     bool ok = qi::phrase_parse(f, l, p, qi::space, parsed_script);      if (!ok) {       std::cout << "invalid input\n";     }      std::cout << parsed_script._n << ": " << parsed_script._s << std::endl;      if (f != l) {       std::cout << "unparsed: '" << std::string(f, l) << "'" << std::endl;     }   } } 

prints:

<start>   <try>one 'test1'@1</try>   <line>     <try>one 'test1'@1</try>     <one>       <try> 'test1'@1</try>       <success></success>       <attributes>[[1, [t, e, s, t, 1]]]</attributes>     </one>     <success></success>     <attributes>[[1, [t, e, s, t, 1]]]</attributes><locals>(0x6386c0)</locals>   </line>   <success></success>   <attributes>[[1, [t, e, s, t, 1]]]</attributes> </start> 1: test1 <start>   <try>two 2@'test2'</try>   <line>     <try>two 2@'test2'</try>     <two>       <try> 2@'test2'</try>       <success></success>       <attributes>[[2, [t, e, s, t, 2]]]</attributes>     </two>     <success></success>     <attributes>[[2, [t, e, s, t, 2]]]</attributes><locals>(0x6386f0)</locals>   </line>   <success></success>   <attributes>[[2, [t, e, s, t, 2]]]</attributes> </start> 2: test2 

Comments

Popular posts from this blog

php - Calling a template part from a post -

Firefox SVG shape not printing when it has stroke -

How to mention the localhost in android -