From f88ac50a4434f6fccc71e0284d550b71d11e91dd Mon Sep 17 00:00:00 2001 From: mateuszb Date: Sun, 20 Mar 2011 18:09:55 +0000 Subject: [PATCH] * improved ERM parser but there is something wrong with printing function or parser --- lib/ERMParser.cpp | 297 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 255 insertions(+), 42 deletions(-) diff --git a/lib/ERMParser.cpp b/lib/ERMParser.cpp index 85e6244d3..62e5c185c 100644 --- a/lib/ERMParser.cpp +++ b/lib/ERMParser.cpp @@ -6,10 +6,13 @@ #include #include #include +#include #include namespace spirit = boost::spirit; namespace qi = boost::spirit::qi; +namespace ascii = spirit::ascii; +namespace phoenix = boost::phoenix; ERMParser::ERMParser(std::string file) :srcFile(file) @@ -56,54 +59,264 @@ void callme(char const& i) std::cout << "fd"; } + + +namespace ERM +{ + + typedef std::vector identifierT; + + struct triggerT + { + std::string name; + boost::optional identifier; + boost::optional condition; + }; + + //a dirty workaround for preprocessor magic that prevents the use types with comma in it in BOOST_FUSION_ADAPT_STRUCT + //see http://comments.gmane.org/gmane.comp.lib.boost.user/62501 for some info + // + //moreover, I encountered a quite serious bug in boost: http://boost.2283326.n4.nabble.com/container-hpp-111-error-C2039-value-type-is-not-a-member-of-td3352328.html + //not sure how serious it is... + + typedef boost::variant bodyItem; + typedef std::vector bodyTbody; + + struct instructionT + { + std::string name; + boost::optional identifier; + boost::optional condition; + bodyTbody body; + }; + + struct receiverT + { + std::string name; + boost::optional identifier; + boost::optional condition; + bodyTbody body; + }; + + struct postOBtriggerT + { + boost::optional identifier; + boost::optional condition; + }; + + typedef boost::variant< + triggerT, + instructionT, + receiverT, + postOBtriggerT, + qi::unused_type + > + commandT; + + typedef boost::variant lineT; + + + //console printer + + void identifierPrinter(const identifierT & id) + { + BOOST_FOREACH(int x, id) + { + tlog2 << "\\" << x; + } + } + + struct CommandPrinter : boost::static_visitor<> + { + void operator()(triggerT const& trig) const + { + tlog2 << "trigger: " << trig.name << " identifier: "; + identifierPrinter(*trig.identifier); + tlog2 << " condition: " << *trig.condition; + + } + + void operator()(instructionT const& trig) const + { + tlog2 << "instruction: " << trig.name << " identifier: "; + identifierPrinter(*trig.identifier); + tlog2 << " condition: " << *trig.condition; + tlog2 << " body items:"; + BOOST_FOREACH(bodyItem bi, trig.body) + { + tlog2 << " " << bi; + } + } + + void operator()(receiverT const& trig) const + { + tlog2 << "receiver: " << trig.name << " identifier: "; + identifierPrinter(*trig.identifier); + tlog2 << " condition: " << *trig.condition; + + } + + void operator()(postOBtriggerT const& trig) const + { + tlog2 << "post OB trigger; " << " identifier: "; + identifierPrinter(*trig.identifier); + tlog2 << " condition: " << *trig.condition; + + } + + void operator()(qi::unused_type const& text) const + { + //tlog2 << "Empty line/comment line" << std::endl; + } + + }; + + struct ERMprinter : boost::static_visitor<> + { + void operator()(commandT const& cmd) const + { + CommandPrinter()(cmd); + + } + + void operator()(qi::unused_type const& nothing) const + { + //tlog2 << "Empty line/comment line" << std::endl; + } + }; + + void printLineAST(const lineT & ast) + { + tlog2 << ""; + ast.apply_visitor(ERMprinter()); + } +} + +BOOST_FUSION_ADAPT_STRUCT( + ERM::triggerT, + (std::string, name) + (boost::optional, identifier) + (boost::optional, condition) + ) + +BOOST_FUSION_ADAPT_STRUCT( + ERM::instructionT, + (std::string, name) + (boost::optional, identifier) + (boost::optional, condition) + (ERM::bodyTbody, body) + ) + +BOOST_FUSION_ADAPT_STRUCT( + ERM::receiverT, + (std::string, name) + (boost::optional, identifier) + (boost::optional, condition) + (ERM::bodyTbody, body) + ) + +BOOST_FUSION_ADAPT_STRUCT( + ERM::postOBtriggerT, + (boost::optional, identifier) + (boost::optional, condition) + ) + +namespace ERM +{ + template + struct ERM_grammar : qi::grammar + { + ERM_grammar() : ERM_grammar::base_type(rline, "ERM script line") + { + comment = *(qi::char_); + commentLine = ~qi::char_('!') >> comment; + cmdName %= +qi::char_/*qi::repeat(2)[qi::char_]*/; + identifier %= (qi::int_ % qi::lit('/')); + condition %= qi::lit('&') >> +qi::char_("0-9a-zA-Z&/|<>=") >> qi::lit(":;"); + trigger %= cmdName >> -identifier >> -condition; ///// + string %= qi::lexeme['^' >> +(qi::char_ - '^') >> '^']; + body %= *( qi::char_("a-zA-Z0-9/ ") | string); + instruction %= cmdName >> -identifier >> -condition >> body; + receiver %= cmdName >> -identifier >> -condition >> body; + postOBtrigger %= qi::lit("$OB") >> -identifier >> -condition; + command %= (qi::char_('!') >> + ( + (qi::char_('?') >> trigger) | + (qi::char_('!') >> instruction) | + (qi::char_('#') >> receiver) | + postOBtrigger + ) >> comment + ); + + rline %= + ( + command | commentLine | spirit::eoi + ); + + //error handling + + string.name("string constant"); + comment.name("comment"); + commentLine.name("comment line"); + cmdName.name("name of a command"); + identifier.name("identifier"); + condition.name("condition"); + trigger.name("trigger"); + body.name("body"); + instruction.name("instruction"); + receiver.name("receiver"); + postOBtrigger.name("post OB trigger"); + command.name("command"); + rline.name("script line"); + + qi::on_error + ( + rline + , std::cout //or phoenix::ref(std::count), is there any difference? + << phoenix::val("Error! Expecting ") + << qi::_4 // what failed? + << phoenix::val(" here: \"") + << phoenix::construct(qi::_3, qi::_2) // iterators to error-pos, end + << phoenix::val("\"") + << std::endl + ); + + } + + qi::rule string; + + qi::rule comment; + qi::rule commentLine; + qi::rule cmdName; + qi::rule identifier; + qi::rule condition; + qi::rule trigger; + qi::rule body; + qi::rule instruction; + qi::rule receiver; + qi::rule postOBtrigger; + qi::rule command; + qi::rule rline; + }; +}; + void ERMParser::parseLine( std::string line ) { - namespace ascii = spirit::ascii; - namespace phoenix = boost::phoenix; - typedef std::string::iterator sit; - qi::rule comment = *(qi::char_); - qi::rule commentLine = ~qi::char_('!') >> comment; - qi::rule cmdName = qi::repeat(2)[ascii::alpha]; - qi::rule identifier = qi::int_ % '/'; - qi::rule condition = qi::char_('&') >> +(qi::int_ | qi::char_("a-zA-Z&/|")) >> qi::char_(":;"); - qi::rule trigger = cmdName >> -identifier >> -condition; - qi::rule string = qi::char_('^') >> ascii::print >> qi::char_('^'); - qi::rule body = *(qi::char_("a-zA-Z0-9/ ") | string); - qi::rule instruction = cmdName >> -identifier >> -condition >> body; - qi::rule receiver = cmdName >> -identifier >> -condition >> body; - qi::rule postOBtrigger = "$OB" >> -identifier >> -condition; - - qi::rule rline = - ( - (qi::char_('!') >> - ( - (qi::char_('?') >> trigger) | - (qi::char_('!') >> instruction) | - (qi::char_('#') >> receiver) | - postOBtrigger - ) >> comment - ) - | commentLine | spirit::eoi - ); - - qi::on_error - ( - rline - , std::cout //or phoenix::ref(std::count), is there any difference? - << phoenix::val("Error! Expecting ") - << qi::_4 // what failed? - << phoenix::val(" here: \"") - << phoenix::construct(qi::_3, qi::_2) // iterators to error-pos, end - << phoenix::val("\"") - << std::endl - ); - - sit beg = line.begin(), + std::string::const_iterator beg = line.begin(), end = line.end(); - bool r = qi::parse(beg, end, rline); + + ERM::ERM_grammar ERMgrammar; + ERM::lineT AST; + + bool r = qi::parse(beg, end, ERMgrammar, AST); if(!r || beg != end) { tlog1 << "Parse error for line " << line << std::endl; tlog1 << "\tCannot parse: " << std::string(beg, end) << std::endl; } + else + { + //parsing succeeded + //ERM::printLineAST(AST); + } }