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:
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;
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 onstring
rule (no semantic actions imply automatic attribute propagation) - we need explicitely
omit[]
result of keyword match, because can't assignrule<>*
ourmyline
attribute
- the
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
Post a Comment