Search code examples
c++parsingboostc++14boost-spirit-x3

eps parser that can fail for a "post-initialization" that could fail


I'm reading the Boost X3 Quick Start tutorial and noticed the line

eps is a special spirit parser that consumes no input but is always successful. We use it to initialize the rule's synthesized attribute, to zero before anything else. [...] Using eps this way is good for doing pre and post initializations.

Now I can't help but wonder if an eps_that_might_fail would be useful to do some sort of semantic/post analysis on a part of the parsed input, which could fail, to have some sort of locality of the check inside the grammar.

Is there a might-fail eps, and is it a good idea to do extra input verification using this construct?

A terrible example of what I'm trying to convey:

int_ >> eps_might_fail[is_prime]

This will only parse prime numbers, if I'm not mistaken, and allow for the full parser to fail at the point where it expects a prime number.


Solution

  • Semantic actions are intended for this.

    Spirit Qi

    The most natural example would be

     qi::int_ [ qi::_pass = is_prime(qi::_1) ]
    

    Be sure to use %= rule assignment in the presence of semantic actions, because without it, semantic actions disable automatic attribute propagation.

    You could, obviously, also be more verbose, and write

     qi::int_ >> qi::eps(is_prime(qi::_val))
    

    As you can see, that quoted documentation is slightly incomplete: eps can already take a parameter, in this case the lazy actor is_prime(qi::_val), that determines whether it succeeds of fails.

    Spirit X3

    In Spirit X3 the same mechanism applies, except that X3 doesn't integrate with Phoenix. This means two things:

    • on the up-side, we can just use core language features (lambdas) for semantic actions, making the learning curve less steep
    • on the downside, there's no 1-argument version of x3::eps that takes a lazy actor

    Here's a demo program with X3:

    Live On Coliru

    #include <boost/spirit/home/x3.hpp>
    
    namespace parser {
        using namespace boost::spirit::x3;
    
        auto is_ltua = [](auto& ctx) {
            _pass(ctx) = 0 == (_attr(ctx) % 42); 
        };
    
        auto start = int_ [ is_ltua ];
    
    }
    
    #include <iostream>
    
    int main() {
        for (std::string const txt : { "43", "42", "84", "85" }) {
            int data;
            if (parse(txt.begin(), txt.end(), parser::start, data))
                std::cout << "Parsed " << data << "\n";
            else
                std::cout << "Parse failed (" << txt << ")\n";
        }
    }
    

    Prints

    Parse failed (43)
    Parsed 42
    Parsed 84
    Parse failed (85)