From 38210fbf7dc66f2ff4084a9c30dc12aa31987d0b Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 22 Sep 2026 03:08:02 +0900 Subject: [PATCH 1/4] Extract sequence member traits to trait specialization --- .../iris/x4/core/detail/parse_sequence.hpp | 2 +- include/iris/x4/operator/sequence.hpp | 38 +++++++++++++------ test/x4/partial_success.cpp | 4 +- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/include/iris/x4/core/detail/parse_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index 6c534f0dd..bff108b26 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -289,7 +289,7 @@ struct parse_into_container_impl> { if constexpr (traits::is_container_v) { constexpr bool sequence_attribute_can_directly_hold_value_type = traits::can_hold< - typename sequence::attribute_type, + typename parser_traits>::attribute_type, typename traits::container_value::type >::value; if constexpr (sequence_attribute_can_directly_hold_value_type) { diff --git a/include/iris/x4/operator/sequence.hpp b/include/iris/x4/operator/sequence.hpp index e05f1fe3a..f0c720403 100644 --- a/include/iris/x4/operator/sequence.hpp +++ b/include/iris/x4/operator/sequence.hpp @@ -32,6 +32,9 @@ namespace iris::x4 { +template +struct sequence; + namespace detail { template @@ -42,7 +45,7 @@ template requires (!std::same_as) && (!traits::X4Container) && - requires (Container& c, Elem&& elem) { + requires(Container& c, Elem&& elem) { traits::push_back(c, std::move(elem)); } struct container_can_hold_element @@ -53,7 +56,7 @@ template requires (!std::same_as) && traits::X4Container && - requires (Container& c, ContainerElem&& container_elem) { + requires(Container& c, ContainerElem&& container_elem) { x4::move_to( std::make_move_iterator(traits::begin(container_elem)), std::make_move_iterator(traits::end(container_elem)), @@ -74,24 +77,37 @@ struct container_can_hold_sequence> : std::conjunction...> {}; -} // detail - template -struct sequence : binary_parser, Left, Right> +struct get_attribute_type> { - using attribute_type = traits::detail::attribute_of_sequence::type; + using type = traits::detail::attribute_of_sequence::type; +}; - static constexpr std::size_t sequence_size = - parser_traits::sequence_size + parser_traits::sequence_size; +template +struct get_sequence_size> +{ + static constexpr std::size_t value = parser_traits::sequence_size + parser_traits::sequence_size; +}; - template - static constexpr bool handles_container = +template +struct get_handles_container, Container> +{ + static constexpr bool value = ( parser_traits::template handles_container && parser_traits::template handles_container ) || - detail::container_can_hold_sequence::value; + container_can_hold_sequence< + Container, + typename parser_traits>::attribute_type + >::value; +}; + +} // detail +template +struct sequence : binary_parser, Left, Right> +{ using binary_parser::binary_parser; template Se, class Context, X4UnusedAttribute UnusedAttr> diff --git a/test/x4/partial_success.cpp b/test/x4/partial_success.cpp index 913c4812d..702cdb51c 100644 --- a/test/x4/partial_success.cpp +++ b/test/x4/partial_success.cpp @@ -265,7 +265,7 @@ TEST_CASE("partial success (list-like)") x4::literal_char >; static_assert(std::same_as, Subject>); - STATIC_CHECK(std::same_as>); + STATIC_CHECK(std::same_as::attribute_type, alloy::tuple>); STATIC_CHECK(x4::detail::container_can_hold_sequence>::value); using Container = std::string; @@ -359,7 +359,7 @@ TEST_CASE("partial success (list-like)") x4::literal_char >; static_assert(std::same_as, Subject>); - STATIC_CHECK(std::same_as>); + STATIC_CHECK(std::same_as::attribute_type, alloy::tuple>); STATIC_CHECK(x4::detail::container_can_hold_sequence>::value); using Container = std::string; From c72163e98274bff265a7eb0c101e2f54a55d11a1 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 22 Sep 2026 10:56:51 +0900 Subject: [PATCH 2/4] Eliminate , noexcept spec --- include/iris/x4/attribute/as.hpp | 14 +----- include/iris/x4/attribute/smart_ptr.hpp | 9 +--- include/iris/x4/char/char_set.hpp | 12 ++--- include/iris/x4/char/literal_char.hpp | 6 +-- include/iris/x4/core/action.hpp | 19 +------ .../iris/x4/core/detail/parse_alternative.hpp | 19 +------ .../x4/core/detail/parse_into_container.hpp | 11 +--- .../iris/x4/core/detail/parse_sequence.hpp | 11 +--- include/iris/x4/core/expectation.hpp | 21 ++++++-- include/iris/x4/core/parser.hpp | 50 +++---------------- include/iris/x4/core/skip_over.hpp | 6 --- include/iris/x4/directive/expect.hpp | 16 ++---- include/iris/x4/directive/lexeme.hpp | 11 +--- include/iris/x4/directive/matches.hpp | 4 -- include/iris/x4/directive/no_case.hpp | 5 -- include/iris/x4/directive/no_skip.hpp | 5 -- include/iris/x4/directive/omit.hpp | 7 +-- include/iris/x4/directive/repeat.hpp | 6 --- include/iris/x4/directive/skip.hpp | 23 ++------- include/iris/x4/directive/with.hpp | 13 +---- include/iris/x4/directive/with_local.hpp | 8 --- include/iris/x4/operator/alternative.hpp | 24 +-------- include/iris/x4/operator/and_predicate.hpp | 4 -- include/iris/x4/operator/delimited_list.hpp | 13 +---- include/iris/x4/operator/kleene.hpp | 8 +-- include/iris/x4/operator/not_predicate.hpp | 4 -- include/iris/x4/operator/optional.hpp | 7 --- include/iris/x4/operator/plus.hpp | 8 +-- include/iris/x4/operator/sequence.hpp | 21 ++------ include/iris/x4/rule.hpp | 21 ++++---- include/iris/x4/string/literal_string.hpp | 3 +- test/x4/as.cpp | 14 ------ test/x4/parser.cpp | 12 ----- 33 files changed, 68 insertions(+), 347 deletions(-) diff --git a/include/iris/x4/attribute/as.hpp b/include/iris/x4/attribute/as.hpp index 04fca9894..75e4a34ed 100644 --- a/include/iris/x4/attribute/as.hpp +++ b/include/iris/x4/attribute/as.hpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -79,7 +78,6 @@ struct as_type_parser : unary_parser, Subject> requires std::same_as, T> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, OuterAttr& outer_attr) const - noexcept(is_nothrow_parsable_v::type, exposed_attr_for_child_t>) { if constexpr (Subject::has_action) { return this->subject.parse(first, last, x4::replace_first_context(ctx, outer_attr), unused); @@ -94,7 +92,6 @@ struct as_type_parser : unary_parser, Subject> (!std::same_as, T>) [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, OuterAttr&) const - noexcept(is_nothrow_parsable_v::type, unused_type>) { if constexpr (Subject::has_action) { return this->subject.parse(first, last, x4::replace_first_context(ctx, unused), unused); @@ -109,10 +106,6 @@ struct as_type_parser : unary_parser, Subject> (!std::same_as, T>) [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, OuterAttr& outer_attr) const - noexcept( - is_nothrow_parsable_v::type, exposed_attr_for_child_t> && - noexcept(x4::move_to(std::declval(), outer_attr)) - ) { // Ideally we should default to default-initialization and avoid value-initialization. // However, there is currently no way to determine whether the attribute is ever touched @@ -135,11 +128,8 @@ struct as_type_parser : unary_parser, Subject> [[nodiscard]] /*constexpr*/ std::string get_x4_info() const { - return std::format( - "as<{}>({})", - typeid(T).name(), - get_info{}(this->subject) - ); + return std::string("as<") + typeid(T).name() + ">(" + + get_info{}(this->subject) + ')'; } }; diff --git a/include/iris/x4/attribute/smart_ptr.hpp b/include/iris/x4/attribute/smart_ptr.hpp index ee0f47d31..54808c722 100644 --- a/include/iris/x4/attribute/smart_ptr.hpp +++ b/include/iris/x4/attribute/smart_ptr.hpp @@ -17,7 +17,6 @@ #include #include #include -#include namespace iris::x4 { @@ -95,7 +94,6 @@ struct unique_ptr_parser_base template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& it, Se const& se, Context const& ctx, Attr& ptr) const - noexcept(false) // never noexcept; requires dynamic memory allocation { static_assert(std::same_as, "Incompatible deleter type provided for unique_ptr_parser"); @@ -122,7 +120,6 @@ struct unique_ptr_parser_base> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& it, Se const& se, Context const& ctx, Attr& ptr) const - noexcept(false) // never noexcept; requires dynamic memory allocation { static_assert(std::same_as>, "Incompatible deleter type provided for unique_ptr_parser"); @@ -158,7 +155,7 @@ struct unique_ptr_parser : detail::unique_ptr_parser_base< [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format("unique_ptr({})", get_info{}(this->subject)); + return "unique_ptr(" + get_info{}(this->subject) + ')'; } }; @@ -260,7 +257,6 @@ struct shared_ptr_parser_base template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& it, Se const& se, Context const& ctx, Attr& ptr) const - noexcept(false) // never noexcept; requires dynamic memory allocation { bool parse_ok = false; auto old_ptr = std::exchange(ptr, std::shared_ptr(new T(), deleter_)); @@ -294,7 +290,6 @@ struct shared_ptr_parser_base> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& it, Se const& se, Context const& ctx, Attr& ptr) const - noexcept(false) // never noexcept; requires dynamic memory allocation { bool parse_ok = false; auto old_ptr = std::exchange(ptr, std::make_shared()); @@ -328,7 +323,7 @@ struct shared_ptr_parser : detail::shared_ptr_parser_base< [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format("shared_ptr({})", get_info{}(this->subject)); + return "shared_ptr(" + get_info{}(this->subject) + ')'; } }; diff --git a/include/iris/x4/char/char_set.hpp b/include/iris/x4/char/char_set.hpp index b577f7c37..07d636e18 100644 --- a/include/iris/x4/char/char_set.hpp +++ b/include/iris/x4/char/char_set.hpp @@ -18,7 +18,6 @@ #include #include -#include #include #include @@ -53,13 +52,11 @@ struct char_range : char_parser, Encoding> [[nodiscard]] std::string get_x4_info() const { - // TODO: make more user-friendly && make the format consistent with above // TODO: escape - return std::format( - "char_range \"{}-{}\"", - iris::unicode::transcode(typename Encoding::string_type(1, this->from)), - iris::unicode::transcode(typename Encoding::string_type(1, this->to)) - ); + return std::string("char_(\"") + + iris::unicode::transcode(typename Encoding::string_type(1, this->from)) + + '-' + iris::unicode::transcode(typename Encoding::string_type(1, this->to)) + + "\")"; } }; @@ -74,7 +71,6 @@ struct char_set : char_parser, Encoding> static constexpr bool has_attribute = !std::is_same_v; constexpr explicit char_set(std::basic_string_view const str) - // never noexcept; requires vector insertion { for (auto definition = std::ranges::begin(str); definition != std::ranges::end(str);) { auto const ch = *definition; diff --git a/include/iris/x4/char/literal_char.hpp b/include/iris/x4/char/literal_char.hpp index fce367ff4..aaf60dcd0 100644 --- a/include/iris/x4/char/literal_char.hpp +++ b/include/iris/x4/char/literal_char.hpp @@ -15,7 +15,6 @@ #include -#include #include #include @@ -56,10 +55,7 @@ struct literal_char : char_parser, Encoding> [[nodiscard]] std::string get_x4_info() const { // TODO: escape quote - return std::format( - "'{}'", - iris::unicode::transcode(typename Encoding::string_type(1, this->classify_ch_)) - ); + return '\'' + iris::unicode::transcode(typename Encoding::string_type(1, this->classify_ch_)) + '\''; } private: diff --git a/include/iris/x4/core/action.hpp b/include/iris/x4/core/action.hpp index 7261ea438..444c4648a 100644 --- a/include/iris/x4/core/action.hpp +++ b/include/iris/x4/core/action.hpp @@ -23,7 +23,6 @@ #include #include #include -#include namespace iris::x4 { @@ -91,10 +90,6 @@ struct action : proxy_parser, Subject> template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, UnusedAttr&) const - noexcept( - std::is_nothrow_default_constructible_v && - noexcept(this->parse_main(first, last, ctx, std::declval())) - ) { typename base_type::attribute_type attr_temp{}; // value-initialize return this->parse_main(first, last, ctx, attr_temp); @@ -125,7 +120,6 @@ struct action : proxy_parser, Subject> requires can_pass_exposed_attr [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(noexcept(this->parse_main(first, last, ctx, attr))) { return this->parse_main(first, last, ctx, attr); } @@ -135,10 +129,6 @@ struct action : proxy_parser, Subject> requires (!can_pass_exposed_attr) [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& /* attr is discarded */) const - noexcept( - std::is_nothrow_default_constructible_v && - noexcept(this->parse_main(first, last, ctx, std::declval())) - ) { typename base_type::attribute_type attr_temp{}; // value-initialize return this->parse_main(first, last, ctx, attr_temp); @@ -148,7 +138,7 @@ struct action : proxy_parser, Subject> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format("{}[f]", get_info{}(this->subject)); + return get_info{}(this->subject) + "[f]"; } private: @@ -156,7 +146,6 @@ struct action : proxy_parser, Subject> template [[nodiscard]] constexpr bool call_action(Context const&, Attr&) const - noexcept(is_nothrow_directly_invocable_v) { // Explicitly make this hard error instead of emitting "no matching overload". // This provides much more human-friendly errors. @@ -187,7 +176,6 @@ struct action : proxy_parser, Subject> requires directly_invocable::type> [[nodiscard]] constexpr bool call_action(Context const& ctx, Attr& attr) const - noexcept(is_nothrow_directly_invocable_v::type>) { using action_return_type = directly_invoke_result_t::type>; constexpr bool action_returns_bool = std::same_as; @@ -232,11 +220,6 @@ struct action : proxy_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse_main(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept( - std::is_copy_assignable_v && - is_nothrow_parsable_v && - noexcept(this->call_action(ctx, attr)) - ) { It const saved_first = first; if (!this->subject.parse(first, last, ctx, attr)) return false; diff --git a/include/iris/x4/core/detail/parse_alternative.hpp b/include/iris/x4/core/detail/parse_alternative.hpp index 9e08eea66..cd6c27009 100644 --- a/include/iris/x4/core/detail/parse_alternative.hpp +++ b/include/iris/x4/core/detail/parse_alternative.hpp @@ -161,13 +161,7 @@ template Se, class parse_alternative( Parser const& p, It& first, Se const& last, Context const& ctx, Attr& attribute -) noexcept( - is_nothrow_parsable_v< - Parser, It, Se, Context, - std::remove_reference_t::type> - > -) -{ +) { return p.parse(first, last, ctx, pass_variant_attribute::call(attribute)); } @@ -177,16 +171,7 @@ template Se, class parse_alternative( Parser const& p, It& first, Se const& last, Context const& ctx, Attr& attribute -) noexcept( - is_nothrow_parsable_v< - Parser, It, Se, Context, std::remove_reference_t::type> - > && - noexcept(x4::move_to( - std::declval::type>(), - attribute - )) -) -{ +) { auto&& actual_attr = pass_variant_attribute::call(attribute); if (!p.parse(first, last, ctx, actual_attr)) return false; x4::move_to(std::move(actual_attr), attribute); diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 9fda9b254..893aef723 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -64,7 +64,6 @@ struct parse_into_container_impl_default { template Se, class Context, X4NonUnusedAttribute Attr> static constexpr bool call(Parser const& parser, It& first, Se const& last, Context& ctx, Attr& attr) - // never noexcept (requires container insertion) { using unwrapped_attribute_type = iris::unwrap_recursive_t; auto& unwrapped_attr = iris::unwrap_recursive(attr); @@ -102,22 +101,16 @@ struct parse_into_container_impl : parse_into_container_impl_default {}; -template -struct parse_into_container_noexcept : std::false_type {}; - -template - requires X4UnusedAttribute || (!has_attribute_v) -struct parse_into_container_noexcept : is_nothrow_parsable {}; - template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse_into_container( Parser const& parser, It& first, Se const& last, Context const& ctx, Attr& attr -) noexcept(parse_into_container_noexcept::value) +) { if constexpr (X4UnusedAttribute || !has_attribute_v) { // handle unused types first return parser.parse(first, last, ctx, unused); + } else { if constexpr (traits::is_variant_v) { // e.g. `char` when the caller is `+char_` diff --git a/include/iris/x4/core/detail/parse_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index bff108b26..cb9d3ac2f 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -207,7 +207,6 @@ struct partition_attribute template Se, class Context, class Attr> [[nodiscard]] constexpr bool parse_sequence(Parser const& parser, It& first, Se const& last, Context const& ctx, Attr& attr) - // TODO: noexcept { static_assert(X4Attribute); @@ -240,9 +239,7 @@ template Se, class requires (parser_traits::sequence_size > 1) [[nodiscard]] constexpr bool parse_sequence_impl(Parser const& parser, It& first, Se const& last, Context const& ctx, Attr& attr) - noexcept(is_nothrow_parsable_v) { - // static_assert(Parsable); return parser.parse(first, last, ctx, attr); } @@ -250,7 +247,6 @@ template Se, class requires (parser_traits::sequence_size <= 1) [[nodiscard]] constexpr bool parse_sequence_impl(Parser const& parser, It& first, Se const& last, Context const& ctx, Attr& attr) - noexcept(noexcept(detail::parse_into_container(parser, first, last, ctx, attr))) { return detail::parse_into_container(parser, first, last, ctx, attr); } @@ -261,11 +257,6 @@ template< > [[nodiscard]] constexpr bool parse_sequence(Parser const& parser, It& first, Se const& last, Context const& ctx, ContainerAttr& container_attr) - noexcept( - std::is_nothrow_copy_assignable_v && - noexcept(detail::parse_sequence_impl(parser.left, first, last, ctx, container_attr)) && - noexcept(detail::parse_sequence_impl(parser.right, first, last, ctx, container_attr)) - ) { It local_it = first; if (detail::parse_sequence_impl(parser.left, local_it, last, ctx, container_attr) && @@ -285,7 +276,7 @@ struct parse_into_container_impl> call( sequence const& parser, It& first, Se const& last, Context const& ctx, Attr& attr - ) // never noexcept (requires container insertion) + ) { if constexpr (traits::is_container_v) { constexpr bool sequence_attribute_can_directly_hold_value_type = traits::can_hold< diff --git a/include/iris/x4/core/expectation.hpp b/include/iris/x4/core/expectation.hpp index c1fd0e232..ae865f1cc 100644 --- a/include/iris/x4/core/expectation.hpp +++ b/include/iris/x4/core/expectation.hpp @@ -47,17 +47,12 @@ constexpr bool has_expectation_failure(Context const& ctx) noexcept return x4::get(ctx).has_value(); } -// -// Creation of a brand-new expectation_failure instance. -// This is the primary overload. -// template constexpr void set_expectation_failure( It where, Subject const& subject, Context const& ctx ) - noexcept(noexcept(x4::get(ctx).emplace(std::move(where), x4::what(subject)))) { static_assert( has_context_v, @@ -67,6 +62,22 @@ constexpr void set_expectation_failure( ); x4::get(ctx).emplace(std::move(where), x4::what(subject)); } +template +constexpr void set_expectation_failure_if_empty( + It where, + Subject const& subject, + Context const& ctx +) +{ + static_assert( + has_context_v, + "Context type was not specified for `x4::contexts::expectation_failure`. " + "You probably forgot: `x4::with(failure)[p]`. " + "Note that you must also bind the context to your skipper." + ); + auto& failure = x4::get(ctx); + if (!failure) failure.emplace(std::move(where), x4::what(subject)); +} template [[nodiscard]] diff --git a/include/iris/x4/core/parser.hpp b/include/iris/x4/core/parser.hpp index c3103eb5e..afb7aeccd 100644 --- a/include/iris/x4/core/parser.hpp +++ b/include/iris/x4/core/parser.hpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -344,58 +345,17 @@ constexpr bool is_parser_nothrow_constructible_v = is_parser_nothrow_constructib template -concept Parsable = requires(Parser const& p) { - { - p.parse( - std::declval(), // first - std::declval(), // last - std::declval(), // context - std::declval() // attr - ) - } -> std::same_as; +concept Parsable = requires(Parser const& p, It& first, Se last, Context const& ctx, Attr& attr) { + { p.parse(first, last, ctx, attr) } -> std::same_as; }; template struct is_parsable : std::bool_constant> -{ - static_assert(X4ExplicitSubject); - static_assert(!std::is_reference_v); - static_assert(std::forward_iterator); - static_assert(std::sentinel_for); - static_assert(!std::is_reference_v); - static_assert(!std::is_reference_v); - static_assert(X4Attribute); -}; +{}; template constexpr bool is_parsable_v = is_parsable::value; -template -struct is_nothrow_parsable -{ - static_assert(X4ExplicitSubject); - static_assert(!std::is_reference_v); - static_assert(std::forward_iterator); - static_assert(std::sentinel_for); - static_assert(!std::is_reference_v); - static_assert(!std::is_reference_v); - static_assert(X4Attribute); - - static constexpr bool value = requires(Parser const& p) { - { - p.parse( - std::declval(), // first - std::declval(), // last - std::declval(), // context - std::declval() // attr - ) - } noexcept -> std::same_as; - }; -}; - -template -constexpr bool is_nothrow_parsable_v = is_nothrow_parsable::value; - template concept X4ExplicitParser = @@ -428,6 +388,8 @@ concept X4Parser = X4ExplicitParser || X4ImplicitParser struct get_info { diff --git a/include/iris/x4/core/skip_over.hpp b/include/iris/x4/core/skip_over.hpp index 83910a2d0..9fa738470 100644 --- a/include/iris/x4/core/skip_over.hpp +++ b/include/iris/x4/core/skip_over.hpp @@ -64,12 +64,6 @@ struct builtin_skipper_traits 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, - std::remove_cvref_t(ctx))>, - unused_type - >) { auto const& skipper = x4::get(ctx); diff --git a/include/iris/x4/directive/expect.hpp b/include/iris/x4/directive/expect.hpp index d80d5dd1a..f288a87cd 100644 --- a/include/iris/x4/directive/expect.hpp +++ b/include/iris/x4/directive/expect.hpp @@ -29,20 +29,10 @@ struct expect_directive : proxy_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - // never noexcept; expectation failure requires construction of debug information { - static_assert( - has_context_v, - "Context type was not specified for `x4::contexts::expectation_failure`. " - "You probably forgot: `x4::with(failure)[p]`. " - "Note that you must also bind the context to your skipper." - ); - bool const r = this->subject.parse(first, last, ctx, attr); - - // only the first failure is needed - if (!r && !x4::has_expectation_failure(ctx)) { - x4::set_expectation_failure(first, this->subject, ctx); + if (!r) { + x4::set_expectation_failure_if_empty(first, this->subject, ctx); } return r; } @@ -84,7 +74,7 @@ struct parse_into_container_impl> call( expect_directive const& parser, It& first, Se const& last, Context const& ctx, Attr& attr - ) // never noexcept; expectation failure requires construction of debug information + ) { static_assert( has_context_v, diff --git a/include/iris/x4/directive/lexeme.hpp b/include/iris/x4/directive/lexeme.hpp index 708c35f02..206a79383 100644 --- a/include/iris/x4/directive/lexeme.hpp +++ b/include/iris/x4/directive/lexeme.hpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -29,14 +28,6 @@ struct lexeme_directive : proxy_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept( - noexcept(x4::skip_over(first, last, ctx)) && - is_nothrow_parsable_v< - Subject, It, Se, - std::remove_cvref_t(ctx))>, - Attr - > - ) { auto it = first; x4::skip_over(it, last, ctx); // pre-skip @@ -52,7 +43,7 @@ struct lexeme_directive : proxy_parser, Subject> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format("lexeme[{}]", get_info{}(this->subject)); + return "lexeme[" + get_info{}(this->subject) + ']'; } }; diff --git a/include/iris/x4/directive/matches.hpp b/include/iris/x4/directive/matches.hpp index 395ac7f63..dd6ce77a6 100644 --- a/include/iris/x4/directive/matches.hpp +++ b/include/iris/x4/directive/matches.hpp @@ -35,10 +35,6 @@ struct matches_directive : unary_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept( - is_nothrow_parsable_v && - noexcept(x4::move_to(std::declval(), attr)) - ) { bool const matched = this->subject.parse(first, last, ctx, unused); diff --git a/include/iris/x4/directive/no_case.hpp b/include/iris/x4/directive/no_case.hpp index 89df4d2bb..eac3d8535 100644 --- a/include/iris/x4/directive/no_case.hpp +++ b/include/iris/x4/directive/no_case.hpp @@ -30,11 +30,6 @@ struct no_case_directive : proxy_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(is_nothrow_parsable_v< - Subject, It, Se, - decltype(x4::make_context(detail::case_compare_no_case, ctx)), - Attr - >) { return this->subject.parse( first, last, diff --git a/include/iris/x4/directive/no_skip.hpp b/include/iris/x4/directive/no_skip.hpp index a64aa0147..373590f55 100644 --- a/include/iris/x4/directive/no_skip.hpp +++ b/include/iris/x4/directive/no_skip.hpp @@ -30,11 +30,6 @@ struct no_skip_directive : proxy_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(is_nothrow_parsable_v< - Subject, It, Se, - std::remove_cvref_t(ctx))>, - Attr - >) { // // No pre-skip here, in contrast to `lexeme` diff --git a/include/iris/x4/directive/omit.hpp b/include/iris/x4/directive/omit.hpp index 9a271afa5..0dfff3ef7 100644 --- a/include/iris/x4/directive/omit.hpp +++ b/include/iris/x4/directive/omit.hpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -35,17 +34,13 @@ struct omit_directive : unary_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr const&) const - noexcept(is_nothrow_parsable_v) { return this->subject.parse(first, last, ctx, unused); } [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format( - "omit[{}]", - get_info{}(this->subject) - ); + return "omit[" + get_info{}(this->subject) + ']'; } }; diff --git a/include/iris/x4/directive/repeat.hpp b/include/iris/x4/directive/repeat.hpp index 07e5a7832..45edbf6a0 100644 --- a/include/iris/x4/directive/repeat.hpp +++ b/include/iris/x4/directive/repeat.hpp @@ -106,7 +106,6 @@ struct repeat_directive : proxy_parser, Subjec template Se, class Context, X4NonUnusedAttribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - // never noexcept; requires container insertion { auto& container_attr = list_like_parser::get_container(attr); list_like_parser::chunk_buffer chunk_buf; @@ -142,11 +141,6 @@ struct repeat_directive : proxy_parser, Subjec template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, UnusedAttr& unused_attr) const - noexcept( - noexcept(detail::parse_into_container(this->subject, first, last, ctx, x4::assume_container(unused_attr))) && - std::is_nothrow_copy_assignable_v && - is_nothrow_parsable_v - ) { It local_it = first; typename Bounds::value_type i{}; diff --git a/include/iris/x4/directive/skip.hpp b/include/iris/x4/directive/skip.hpp index 0e7af5643..ee3d9a74f 100644 --- a/include/iris/x4/directive/skip.hpp +++ b/include/iris/x4/directive/skip.hpp @@ -16,7 +16,6 @@ #include -#include #include #include #include @@ -48,18 +47,14 @@ struct skip_directive : proxy_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(is_nothrow_parsable_v, Attr>) { return this->subject.parse(first, last, x4::replace_first_context(ctx, skipper_), attr); } [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format( - "skip({})[{}]", - get_info{}(this->skipper_), - get_info{}(this->subject) - ); + return "skip(" + get_info{}(this->skipper_) + ")[" + + get_info{}(this->subject) + ']'; } private: @@ -82,7 +77,6 @@ struct builtin_skip_directive : proxy_parser Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(is_nothrow_parsable_v) { builtin_skipper_kind& skipper_kind = x4::get(ctx); auto const old_skipper_kind = skipper_kind; @@ -101,11 +95,6 @@ struct builtin_skip_directive : proxy_parser, builtin_skipper_kind>) [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(is_nothrow_parsable_v< - Subject, It, Se, - std::remove_cvref_t(ctx, std::declval()))>, - Attr - >) { // This value could be reset by some nested parsers, so it can't be const /* constexpr */ builtin_skipper_kind skipper_kind = Kind; @@ -114,11 +103,9 @@ struct builtin_skip_directive : proxy_parser::name, - get_info{}(this->subject) - ); + return std::string("skip(") + + detail::builtin_skipper_traits::name + ")[" + + get_info{}(this->subject) + ']'; } }; diff --git a/include/iris/x4/directive/with.hpp b/include/iris/x4/directive/with.hpp index 64c6c1dfd..25e4feadd 100644 --- a/include/iris/x4/directive/with.hpp +++ b/include/iris/x4/directive/with.hpp @@ -12,7 +12,6 @@ #include -#include #include #include #include @@ -117,7 +116,6 @@ struct with_directive : detail::with_directive_impl template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(is_nothrow_parsable_v, Attr>) { return this->subject.parse( first, last, @@ -128,7 +126,7 @@ struct with_directive : detail::with_directive_impl [[nodiscard]] std::string get_x4_info() const { - return std::format("with<...>[{}]", get_info{}(this->subject)); + return "with<...>[" + get_info{}(this->subject) + ']'; } private: @@ -234,20 +232,13 @@ struct without_directive : proxy_parser, Subj template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept( - x4::is_nothrow_parsable_v< - Subject, It, Se, - std::remove_cvref_t(ctx))>, - Attr - > - ) { return this->subject.parse(first, last, x4::remove_all_contexts(ctx), attr); } [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format("without<...>[{}]", get_info{}(this->subject)); + return "without<...>[" + get_info{}(this->subject) + ']'; } }; diff --git a/include/iris/x4/directive/with_local.hpp b/include/iris/x4/directive/with_local.hpp index 61dcdade0..82daa7960 100644 --- a/include/iris/x4/directive/with_local.hpp +++ b/include/iris/x4/directive/with_local.hpp @@ -67,14 +67,6 @@ struct with_local_directive : proxy_parser, template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept( - std::is_nothrow_default_constructible_v && - x4::is_nothrow_parsable_v< - Subject, It, Se, - decltype(x4::replace_first_context(ctx, std::declval())), - Attr - > - ) { // `x4::make_context(...)` cannot be used here as it invokes infinite recursive instantiation. diff --git a/include/iris/x4/operator/alternative.hpp b/include/iris/x4/operator/alternative.hpp index ffcbdd1bc..849b0803f 100644 --- a/include/iris/x4/operator/alternative.hpp +++ b/include/iris/x4/operator/alternative.hpp @@ -22,7 +22,6 @@ #include // IWYU pragma: keep -#include #include #include #include @@ -101,10 +100,6 @@ struct alternative : binary_parser, Left, Right> template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, UnusedAttr const&) const - noexcept( - is_nothrow_parsable_v && - is_nothrow_parsable_v - ) { if constexpr (has_context_v) { return this->left.parse(first, last, ctx, unused) || @@ -126,12 +121,6 @@ struct alternative : binary_parser, Left, Right> requires (!traits::X4Container) [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept( - noexcept(detail::parse_alternative(this->left, first, last, ctx, attr)) && - noexcept(detail::parse_alternative(this->right, first, last, ctx, attr)) && - std::is_nothrow_default_constructible_v> && - noexcept(x4::move_to(std::declval>(), attr)) - ) { static_assert( std::default_initializable>, @@ -157,13 +146,6 @@ struct alternative : binary_parser, Left, Right> template Se, class Context, traits::X4Container ContainerAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, ContainerAttr& attr) const - noexcept( - noexcept(detail::parse_alternative(this->left, first, last, ctx, attr)) && - noexcept(detail::parse_alternative(this->right, first, last, ctx, attr)) && - noexcept(x4::move_to(std::declval(), attr)) && - std::is_nothrow_default_constructible_v && - noexcept(traits::clear(attr)) - ) { static_assert(!std::same_as, unused_type>); static_assert(!std::same_as, unused_container_type>); @@ -218,11 +200,7 @@ struct alternative : binary_parser, Left, Right> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format( - "{} | {}", - get_info{}(this->left), - get_info{}(this->right) - ); + return get_info{}(this->left) + " | " + get_info{}(this->right); } }; diff --git a/include/iris/x4/operator/and_predicate.hpp b/include/iris/x4/operator/and_predicate.hpp index 301a22413..34606c02f 100644 --- a/include/iris/x4/operator/and_predicate.hpp +++ b/include/iris/x4/operator/and_predicate.hpp @@ -30,10 +30,6 @@ struct and_predicate : unary_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& /*attr*/) const - noexcept( - std::is_nothrow_copy_assignable_v && - is_nothrow_parsable_v - ) { auto it = first; return this->subject.parse(it, last, ctx, unused); diff --git a/include/iris/x4/operator/delimited_list.hpp b/include/iris/x4/operator/delimited_list.hpp index 6fc1ca606..68b3c416e 100644 --- a/include/iris/x4/operator/delimited_list.hpp +++ b/include/iris/x4/operator/delimited_list.hpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -39,7 +38,6 @@ struct delimited_list : binary_parser, Left, Right> template Se, class Context, X4NonUnusedAttribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - // never noexcept; requires container insertion { auto& container_attr = list_like_parser::get_container(attr); list_like_parser::chunk_buffer chunk_buf; @@ -70,11 +68,6 @@ struct delimited_list : binary_parser, Left, Right> template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, UnusedAttr& unused_attr) const - noexcept( - noexcept(detail::parse_into_container(this->left, first, last, ctx, x4::assume_container(unused_attr))) && - std::is_nothrow_copy_assignable_v && - is_nothrow_parsable_v - ) { // In order to succeed we need to match at least one element if (!detail::parse_into_container(this->left, first, last, ctx, x4::assume_container(unused_attr))) { @@ -99,11 +92,7 @@ struct delimited_list : binary_parser, Left, Right> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format( - "({} % {})", - get_info{}(this->left), - get_info{}(this->right) - ); + return '(' + get_info{}(this->left) + " % " + get_info{}(this->right); } }; diff --git a/include/iris/x4/operator/kleene.hpp b/include/iris/x4/operator/kleene.hpp index cf45fb26d..80c9535ea 100644 --- a/include/iris/x4/operator/kleene.hpp +++ b/include/iris/x4/operator/kleene.hpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -40,7 +39,6 @@ struct kleene : unary_parser, Subject> template Se, class Context, X4NonUnusedAttribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - // never noexcept; requires container insertion { auto& container_attr = list_like_parser::get_container(attr); list_like_parser::chunk_buffer chunk_buf; @@ -59,7 +57,6 @@ struct kleene : unary_parser, Subject> template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, UnusedAttr& unused_attr) const - noexcept(noexcept(detail::parse_into_container(this->subject, first, last, ctx, x4::assume_container(unused_attr)))) { while (detail::parse_into_container(this->subject, first, last, ctx, x4::assume_container(unused_attr))) /* loop */; @@ -73,10 +70,7 @@ struct kleene : unary_parser, Subject> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format( - "*{}", - get_info{}(this->subject) - ); + return '*' + get_info{}(this->subject); } }; diff --git a/include/iris/x4/operator/not_predicate.hpp b/include/iris/x4/operator/not_predicate.hpp index ea26fd31f..2100412c9 100644 --- a/include/iris/x4/operator/not_predicate.hpp +++ b/include/iris/x4/operator/not_predicate.hpp @@ -32,10 +32,6 @@ struct not_predicate : unary_parser, Subject> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& /*attr*/) const - noexcept( - std::is_nothrow_copy_assignable_v && - is_nothrow_parsable_v - ) { It local_first = first; diff --git a/include/iris/x4/operator/optional.hpp b/include/iris/x4/operator/optional.hpp index e0fc7c140..6f1256ba9 100644 --- a/include/iris/x4/operator/optional.hpp +++ b/include/iris/x4/operator/optional.hpp @@ -47,7 +47,6 @@ struct optional : unary_parser, Subject> > [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(is_nothrow_parsable_v) { // discard [[nodiscard]] (void)this->subject.parse(first, last, ctx, attr); @@ -66,7 +65,6 @@ struct optional : unary_parser, Subject> > [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(noexcept(detail::parse_into_container(this->subject, first, last, ctx, attr))) { // discard [[nodiscard]] (void)detail::parse_into_container(this->subject, first, last, ctx, attr); @@ -85,11 +83,6 @@ struct optional : unary_parser, Subject> > [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept( - std::is_nothrow_default_constructible_v::type> && - is_nothrow_parsable_v::type> && - noexcept(x4::move_to(std::declval::type&&>(), attr)) - ) { typename traits::optional_value::type val{}; // value-initialize diff --git a/include/iris/x4/operator/plus.hpp b/include/iris/x4/operator/plus.hpp index 0f618ae8a..289de9b29 100644 --- a/include/iris/x4/operator/plus.hpp +++ b/include/iris/x4/operator/plus.hpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -40,7 +39,6 @@ struct plus : unary_parser, Subject> template Se, class Context, X4NonUnusedAttribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - // never noexcept; requires container insertion { auto& container_attr = list_like_parser::get_container(attr); list_like_parser::chunk_buffer chunk_buf; @@ -65,7 +63,6 @@ struct plus : unary_parser, Subject> template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, UnusedAttr& unused_attr) const - noexcept(noexcept(detail::parse_into_container(this->subject, first, last, ctx, x4::assume_container(unused_attr)))) { if (!detail::parse_into_container(this->subject, first, last, ctx, x4::assume_container(unused_attr))) { return false; @@ -83,10 +80,7 @@ struct plus : unary_parser, Subject> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format( - "+{}", - get_info{}(this->subject) - ); + return '+' + get_info{}(this->subject); } }; diff --git a/include/iris/x4/operator/sequence.hpp b/include/iris/x4/operator/sequence.hpp index f0c720403..2d975828a 100644 --- a/include/iris/x4/operator/sequence.hpp +++ b/include/iris/x4/operator/sequence.hpp @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -113,11 +112,6 @@ struct sequence : binary_parser, Left, Right> template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, UnusedAttr const&) const - noexcept( - std::is_nothrow_copy_assignable_v && - is_nothrow_parsable_v && - is_nothrow_parsable_v - ) { It const first_saved = first; @@ -141,7 +135,6 @@ struct sequence : binary_parser, Left, Right> template Se, class Context, X4NonUnusedAttribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(noexcept(detail::parse_sequence(*this, first, last, ctx, attr))) { return detail::parse_sequence(*this, first, last, ctx, attr); } @@ -149,17 +142,11 @@ struct sequence : binary_parser, Left, Right> [[nodiscard]] constexpr std::string get_x4_info() const { if constexpr (iris::is_ttp_specialization_of_v) { - return std::format( - "{} > {}", - get_info{}(this->left), - get_info{}(this->right.subject) - ); + return get_info{}(this->left) + " > " + + get_info{}(this->right.subject); } else { - return std::format( - "{} >> {}", - get_info{}(this->left), - get_info{}(this->right) - ); + return get_info{}(this->left) + " >> " + + get_info{}(this->right); } } }; diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index f965aa4db..310bee1f3 100644 --- a/include/iris/x4/rule.hpp +++ b/include/iris/x4/rule.hpp @@ -38,7 +38,7 @@ namespace iris::x4 { -template +template struct rule; namespace detail { @@ -165,7 +165,7 @@ struct rule_impl parse_rhs( RHS const& rhs, It& first, Se const& last, RContext const& rcontext, RHSAttr& rhs_attr - ) // never noexcept; requires complex handling + ) { // See if the user has `IRIS_X4_DEFINE` for this rule constexpr bool is_default_parse_rule = std::same_as< @@ -249,7 +249,7 @@ struct rule_impl { // Do down-stream transformation, provide attribute for `rhs` parser using transform = traits::transform_attribute; - using transform_attr = typename transform::type; + using transform_attr = transform::type; transform_attr rhs_attr = transform::pre(exposed_attr); // Creates a place to hold the result of parse_rhs @@ -301,11 +301,9 @@ struct rule_impl } }; -template +template struct rule_definition : parser> { - static_assert(!std::same_as, unused_container_type>, "`rule_definition` with `unused_container_type` is not supported"); - using this_type = rule_definition; using id = RuleID; using lhs_type = rule; @@ -321,12 +319,15 @@ struct rule_definition : parser) : rhs_(std::forward(rhs)) , name(std::move(name)) - {} + { + static_assert(X4Subject); + static_assert(X4Attribute); + static_assert(!std::same_as, unused_container_type>, "`rule_definition` with `unused_container_type` is not supported"); + } template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - // never noexcept; requires very complex implementation details { return rule_impl ::template call_rule_definition( @@ -388,7 +389,7 @@ concept RuleAttrCompatible = } // detail -template +template struct rule : parser> { // This type MUST be constructible with incomplete types. @@ -420,7 +421,6 @@ struct rule : parser> detail::RuleAttrCompatible [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Exposed& exposed_attr) const - // never noexcept; requires very complex implementation details { check_invariants(); static_assert(has_attribute, "A rule must have an attribute. Check your rule definition."); @@ -478,7 +478,6 @@ struct rule : parser> template Se, class Context> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, unused_type const&) const - // never noexcept; requires very complex implementation details { check_invariants(); // make sure we pass exactly the rule attribute type diff --git a/include/iris/x4/string/literal_string.hpp b/include/iris/x4/string/literal_string.hpp index 71839ec22..22f92dc8b 100644 --- a/include/iris/x4/string/literal_string.hpp +++ b/include/iris/x4/string/literal_string.hpp @@ -20,7 +20,6 @@ #include -#include #include #include #include @@ -73,7 +72,7 @@ struct literal_string : parser> [[nodiscard]] std::string get_x4_info() const { // TODO: escape quotes - return std::format("\"{}\"", iris::unicode::transcode(std::basic_string_view{this->str_})); + return '"' + iris::unicode::transcode(std::basic_string_view{this->str_}) + '"'; } private: diff --git a/test/x4/as.cpp b/test/x4/as.cpp index 9b036f9e8..5d066c335 100644 --- a/test/x4/as.cpp +++ b/test/x4/as.cpp @@ -321,16 +321,9 @@ TEST_CASE("as (single type)") { constexpr auto p = x4::as(eps); using Underlying = std::remove_const_t; - using AsParser = std::remove_const_t; static_assert(std::same_as::attribute_type, unused_type>); - static_assert(x4::is_nothrow_parsable_v); - static_assert(x4::is_nothrow_parsable_v); // Arbitrary exposed attribute - - static_assert(x4::is_nothrow_parsable_v); - static_assert(x4::is_nothrow_parsable_v); // Arbitrary exposed attribute - std::string_view input; It first = input.begin(); Se const last = input.end(); @@ -343,16 +336,9 @@ TEST_CASE("as (single type)") { constexpr auto p = x4::as(eps); using Underlying = std::remove_const_t; - using AsParser = std::remove_const_t; static_assert(std::same_as::attribute_type, unused_type>); - static_assert(x4::is_nothrow_parsable_v); - static_assert(x4::is_nothrow_parsable_v); // Arbitrary exposed attribute - - static_assert(x4::is_nothrow_parsable_v); - static_assert(x4::is_nothrow_parsable_v); // Arbitrary exposed attribute - std::string_view input; It first = input.begin(); Se const last = input.end(); diff --git a/test/x4/parser.cpp b/test/x4/parser.cpp index 0f0fddfc2..0ed6165bf 100644 --- a/test/x4/parser.cpp +++ b/test/x4/parser.cpp @@ -106,7 +106,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); @@ -142,7 +141,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_unary_parser{}, unused)); @@ -151,7 +149,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_binary_parser{}, unused)); @@ -161,7 +158,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_unused_parser{}, unused)); @@ -170,7 +166,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_unary_unused_parser{}, unused)); @@ -179,7 +174,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_binary_unused_parser{}, unused)); @@ -195,7 +189,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_parser{}, unused)); @@ -204,7 +197,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_unary_parser{}, unused)); @@ -213,7 +205,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_binary_parser{}, unused)); @@ -223,7 +214,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_unused_parser{}, unused)); @@ -232,7 +222,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_unary_unused_parser{}, unused)); @@ -241,7 +230,6 @@ TEST_CASE("parser") STATIC_CHECK(x4::X4Subject); STATIC_CHECK(x4::X4ExplicitSubject); STATIC_CHECK(x4::is_parsable_v); - STATIC_CHECK(!x4::is_nothrow_parsable_v); STATIC_CHECK(x4::X4Parser); STATIC_CHECK(x4::X4ExplicitParser); STATIC_CHECK(x4::parse("", minimal_binary_unused_parser{}, unused)); From 3e1482125f33b8c767ac37e5f2641062cabb413f Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Tue, 22 Sep 2026 16:32:56 +0900 Subject: [PATCH 3/4] Rewrite sequence with variadic args --- .../x4/core/detail/parse_into_container.hpp | 5 +- .../iris/x4/core/detail/parse_sequence.hpp | 316 +++++++----------- include/iris/x4/core/multi_parser.hpp | 35 ++ include/iris/x4/core/multi_parser_storage.hpp | 89 +++++ include/iris/x4/operator/sequence.hpp | 151 +++++---- .../iris/x4/traits/attribute_of_binary.hpp | 30 +- test/x4/iterator.cpp | 8 +- test/x4/partial_success.cpp | 12 +- 8 files changed, 371 insertions(+), 275 deletions(-) create mode 100644 include/iris/x4/core/multi_parser.hpp create mode 100644 include/iris/x4/core/multi_parser_storage.hpp diff --git a/include/iris/x4/core/detail/parse_into_container.hpp b/include/iris/x4/core/detail/parse_into_container.hpp index 893aef723..9cde4bc82 100644 --- a/include/iris/x4/core/detail/parse_into_container.hpp +++ b/include/iris/x4/core/detail/parse_into_container.hpp @@ -10,9 +10,9 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ -#include +#include // IWYU pragma: keep -#include +#include #include #include @@ -87,7 +87,6 @@ struct parse_into_container_impl_default // attribute is single element tuple-like; unwrap and try again return parse_into_container_impl_default::call(parser, first, last, ctx, alloy::get<0>(unwrapped_attr)); } else { - //attr = nullptr; static_assert(false, "[BUG] parse_into_container accepts a container, a variant of container or a single element tuple-like of container"); return false; } diff --git a/include/iris/x4/core/detail/parse_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index cb9d3ac2f..8bfd368c6 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -13,6 +13,8 @@ #include #include +#include +#include #include #include @@ -21,277 +23,207 @@ #include #include -#include +#include #include #include -#include #include +#include + namespace iris::x4 { -template +template struct sequence; } // iris::x4 namespace iris::x4::detail { -struct pass_sequence_attribute_unused +template +struct sequence_layout { - using type = unused_type; - - template - [[nodiscard]] static constexpr unused_type - call(T&) noexcept - { - return unused_type{}; - } -}; + static constexpr std::size_t parser_count = sizeof...(Ps); -template -struct pass_sequence_attribute_size_one_view -{ - using type = alloy::tuple_element_t<0, Attr>; + static constexpr std::array elem_sequence_sizes{parser_traits::sequence_size...}; + static constexpr std::size_t total_sequence_size = (std::size_t{0} + ... + parser_traits::sequence_size); - [[nodiscard]] static constexpr type - call(Attr& attribute) - noexcept(noexcept(alloy::get<0>(attribute))) - { - return alloy::get<0>(attribute); - } -}; + static constexpr std::array elem_offsets = [] { + std::array result{}; + std::size_t offset = 0; + for (std::size_t i = 0; i < parser_count; ++i) { + result[i] = offset; + offset += elem_sequence_sizes[i]; + } + return result; + }(); -template -struct pass_through_sequence_attribute -{ - using type = Attr&; + static constexpr std::size_t attributed_count = (std::size_t{0} + ... + std::size_t{has_attribute_v}); - template - [[nodiscard]] static constexpr Attr_& - call(Attr_& attribute) noexcept - { - return attribute; - } + static constexpr std::size_t single_attributed_index = [] { + std::array const is_attributed{has_attribute_v...}; + for (std::size_t i = 0; i < parser_count; ++i) { + if (is_attributed[i]) return i; + } + return parser_count; + }(); }; -template -struct pass_sequence_attribute : std::conditional_t< - traits::is_size_one_view_v, - pass_sequence_attribute_size_one_view, - pass_through_sequence_attribute -> -{}; +template +struct sequence_passes_view : std::false_type {}; -template -struct pass_sequence_attribute, Attr> - : pass_through_sequence_attribute -{}; +template +struct sequence_passes_view> : std::true_type {}; -template - requires requires { - typename Parser::proxy_backend_type; - } -struct pass_sequence_attribute - : pass_sequence_attribute -{}; - -template -struct partition_attribute {}; - -template Attr> - requires - has_attribute_v && - has_attribute_v -struct partition_attribute -{ - static constexpr std::size_t l_size = parser_traits::sequence_size; - static constexpr std::size_t r_size = parser_traits::sequence_size; - - static constexpr std::size_t actual_size = alloy::tuple_size_v; - static constexpr std::size_t expected_size = l_size + r_size; - - // If you got an error here, then you are trying to pass - // a tuple-like with the wrong number of elements - // as that expected by the (sequence) parser. - static_assert( - actual_size >= expected_size, - "Sequence size of the passed attribute is less than expected." - ); - static_assert( - actual_size <= expected_size, - "Sequence size of the passed attribute is greater than expected." - ); - - using view = alloy::tuple_ref_t; - using splitted = alloy::tuple_split_t; - using l_part = alloy::tuple_element_t<0, splitted>; - using r_part = alloy::tuple_element_t<1, splitted>; - using l_pass = pass_sequence_attribute; - using r_pass = pass_sequence_attribute; - - [[nodiscard]] static constexpr l_part left(Attr& s) - // TODO: noexcept - { - return alloy::get<0>(alloy::tuple_split(alloy::tuple_ref(s))); - } +template + requires requires { typename P::proxy_backend_type; } +struct sequence_passes_view

: sequence_passes_view {}; - [[nodiscard]] static constexpr r_part right(Attr& s) - // TODO: noexcept - { - return alloy::get<1>(alloy::tuple_split(alloy::tuple_ref(s))); - } -}; -template - requires - (!has_attribute_v) && - has_attribute_v -struct partition_attribute +template +struct parse_sequence_tuple { - using l_pass = pass_sequence_attribute_unused; - using r_pass = pass_sequence_attribute; + using layout = sequence_layout; - [[nodiscard]] static constexpr unused_type left(Attr&) noexcept + template Se, class Context> + [[nodiscard]] static constexpr bool + parse_element(sequence const& seq, It& first, Se const& last, Context const& ctx, Attr& attr) { - return unused; - } + using parser_type = multi_parser_t; + auto const& elem = x4::get_parser(seq.elems); + constexpr std::size_t sequence_size = layout::elem_sequence_sizes[I]; + constexpr std::size_t offset = layout::elem_offsets[I]; - [[nodiscard]] static constexpr Attr& right(Attr& s) noexcept - { - return s; - } -}; + if constexpr (layout::attributed_count == 1) { + if constexpr (I != layout::single_attributed_index) { + return elem.parse(first, last, ctx, unused); -template - requires - has_attribute_v && - (!has_attribute_v) -struct partition_attribute -{ - using l_pass = pass_sequence_attribute; - using r_pass = pass_sequence_attribute_unused; + } else if constexpr (traits::is_size_one_view_v && !sequence_passes_view::value) { + auto&& elem_attr = x4::make_container_appender(alloy::get<0>(attr)); + return elem.parse(first, last, ctx, elem_attr); - [[nodiscard]] static constexpr Attr& left(Attr& s) noexcept - { - return s; - } + } else { + auto&& elem_attr = x4::make_container_appender(attr); + return elem.parse(first, last, ctx, elem_attr); + } - [[nodiscard]] static constexpr unused_type right(Attr&) noexcept - { - return unused; - } -}; + } else { + if constexpr (sequence_size == 0) { + return elem.parse(first, last, ctx, unused); -template - requires - (!has_attribute_v) && - (!has_attribute_v) -struct partition_attribute -{ - using l_pass = pass_sequence_attribute_unused; - using r_pass = pass_sequence_attribute_unused; + } else if constexpr (sequence_size == 1 && !sequence_passes_view::value) { + auto&& elem_attr = x4::make_container_appender(alloy::get(attr)); + return elem.parse(first, last, ctx, elem_attr); - [[nodiscard]] static constexpr unused_type left(Attr&) noexcept - { - return unused; + } else { + auto slice = [&](std::index_sequence) { + return alloy::tuple&...>( + alloy::get(attr)... + ); + }(std::make_index_sequence{}); + return elem.parse(first, last, ctx, slice); + } + } } - [[nodiscard]] static constexpr unused_type right(Attr&) noexcept + template Se, class Context> + [[nodiscard]] static constexpr bool + parse_all(std::index_sequence, sequence const& seq, It& first, Se const& last, Context const& ctx, Attr& attr) { - return unused; + return (parse_sequence_tuple::parse_element(seq, first, last, ctx, attr) && ...); } }; -// Default overload, no constraints on attribute category -template Se, class Context, class Attr> + +// Default overload; attribute is NOT a container +template Se, class Context, class Attr> [[nodiscard]] constexpr bool -parse_sequence(Parser const& parser, It& first, Se const& last, Context const& ctx, Attr& attr) +parse_sequence(sequence const& seq, It& first, Se const& last, Context const& ctx, Attr& attr) { static_assert(X4Attribute); - - using partition = partition_attribute< - typename Parser::left_type, - typename Parser::right_type, - Attr - >; - - auto&& l_part = partition::left(attr); - auto&& r_part = partition::right(attr); - auto&& l_attr = partition::l_pass::call(l_part); - auto&& r_attr = partition::r_pass::call(r_part); - - auto&& l_attr_appender = x4::make_container_appender(l_attr); - auto&& r_attr_appender = x4::make_container_appender(r_attr); + static_assert(!traits::CategorizedAttr); + + using layout = sequence_layout; + + if constexpr (layout::attributed_count >= 2) { + static_assert( + traits::CategorizedAttr, + "The attribute of a sequence with >=2 attributed elements must be tuple-like." + ); + static_assert( + alloy::tuple_size_v >= layout::total_sequence_size, + "Sequence size of the passed attribute is less than expected." + ); + static_assert( + alloy::tuple_size_v <= layout::total_sequence_size, + "Sequence size of the passed attribute is greater than expected." + ); + } It local_it = first; - if (parser.left.parse(local_it, last, ctx, l_attr_appender) && - parser.right.parse(local_it, last, ctx, r_attr_appender) - ) { + if (parse_sequence_tuple::parse_all(std::index_sequence_for{}, seq, local_it, last, ctx, attr)) { first = std::move(local_it); return true; } - return false; } -template Se, class Context, X4Attribute Attr> - requires (parser_traits::sequence_size > 1) -[[nodiscard]] constexpr bool -parse_sequence_impl(Parser const& parser, It& first, Se const& last, Context const& ctx, Attr& attr) -{ - return parser.parse(first, last, ctx, attr); -} - -template Se, class Context, X4Attribute Attr> - requires (parser_traits::sequence_size <= 1) -[[nodiscard]] constexpr bool -parse_sequence_impl(Parser const& parser, It& first, Se const& last, Context const& ctx, Attr& attr) -{ - return detail::parse_into_container(parser, first, last, ctx, attr); -} - +// Attribute is a container template< - class Parser, std::forward_iterator It, std::sentinel_for Se, class Context, + class... Ps, std::forward_iterator It, std::sentinel_for Se, class Context, traits::CategorizedAttr ContainerAttr > [[nodiscard]] constexpr bool -parse_sequence(Parser const& parser, It& first, Se const& last, Context const& ctx, ContainerAttr& container_attr) +parse_sequence(sequence const& seq, It& first, Se const& last, Context const& ctx, ContainerAttr& container_attr) { It local_it = first; - if (detail::parse_sequence_impl(parser.left, local_it, last, ctx, container_attr) && - detail::parse_sequence_impl(parser.right, local_it, last, ctx, container_attr) - ) { + bool const ok = [&](std::index_sequence) -> bool { + auto parse_sequence_impl = [&](P const& parser) -> bool { + if constexpr (parser_traits

::sequence_size > 1) { + // Exposed attribute = container, Parser expects sequence attribute + return parser.parse(local_it, last, ctx, container_attr); + + } else { + // Exposed attribute = container, Parser expects non-sequence attribute + return detail::parse_into_container(parser, local_it, last, ctx, container_attr); + } + }; + return (parse_sequence_impl(x4::get_parser(seq.elems)) && ...); + }(std::index_sequence_for{}); + + if (ok) { first = std::move(local_it); return true; } return false; } -template -struct parse_into_container_impl> +template +struct parse_into_container_impl> { template Se, class Context, X4Attribute Attr> [[nodiscard]] static constexpr bool call( - sequence const& parser, It& first, Se const& last, + sequence const& seq, It& first, Se const& last, Context const& ctx, Attr& attr ) { if constexpr (traits::is_container_v) { constexpr bool sequence_attribute_can_directly_hold_value_type = traits::can_hold< - typename parser_traits>::attribute_type, + typename parser_traits>::attribute_type, typename traits::container_value::type >::value; + if constexpr (sequence_attribute_can_directly_hold_value_type) { - return parse_into_container_impl_default>::call(parser, first, last, ctx, attr); + return parse_into_container_impl_default>::call(seq, first, last, ctx, attr); } else { auto&& appender = x4::make_container_appender(x4::assume_container(attr)); - return detail::parse_sequence(parser, first, last, ctx, appender); + return detail::parse_sequence(seq, first, last, ctx, appender); } + } else { - return parse_into_container_impl_default>::call(parser, first, last, ctx, attr); + return parse_into_container_impl_default>::call(seq, first, last, ctx, attr); } } }; diff --git a/include/iris/x4/core/multi_parser.hpp b/include/iris/x4/core/multi_parser.hpp new file mode 100644 index 000000000..9d4e3c1ce --- /dev/null +++ b/include/iris/x4/core/multi_parser.hpp @@ -0,0 +1,35 @@ +#ifndef IRIS_ZZ_X4_CORE_MULTI_PARSER_HPP +#define IRIS_ZZ_X4_CORE_MULTI_PARSER_HPP + +/*============================================================================= + Copyright (c) 2026 The Iris Project Contributors + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include // IWYU pragma: keep + +#include +#include + +#include // IWYU pragma: keep + +namespace iris::x4 { + +template +struct multi_parser : parser +{ + static_assert(sizeof...(Ps) >= 2); + static constexpr std::size_t element_count = sizeof...(Ps); + + static constexpr bool has_action = (Ps::has_action || ...); + static constexpr bool need_rcontext = (Ps::need_rcontext || ...); + + IRIS_NO_UNIQUE_ADDRESS multi_parser_storage_t + elems; +}; + +} // iris::x4 + +#endif diff --git a/include/iris/x4/core/multi_parser_storage.hpp b/include/iris/x4/core/multi_parser_storage.hpp new file mode 100644 index 000000000..9833576b1 --- /dev/null +++ b/include/iris/x4/core/multi_parser_storage.hpp @@ -0,0 +1,89 @@ +#ifndef IRIS_ZZ_X4_CORE_MULTI_PARSER_STORAGE_HPP +#define IRIS_ZZ_X4_CORE_MULTI_PARSER_STORAGE_HPP + +/*============================================================================= + Copyright (c) 2026 The Iris Project Contributors + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include // IWYU pragma: keep + +#include + +#include // IWYU pragma: keep + +namespace iris::x4 { + +namespace detail { + +template +struct multi_parser_element : P +{}; + +template +struct multi_parser_storage; + +template +struct multi_parser_storage, Ps...> + : multi_parser_element... +{}; + +} // detail + +template +using multi_parser_storage_t = detail::multi_parser_storage, Ps...>; + +// -------------------------------------------------------- + +template +[[nodiscard]] constexpr P const& +multi_parser_get(detail::multi_parser_element const& elem) noexcept +{ + return elem; +} + +template +[[nodiscard]] constexpr P const& +multi_parser_forward(detail::multi_parser_element const& elem) noexcept +{ + return elem; +} + +template +[[nodiscard]] constexpr P&& +multi_parser_forward(detail::multi_parser_element&& elem) noexcept +{ + return static_cast(elem); +} + +namespace detail { + +template +[[nodiscard]] P deduce_multi_parser_element(multi_parser_element const&) noexcept; + +} // detail + +template +using multi_parser_t = decltype(detail::deduce_multi_parser_element( + std::declval, Ps...> const&>() +)); + + +template +[[nodiscard]] constexpr P const& +get_parser(detail::multi_parser_element const& elems IRIS_LIFETIMEBOUND) noexcept +{ + return elems; +} +template +[[nodiscard]] constexpr P&& +get_parser(detail::multi_parser_element&& elems IRIS_LIFETIMEBOUND) noexcept +{ + return static_cast(elems); +} + +} // iris::x4 + +#endif diff --git a/include/iris/x4/operator/sequence.hpp b/include/iris/x4/operator/sequence.hpp index 2d975828a..ccc7b1c3d 100644 --- a/include/iris/x4/operator/sequence.hpp +++ b/include/iris/x4/operator/sequence.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -22,16 +22,20 @@ #include #include -#include + +#include #include #include +#include #include #include +#include + namespace iris::x4 { -template +template struct sequence; namespace detail { @@ -72,52 +76,48 @@ struct container_can_hold_sequence : container_can_hold_element struct container_can_hold_sequence> - // this should not delegate to `container_can_hold_sequence`; we don't want recursive expansion here. + // this should not delegate to `container_can_hold_sequence`; we don't want recursive expansion : std::conjunction...> {}; -template -struct get_attribute_type> +template +struct get_attribute_type> { - using type = traits::detail::attribute_of_sequence::type; + using type = traits::detail::attribute_of_sequence::type; }; -template -struct get_sequence_size> +template +struct get_sequence_size> { - static constexpr std::size_t value = parser_traits::sequence_size + parser_traits::sequence_size; + static constexpr std::size_t value = sequence_layout::total_sequence_size; }; -template -struct get_handles_container, Container> +template +struct get_handles_container, Container> { static constexpr bool value = - ( - parser_traits::template handles_container && - parser_traits::template handles_container - ) || + (parser_traits::template handles_container && ...) || container_can_hold_sequence< Container, - typename parser_traits>::attribute_type + typename parser_traits>::attribute_type >::value; }; } // detail -template -struct sequence : binary_parser, Left, Right> +template +struct sequence : multi_parser, Ps...> { - using binary_parser::binary_parser; - template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, UnusedAttr const&) const { It const first_saved = first; - if (this->left.parse(first, last, ctx, unused) - && this->right.parse(first, last, ctx, unused) - ) { + bool const ok = [&](std::index_sequence) { + return (x4::get_parser(this->elems).parse(first, last, ctx, unused) && ...); + }(std::index_sequence_for{}); + if (ok) { return true; } @@ -127,7 +127,6 @@ struct sequence : binary_parser, Left, Right> return false; } } - first = first_saved; return false; } @@ -141,53 +140,93 @@ struct sequence : binary_parser, Left, Right> [[nodiscard]] constexpr std::string get_x4_info() const { - if constexpr (iris::is_ttp_specialization_of_v) { - return get_info{}(this->left) + " > " - + get_info{}(this->right.subject); + return [&](std::index_sequence) { + std::string info; + ((info += this->template get_x4_element_info()), ...); + return info; + }(std::index_sequence_for{}); + } + +private: + template + [[nodiscard]] constexpr std::string get_x4_element_info() const + { + using element_type = multi_parser_t; + auto const& elem = x4::get_parser(this->elems); + + if constexpr (I == 0) { + return get_info{}(elem); + + } else if constexpr (is_ttp_specialization_of_v) { + return " > " + get_info{}(elem.subject); + } else { - return get_info{}(this->left) + " >> " - + get_info{}(this->right); + return " >> " + get_info{}(elem); } } }; +namespace detail { + +template +[[nodiscard]] constexpr sequence +sequence_append_impl(std::index_sequence, sequence const& left, Right right) +{ + return {{ {}, { {x4::get_parser(left.elems)}..., {std::move(right)} } }}; +} + +template +[[nodiscard]] constexpr sequence +sequence_append_impl(std::index_sequence, sequence&& left, Right right) +{ + return {{ {}, { {x4::get_parser(std::move(left).elems)}..., {std::move(right)} } }}; +} + +} // detail + template + requires (!is_ttp_specialization_of_v, sequence>) [[nodiscard]] constexpr sequence, as_parser_plain_t> operator>>(Left&& left, Right&& right) - noexcept( - is_parser_nothrow_castable_v && - is_parser_nothrow_castable_v && - std::is_nothrow_constructible_v< - sequence, as_parser_plain_t>, - as_parser_t, - as_parser_t - > - ) { - return {as_parser(std::forward(left)), as_parser(std::forward(right))}; + return {{ {}, { {as_parser(std::forward(left))}, {as_parser(std::forward(right))} } }}; +} + +template + requires is_ttp_specialization_of_v, sequence> +[[nodiscard]] constexpr auto +operator>>(Left&& left, Right&& right) +{ + return detail::sequence_append_impl( + std::make_index_sequence::element_count>{}, + std::forward(left), as_parser(std::forward(right)) + ); } template + requires (!is_ttp_specialization_of_v, sequence>) [[nodiscard]] constexpr sequence, expect_directive>> operator>(Left&& left, Right&& right) - noexcept( - is_parser_nothrow_castable_v && - is_parser_nothrow_castable_v && - std::is_nothrow_constructible_v< - expect_directive>, - as_parser_t - > && - std::is_nothrow_constructible_v< - sequence, expect_directive>>, - as_parser_t, - expect_directive> - > - ) { - return { - as_parser(std::forward(left)), + return {{ + {}, + { + {as_parser(std::forward(left))}, + {expect_directive>(as_parser(std::forward(right)))} + } + }}; +} + +template + requires is_ttp_specialization_of_v, sequence> +[[nodiscard]] constexpr auto +operator>(Left&& left, Right&& right) +{ + return detail::sequence_append_impl( + std::make_index_sequence::element_count>{}, + std::forward(left), expect_directive>(as_parser(std::forward(right))) - }; + ); } } // iris::x4 diff --git a/include/iris/x4/traits/attribute_of_binary.hpp b/include/iris/x4/traits/attribute_of_binary.hpp index 87fd59bae..6dbd8daf0 100644 --- a/include/iris/x4/traits/attribute_of_binary.hpp +++ b/include/iris/x4/traits/attribute_of_binary.hpp @@ -21,15 +21,20 @@ namespace iris::x4::traits { namespace detail { -template +template struct concat_type_list; -template -struct concat_type_list, type_list> +template +struct concat_type_list> { - using type = type_list; + using type = type_list; }; +template +struct concat_type_list, type_list, Rest...> + : concat_type_list, Rest...> +{}; + #if 0 #define IRIS_X4_TRAITS_DETAIL_DEFINE_TYPE_LIST_CONV(postfix, tmpl) \ template \ @@ -66,12 +71,11 @@ struct concat_type_list, type_list> using type = tmpl; \ }; \ \ - template \ + template \ struct attribute_of_##postfix { \ using type = detail::from_type_list_##postfix< \ typename detail::concat_type_list< \ - typename detail::to_type_list_##postfix::attribute_type>::type, \ - typename detail::to_type_list_##postfix::attribute_type>::type \ + typename detail::to_type_list_##postfix::attribute_type>::type... \ >::type \ >::type; \ }; @@ -83,24 +87,26 @@ struct concat_type_list, type_list> // Code style is kept as-is. // -// IRIS_X4_TRAITS_DETAIL_DEFINE_TYPE_LIST_CONV(sequence, alloy::tuple) +//IRIS_X4_TRAITS_DETAIL_DEFINE_TYPE_LIST_CONV(sequence, alloy::tuple) template struct to_type_list_sequence { using type = type_list; }; template<> struct to_type_list_sequence { using type = type_list<>; }; template struct to_type_list_sequence> { using type = type_list; -}; template struct from_type_list_sequence {}; template<> struct from_type_list_sequence> { +}; template struct from_type_list_sequence { +}; template<> struct from_type_list_sequence> { using type = unused_type; }; template struct from_type_list_sequence> { using type = T; }; template struct from_type_list_sequence> { using type = alloy::tuple; -}; template struct attribute_of_sequence { - using type = detail::from_type_list_sequence< typename detail::concat_type_list< typename detail::to_type_list_sequence::attribute_type>::type, typename detail::to_type_list_sequence::attribute_type>::type >::type >::type; +}; template struct attribute_of_sequence { + using type = detail::from_type_list_sequence< typename detail::concat_type_list< typename detail::to_type_list_sequence::attribute_type>::type... >::type >::type; }; -// IRIS_X4_TRAITS_DETAIL_DEFINE_TYPE_LIST_CONV(alternative, rvariant) + +//IRIS_X4_TRAITS_DETAIL_DEFINE_TYPE_LIST_CONV(alternative, rvariant) template struct to_type_list_alternative { using type = type_list; }; template<> struct to_type_list_alternative { diff --git a/test/x4/iterator.cpp b/test/x4/iterator.cpp index cfb2693d8..87db610c3 100644 --- a/test/x4/iterator.cpp +++ b/test/x4/iterator.cpp @@ -338,7 +338,7 @@ TEST_CASE("rollback on failed parse (directive)") std::vector dummy_ints; REQUIRE_FALSE((int_ >> expect[','] >> int_).parse(first, input.end(), ctx, dummy_ints)); CHECK(first == input.begin()); - CHECK(dummy_ints == std::vector{}); // sequence parser has NO side effect because attribute is a container + CHECK(dummy_ints == std::vector{42}); // each element appends on its own; iterator = rolled back, container = not rolled back } { @@ -361,7 +361,7 @@ TEST_CASE("rollback on failed parse (directive)") std::vector 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 + CHECK(dummy_ints == std::vector{42}); // each element appends on its own; iterator = rolled back, container = not rolled back } { @@ -415,7 +415,7 @@ TEST_CASE("rollback on failed parse (directive)") std::vector 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 + CHECK(dummy_ints == std::vector{42}); // each element appends on its own; iterator = rolled back, container = not rolled back } { @@ -596,7 +596,7 @@ TEST_CASE("rollback on failed parse (operator)") std::vector 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 + CHECK(dummy_ints == std::vector{42}); // each element appends on its own; iterator = rolled back, container = not rolled back } { constexpr auto input = " 42,43"sv; diff --git a/test/x4/partial_success.cpp b/test/x4/partial_success.cpp index 702cdb51c..d3d2f02f3 100644 --- a/test/x4/partial_success.cpp +++ b/test/x4/partial_success.cpp @@ -258,10 +258,8 @@ TEST_CASE("partial success (list-like)") // abc ---------------------------------------------- { using Subject = x4::sequence< - x4::sequence< - x4::literal_char, - x4::literal_char - >, + x4::literal_char, + x4::literal_char, x4::literal_char >; static_assert(std::same_as, Subject>); @@ -352,10 +350,8 @@ TEST_CASE("partial success (list-like)") // aXXc ---------------------------------------------- { using Subject = x4::sequence< - x4::sequence< - x4::literal_char, - x4::literal_string, standard> - >, + x4::literal_char, + x4::literal_string, standard>, x4::literal_char >; static_assert(std::same_as, Subject>); From 3e6132baaa91db1c465671753220396a88f9db6c Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Wed, 23 Sep 2026 18:36:23 +0900 Subject: [PATCH 4/4] Flatten both sides of sequence, not only left --- .../iris/x4/core/detail/parse_sequence.hpp | 61 +++++++++----- include/iris/x4/operator/sequence.hpp | 79 ++++++++++--------- test/x4/sequence.cpp | 4 + 3 files changed, 89 insertions(+), 55 deletions(-) diff --git a/include/iris/x4/core/detail/parse_sequence.hpp b/include/iris/x4/core/detail/parse_sequence.hpp index 8bfd368c6..cde285062 100644 --- a/include/iris/x4/core/detail/parse_sequence.hpp +++ b/include/iris/x4/core/detail/parse_sequence.hpp @@ -80,6 +80,19 @@ template struct sequence_passes_view

: sequence_passes_view {}; +// A helper to isolate the actual logic inside a single struct. +// +// Theoretically, this can be written directly inside a lambda in `parse_sequence`. +// However, MSVC historically fails to optimize the compilation time of this kind +// of logic when it is written directly inside a large function. +// +// MSVC has a bad behavior where it always reparses the entire tokens of large +// function body when it needs to be "reinspected" for some arbitrary reason, like +// different types of specialization, etc. This is NOT the matter of the template +// instantiation cost; it is due to the function parsing and tokenization behavior. +// +// The result is about 50-80ms reduced compilation time (in realistic code) when +// this is isolated in a struct like below. template struct parse_sequence_tuple { @@ -145,27 +158,37 @@ parse_sequence(sequence const& seq, It& first, Se const& last, Context co using layout = sequence_layout; - if constexpr (layout::attributed_count >= 2) { - static_assert( - traits::CategorizedAttr, - "The attribute of a sequence with >=2 attributed elements must be tuple-like." - ); - static_assert( - alloy::tuple_size_v >= layout::total_sequence_size, - "Sequence size of the passed attribute is less than expected." - ); - static_assert( - alloy::tuple_size_v <= layout::total_sequence_size, - "Sequence size of the passed attribute is greater than expected." - ); - } + // Intentionally verbose branches for avoiding instantiation of erroneous grammar stem, + // significantly reducing the amount of compilation error. - It local_it = first; - if (parse_sequence_tuple::parse_all(std::index_sequence_for{}, seq, local_it, last, ctx, attr)) { - first = std::move(local_it); - return true; + if constexpr (layout::attributed_count < 2) { + It local_it = first; + if (parse_sequence_tuple::parse_all(std::index_sequence_for{}, seq, local_it, last, ctx, attr)) { + first = std::move(local_it); + return true; + } + return false; + + } else if constexpr (!traits::CategorizedAttr) { + static_assert(false, "The attribute of a sequence with >=2 attributed elements must be tuple-like."); + return false; + + } else if constexpr (alloy::tuple_size_v < layout::total_sequence_size) { + static_assert(false, "Sequence size of the passed attribute is less than expected."); + return false; + + } else if constexpr (alloy::tuple_size_v > layout::total_sequence_size) { + static_assert(false, "Sequence size of the passed attribute is greater than expected."); + return false; + + } else { + It local_it = first; + if (parse_sequence_tuple::parse_all(std::index_sequence_for{}, seq, local_it, last, ctx, attr)) { + first = std::move(local_it); + return true; + } + return false; } - return false; } // Attribute is a container diff --git a/include/iris/x4/operator/sequence.hpp b/include/iris/x4/operator/sequence.hpp index ccc7b1c3d..7b42d8396 100644 --- a/include/iris/x4/operator/sequence.hpp +++ b/include/iris/x4/operator/sequence.hpp @@ -168,63 +168,70 @@ struct sequence : multi_parser, Ps...> namespace detail { -template -[[nodiscard]] constexpr sequence -sequence_append_impl(std::index_sequence, sequence const& left, Right right) +template +[[nodiscard]] constexpr decltype(auto) +sequence_element_at(T&& parser) noexcept { - return {{ {}, { {x4::get_parser(left.elems)}..., {std::move(right)} } }}; + if constexpr (is_ttp_specialization_of_v, sequence>) { + return x4::get_parser(std::forward(parser).elems); + + } else { + static_assert(I == 0); + return std::forward(parser); + } } -template -[[nodiscard]] constexpr sequence -sequence_append_impl(std::index_sequence, sequence&& left, Right right) +template +[[nodiscard]] constexpr sequence...> +make_sequence(Ps&&... ps) { - return {{ {}, { {x4::get_parser(std::move(left).elems)}..., {std::move(right)} } }}; + return {{ {}, { {std::forward(ps)}... } }}; } -} // detail - -template - requires (!is_ttp_specialization_of_v, sequence>) -[[nodiscard]] constexpr sequence, as_parser_plain_t> -operator>>(Left&& left, Right&& right) +template +[[nodiscard]] constexpr auto +sequence_concat_impl(std::index_sequence, std::index_sequence, Left&& left, Right&& right) { - return {{ {}, { {as_parser(std::forward(left))}, {as_parser(std::forward(right))} } }}; + return detail::make_sequence( + detail::sequence_element_at(std::forward(left))..., + detail::sequence_element_at(std::forward(right))... + ); } -template - requires is_ttp_specialization_of_v, sequence> +// This is NOT the same as `parser_traits

::sequence_size` because we need the +// element count here, not the count of non-unused attributes +template +inline constexpr std::size_t sequence_parser_count = 1; + +template +inline constexpr std::size_t sequence_parser_count> = sizeof...(Ps); + +template [[nodiscard]] constexpr auto -operator>>(Left&& left, Right&& right) +sequence_concat(Left&& left, Right&& right) { - return detail::sequence_append_impl( - std::make_index_sequence::element_count>{}, - std::forward(left), as_parser(std::forward(right)) + return detail::sequence_concat_impl( + std::make_index_sequence>>{}, + std::make_index_sequence>>{}, + std::forward(left), std::forward(right) ); } +} // detail + template - requires (!is_ttp_specialization_of_v, sequence>) -[[nodiscard]] constexpr sequence, expect_directive>> -operator>(Left&& left, Right&& right) +[[nodiscard]] constexpr auto +operator>>(Left&& left, Right&& right) { - return {{ - {}, - { - {as_parser(std::forward(left))}, - {expect_directive>(as_parser(std::forward(right)))} - } - }}; + return detail::sequence_concat(as_parser(std::forward(left)), as_parser(std::forward(right))); } -template - requires is_ttp_specialization_of_v, sequence> +template [[nodiscard]] constexpr auto operator>(Left&& left, Right&& right) { - return detail::sequence_append_impl( - std::make_index_sequence::element_count>{}, - std::forward(left), + return detail::sequence_concat( + as_parser(std::forward(left)), expect_directive>(as_parser(std::forward(right))) ); } diff --git a/test/x4/sequence.cpp b/test/x4/sequence.cpp index 11512cd8e..770d018d6 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -59,6 +59,10 @@ TEST_CASE("sequence") IRIS_X4_ASSERT_CONSTEXPR_CTORS(char_ >> char_); + STATIC_CHECK(std::same_as> int_), x4::sequence, x4::int_parser>>); + STATIC_CHECK(std::same_as> int_ >> int_), x4::sequence, x4::int_parser, x4::int_parser>>); + STATIC_CHECK(std::same_as> int_) >> int_), decltype(int_ >> (int_ >> int_))>); + CHECK(parse("aa", char_ >> char_)); CHECK(parse("aa", char_ >> 'a')); CHECK(parse("aaa", char_ >> char_ >> char_('a')));