foonathan / lexy

C++ parsing DSL
https://lexy.foonathan.net
Boost Software License 1.0
991 stars 66 forks source link

Confusing error messages. #136

Closed rkaminsk closed 1 year ago

rkaminsk commented 1 year ago

The following example produces confusing error messages. Is some kind of error recovery happening treating the empty second argument of + as an expression? It would be more helpful, if the second part of the error message is just listed as context (or not at all in this case).

#include <lexy/dsl.hpp>
#include <lexy/input/string_input.hpp>
#include <lexy/action/validate.hpp>
#include <lexy_ext/report_error.hpp>

#include <catch2/catch_test_macros.hpp>

namespace example {

namespace dsl = lexy::dsl;

struct id : lexy::token_production {
    static constexpr auto rule = []() {
        auto head = dsl::ascii::lower;
        auto tail = dsl::ascii::alpha_underscore;
        auto id = dsl::identifier(head, tail);

        return id;
    }();
};

struct nested_expr : lexy::transparent_production {
    static constexpr auto rule = dsl::recurse<struct expr>;
};

struct expr : lexy::expression_production {
    struct expected_expr {
        static constexpr auto name = "expected expr";
    };

    static constexpr auto atom = dsl::p<id> | dsl::error<expected_expr>;

    struct math_sum : dsl::infix_op_right {
        static constexpr auto op = dsl::op<0>(LEXY_LIT("+"));
        using operand = dsl::atom;
    };

    using operation = math_sum;
};

struct stm {
    static constexpr auto whitespace = dsl::ascii::space | dsl::newline;
    static constexpr auto rule = dsl::p<expr> + dsl::lit_c<';'>;
};

}

TEST_CASE("example") {
    auto input = lexy::zstring_input("a + 5");
    auto res = lexy::validate<example::stm>(input, lexy_ext::report_error);
    REQUIRE(res.is_error());
}

The output is

error: while parsing expr
     |
   1 | a + 5
     |     ^ expected expr
error: while parsing stm
     |
   1 | a + 5
     |     ^ expected ';'
foonathan commented 1 year ago

Playground link to the trace: https://lexy.foonathan.net/playground/?id=ExfrcWWor&mode=trace

I'm not sure what you want exactly: It tries to parse 5 as an expression, which then fails, so it gets your custom "expected expr" error message. It then cancels that and tries to finish the statement, but is missing a semicolon for that.

rkaminsk commented 1 year ago

It then cancels that and tries to finish the statement, but is missing a semicolon for that.

My point is that the second error message does not really help the user. The actual error would not be fixed, even if a semicolon would be used instead of a 5. That's why I wrote that the second part is confusing.

Not sure what would be the best way to handle this in practice. Maybe report the first error, then implement some custom form of error recovery and only after successful recovery report further errors. I have not yet looked at error recovery with lexy in detail yet. I only skimmed some examples.

foonathan commented 1 year ago

I'm closing this as the library can't really do anything else here.