diff --git a/include/iris/x4/core/skip_over.hpp b/include/iris/x4/core/skip_over.hpp index 56a9f649e..d312c6f95 100644 --- a/include/iris/x4/core/skip_over.hpp +++ b/include/iris/x4/core/skip_over.hpp @@ -13,7 +13,6 @@ #include -#include #include #include #include @@ -43,86 +42,25 @@ enum struct builtin_skipper_kind : char space, }; -namespace detail { - -template -struct skip_over_context -{ - using type = unused_type; -}; - -template - requires (!std::is_same_v, unused_type>) -struct skip_over_context -{ - using type = std::remove_cvref_t( - x4::get(std::declval()) - ))>; -}; - -} // detail - template Se, class Context> requires X4Subject> constexpr void skip_over(It& first, Se const& last, Context const& ctx) noexcept(is_nothrow_parsable_v< get_context_plain_t, It, Se, - typename detail::skip_over_context::type, unused_type + std::remove_cvref_t(ctx))>, + unused_type >) { auto const& skipper = x4::get(ctx); - if constexpr (!has_context_v) { - // The context given by parent was truly `unused_type`. - // There exists only one such case in core; that is - // `x4::phrase_parse(...)` which creates a fresh context - // for the (post)skipper. - // - // In that case, it is perfectly fine to pass `unused` - // because the skipper should have been wrapped - // like `x4::with(failure)[skipper]`. - // (Note that we have plenty of static_asserts in other - // locations to detect the absence of the context.) - // - // If we encounter this branch in any other situations, - // that should be a BUG of `expectation_failure` logic. - - while (skipper.parse(first, last, unused, unused)) - /* loop */; - - } else { - // In order to cut the template instantiation chain, - // we must *forget* the original context at least once - // during the (recursive) invocation of skippers. - // - // Traditionally, implementation detail of `skip_over` - // was disposing the context because we can clearly assume - // that any 'context,' including those provided by users, - // is semantically meaningless as long as we're just - // *skipping* iterators. As you can see in the other branch, - // `unused` was passed for that purpose. - // - // However, we need to do a quite different thing when the - // non-throwing expectation_failure mode is enabled. - // - // Since the reference bound to `x4::contexts::expectation_failure` is - // provided by the user in the first place, if we do forget it - // then it will be impossible to resurrect the value afterward. - // It will also be problematic for `skip_over` itself because the - // underlying skipper may (or may not) raise an expectation failure. - // In traditional mode, the error was thrown by a C++ exception. - // But how can we propagate that error without throwing? - // - // For this reason we're going to cherry-pick the reference - // and repack it into a brand-new context. - - auto const local_ctx = x4::make_context( - x4::get(ctx)); - - while (skipper.parse(first, last, local_ctx, unused)) - /* loop */; - } + // A custom skipper may require the context object as-is. However, we should omit the + // `contexts::skipper` because not doing so would invoke infinite instantiation when + // the skipper itself has a recursive skipper. + auto const local_ctx = x4::remove_first_context(ctx); + + while (skipper.parse(first, last, local_ctx, unused)) + /* loop */; } // Implemented in `char_class.hpp`