// test case for strange BOOST_SPIRIT_DEBUG interaction for qi+lexer combination // My grammar contains a non-fatal bug. In the course of debugging it I found that // the parser behavior changes when BOOST_SPIRIT_DEBUG is defined, making it difficult // to debug. // Jeff Trull #include #include #include #include #include // if this gets defined, my parse passes and all input is consumed #define BOOST_SPIRIT_DEBUG // if it is not defined, my parse passes but not all input is consumed #include #include #include // typedef for stream iterator we will use typedef boost::spirit::istream_iterator LefDefIter; // lexer needs the iterator type and a list of token attribute types typedef boost::spirit::lex::lexertl::position_token > LefDefToken; typedef boost::spirit::lex::lexertl::actor_lexer LefDefLexer; enum TokenIds { T_NAMESCASESENSITIVE = 1000, T_ON, T_OFF, T_END, T_ANY }; template struct LefTokens : boost::spirit::lex::lexer { LefTokens() : double_("-?[0-9]+(\\.[0-9]+)?") { namespace lex = boost::spirit::lex; using boost::phoenix::ref; ident = "[a-zA-Z][a-zA-Z0-9_]+"; this->self = lex::string("NAMESCASESENSITIVE", T_NAMESCASESENSITIVE) | lex::string("ON", T_ON) | lex::string("OFF", T_OFF) | lex::string("END", T_END) | ident | double_ | '+' | '-' | '(' | ')' | ';' ; // whitespace this->self += lex::string("([ \\t\\n]+)") [ lex::_pass = lex::pass_flags::pass_ignore ]; // catchall (mostly for error processing) this->self += lex::string(".", T_ANY); } // identifiers have a string attribute boost::spirit::lex::token_def ident; // numbers boost::spirit::lex::token_def double_; }; template struct lefparser : boost::spirit::qi::grammar { template lefparser(TokenDef const& tok) : lefparser::base_type(lef_file) { namespace qi = boost::spirit::qi; using boost::phoenix::ref; // for storing parsing info into parser var (instead of attributes) // here's MY bug: ">" binds more tightly than "|" // the ON/OFF options need parens // it's in trying to debug this that I noticed the BOOST_SPIRIT_DEBUG problem casesens = qi::raw_token(T_NAMESCASESENSITIVE) > qi::raw_token(T_ON)[ref(case_on) = true] | qi::raw_token(T_OFF)[ref(case_on) = false] > ';' ; lef_file = -casesens >> -qi::raw_token(T_END) ; BOOST_SPIRIT_DEBUG_NODE(casesens); BOOST_SPIRIT_DEBUG_NODE(lef_file); } bool case_on; boost::spirit::qi::rule lef_file; boost::spirit::qi::rule casesens; }; int main() { LefTokens lefTokens; lefparser::iterator_type, LefTokens::lexer_def > lefParser(lefTokens); std::stringstream testlef("NAMESCASESENSITIVE ON ;\nEND\n"); testlef.unsetf(std::ios::skipws); LefDefIter beg(testlef), end; LefTokens::iterator_type it = lefTokens.begin(beg, LefDefIter()); LefTokens::iterator_type lex_end = lefTokens.end(); if (!parse(it, lex_end, lefParser)) { std::cerr << "parse failed\n"; return 1; } else if (it != lex_end) { std::cerr << "Not all input consumed. " << std::distance(it, lex_end) << " tokens remain:" << std::endl; for (auto tok_it = it; tok_it != lex_end; ++tok_it) { std::cerr << "|" << boost::get >(tok_it->value()) << "|" << std::endl; } return 1; } std::cerr << "parse passed\n"; return 0; }