how to make error handling work for boost::spirit

后端 未结 1 534
不思量自难忘°
不思量自难忘° 2021-01-18 18:01

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

#include 
#include 

        
相关标签:
1条回答
  • 2021-01-18 18:20

    Three steps:

    1. Qualify the placeholders:

      on_error<fail>(start, 
              std::cout
                 << val("Error! Expecting ")
                 << qi::_4
                 << val(" here: \"")
                 << construct<std::string>(qi::_3, qi::_2)
                 << val("\"")
                 << std::endl
          );
      
    2. You'll also need to make sure you have expectation points to trigger the error handler.

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

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

    3. (optionally) Name your rules

      start.name("start");
      

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

    See it Live on Coliru (cleaned up 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 something I've been testing with - haven't exactly made it to work yet, but the error handler is getting invoked and eating input as it should. Maybe it could be of 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 under Important in the documentation for multi_pass<>

    0 讨论(0)
提交回复
热议问题