Search code examples
c++boostboost-spirit-qi

How to make the rule fail if the right side of operator ">>" fails, in boost spirit qi


To keep it simple let's say I need to parse A followed by B

I don't mean the letter, of course, A and B could be (qi::char_), or (qi::char_('$') | qi::char_('_') | ascii::alpha), or whatever.

so:

rule = A >> B;

Problem is that if B fails, rule doesn't fail, I get A. I tried with +B but the result was the same. And:

rule = A > B;

Throws and exception if it fails.

UPDATE: I edited this question to explain that the above statements are false (as user sehe proved), I believed that the rule wasn't failing because when I tried to:

auto A = qi::copy(qi::char_("$") | qi::alpha);
auto B = qi::copy(qi::char_("z"));
auto C = qi::copy(qi::string("$x"));
std::string s1 = "$x";
std::string s2 = "";
qi::parse(s1.begin(), s1.end(), (A >> B) | C, s2);

I would get that s2="$$x" , so I thought that A was not failing. Now I know that this is a different problem. But the rule does fail.


Solution

  • Q. Problem is that if B fails, rule doesn't fail, I get A

    A. That's not true, see extensive demo below


    Q. it could be followed by E, but I don't want to get CE

    A. In the test cases below look for positive assertion (C >> &E would do here)


    This is because I'm trying to parse keywords and identifiers, so int is a keyword, int4 is identifier. Therefore keyword is a string I define that is not followed by digits or letters. Any idea?

    Yes, see:

    Live Coliru

    #include <boost/spirit/include/qi.hpp>
    
    namespace qi = boost::spirit::qi;
    
    template <typename Grammar>
    bool check(Grammar const& g, std::string const& input, qi::unused_type) {
        auto f = input.begin(), l = input.end();
        try {
            return qi::parse(f, l, g);
        } catch(...) {
            return false;
        }
    }
    
    template <typename Grammar, typename Skipper>
    bool check(Grammar const& g, std::string const& input, Skipper const& s) {
        auto f = input.begin(), l = input.end();
        try {
            return qi::phrase_parse(f, l, g, s);
        } catch(...) {
            return false;
        }
    }
    
    #define REPORT(g, i, s, expect) do { assert(expect == check(g, i, s)); } while(0)
    #define SHOULD_WORK(g, i, s) REPORT(g, i, s, true)
    #define SHOULD_FAIL(g, i, s) REPORT(g, i, s, false)
    
    template <typename Skipper = qi::unused_type>
    void do_all_tests(Skipper const& s = Skipper()) {
    
        auto A = qi::copy(qi::char_("$_") | qi::alpha);
        auto B = qi::copy(qi::char_("z"));
    
        // using skipper:
        SHOULD_WORK(A >> B , "$z", s);
        SHOULD_FAIL(A >> B , "$.", s);
        SHOULD_FAIL(A >> B , "$" , s);
    
        SHOULD_WORK(A  > B , "$z", s);
        SHOULD_FAIL(A  > B , "$.", s);
        SHOULD_FAIL(A  > B , "$" , s);
    
        // positive assertion (does not consume B)
        SHOULD_WORK(A >> &B, "$z", s);
        SHOULD_FAIL(A >> &B, "$.", s);
        SHOULD_FAIL(A >> &B, "$" , s);
    
        SHOULD_WORK(A  > &B, "$z", s);
        SHOULD_FAIL(A  > &B, "$.", s);
        SHOULD_FAIL(A  > &B, "$" , s);
    
        // negative assertion:
        SHOULD_FAIL(A >> !B, "$z", s);
        SHOULD_WORK(A >> !B, "$.", s);
        SHOULD_WORK(A >> !B, "$" , s);
    
        SHOULD_FAIL(A  > !B, "$z", s);
        SHOULD_WORK(A  > !B, "$.", s);
        SHOULD_WORK(A  > !B, "$" , s);
    
    }
    
    int main() {
        do_all_tests(qi::unused); // no skipper
        do_all_tests(qi::space);
        std::cout << "All tests succeeded\n";
    }
    

    Prints

    All tests succeeded