1

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.

rubenvb
  • 74,642
  • 33
  • 187
  • 332

1 Answers1

4

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)
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Ouch I noticed X3 late. It's no doubt similar let met try. – sehe Jul 11 '16 at 09:28
  • 1
    Extended the answer with X3 demo, difference and rationales! [Live On Coliru](http://coliru.stacked-crooked.com/a/e7281f5ec19b6190) – sehe Jul 11 '16 at 11:42
  • Awesome answer, thanks :). I find it hard to find good resources on X3 (there seem to be none except the cppnow2015 slides and documentation...). So thanks for being a valuable resource :D. – rubenvb Jul 11 '16 at 12:57