diff --git a/include/iris/x4/operator/difference.hpp b/include/iris/x4/operator/difference.hpp index b81144b12..899b0df4e 100644 --- a/include/iris/x4/operator/difference.hpp +++ b/include/iris/x4/operator/difference.hpp @@ -33,27 +33,28 @@ struct difference : binary_parser> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const { - // Try Right first - It start = first; + // Try `Right` + It const orig_first = first; if (this->right.parse(first, last, ctx, unused)) { - // Right succeeds, we fail. - first = start; + // `Right` succeeds, we fail. + first = orig_first; return false; } - if constexpr (has_context_v) { // In case of `Left - expect[r]`, // if Right yielded expectation error, // the whole difference expression (*this) should also yield error. - // In other words, when the THROW macro was 1 (i.e. traditional behavior), - // Right should already have thrown an exception. if (x4::has_expectation_failure(ctx)) { // don't rollback iterator (mimicking exception-like behavior) return false; } } + // `Right` failed, now try `Left` ------------------ - // Right fails, now try Left + // Rollback iterator + // This rolls back the iterator position, including the amount skipped by + // `Right`'s skipper (`x4::skip_over(...)`). + first = orig_first; return this->left.parse(first, last, ctx, attr); } }; diff --git a/test/x4/difference.cpp b/test/x4/difference.cpp index dc6543266..d561f60d5 100644 --- a/test/x4/difference.cpp +++ b/test/x4/difference.cpp @@ -15,14 +15,18 @@ #include #include #include +#include +#include #include TEST_CASE("difference") { using x4::standard::char_; + using x4::standard::blank; using x4::standard::space; using x4::lit; + using x4::skip; using x4::_attr; IRIS_X4_ASSERT_CONSTEXPR_CTORS(char_ - 'a'); @@ -66,4 +70,20 @@ TEST_CASE("difference") )); CHECK(s == "abcdefghijk"); } + + { + // Rollback on failed parse + std::string s; + REQUIRE(parse( + "foo die ,", + +( + // RHS fails, so the difference parser should rollback iterator + // Note that RHS has a skipper, so the buffer skipped by `skip_over` + // should also be rolled back + char_ - skip(blank)[','] + ) >> skip(blank)[','], + s + )); + CHECK(s == "foo die"); // wrong implementation yields "foodie" + } }