diff --git a/include/iris/x4/char/char_parser.hpp b/include/iris/x4/char/char_parser.hpp index 393242cd0..2f6e1ef4c 100644 --- a/include/iris/x4/char/char_parser.hpp +++ b/include/iris/x4/char/char_parser.hpp @@ -39,6 +39,7 @@ struct char_parser : parser [[nodiscard]] static constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && noexcept(first != last) && noexcept(Derived::test(static_cast(*first), ctx)) && @@ -49,11 +50,12 @@ struct char_parser : parser static_assert(!CharIncompatibleWith, char_type>, "Mixing incompatible char types is not allowed"); static_assert(!CharLike || !CharIncompatibleWith, "Mixing incompatible char types is not allowed"); - x4::skip_over(first, last, ctx); + auto it = first; + x4::skip_over(it, last, ctx); - if (first != last && Derived::test(static_cast(*first), ctx)) { - x4::move_to(std::iter_value_t{*first}, attr); - ++first; + if (it != last && Derived::test(static_cast(*it), ctx)) { + x4::move_to(std::iter_value_t{*it++}, attr); + first = it; return true; } return false; @@ -64,6 +66,7 @@ struct char_parser : parser [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && noexcept(first != last) && noexcept(this->derived().test(static_cast(*first), ctx)) && @@ -74,11 +77,12 @@ struct char_parser : parser static_assert(!CharIncompatibleWith, char_type>, "Mixing incompatible char types is not allowed"); static_assert(!CharLike || !CharIncompatibleWith, "Mixing incompatible char types is not allowed"); - x4::skip_over(first, last, ctx); + auto it = first; + x4::skip_over(it, last, ctx); - if (first != last && this->derived().test(static_cast(*first), ctx)) { - x4::move_to(std::iter_value_t{*first}, attr); - ++first; + if (it != last && this->derived().test(static_cast(*it), ctx)) { + x4::move_to(std::iter_value_t{*it++}, attr); + first = it; return true; } return false; diff --git a/include/iris/x4/directive/lexeme.hpp b/include/iris/x4/directive/lexeme.hpp index d12dfc20f..c4120d89a 100644 --- a/include/iris/x4/directive/lexeme.hpp +++ b/include/iris/x4/directive/lexeme.hpp @@ -36,13 +36,16 @@ struct lexeme_directive : proxy_parser> > ) { - x4::skip_over(first, last, ctx); // pre-skip + auto it = first; + x4::skip_over(it, last, ctx); // pre-skip - return this->subject.parse( - first, last, + bool const ok = this->subject.parse( + it, last, x4::remove_first_context(ctx), // no skipper attr ); + if (ok) first = it; + return ok; } [[nodiscard]] constexpr std::string get_x4_info() const diff --git a/include/iris/x4/directive/no_skip.hpp b/include/iris/x4/directive/no_skip.hpp index f1436220a..40e48061c 100644 --- a/include/iris/x4/directive/no_skip.hpp +++ b/include/iris/x4/directive/no_skip.hpp @@ -13,7 +13,6 @@ =============================================================================*/ #include -#include #include #include diff --git a/include/iris/x4/directive/omit.hpp b/include/iris/x4/directive/omit.hpp index 8d75e98b9..318b924e3 100644 --- a/include/iris/x4/directive/omit.hpp +++ b/include/iris/x4/directive/omit.hpp @@ -33,7 +33,6 @@ struct omit_directive : unary_parser> parse(It& first, Se const& last, Context const& ctx, Attr const&) const noexcept(is_nothrow_parsable_v) { - static_assert(Parsable); return this->subject.parse(first, last, ctx, unused); } }; diff --git a/include/iris/x4/directive/skip.hpp b/include/iris/x4/directive/skip.hpp index 83e10bf90..6b8b343a0 100644 --- a/include/iris/x4/directive/skip.hpp +++ b/include/iris/x4/directive/skip.hpp @@ -12,7 +12,6 @@ =============================================================================*/ #include -#include #include #include diff --git a/include/iris/x4/numeric/bool.hpp b/include/iris/x4/numeric/bool.hpp index 26c1a13cc..759ab6957 100644 --- a/include/iris/x4/numeric/bool.hpp +++ b/include/iris/x4/numeric/bool.hpp @@ -74,6 +74,7 @@ struct bool_parser : parser> constexpr bool_parser(Policy const&) = delete; // Policy should be stateless + // Attribute is `T` or `unused_type` template Se, class Context, class U> requires std::same_as, T> || @@ -81,18 +82,24 @@ struct bool_parser : parser> [[nodiscard]] static constexpr bool parse(It& first, Se const& last, Context const& ctx, U& attr) noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && - noexcept(Policy::parse_true(first, last, attr, x4::get_case_compare>>>(ctx))) && - noexcept(Policy::parse_false(first, last, attr, x4::get_case_compare>>>(ctx))) + noexcept(Policy::parse_true(first, last, attr, x4::get_case_compare>>(ctx))) && + noexcept(Policy::parse_false(first, last, attr, x4::get_case_compare>>(ctx))) ) { - x4::skip_over(first, last, ctx); + auto it = first; + x4::skip_over(it, last, ctx); - auto const& compare = x4::get_case_compare>>>(ctx); - return Policy::parse_true(first, last, attr, compare) - || Policy::parse_false(first, last, attr, compare); + auto const& compare = x4::get_case_compare>>(ctx); + bool const ok = Policy::parse_true(it, last, attr, compare) + || Policy::parse_false(it, last, attr, compare); + + if (ok) first = it; + return ok; } + // Attribute is NOT (`T` or `unused_type`) template Se, class Context, class Attr> [[nodiscard]] static constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) @@ -141,6 +148,7 @@ struct literal_bool_parser : parser> template constexpr literal_bool_parser(U&&, Policy const&) = delete; // Policy should be stateless + // Attribute is `T` or `unused_type` template Se, class Context, class U> requires std::same_as, T> || @@ -148,18 +156,25 @@ struct literal_bool_parser : parser> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, U& attr) const noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && - noexcept(Policy::parse_true(first, last, attr, x4::get_case_compare>>>(ctx))) && - noexcept(Policy::parse_false(first, last, attr, x4::get_case_compare>>>(ctx))) + noexcept(Policy::parse_true(first, last, attr, x4::get_case_compare>>(ctx))) && + noexcept(Policy::parse_false(first, last, attr, x4::get_case_compare>>(ctx))) ) { - x4::skip_over(first, last, ctx); + auto it = first; + x4::skip_over(it, last, ctx); + + auto const& compare = x4::get_case_compare>>(ctx); + bool const ok = (expected_bool_ && Policy::parse_true(it, last, attr, compare)) + || (!expected_bool_ && Policy::parse_false(it, last, attr, compare)); - auto const& compare = x4::get_case_compare>>>(ctx); - return (expected_bool_ && Policy::parse_true(first, last, attr, compare)) - || (!expected_bool_ && Policy::parse_false(first, last, attr, compare)); + if (ok) first = it; + return ok; } + // Attribute is NOT (`T` or `unused_type`) + // Needs temporary instance of `T` and do conversion template Se, class Context, class Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -171,8 +186,7 @@ struct literal_bool_parser : parser> { static_assert(X4NonUnusedAttribute); - // this case is called when Attribute is not T - T attr_{}; + T attr_; if (literal_bool_parser::parse(first, last, ctx, attr_)) { x4::move_to(std::move(attr_), attr); return true; diff --git a/include/iris/x4/numeric/int.hpp b/include/iris/x4/numeric/int.hpp index ff9d1b2f5..b5f8a2519 100644 --- a/include/iris/x4/numeric/int.hpp +++ b/include/iris/x4/numeric/int.hpp @@ -42,12 +42,16 @@ struct int_parser : parser> [[nodiscard]] static constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && noexcept(numeric::extract_int::call(first, last, attr)) ) { - x4::skip_over(first, last, ctx); - return numeric::extract_int::call(first, last, attr); + auto it = first; + x4::skip_over(it, last, ctx); + bool const ok = numeric::extract_int::call(it, last, attr); + if (ok) first = it; + return ok; } [[nodiscard]] static std::string get_x4_info() diff --git a/include/iris/x4/numeric/real.hpp b/include/iris/x4/numeric/real.hpp index 26c58f45a..57b94a683 100644 --- a/include/iris/x4/numeric/real.hpp +++ b/include/iris/x4/numeric/real.hpp @@ -229,12 +229,16 @@ struct real_parser : parser> [[nodiscard]] static constexpr bool parse(It& first, Se const& last, Context const& ctx, U& attr) noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && noexcept(numeric::extract_real::parse(first, last, attr)) ) { - x4::skip_over(first, last, ctx); - return numeric::extract_real::parse(first, last, attr); + auto it = first; + x4::skip_over(it, last, ctx); + bool const ok = numeric::extract_real::parse(it, last, attr); + if (ok) first = it; + return ok; } template Se, class Context, class Attr> diff --git a/include/iris/x4/numeric/uint.hpp b/include/iris/x4/numeric/uint.hpp index 366902131..eefd34208 100644 --- a/include/iris/x4/numeric/uint.hpp +++ b/include/iris/x4/numeric/uint.hpp @@ -42,12 +42,16 @@ struct uint_parser : parser> [[nodiscard]] static constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && noexcept(numeric::extract_uint::call(first, last, attr)) ) { - x4::skip_over(first, last, ctx); - return numeric::extract_uint::call(first, last, attr); + auto it = first; + x4::skip_over(it, last, ctx); + bool const ok = numeric::extract_uint::call(it, last, attr); + if (ok) first = it; + return ok; } [[nodiscard]] static std::string get_x4_info() diff --git a/include/iris/x4/operator/alternative.hpp b/include/iris/x4/operator/alternative.hpp index ca187c5c7..a8185bc30 100644 --- a/include/iris/x4/operator/alternative.hpp +++ b/include/iris/x4/operator/alternative.hpp @@ -97,6 +97,7 @@ struct alternative : binary_parser> public: using binary_parser::binary_parser; + // unused_type attribute template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, UnusedAttr const&) const @@ -120,6 +121,7 @@ struct alternative : binary_parser> // // You also should add test to `attribute.cpp`. + // Non-container attribute template Se, class Context, X4NonUnusedAttribute Attr> requires (!traits::X4Container) [[nodiscard]] constexpr bool @@ -151,6 +153,7 @@ struct alternative : binary_parser> return false; // `attr` is untouched } + // Container attribute template Se, class Context, traits::X4Container ContainerAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, ContainerAttr& attr) const diff --git a/include/iris/x4/operator/and_predicate.hpp b/include/iris/x4/operator/and_predicate.hpp index cc5e61f98..4b03ad795 100644 --- a/include/iris/x4/operator/and_predicate.hpp +++ b/include/iris/x4/operator/and_predicate.hpp @@ -33,7 +33,7 @@ struct and_predicate : unary_parser> is_nothrow_parsable_v ) { - It it = first; + auto it = first; return this->subject.parse(it, last, ctx, unused); } }; diff --git a/include/iris/x4/operator/difference.hpp b/include/iris/x4/operator/difference.hpp index 5a1387010..7744f9988 100644 --- a/include/iris/x4/operator/difference.hpp +++ b/include/iris/x4/operator/difference.hpp @@ -32,10 +32,10 @@ struct difference : binary_parser> parse(It& first, Se const& last, Context const& ctx, Attr& attr) const { // Try `Right` - It const orig_first = first; - if (this->right.parse(first, last, ctx, unused)) { + auto it = first; + if (this->right.parse(it, last, ctx, unused)) { // `Right` succeeds, we fail. - first = orig_first; + // We don't need to advance the iterator on this situation. return false; } if constexpr (has_context_v) { @@ -49,11 +49,12 @@ struct difference : binary_parser> } // `Right` failed, 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); + // Try `Left` on the original position, effectively reverting the amount + // skipped by `Right`'s skipper (`x4::skip_over(...)`). + it = first; + bool const ok = this->left.parse(it, last, ctx, attr); + if (ok) first = it; + return ok; } }; diff --git a/include/iris/x4/primitive/eoi.hpp b/include/iris/x4/primitive/eoi.hpp index d276da8ec..e4549aa88 100644 --- a/include/iris/x4/primitive/eoi.hpp +++ b/include/iris/x4/primitive/eoi.hpp @@ -16,6 +16,7 @@ #include #include +#include namespace iris::x4 { @@ -24,15 +25,19 @@ struct eoi_parser : parser using attribute_type = unused_type; template Se, class Context, X4Attribute Attr> - [[nodiscard]] constexpr bool - parse(It& first, Se const& last, Context const& ctx, Attr&) const + [[nodiscard]] static constexpr bool + parse(It& first, Se const& last, Context const& ctx, Attr&) noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && noexcept(first == last) ) { - x4::skip_over(first, last, ctx); - return first == last; + auto it = first; + x4::skip_over(it, last, ctx); + bool const ok = it == last; + if (ok) first = it; + return ok; } }; diff --git a/include/iris/x4/primitive/eol.hpp b/include/iris/x4/primitive/eol.hpp index 20bc70999..c43d23443 100644 --- a/include/iris/x4/primitive/eol.hpp +++ b/include/iris/x4/primitive/eol.hpp @@ -24,26 +24,32 @@ struct eol_parser : parser using attribute_type = unused_type; template Se, class Context, X4Attribute Attr> - [[nodiscard]] constexpr bool - parse(It& first, Se const& last, Context const& ctx, Attr&) const - // TODO: noexcept + [[nodiscard]] static constexpr bool + parse(It& first, Se const& last, Context const& ctx, Attr&) + noexcept( + std::is_nothrow_copy_assignable_v && + noexcept(x4::skip_over(first, last, ctx)) && + noexcept(first != last) && + noexcept(*first == static_cast>('\r')) && + noexcept(++first) + ) { - x4::skip_over(first, last, ctx); - It iter = first; + auto it = first; + x4::skip_over(it, last, ctx); bool matched = false; using iter_value_type = std::iter_value_t; - if (iter != last && *iter == static_cast('\r')) { + if (it != last && *it == static_cast('\r')) { matched = true; - ++iter; + ++it; } - if (iter != last && *iter == static_cast('\n')) { + if (it != last && *it == static_cast('\n')) { matched = true; - ++iter; + ++it; } - if (matched) first = iter; + if (matched) first = it; return matched; } }; diff --git a/include/iris/x4/primitive/eps.hpp b/include/iris/x4/primitive/eps.hpp index 2e9f99b5a..f45c5a324 100644 --- a/include/iris/x4/primitive/eps.hpp +++ b/include/iris/x4/primitive/eps.hpp @@ -36,7 +36,9 @@ struct semantic_predicate : parser parse(It& first, Se const& last, Context const& ctx, Attr&) const noexcept(noexcept(x4::skip_over(first, last, ctx))) { - x4::skip_over(first, last, ctx); + if (predicate_) { + x4::skip_over(first, last, ctx); + } return predicate_; } @@ -62,24 +64,21 @@ struct lazy_semantic_predicate : parser> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr&) const { - x4::skip_over(first, last, ctx); + auto it = first; + x4::skip_over(it, last, ctx); if constexpr (std::invocable) { static_assert(std::same_as, bool>); - return f_(ctx); - - } else if constexpr (std::invocable) { - static_assert( - false, - "We no longer accept a lazy semantic predicate that expects a single `unused_type`. " - "Just make your functor take no arguments instead." - ); - return false; // silence linter warning + bool const ok = f_(ctx); + if (ok) first = it; + return ok; } else { static_assert(std::invocable); static_assert(std::same_as, bool>); - return f_(); + bool const ok = f_(); + if (ok) first = it; + return ok; } } @@ -94,8 +93,8 @@ struct eps_parser : parser using attribute_type = unused_type; template Se, class Context, X4Attribute Attr> - [[nodiscard]] constexpr bool - parse(It& first, Se const& last, Context const& ctx, Attr&) const + [[nodiscard]] static constexpr bool + parse(It& first, Se const& last, Context const& ctx, Attr&) noexcept(noexcept(x4::skip_over(first, last, ctx))) { x4::skip_over(first, last, ctx); diff --git a/include/iris/x4/string/literal_string.hpp b/include/iris/x4/string/literal_string.hpp index c2b430149..ca367844c 100644 --- a/include/iris/x4/string/literal_string.hpp +++ b/include/iris/x4/string/literal_string.hpp @@ -58,13 +58,17 @@ struct literal_string : parser> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr_& attr) const noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && noexcept(detail::string_parse(str_, first, last, x4::assume_container(attr), x4::get_case_compare(ctx))) ) { static_assert(std::same_as, char_type>, "Mixing incompatible char types is not allowed"); - x4::skip_over(first, last, ctx); - return detail::string_parse(str_, first, last, x4::assume_container(attr), x4::get_case_compare(ctx)); + auto it = first; + x4::skip_over(it, last, ctx); + bool const ok = detail::string_parse(str_, it, last, x4::assume_container(attr), x4::get_case_compare(ctx)); + if (ok) first = it; + return ok; } [[nodiscard]] std::string get_x4_info() const diff --git a/include/iris/x4/symbols.hpp b/include/iris/x4/symbols.hpp index 73a1ef2f8..dd918b28a 100644 --- a/include/iris/x4/symbols.hpp +++ b/include/iris/x4/symbols.hpp @@ -240,14 +240,17 @@ struct symbols_parser_impl : parser [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const noexcept( + std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && noexcept(x4::move_to(std::declval(), attr)) ) { - x4::skip_over(first, last, ctx); + auto it = first; + x4::skip_over(it, last, ctx); - if (value_type const* val_ptr = lookup->find(first, last, x4::get_case_compare(ctx))) { + if (value_type const* val_ptr = lookup->find(it, last, x4::get_case_compare(ctx))) { x4::move_to(*val_ptr, attr); + first = it; return true; } return false; diff --git a/modules/iris b/modules/iris index ebd833f7a..3ee6f465b 160000 --- a/modules/iris +++ b/modules/iris @@ -1 +1 @@ -Subproject commit ebd833f7ab62575004bca5dc8d9c43087cdac3f3 +Subproject commit 3ee6f465b747694547cd1c71f295430858e0eb7b diff --git a/test/x4/expect.cpp b/test/x4/expect.cpp index 6f13d3805..797816a6e 100644 --- a/test/x4/expect.cpp +++ b/test/x4/expect.cpp @@ -394,7 +394,7 @@ TEST_CASE("expect") X4_TEST_FAILURE_EX(" x i", char_('x') > char_('o'), space, { CHECK(which == "'o'"sv); - CHECK(where == "i"sv); + CHECK(where == " i"sv); }); } @@ -592,7 +592,7 @@ TEST_CASE("expect") X4_TEST_FAILURE_EX("a b c", lit('a') > 'c', space, { CHECK(which == "'c'"sv); - CHECK(where == "b c"sv); + CHECK(where == " b c"sv); }); @@ -613,7 +613,7 @@ TEST_CASE("expect") std::string s; X4_TEST_ATTR_FAILURE("a b\n c\n d", char_('a') > char_('z') > skip(space)[char_('c') > char_('d')], blank, s, { CHECK(which == "'z'"sv); - CHECK(where == "b\n c\n d"sv); + CHECK(where == " b\n c\n d"sv); }); } { @@ -626,7 +626,7 @@ TEST_CASE("expect") std::string s; X4_TEST_ATTR_FAILURE("a b\n c\n d", char_('a') > char_('b') > skip(space)[char_('c') > char_('z')], blank, s, { CHECK(which == "'z'"sv); - CHECK(where == "d"sv); + CHECK(where == "\n d"sv); }); } } diff --git a/test/x4/iterator.cpp b/test/x4/iterator.cpp index f346acdbf..5e8202b23 100644 --- a/test/x4/iterator.cpp +++ b/test/x4/iterator.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -70,6 +71,9 @@ using namespace std::string_view_literals; // NOLINTBEGIN(readability-container-size-empty) +constexpr auto skipper_ctx = x4::make_context(x4::standard::blank); +constexpr auto uskipper_ctx = x4::make_context(x4::unicode::blank); + TEST_CASE("rollback on failed parse (numeric)") { using x4::int_; @@ -77,35 +81,35 @@ TEST_CASE("rollback on failed parse (numeric)") using x4::double_; { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -99; - REQUIRE_FALSE(int_.parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(int_.parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -99); } { - constexpr auto input = "-"sv; + constexpr auto input = " -"sv; auto first = input.begin(); int dummy_int = -99; - REQUIRE_FALSE(int_.parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(int_.parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -99); } { - constexpr auto input = "-9999999999999999999999999999999999999"sv; // overflow + constexpr auto input = " -9999999999999999999999999999999999999"sv; // overflow auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(int_.parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(int_.parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } { - constexpr auto input = "9999999999999999999999999999999999999"sv; // overflow + constexpr auto input = " 9999999999999999999999999999999999999"sv; // overflow auto first = input.begin(); unsigned dummy_uint = static_cast(-1); - REQUIRE_FALSE(uint_.parse(first, input.end(), unused, dummy_uint)); + REQUIRE_FALSE(uint_.parse(first, input.end(), skipper_ctx, dummy_uint)); CHECK(first == input.begin()); CHECK(dummy_uint == static_cast(-1)); } @@ -113,10 +117,10 @@ TEST_CASE("rollback on failed parse (numeric)") // TODO: https://github.com/boostorg/spirit_x4/issues/63 //{ // //constexpr double aaa = 9.9e999999999999999999999; // error - // constexpr auto input = "9.9e999999999999999999999"sv; // overflow + // constexpr auto input = " 9.9e999999999999999999999"sv; // overflow // auto first = input.begin(); // double dummy_double = 3.14; - // REQUIRE_FALSE(double_.parse(first, input.end(), unused, dummy_double)); + // REQUIRE_FALSE(double_.parse(first, input.end(), skipper_ctx, dummy_double)); // CHECK(first == input.begin()); // CHECK(dummy_double == static_cast(3.14)); //} @@ -128,51 +132,51 @@ TEST_CASE("rollback on failed parse (char)") using x4::lit; { - constexpr auto input = U"\x00110000"sv; // not a valid Unicode code point + constexpr auto input = U" \x00110000"sv; // not a valid Unicode code point auto first = input.begin(); char32_t dummy_char = U'd'; - REQUIRE_FALSE(x4::unicode::char_.parse(first, input.end(), unused, dummy_char)); // NOLINT(readability-static-accessed-through-instance) + REQUIRE_FALSE(x4::unicode::char_.parse(first, input.end(), uskipper_ctx, dummy_char)); // NOLINT(readability-static-accessed-through-instance) CHECK(first == input.begin()); CHECK(dummy_char == U'd'); } { - constexpr auto input = "x"sv; + constexpr auto input = " x"sv; auto first = input.begin(); char dummy_char = 'd'; - REQUIRE_FALSE(char_('a').parse(first, input.end(), unused, dummy_char)); + REQUIRE_FALSE(char_('a').parse(first, input.end(), skipper_ctx, dummy_char)); CHECK(first == input.begin()); CHECK(dummy_char == 'd'); } { - constexpr auto input = "1"sv; + constexpr auto input = " 1"sv; auto first = input.begin(); char dummy_char = 'd'; - REQUIRE_FALSE(char_('a', 'z').parse(first, input.end(), unused, dummy_char)); + REQUIRE_FALSE(char_('a', 'z').parse(first, input.end(), skipper_ctx, dummy_char)); CHECK(first == input.begin()); CHECK(dummy_char == 'd'); } { - constexpr auto input = "1"sv; + constexpr auto input = " 1"sv; auto first = input.begin(); char dummy_char = 'd'; - REQUIRE_FALSE(char_("a-z").parse(first, input.end(), unused, dummy_char)); + REQUIRE_FALSE(char_("a-z").parse(first, input.end(), skipper_ctx, dummy_char)); CHECK(first == input.begin()); CHECK(dummy_char == 'd'); } { - constexpr auto input = "1"sv; + constexpr auto input = " 1"sv; auto first = input.begin(); char dummy_char = 'd'; - REQUIRE_FALSE((~char_).parse(first, input.end(), unused, dummy_char)); + REQUIRE_FALSE((~char_).parse(first, input.end(), skipper_ctx, dummy_char)); CHECK(first == input.begin()); CHECK(dummy_char == 'd'); } { - constexpr auto input = "1"sv; + constexpr auto input = " 1"sv; auto first = input.begin(); char dummy_char = 'd'; - REQUIRE_FALSE(lit('a').parse(first, input.end(), unused, dummy_char)); + REQUIRE_FALSE(lit('a').parse(first, input.end(), skipper_ctx, dummy_char)); CHECK(first == input.begin()); CHECK(dummy_char == 'd'); } @@ -185,25 +189,25 @@ TEST_CASE("rollback on failed parse (string)") using x4::unique_symbols; { - constexpr auto input = "x"sv; + constexpr auto input = " x"sv; auto first = input.begin(); - REQUIRE_FALSE(lit("foo").parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(lit("foo").parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "x"sv; + constexpr auto input = " x"sv; auto first = input.begin(); std::string dummy_string = "dummy"; - REQUIRE_FALSE(string("foo").parse(first, input.end(), unused, dummy_string)); + REQUIRE_FALSE(string("foo").parse(first, input.end(), skipper_ctx, dummy_string)); CHECK(first == input.begin()); CHECK(dummy_string == "dummy"); } { unique_symbols syms{{"foo", 0}, {"bar", 1}}; - constexpr auto input = "baz"sv; + constexpr auto input = " baz"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(syms.parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(syms.parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } @@ -216,24 +220,24 @@ TEST_CASE("rollback on failed parse (action)") using x4::_pass; { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(eps.on_match([](auto&&) { return false; }).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(eps.on_match([](auto&&) { return false; }).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(int_.on_match([](auto&&) { return false; }).parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(int_.on_match([](auto&&) { return false; }).parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser itself succeeds; always results in side effect } { - constexpr auto input = "42,43"sv; + constexpr auto input = " 42,43"sv; auto first = input.begin(); std::vector dummy_ints; - REQUIRE_FALSE((int_ >> ',' >> int_).on_match([](auto&&) { return false; }).parse(first, input.end(), unused, dummy_ints)); + REQUIRE_FALSE((int_ >> ',' >> int_).on_match([](auto&&) { return false; }).parse(first, input.end(), skipper_ctx, dummy_ints)); CHECK(first == input.begin()); CHECK(dummy_ints == std::vector{42, 43}); // sequence parser itself succeeds; always results in side effect } @@ -247,34 +251,35 @@ TEST_CASE("rollback on failed parse (primitive)") using x4::eol; { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE((fixed_value(42) >> eps(false)).parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE((fixed_value(42) >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser has side effect because attribute is not a container } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(eoi.parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(eoi.parse(first, input.end(), skipper_ctx, unused)); + CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(eol.parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(eol.parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(eps(false).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(eps(false).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(eps([]{ return false; }).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(eps([]{ return false; }).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } } @@ -299,38 +304,38 @@ TEST_CASE("rollback on failed parse (directive)") using x4::with; { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); x4::expectation_failure failure; - auto const ctx = x4::make_context(failure); + auto const ctx = x4::make_context(failure, skipper_ctx); REQUIRE_FALSE(expect[eps(false)].parse(first, input.end(), ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); x4::expectation_failure failure; - auto const ctx = x4::make_context(failure); + auto const ctx = x4::make_context(failure, skipper_ctx); int dummy_int = -1; REQUIRE_FALSE(expect[int_].parse(first, input.end(), ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } { - constexpr auto input = "42!"sv; + constexpr auto input = " 42!"sv; auto first = input.begin(); x4::expectation_failure failure; - auto const ctx = x4::make_context(failure); + auto const ctx = x4::make_context(failure, skipper_ctx); int dummy_int = -1; REQUIRE_FALSE((int_ >> expect['i']).parse(first, input.end(), ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser has side effect because attribute is not a container } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); x4::expectation_failure failure; - auto const ctx = x4::make_context(failure); + auto const ctx = x4::make_context(failure, skipper_ctx); std::vector dummy_ints; REQUIRE_FALSE((int_ >> expect[','] >> int_).parse(first, input.end(), ctx, dummy_ints)); CHECK(first == input.begin()); @@ -338,93 +343,93 @@ TEST_CASE("rollback on failed parse (directive)") } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(lexeme[eps(false)].parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(lexeme[eps(false)].parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(lexeme[int_ >> eps(false)].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(lexeme[int_ >> eps(false)].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser has side effect because attribute is not a container } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); std::vector dummy_ints; - REQUIRE_FALSE(lexeme[int_ >> ',' >> int_].parse(first, input.end(), unused, dummy_ints)); + REQUIRE_FALSE(lexeme[int_ >> ',' >> int_].parse(first, input.end(), skipper_ctx, dummy_ints)); CHECK(first == input.begin()); CHECK(dummy_ints == std::vector{}); // sequence parser has NO side effect because attribute is a container } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE(matches[eps(false)].parse(first, input.end(), unused, unused)); + REQUIRE(matches[eps(false)].parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); bool dummy_bool = false; - REQUIRE(matches[int_].parse(first, input.end(), unused, dummy_bool)); + REQUIRE(matches[int_].parse(first, input.end(), skipper_ctx, dummy_bool)); CHECK(first == input.end()); CHECK(dummy_bool == true); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); bool dummy_bool = true; - REQUIRE(matches[int_].parse(first, input.end(), unused, dummy_bool)); + REQUIRE(matches[int_].parse(first, input.end(), skipper_ctx, dummy_bool)); CHECK(first == input.begin()); CHECK(dummy_bool == false); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(no_case[eps(false)].parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(no_case[eps(false)].parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(no_case[int_].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(no_case[int_].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(no_case[int_ >> eps(false)].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(no_case[int_ >> eps(false)].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser has side effect because attribute is not a container } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); std::vector dummy_ints; - REQUIRE_FALSE(no_case[int_ >> ',' >> int_].parse(first, input.end(), unused, dummy_ints)); + REQUIRE_FALSE(no_case[int_ >> ',' >> int_].parse(first, input.end(), skipper_ctx, dummy_ints)); CHECK(first == input.begin()); CHECK(dummy_ints == std::vector{}); // sequence parser has NO side effect because attribute is a container } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(no_skip[eps(false)].parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(no_skip[eps(false)].parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(no_skip[int_].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(no_skip[int_].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } @@ -432,7 +437,7 @@ TEST_CASE("rollback on failed parse (directive)") constexpr auto input = "42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(no_skip[int_ >> eps(false)].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(no_skip[int_ >> eps(false)].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser has side effect because attribute is not a container } @@ -440,114 +445,114 @@ TEST_CASE("rollback on failed parse (directive)") constexpr auto input = "42"sv; auto first = input.begin(); std::vector dummy_ints; - REQUIRE_FALSE(no_skip[int_ >> ',' >> int_].parse(first, input.end(), unused, dummy_ints)); - CHECK(first == input.begin()); // sequence parser has NO side effect because attribute is not a container + REQUIRE_FALSE(no_skip[int_ >> ',' >> int_].parse(first, input.end(), skipper_ctx, dummy_ints)); + CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(omit[eps(false)].parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(omit[eps(false)].parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(omit[int_].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(omit[int_].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(omit[int_ >> eps(false)].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(omit[int_ >> eps(false)].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); // `omit` never yields an attribute } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(repeat(1)[eps(false)].parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(repeat(1)[eps(false)].parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); std::vector dummy_ints; - REQUIRE_FALSE(repeat(1)[int_ >> eps(false)].parse(first, input.end(), unused, dummy_ints)); + REQUIRE_FALSE(repeat(1)[int_ >> eps(false)].parse(first, input.end(), skipper_ctx, dummy_ints)); CHECK(first == input.begin()); CHECK(dummy_ints == std::vector{}); } { - constexpr auto input = "true123"sv; + constexpr auto input = " true123"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE(repeat(1)[true_ >> true_].parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE(repeat(1)[true_ >> true_].parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{}); } { - constexpr auto input = "true123"sv; + constexpr auto input = " true123"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE(repeat(2)[true_].parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE(repeat(2)[true_].parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{}); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(skip(space)[eps(false)].parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(skip(space)[eps(false)].parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(skip(space)[int_].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(skip(space)[int_].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(skip(space)[int_ >> eps(false)].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(skip(space)[int_ >> eps(false)].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } { - constexpr auto input = "42 foo"sv; + constexpr auto input = " 42 foo"sv; auto first = input.begin(); std::vector dummy_ints; - REQUIRE_FALSE(skip(space)[int_ >> int_].parse(first, input.end(), unused, dummy_ints)); + REQUIRE_FALSE(skip(space)[int_ >> int_].parse(first, input.end(), skipper_ctx, dummy_ints)); CHECK(first == input.begin()); CHECK(dummy_ints == std::vector{42}); // sequence parser has side effect } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE(with(input)[eps(false)].parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(with(input)[eps(false)].parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(with(input)[int_].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(with(input)[int_].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE(with(input)[int_ >> eps(false)].parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE(with(input)[int_ >> eps(false)].parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser has side effect } @@ -562,16 +567,16 @@ TEST_CASE("rollback on failed parse (operator)") // `sequence` should be tested first because it is the requirement for subsequent test cases { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE((eps >> eps(false)).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((eps >> eps(false)).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "truefalse"sv; + constexpr auto input = " truefalse"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE((true_ >> true_).parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE((true_ >> true_).parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{true}); // `sequence` parser exposes the side effects } @@ -579,259 +584,259 @@ TEST_CASE("rollback on failed parse (operator)") // ----------------------------------------------- { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE((int_ >> eps(false)).parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE((int_ >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == 42); // sequence parser has side effect because attribute is not a container } { - constexpr auto input = "42,43"sv; + constexpr auto input = " 42,43"sv; auto first = input.begin(); std::vector dummy_ints; - REQUIRE_FALSE((int_ >> eps(false) >> int_).parse(first, input.end(), unused, dummy_ints)); + REQUIRE_FALSE((int_ >> eps(false) >> int_).parse(first, input.end(), skipper_ctx, dummy_ints)); CHECK(first == input.begin()); CHECK(dummy_ints == std::vector{}); // sequence parser has NO side effect because LHS is `sequence`, which does not move the result until `eps(false)` is evaluated } { - constexpr auto input = "42,43"sv; + constexpr auto input = " 42,43"sv; auto first = input.begin(); std::vector dummy_ints; - REQUIRE_FALSE((int_ >> (eps(false) >> int_)).parse(first, input.end(), unused, dummy_ints)); + REQUIRE_FALSE((int_ >> (eps(false) >> int_)).parse(first, input.end(), skipper_ctx, dummy_ints)); CHECK(first == input.begin()); CHECK(dummy_ints == std::vector{42}); // sequence parser has side effect, in contrast to above } // NOLINTBEGIN(misc-redundant-expression) { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE((eps(false) | eps(false)).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((eps(false) | eps(false)).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE((eps(false) | int_).parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE((eps(false) | int_).parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE((eps(false) | int_ >> eps(false)).parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE((eps(false) | int_ >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); // `alternative` parser shall not expose the side effects on the failed branch } { - constexpr auto input = "true"sv; + constexpr auto input = " true"sv; auto first = input.begin(); bool dummy_bool = true; - REQUIRE_FALSE((false_ | false_).parse(first, input.end(), unused, dummy_bool)); + REQUIRE_FALSE((false_ | false_).parse(first, input.end(), skipper_ctx, dummy_bool)); CHECK(first == input.begin()); CHECK(dummy_bool == true); } { - constexpr auto input = "true"sv; + constexpr auto input = " true"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE((true_ >> false_ | true_ >> false_).parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE((true_ >> false_ | true_ >> false_).parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{}); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE((eps - eps).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((eps - eps).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE((int_ - eps).parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE((int_ - eps).parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); } { - constexpr auto input = "42"sv; + constexpr auto input = " 42"sv; auto first = input.begin(); int dummy_int = -1; - REQUIRE_FALSE((int_ - eps).parse(first, input.end(), unused, dummy_int)); + REQUIRE_FALSE((int_ - eps).parse(first, input.end(), skipper_ctx, dummy_int)); CHECK(first == input.begin()); CHECK(dummy_int == -1); // `difference` parser shall not expose the side effects } { - constexpr auto input = "truefalse"sv; + constexpr auto input = " truefalse"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE(((true_ >> true_) - eps).parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE(((true_ >> true_) - eps).parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{}); // `difference` parser shall not expose the side effects } { - constexpr auto input = "truetrue"sv; + constexpr auto input = " truetrue"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE(((true_ >> true_) - eps).parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE(((true_ >> true_) - eps).parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{}); // `difference` parser shall not expose the side effects } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE((*eps(false) >> eps(false)).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((*eps(false) >> eps(false)).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "truefalse"sv; + constexpr auto input = " truefalse"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE((*true_ >> eps(false)).parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE((*true_ >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{true}); // `kleene` parser (within sequence) exposes the side effects } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE((+eps(false)).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((+eps(false)).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "truefalse"sv; + constexpr auto input = " truefalse"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE((+true_ >> eps(false)).parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE((+true_ >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{true}); // `plus` parser (within sequence) exposes the side effects } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE((eps(false) % eps(false)).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((eps(false) % eps(false)).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "false"sv; + constexpr auto input = " false"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE((true_ % eps(false)).parse(first, input.end(), unused, dummy_bools)); // initial unmatch shall result in total parse failure + REQUIRE_FALSE((true_ % eps(false)).parse(first, input.end(), skipper_ctx, dummy_bools)); // initial unmatch shall result in total parse failure CHECK(first == input.begin()); } { - constexpr auto input = "false"sv; + constexpr auto input = " false"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE((true_ % eps(true)).parse(first, input.end(), unused, dummy_bools)); // initial unmatch shall result in total parse failure (even if the delimiter parse succeeds) + REQUIRE_FALSE((true_ % eps(true)).parse(first, input.end(), skipper_ctx, dummy_bools)); // initial unmatch shall result in total parse failure (even if the delimiter parse succeeds) CHECK(first == input.begin()); } { - constexpr auto input = "truefalse"sv; + constexpr auto input = " truefalse"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE((true_ % eps(false) >> eps(false)).parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE((true_ % eps(false) >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{true});// `delimited_list` parser (within sequence) exposes the side effects } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE((&eps(false)).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((&eps(false)).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "true"sv; + constexpr auto input = " true"sv; auto first = input.begin(); - REQUIRE_FALSE((&false_).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((&false_).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "truefalse"sv; + constexpr auto input = " truefalse"sv; auto first = input.begin(); - REQUIRE_FALSE((&(true_ >> true_)).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((&(true_ >> true_)).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE((!eps).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((!eps).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "true"sv; + constexpr auto input = " true"sv; auto first = input.begin(); - REQUIRE_FALSE((!true_).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((!true_).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "truetrue"sv; + constexpr auto input = " truetrue"sv; auto first = input.begin(); - REQUIRE_FALSE((!(true_ >> true_)).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((!(true_ >> true_)).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "foo"sv; + constexpr auto input = " foo"sv; auto first = input.begin(); - REQUIRE_FALSE((-eps >> eps(false)).parse(first, input.end(), unused, unused)); + REQUIRE_FALSE((-eps >> eps(false)).parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "false"sv; + constexpr auto input = " false"sv; auto first = input.begin(); bool dummy_bool = false; - REQUIRE_FALSE((-true_ >> eps(false)).parse(first, input.end(), unused, dummy_bool)); + REQUIRE_FALSE((-true_ >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_bool)); CHECK(first == input.begin()); CHECK(dummy_bool == false); } { - constexpr auto input = "true"sv; + constexpr auto input = " true"sv; auto first = input.begin(); bool dummy_bool = false; - REQUIRE_FALSE((-true_ >> eps(false)).parse(first, input.end(), unused, dummy_bool)); + REQUIRE_FALSE((-true_ >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_bool)); CHECK(first == input.begin()); CHECK(dummy_bool == true); } { - constexpr auto input = "false"sv; + constexpr auto input = " false"sv; auto first = input.begin(); std::optional dummy_optional_bool = false; - REQUIRE_FALSE((-true_ >> eps(false)).parse(first, input.end(), unused, dummy_optional_bool)); + REQUIRE_FALSE((-true_ >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_optional_bool)); CHECK(first == input.begin()); REQUIRE(dummy_optional_bool.has_value()); CHECK(*dummy_optional_bool == false); } { - constexpr auto input = "true"sv; + constexpr auto input = " true"sv; auto first = input.begin(); std::optional dummy_optional_bool = false; - REQUIRE_FALSE((-true_ >> eps(false)).parse(first, input.end(), unused, dummy_optional_bool)); + REQUIRE_FALSE((-true_ >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_optional_bool)); CHECK(first == input.begin()); REQUIRE(dummy_optional_bool.has_value()); CHECK(*dummy_optional_bool == true); } { - constexpr auto input = "truefalse"sv; + constexpr auto input = " truefalse"sv; auto first = input.begin(); std::vector dummy_bools; - REQUIRE_FALSE((-(true_ >> true_) >> eps(false)).parse(first, input.end(), unused, dummy_bools)); + REQUIRE_FALSE((-(true_ >> true_) >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_bools)); CHECK(first == input.begin()); CHECK(dummy_bools == std::vector{true}); } { - constexpr auto input = "truefalse"sv; + constexpr auto input = " truefalse"sv; auto first = input.begin(); std::optional> dummy_optional_bools; - REQUIRE_FALSE((-(true_ >> true_) >> eps(false)).parse(first, input.end(), unused, dummy_optional_bools)); + REQUIRE_FALSE((-(true_ >> true_) >> eps(false)).parse(first, input.end(), skipper_ctx, dummy_optional_bools)); CHECK(first == input.begin()); CHECK(dummy_optional_bools.has_value() == false); // `optional` parser for `optional` shall not expose the side effects } @@ -844,19 +849,19 @@ TEST_CASE("rollback on failed parse (rule)") using x4::lit; { - constexpr auto input = "ab"sv; + constexpr auto input = " ab"sv; auto first = input.begin(); constexpr x4::rule r("r"); constexpr auto p = r = lit('a') >> r; - REQUIRE_FALSE(p.parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(p.parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } { - constexpr auto input = "ab"sv; + constexpr auto input = " ab"sv; auto first = input.begin(); constexpr x4::rule r("r"); constexpr auto p = r %= lit('a') >> r; - REQUIRE_FALSE(p.parse(first, input.end(), unused, unused)); + REQUIRE_FALSE(p.parse(first, input.end(), skipper_ctx, unused)); CHECK(first == input.begin()); } }