Composite Grammar in Boost :: Spirit

I have the following grammar that works as expected.

struct query_term { std::string term; bool is_tag; query_term(const std::string &a, bool tag = false): term(a), is_tag(tag) { } }; template<typename Iterator> struct query_grammar: grammar<Iterator, std::vector<query_term>(), space_type> { query_grammar(): query_grammar::base_type(query) { word %= +alnum; tag = (omit[word >> ':'] >> word[_val = phoenix::construct<query_term>(_1, true)]); non_tag = word[_val = phoenix::construct<query_term>(_1, false)]; query = ( (omit[word >> ':'] >> word[push_back(_val, phoenix::construct<query_term>(_1, true))]) | word[push_back(_val, phoenix::construct<query_term>(_1)) ] ) % space; }; qi::rule<Iterator, std::string(), space_type> word; qi::rule<Iterator, query_term, space_type> tag; qi::rule<Iterator, query_term, space_type> non_tag; qi::rule<Iterator, std::vector<query_term>(), space_type> query; }; 

But when I replace the request

 query = ( tag[phoenix::push_back(_val, _1)] | word[push_back(_val, phoenix::construct<query_term>(_1)) ] ) % space; 

the code does not compile. I am basically trying to break down the grammar into components that can be reused in a larger grammar. When parsing words or tags, create a query_term object with the appropriate flag in the tag and word rule. Reuse these attributes in the query rule.

In the previous version, tag rules and words are embedded in the query grammar.

I'm not sure what I am missing here. Any help would be greatly appreciated.

FYI: This is not the final code. I am trying to use rules before using them in production code.

Thanx, - baliga

+4
source share
1 answer

The real problem is that you define the attribute for the tag / non_tag rules as query_term (instead of query_term() ).

Some minor issues are as follows:

  • using word instead of non_tag (provides std :: string, which does not convert to query_type )
  • using % space with a space skipper doesn't make sense
  • you probably wanted lexeme in the word rule, because otherwise it will just keep the "eat" characters regardless of spaces.

Other offers:

  • avoid excessive volume using namespace (or completely avoid). You will encounter conflicts with hard or difficult fixes (for example, boost :: cref vs. std :: cref, std :: string vs. qi :: string, etc.).

  • Try to stay away from using Phoenix. In this case, I think it will be much easier for you to use qi::attr with an adapted structure.

  • use the BOOST_SPIRIT_DEBUG_ * macros to get an idea of ​​your parser

Here is the whole grammar, as I would suggest:

 template<typename Iterator> struct query_grammar: qi::grammar<Iterator, std::vector<query_term>(), qi::space_type> { query_grammar() : query_grammar::base_type(query) { using namespace qi; word = lexeme[ +alnum ]; tag = omit[word >> ':'] >> word >> attr(true); non_tag = word >> attr(false); query = *(tag | non_tag); }; qi::rule<Iterator, std::string() , qi::space_type> word; qi::rule<Iterator, query_term() , qi::space_type> tag, non_tag; qi::rule<Iterator, std::vector<query_term>(), qi::space_type> query; }; 

A fully working example with output (trivially onelined using karma):

 // #define BOOST_SPIRIT_DEBUG #include <boost/fusion/adapted/struct.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/karma.hpp> namespace qi = boost::spirit::qi; namespace karma = boost::spirit::karma; struct query_term { std::string term; bool is_tag; }; BOOST_FUSION_ADAPT_STRUCT(query_term, (std::string,term)(bool,is_tag)); template<typename Iterator> struct query_grammar: qi::grammar<Iterator, std::vector<query_term>(), qi::space_type> { query_grammar() : query_grammar::base_type(query) { using namespace qi; word = lexeme[ +alnum ]; tag = omit[word >> ':'] >> word >> attr(true); non_tag = word >> attr(false); query = *(tag | non_tag); BOOST_SPIRIT_DEBUG_NODE(word); BOOST_SPIRIT_DEBUG_NODE(tag); BOOST_SPIRIT_DEBUG_NODE(non_tag); BOOST_SPIRIT_DEBUG_NODE(query); }; qi::rule<Iterator, std::string() , qi::space_type> word; qi::rule<Iterator, query_term() , qi::space_type> tag, non_tag; qi::rule<Iterator, std::vector<query_term>(), qi::space_type> query; }; int main() { const std::string input = "apple tag:beer banana grape"; typedef std::string::const_iterator It; query_grammar<It> parser; std::vector<query_term> data; It f(input.begin()), l(input.end()); bool ok = qi::phrase_parse(f, l, parser, qi::space, data); if (ok) std::cout << karma::format(karma::delimit [ karma::auto_ ] % karma::eol, data) << '\n'; if (f!=l) std::cerr << "Unparsed: '" << std::string(f,l) << "'\n"; return ok? 0 : 255; } 

Conclusion:

 apple false beer true banana false grape false 
+4
source

Source: https://habr.com/ru/post/1403210/


All Articles