How to do error handling work for boost :: spirit

in boost :: spirit, I added error handling code based on the roman example.

#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix_operator.hpp> #include <boost/spirit/include/phoenix_core.hpp> #include <boost/spirit/include/phoenix_fusion.hpp> #include <boost/spirit/include/phoenix_object.hpp> #include <boost/foreach.hpp> #include <iostream> #include <fstream> #include <string> #include <vector> namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; namespace phoenix = boost::phoenix; template <typename Iterator> struct roman : qi::grammar<Iterator> { roman() : roman::base_type(start) { using qi::eps; using qi::lit; using qi::lexeme; using qi::_val; using qi::_1; using ascii::char_; // for on_error using qi::on_error; using qi::fail; using phoenix::construct; using phoenix::val; start = +(lit('M') ) >> "</>"; on_error<fail> ( start , std::cout << val("Error! Expecting ") // << _4 // what failed? << val(" here: \"") // << construct<std::string>(_3, _2) // iterators to error-pos, end << val("\"") << std::endl ); } qi::rule<Iterator> start; }; int main() { std::cout << "/////////////////////////////////////////////////////////\n\n"; std::cout << "\t\tRoman Numerals Parser\n\n"; std::cout << "/////////////////////////////////////////////////////////\n\n"; std::cout << "Type a Roman Numeral ...or [q or Q] to quit\n\n"; typedef std::string::const_iterator iterator_type; typedef roman<iterator_type> roman; roman roman_parser; // Our grammar std::string str; unsigned result; while (std::getline(std::cin, str)) { if (str.empty() || str[0] == 'q' || str[0] == 'Q') break; std::string::const_iterator iter = str.begin(); std::string::const_iterator end = str.end(); //[tutorial_roman_grammar_parse bool r = parse(iter, end, roman_parser, result); if (r && iter == end) { std::cout << "-------------------------\n"; std::cout << "Parsing succeeded\n"; std::cout << "result = " << result << std::endl; std::cout << "-------------------------\n"; } else { std::string rest(iter, end); std::cout << "-------------------------\n"; std::cout << "Parsing failed\n"; std::cout << "stopped at: \": " << rest << "\"\n"; std::cout << "-------------------------\n"; } //] } std::cout << "Bye... :-) \n\n"; return 0; } 

my questions:

  • "on_error" does not start, why?
  • I comment "<<_4" if I want to print part of the failure, how to do it?
+4
source share
1 answer

Three steps:

  • Tick โ€‹โ€‹placeholders:

     on_error<fail>(start, std::cout << val("Error! Expecting ") << qi::_4 << val(" here: \"") << construct<std::string>(qi::_3, qi::_2) << val("\"") << std::endl ); 
  • You also need to make sure that you have waiting points in order to call the error handler.

     start = eps > +(lit('M') ) >> "</>"; 

    See Boost.Spirit.Qi - Errors at the beginning of a rule for an explanation.

  • (optional) What are your rules

     start.name("start"); 

    Using BOOST_SPIRIT_DEBUG_NODE (S) is another way to implicitly name your rules.

See Live on Coliru (cleansed and simplified in places)

Now it prints (input iv ):

 Error! Expecting <sequence>"M""</>" here: 'iv' Parsing failed stopped at: 'iv' 

Full code

 #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> #include <iostream> #include <fstream> namespace qi = boost::spirit::qi; namespace phx = boost::phoenix; template <typename Iterator> struct roman : qi::grammar<Iterator> { roman() : roman::base_type(start) { using namespace qi; start = eps > +lit('M') >> "</>"; start.name("start"); on_error<fail>(start, phx::ref(std::cout) << "Error! Expecting " << qi::_4 << " here: '" << phx::construct<std::string>(qi::_3, qi::_2) << "'\n" ); } qi::rule<Iterator> start; }; int main() { typedef std::string::const_iterator iterator_type; roman<iterator_type> roman_parser; // Our grammar std::string str; while (std::getline(std::cin, str)) { if (str.empty() || str[0] == 'q' || str[0] == 'Q') break; iterator_type iter = str.begin(), end = str.end(); unsigned result; bool r = parse(iter, end, roman_parser, result); if (r && iter == end) { std::cout << "Parsing succeeded\n"; std::cout << "result = " << result << std::endl; } else { std::string rest(iter, end); std::cout << "Parsing failed\n"; std::cout << "stopped at: '" << rest << "'\n"; } } } 

In addition to the comment: This is what I tested with - I have not done it yet, but the error handler starts and feeds as it should. Maybe this can help?

 static auto const at_eol = (*_1 == '\r') || (*_1 == '\n'); static auto const at_eoi = (_1 == _2); on_error<retry>(start, ( (phx::ref(std::cout) << "rule start: expecting " << _4 << " here: '" << escape_(_3, _2) << "'\n"), phx::while_ (!at_eoi && !at_eol) [ ++_1, phx::ref(std::cout) << "\nadvance to newline\n" ], phx::while_ (!at_eoi && at_eol) [ ++_1, phx::ref(std::cout) << "\neat newline\n" ], phx::if_ (at_eoi) [ _pass = fail ] ) ); 

See also the note in the Important section of the documentation for multi_pass <>

+7
source

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


All Articles