boostorg / spirit

Boost.org spirit module
http://boost.org/libs/spirit
392 stars 161 forks source link

Custom error on rule level? #657

Closed arbinada-com closed 3 years ago

arbinada-com commented 3 years ago

Hello, is it possible to have a custom error handler on the rule level (for every rule in the limit) ? Cannot find this point in docs/examples. I.e.

DO WITH opt1 // should raise "Option opt1 is unknown"
DO WITH opt2 = 123 // should raise "Invalid value 123 for option opt2"
Kojoley commented 3 years ago

Did you miss https://www.boost.org/doc/libs/1_75_0/libs/spirit/doc/x3/html/spirit_x3/tutorials/error_handling.html?

arbinada-com commented 3 years ago

Thank you for answer, I didn't miss this link. However, this example is "one error handler for a whole parser" wheras I need "one handler for every rule".

Kojoley commented 3 years ago

I did not read the doc myself, maybe it is somewhat misleading, though examples (noting calc5/calc6) give a pretty much good picture.

this example is "one error handler for a whole parser" wheras I need "one handler for every rule"

That's not true, it shows how to attach an error handler to a particular rule placeholder. Probably it is not trivial to extrapolate from one to many, so here you are:

#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;

template <typename RuleID>
struct my_error_handler
{
    template <typename Iterator, typename Exception, typename Context>
    static x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const&, Context const&)
    {
        std::cerr << "(" << typeid(RuleID).name() << " error handler fired) ";
        return x3::error_handler_result::accept;
    }
};

struct rule_a : my_error_handler<rule_a> {};
struct rule_b : my_error_handler<rule_b> {};
struct rule_grammar : my_error_handler<rule_grammar> {};

x3::rule<rule_a> const a;
x3::rule<rule_b> const b;
x3::rule<rule_grammar> const grammar;

auto const a_def = x3::expect[x3::double_];
auto const b_def = x3::expect[x3::int_];
auto const grammar_def = 'a' >> a | 'b' >> b | x3::expect['x'];

BOOST_SPIRIT_DEFINE(a, b, grammar)

int main()
{
    std::vector<std::string> tests = { "a 1.23", "b 123", "a .e-z", "b .0", "x", "d" };
    for (auto& test: tests) {
        auto s = test.data(), e = s + test.size();
        std::cerr << "'" << test << "' ";
        if (!phrase_parse(s, e, grammar, x3::space)) {
            std::cerr << "parse error\n";
        }
        else {
            std::cerr << "ok\n";
        }
    }
}

To exclude any confusion, there are three different error handlers, each rule has its own.


There is also an p.on_error(f) method on every parser, though I do not know if it is documented.

arbinada-com commented 3 years ago

Thank you for example, it's much more comprehensive now. Additional questions:

Kojoley commented 3 years ago

I think these questions are covered in docs.

what is recommended way to inject some external dependency in error handler (i.e. custom message collector)?

x3::with<tag>(data)[p] directive.

how to acces to the last lexeme, the line number and the position of error?

x3::error_handler<iterator_type> implements such functionality, you can roll your own solution if it does not satisfy your needs.