How can I extend the grammar of the spirit of enhancement

The fact is that I made a grammar that was useful for the task, but now the task has changed, and I need to define new rules.

But I would not want to change the grammar that I already have, instead of creating a new grammar that uses the existing grammar, I do not have duplication code, so I just need to define new rules that I need. I tried like this, but it does not work:

struct New_grammar : Old_grammar<Iterator, Skipper> { New_grammar() : New_grammar::base_type(Command_list) { Command_list %= qi::eps >> + Commands; Comandos %= oneoldCommand | NewCommand; NewCommand = ("NewCommand" >> stmt)[qi::_val = phoenix::new_<NewCom>(qi::_1)]; } // this is a new rule I need: qi::rule<Iterator, Commands*(), qi::locals<std::string>, Skipper> NewCommand; }; 

Basically Old_grammar has a grammar that I already have, and I just want to add a new rule that I need in New_grammar , and also be able to use the rules and grammars that I already have in Old_gramar .

+4
source share
1 answer

I would not complicate matters by inheriting. The composition is often more than enough, and it will not confuse the interface of par-qi.

I developed a small example of how versioning grammar could be used. Suppose the old grammar:

 template <typename It, typename Skipper> struct OldGrammar : qi::grammar<It, Skipper, std::string()> { OldGrammar() : OldGrammar::base_type(mainrule) { using namespace qi; rule1 = int_(1); // expect version 1 rule2 = *char_; // hopefully some interesting grammar mainrule = omit [ "version" > rule1 ] >> rule2; } private: qi::rule<It, Skipper, std::string()> mainrule; qi::rule<It, Skipper, int()> rule1; qi::rule<It, Skipper, std::string()> rule2; }; 

As you can see, this was pretty restrictive, requiring the version to be exactly 1. However, the future happened and a new version of the grammar was invented. Now i will add

 friend struct NewGrammar<It, Skipper>; 

to the old grammar and proceed with the implementation of the new grammar, which kindly returns to the old grammar, if necessary:

 template <typename It, typename Skipper> struct NewGrammar : qi::grammar<It, Skipper, std::string()> { NewGrammar() : NewGrammar::base_type(mainrule) { using namespace qi; new_rule1 = int_(2); // support version 2 now new_start = omit [ "version" >> new_rule1 ] >> old.rule2; // note, no expectation point mainrule = new_start | old.mainrule; // or fall back to version 1 grammar } private: OldGrammar<It, Skipper> old; qi::rule<It, Skipper, std::string()> new_start, mainrule; qi::rule<It, Skipper, int()> new_rule1; }; 

(I did not try to get it to work with inheritance, although in all likelihood it should work as well.)

Check out this baby:

 template <template <typename It,typename Skipper> class Grammar> bool test(std::string const& input) { auto f(input.begin()), l(input.end()); static const Grammar<std::string::const_iterator, qi::space_type> p; try { return qi::phrase_parse(f,l,p,qi::space) && (f == l); // require full input consumed } catch(...) { return false; } // qi::expectation_failure<> } int main() { assert(true == test<OldGrammar>("version 1 woot")); assert(false == test<OldGrammar>("version 2 nope")); assert(true == test<NewGrammar>("version 1 woot")); assert(true == test<NewGrammar>("version 2 woot as well")); } 

All tests pass, obviously: see him live on Coliru 1 Hope this helps!


1 Well, hell. Kolira is too slow to compile it today. So here is the complete test program:

 #define BOOST_SPIRIT_DEBUG #include <boost/spirit/include/qi.hpp> namespace qi = boost::spirit::qi; template <typename It, typename Skipper> struct NewGrammar; // forward declare for friend declaration template <typename It, typename Skipper> struct OldGrammar : qi::grammar<It, Skipper, std::string()> { friend struct NewGrammar<It, Skipper>; // NOTE OldGrammar() : OldGrammar::base_type(mainrule) { using namespace qi; rule1 = int_(1); // expect version 1 rule2 = *char_; // hopefully some interesting grammar mainrule = omit [ "version" > rule1 ] >> rule2; BOOST_SPIRIT_DEBUG_NODE(mainrule); BOOST_SPIRIT_DEBUG_NODE(rule1); BOOST_SPIRIT_DEBUG_NODE(rule2); } private: qi::rule<It, Skipper, std::string()> mainrule; qi::rule<It, Skipper, int()> rule1; qi::rule<It, Skipper, std::string()> rule2; }; template <typename It, typename Skipper> struct NewGrammar : qi::grammar<It, Skipper, std::string()> { NewGrammar() : NewGrammar::base_type(mainrule) { using namespace qi; new_rule1 = int_(2); // support version 2 now new_start = omit [ "version" >> new_rule1 ] >> old.rule2; // note, no expectation point mainrule = new_start | old.mainrule; // or fall back to version 1 grammar BOOST_SPIRIT_DEBUG_NODE(new_start); BOOST_SPIRIT_DEBUG_NODE(mainrule); BOOST_SPIRIT_DEBUG_NODE(new_rule1); } private: OldGrammar<It, Skipper> old; qi::rule<It, Skipper, std::string()> new_start, mainrule; qi::rule<It, Skipper, int()> new_rule1; }; template <template <typename It,typename Skipper> class Grammar> bool test(std::string const& input) { auto f(input.begin()), l(input.end()); static const Grammar<std::string::const_iterator, qi::space_type> p; try { return qi::phrase_parse(f,l,p,qi::space) && (f == l); // require full input consumed } catch(...) { return false; } // qi::expectation_failure<> } int main() { assert(true == test<OldGrammar>("version 1 woot")); assert(false == test<OldGrammar>("version 2 nope")); assert(true == test<NewGrammar>("version 1 woot")); assert(true == test<NewGrammar>("version 2 woot as well")); } 
+8
source

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


All Articles