From 3117628ca1b2f2c30e3c69d59fdb04b012c78849 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Sat, 19 Sep 2026 19:52:23 +0900 Subject: [PATCH 1/5] Update iris --- modules/iris | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/iris b/modules/iris index ba5c2bdea..4df793a55 160000 --- a/modules/iris +++ b/modules/iris @@ -1 +1 @@ -Subproject commit ba5c2bdea2fef9e86a17d5c189fd6af67a0aa4a5 +Subproject commit 4df793a5528afabddbe91038712a5fd4593d9596 From 9b83e60e71bcec8e81d15a282237bcc6c4918aeb Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Sat, 19 Sep 2026 23:52:11 +0900 Subject: [PATCH 2/5] Apply EBO to `unary_parser` --- include/iris/x4/attribute/as.hpp | 18 +- include/iris/x4/attribute/smart_ptr.hpp | 25 +- include/iris/x4/attribute/value.hpp | 22 +- include/iris/x4/char.hpp | 1 - include/iris/x4/char/char.hpp | 40 +- include/iris/x4/char/char_parser.hpp | 65 ++- include/iris/x4/char/negated_char.hpp | 59 +-- include/iris/x4/core/action.hpp | 6 +- .../iris/x4/core/detail/parse_alternative.hpp | 4 +- include/iris/x4/core/parser.hpp | 384 ++++++++++-------- include/iris/x4/core/parser_traits.hpp | 9 +- include/iris/x4/directive/expect.hpp | 8 +- include/iris/x4/directive/lexeme.hpp | 4 +- include/iris/x4/directive/matches.hpp | 4 +- include/iris/x4/directive/no_case.hpp | 14 +- include/iris/x4/directive/no_skip.hpp | 2 +- include/iris/x4/directive/omit.hpp | 6 +- include/iris/x4/directive/repeat.hpp | 10 +- include/iris/x4/directive/skip.hpp | 10 +- include/iris/x4/directive/with.hpp | 16 +- include/iris/x4/directive/with_local.hpp | 4 +- include/iris/x4/numeric/bool.hpp | 39 +- include/iris/x4/numeric/int.hpp | 2 +- include/iris/x4/numeric/real.hpp | 2 +- include/iris/x4/numeric/uint.hpp | 3 +- include/iris/x4/operator/and_predicate.hpp | 4 +- include/iris/x4/operator/kleene.hpp | 10 +- include/iris/x4/operator/not_predicate.hpp | 6 +- include/iris/x4/operator/optional.hpp | 12 +- include/iris/x4/operator/plus.hpp | 14 +- include/iris/x4/operator/sequence.hpp | 2 +- include/iris/x4/primitive/eoi.hpp | 2 +- include/iris/x4/primitive/eol.hpp | 2 +- include/iris/x4/primitive/eps.hpp | 33 +- include/iris/x4/rule.hpp | 11 +- include/iris/x4/string/literal_string.hpp | 22 +- include/iris/x4/string/string.hpp | 45 +- include/iris/x4/symbols.hpp | 15 +- test/x4/as.cpp | 1 - test/x4/char.cpp | 1 - test/x4/char_class.cpp | 1 - test/x4/expect.cpp | 1 - test/x4/iris_x4_test.hpp | 2 +- test/x4/iterator.cpp | 1 - test/x4/no_skip.cpp | 1 - test/x4/parser.cpp | 8 +- test/x4/rule4.cpp | 1 - test/x4/sequence.cpp | 5 +- test/x4/smart_ptr.cpp | 2 +- test/x4/symbols1.cpp | 16 - test/x4/without.cpp | 2 +- 51 files changed, 457 insertions(+), 520 deletions(-) diff --git a/include/iris/x4/attribute/as.hpp b/include/iris/x4/attribute/as.hpp index 8c9aa32b3..beb7ab98d 100644 --- a/include/iris/x4/attribute/as.hpp +++ b/include/iris/x4/attribute/as.hpp @@ -47,7 +47,7 @@ struct as_type_parser_ctx_impl // to be `T`. When `T` is `unused_type`, this is equivalent to // `omit_directive`. template -struct as_type_parser : unary_parser> +struct as_type_parser : unary_parser { static_assert(!std::is_const_v); // Forbid const `unused_type` static_assert(!std::same_as); // Unknown use case, not supported for now @@ -65,6 +65,8 @@ struct as_type_parser : unary_parser> // `parser_traits>::handles_container` must transparently // handle this case. + using unary_parser::unary_parser; + private: template using exposed_attr_for_child_t = std::conditional_t< @@ -80,9 +82,9 @@ struct as_type_parser : unary_parser> 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); + return this->subject().parse(first, last, x4::replace_first_context(ctx, outer_attr), unused); } else { - return this->subject.parse(first, last, ctx, outer_attr); + return this->subject().parse(first, last, ctx, outer_attr); } } @@ -95,9 +97,9 @@ struct as_type_parser : unary_parser> 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); + return this->subject().parse(first, last, x4::replace_first_context(ctx, unused), unused); } else { - return this->subject.parse(first, last, ctx, unused); + return this->subject().parse(first, last, ctx, unused); } } @@ -122,9 +124,9 @@ struct as_type_parser : unary_parser> T attr_{}; // value-initialize if constexpr (Subject::has_action) { - if (!this->subject.parse(first, last, x4::replace_first_context(ctx, attr_), unused)) return false; + if (!this->subject().parse(first, last, x4::replace_first_context(ctx, attr_), unused)) return false; } else { - if (!this->subject.parse(first, last, ctx, attr_)) return false; + if (!this->subject().parse(first, last, ctx, attr_)) return false; } x4::move_to(std::move(attr_), outer_attr); @@ -136,7 +138,7 @@ struct as_type_parser : unary_parser> return std::format( "as<{}>({})", typeid(T).name(), - get_info{}(this->subject) + get_info{}(this->subject()) ); } }; diff --git a/include/iris/x4/attribute/smart_ptr.hpp b/include/iris/x4/attribute/smart_ptr.hpp index 4cd6ed234..d1d7d850e 100644 --- a/include/iris/x4/attribute/smart_ptr.hpp +++ b/include/iris/x4/attribute/smart_ptr.hpp @@ -105,28 +105,19 @@ struct unique_ptr_parser_base smart_ptr_rollback_guard> guard{parse_ok, old_ptr, ptr}; - parse_ok = this->subject.parse(it, se, ctx, *ptr); + parse_ok = this->subject().parse(it, se, ctx, *ptr); return parse_ok; } private: - IRIS_NO_UNIQUE_ADDRESS DeleterT deleter_{}; + DeleterT deleter_{}; }; template struct unique_ptr_parser_base> : proxy_parser { - using base_type = proxy_parser; - - template - requires - (!std::same_as, Derived>) && - std::is_constructible_v - constexpr explicit unique_ptr_parser_base(SubjectT&& subject) - noexcept(std::is_nothrow_constructible_v) - : base_type(std::forward(subject)) - {} + using proxy_parser::proxy_parser; template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool @@ -141,7 +132,7 @@ struct unique_ptr_parser_base> smart_ptr_rollback_guard>> guard{parse_ok, old_ptr, ptr}; - parse_ok = this->subject.parse(it, se, ctx, *ptr); + parse_ok = this->subject().parse(it, se, ctx, *ptr); return parse_ok; } }; @@ -167,7 +158,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 std::format("unique_ptr({})", get_info{}(this->subject())); } }; @@ -277,7 +268,7 @@ struct shared_ptr_parser_base smart_ptr_rollback_guard> guard{parse_ok, old_ptr, ptr}; - parse_ok = this->subject.parse(it, se, ctx, *ptr); + parse_ok = this->subject().parse(it, se, ctx, *ptr); return parse_ok; } @@ -311,7 +302,7 @@ struct shared_ptr_parser_base> smart_ptr_rollback_guard> guard{parse_ok, old_ptr, ptr}; - parse_ok = this->subject.parse(it, se, ctx, *ptr); + parse_ok = this->subject().parse(it, se, ctx, *ptr); return parse_ok; } }; @@ -337,7 +328,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 std::format("shared_ptr({})", get_info{}(this->subject())); } }; diff --git a/include/iris/x4/attribute/value.hpp b/include/iris/x4/attribute/value.hpp index 2f89cdeee..ec01a3d3a 100644 --- a/include/iris/x4/attribute/value.hpp +++ b/include/iris/x4/attribute/value.hpp @@ -28,7 +28,7 @@ namespace iris::x4 { // `fixed_value(...)` template -struct fixed_value_parser : parser> +struct fixed_value_parser : parser { static_assert(X4Attribute); static_assert(!X4UnusedAttribute, "fixed_value_parser with `unused_type` is meaningless"); @@ -44,38 +44,30 @@ struct fixed_value_parser : parser> using attribute_type = T; using held_value_type = HeldValueT; - template - requires - (!std::is_same_v, fixed_value_parser>) && - std::is_constructible_v - constexpr explicit fixed_value_parser(U&& value) - noexcept(std::is_nothrow_constructible_v) - : held_value_(std::forward(value)) - {} + using parser::parser; template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It&, Se const&, Context const&, Attr& attr_) const - noexcept(noexcept(x4::move_to(std::as_const(held_value_), attr_))) + noexcept(noexcept(x4::move_to(std::declval(), attr_))) { // Always copy (need reuse in repetitive invocations) - x4::move_to(std::as_const(held_value_), attr_); + x4::move_to(this->storage(), attr_); return true; } - -private: - HeldValueT held_value_; }; // `reset_value` template -struct fixed_value_parser : parser> +struct fixed_value_parser : parser<> { static_assert(X4Attribute); static_assert(!X4UnusedAttribute, "fixed_value_parser with `unused_type` is meaningless"); using attribute_type = T; + using parser<>::parser; + template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] static constexpr bool parse(It&, Se const&, Context const&, UnusedAttr const&) noexcept diff --git a/include/iris/x4/char.hpp b/include/iris/x4/char.hpp index 8e1773114..b7e46fe4e 100644 --- a/include/iris/x4/char.hpp +++ b/include/iris/x4/char.hpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include diff --git a/include/iris/x4/char/char.hpp b/include/iris/x4/char/char.hpp index 867959364..55cd566bc 100644 --- a/include/iris/x4/char/char.hpp +++ b/include/iris/x4/char/char.hpp @@ -152,15 +152,13 @@ using x4::lit; } // parsers -namespace extension { +namespace traits { template<> struct as_parser { - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(char ch) noexcept + [[nodiscard]] static constexpr literal_char + operator()(char ch) noexcept { return {ch}; } @@ -170,10 +168,8 @@ struct as_parser template<> struct as_parser { - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(wchar_t ch) noexcept + [[nodiscard]] static constexpr literal_char + operator()(wchar_t ch) noexcept { return {ch}; } @@ -184,10 +180,8 @@ struct as_parser template<> struct as_parser { - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(char32_t ch) noexcept + [[nodiscard]] static constexpr literal_char + operator()(char32_t ch) noexcept { return {ch}; } @@ -197,10 +191,8 @@ struct as_parser template<> struct as_parser { - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(char const ch[]) noexcept + [[nodiscard]] static constexpr literal_char + operator()(char const (&ch)[2]) noexcept { return {ch[0]}; } @@ -210,10 +202,8 @@ struct as_parser template<> struct as_parser { - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(wchar_t const ch[]) noexcept + [[nodiscard]] static constexpr literal_char + operator()(wchar_t const (&ch)[2]) noexcept { return {ch[0]}; } @@ -224,17 +214,15 @@ struct as_parser template<> struct as_parser { - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(char32_t const ch[]) noexcept + [[nodiscard]] static constexpr literal_char + operator()(char32_t const (&ch)[2]) noexcept { return {ch[0]}; } }; #endif // IRIS_X4_UNICODE -} // extension +} // traits } // iris::x4 diff --git a/include/iris/x4/char/char_parser.hpp b/include/iris/x4/char/char_parser.hpp index 2f6e1ef4c..744b8efab 100644 --- a/include/iris/x4/char/char_parser.hpp +++ b/include/iris/x4/char/char_parser.hpp @@ -17,15 +17,57 @@ #include #include +#include +#include namespace iris::x4 { template -struct char_parser : parser +struct char_parser; + +// `negated_char_parser` handles `~cp`, where `cp` is a `char_parser` +template +struct negated_char_parser : char_parser> +{ + static_assert(X4ExplicitSubject); + + using attribute_type = parser_traits::attribute_type; + using encoding_type = Positive::encoding_type; + + template + requires + (!std::is_same_v, negated_char_parser>) && + std::is_constructible_v + constexpr explicit negated_char_parser(PositiveT&& positive) + noexcept(std::is_nothrow_constructible_v) + : positive_(std::forward(positive)) + {} + + // TODO: implement static version + template + [[nodiscard]] constexpr bool + test(CharT ch, Context const& ctx) const noexcept + { + static_assert(noexcept(!positive_.test(ch, ctx))); + return !positive_.test(ch, ctx); + } + + template + [[nodiscard]] constexpr auto&& operator~(this Self&& self IRIS_LIFETIMEBOUND) noexcept + { + return std::forward(self).positive_; + } + +private: + Positive positive_; // TODO: EBO +}; + +template +struct char_parser : parser<> { using encoding_type = Encoding; - using char_type = typename Encoding::char_type; - using classify_type = typename Encoding::classify_type; + using char_type = Encoding::char_type; + using classify_type = Encoding::classify_type; private: template @@ -61,15 +103,15 @@ struct char_parser : parser return false; } - template Se, class Context, X4Attribute Attr> + template Se, class Context, X4Attribute Attr> requires (!has_static_test) [[nodiscard]] constexpr bool - parse(It& first, Se const& last, Context const& ctx, Attr& attr) const + parse(this Self const& self /* require const& */, 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(this->derived().test(static_cast(*first), ctx)) && + noexcept(self.test(static_cast(*first), ctx)) && noexcept(x4::move_to(std::iter_value_t{*first}, attr)) && noexcept(++first) ) @@ -80,13 +122,22 @@ struct char_parser : parser auto it = first; x4::skip_over(it, last, ctx); - if (it != last && this->derived().test(static_cast(*it), ctx)) { + if (it != last && self.test(static_cast(*it), ctx)) { x4::move_to(std::iter_value_t{*it++}, attr); first = it; return true; } return false; } + + template + requires (!std::derived_from, negated_char_parser>>) + [[nodiscard]] constexpr negated_char_parser> + operator~(this Self&& self) + noexcept(std::is_nothrow_constructible_v>, Self>) + { + return negated_char_parser>{std::forward(self)}; + } }; } // iris::x4 diff --git a/include/iris/x4/char/negated_char.hpp b/include/iris/x4/char/negated_char.hpp index f2b94e61d..e2a1a5c8d 100644 --- a/include/iris/x4/char/negated_char.hpp +++ b/include/iris/x4/char/negated_char.hpp @@ -10,63 +10,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include - -#include -#include - -namespace iris::x4 { - -// `negated_char_parser` handles `~cp`, where `cp` is a `char_parser` -template -struct negated_char_parser : char_parser> -{ - static_assert(X4ExplicitSubject); - - using attribute_type = parser_traits::attribute_type; - using encoding_type = typename Positive::encoding_type; - - template - requires - (!std::is_same_v, negated_char_parser>) && - std::is_constructible_v - constexpr explicit negated_char_parser(PositiveT&& positive) - noexcept(std::is_nothrow_constructible_v) - : positive_(std::forward(positive)) - {} +#warning "This header is deprecated; please #include " - template - [[nodiscard]] constexpr bool - test(CharT ch, Context const& ctx) const noexcept - { - static_assert(noexcept(!positive_.test(ch, ctx))); - return !positive_.test(ch, ctx); - } - - [[nodiscard]] constexpr Positive const& positive() const noexcept - { - return positive_; - } - -private: - Positive positive_; -}; - -template -[[nodiscard]] constexpr negated_char_parser -operator~(char_parser const& cp) - noexcept(std::is_nothrow_constructible_v, Positive const&>) -{ - return negated_char_parser{cp.derived()}; -} - -template -[[nodiscard]] constexpr Positive const& -operator~(negated_char_parser const& cp) noexcept -{ - return cp.positive(); -} - -} // iris::x4 +#include #endif diff --git a/include/iris/x4/core/action.hpp b/include/iris/x4/core/action.hpp index 6341b4a0d..b1e44a559 100644 --- a/include/iris/x4/core/action.hpp +++ b/include/iris/x4/core/action.hpp @@ -75,7 +75,7 @@ struct action : proxy_parser> ActionF f; template - requires std::is_constructible_v && std::is_constructible_v + requires /*std::is_constructible_v &&*/ std::is_constructible_v constexpr action(SubjectT&& subject, ActionT&& f) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) : base_type(std::forward(subject)) @@ -146,7 +146,7 @@ struct action : proxy_parser> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format("{}[f]", get_info{}(this->subject)); + return std::format("{}[f]", get_info{}(this->subject())); } private: @@ -237,7 +237,7 @@ struct action : proxy_parser> ) { It const saved_first = first; - if (!this->subject.parse(first, last, ctx, attr)) return false; + if (!this->subject().parse(first, last, ctx, attr)) return false; if (this->call_action(ctx, attr)) { return true; diff --git a/include/iris/x4/core/detail/parse_alternative.hpp b/include/iris/x4/core/detail/parse_alternative.hpp index 5176df180..4e6d27298 100644 --- a/include/iris/x4/core/detail/parse_alternative.hpp +++ b/include/iris/x4/core/detail/parse_alternative.hpp @@ -218,9 +218,9 @@ struct alternative_helper : proxy_parser> template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(noexcept(detail::parse_alternative(this->subject, first, last, ctx, attr))) + noexcept(noexcept(detail::parse_alternative(this->subject(), first, last, ctx, attr))) { - return detail::parse_alternative(this->subject, first, last, ctx, attr); + return detail::parse_alternative(this->subject(), first, last, ctx, attr); } }; diff --git a/include/iris/x4/core/parser.hpp b/include/iris/x4/core/parser.hpp index 08c1f6a7b..5ef6b274a 100644 --- a/include/iris/x4/core/parser.hpp +++ b/include/iris/x4/core/parser.hpp @@ -37,100 +37,204 @@ namespace detail { struct parser_base {}; struct parser_id; -} // detail +template +struct parser_storage + : private parser_base + , protected Storage // needs to be non-private because we want to expose static public members +{ + // `Storage` is a class type + + using Storage::Storage; + + template + requires + (!std::same_as) && + std::constructible_from + constexpr explicit(!std::convertible_to) + parser_storage(StorageT&& storage) + noexcept(std::is_nothrow_constructible_v) + : Storage(std::forward(storage)) + {} +protected: + [[nodiscard]] constexpr Storage& storage() & noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } + [[nodiscard]] constexpr Storage const& storage() const& noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } + [[nodiscard]] constexpr Storage&& storage() && noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } + [[nodiscard]] constexpr Storage const&& storage() const&& noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } +}; -template -struct parser : private detail::parser_base +template + requires (!std::is_class_v) +struct parser_storage + : private parser_base { - static_assert(!std::is_reference_v); - using derived_type = Derived; + // `Storage` is some non-class type (can't inherit from it; no EBO needed) - static constexpr bool has_action = false; - static constexpr bool need_rcontext = false; - static constexpr bool requires_exact_attribute_type = false; + template + requires + (!std::same_as) && + std::constructible_from + constexpr explicit(!std::convertible_to) + parser_storage(StorageT&& storage) + noexcept(std::is_nothrow_constructible_v) + : storage_(std::forward(storage)) + {} - [[nodiscard]] constexpr Derived& derived() & noexcept +protected: + [[nodiscard]] constexpr Storage& storage() & noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(storage_); } - - [[nodiscard]] constexpr Derived const& derived() const& noexcept + [[nodiscard]] constexpr Storage const& storage() const& noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(storage_); } - - [[nodiscard]] constexpr Derived&& derived() && noexcept + [[nodiscard]] constexpr Storage&& storage() && noexcept IRIS_LIFETIMEBOUND + { + return static_cast(storage_); + } + [[nodiscard]] constexpr Storage const&& storage() const&& noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(storage_); } - [[nodiscard]] constexpr Derived const&& derived() const&& noexcept +private: + Storage storage_; +}; + +template + requires + (!std::is_void_v) && + std::is_base_of_v +struct parser_storage + : protected Storage // needs to be non-private because we want to expose static public members +{ + // `Storage` is some X4 parser class (possibly a `Subject`) + // (which means we must not double-inherit from `parser_base`) + + using Storage::Storage; + + template + requires + (!std::same_as) && + std::constructible_from + constexpr explicit(!std::convertible_to) + parser_storage(StorageT&& storage) + noexcept(std::is_nothrow_constructible_v) + : Storage(std::forward(storage)) + {} + +protected: + [[nodiscard]] constexpr Storage& storage() & noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } + [[nodiscard]] constexpr Storage const& storage() const& noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } + [[nodiscard]] constexpr Storage&& storage() && noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } + [[nodiscard]] constexpr Storage const&& storage() const&& noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(*this); } +}; + +template<> +struct parser_storage + : private parser_base +{}; + +} // detail + + +template +struct parser : detail::parser_storage +{ + static constexpr bool has_action = false; + static constexpr bool need_rcontext = false; + static constexpr bool requires_exact_attribute_type = false; + + using detail::parser_storage::parser_storage; template - requires std::is_constructible_v< - action>, - decltype(std::declval().derived()), - Action + requires std::constructible_from< + action, std::remove_cvref_t>, + Self, Action > [[nodiscard]] - constexpr action> + constexpr action, std::remove_cvref_t> on_match(this Self&& self, Action&& f) noexcept(std::is_nothrow_constructible_v< - action>, - decltype(std::forward(self).derived()), - Action + action, std::remove_cvref_t>, + Self, Action >) { - return {std::forward(self).derived(), std::forward(f)}; + return {std::forward(self), std::forward(f)}; } template - requires std::is_constructible_v< - action>, - decltype(std::declval().derived()), - Action + requires std::constructible_from< + action, std::remove_cvref_t>, + Self, Action > [[nodiscard, deprecated("Use `p.on_match(...)` instead. The legacy `operator[]` syntax will be removed because it frequently conflicts with lambda syntax.")]] - constexpr action> + constexpr action, std::remove_cvref_t> operator[](this Self&& self, Action&& f) noexcept(std::is_nothrow_constructible_v< - action>, - decltype(std::forward(self).derived()), - Action + action, std::remove_cvref_t>, + Self, Action >) { return std::forward(self).on_match(std::forward(f)); } }; -template -struct unary_parser : parser +template +struct unary_parser : parser { using subject_type = Subject; static constexpr bool has_action = Subject::has_action; static constexpr bool need_rcontext = Subject::need_rcontext; - constexpr unary_parser() = default; + using parser::parser; - template - requires - (!std::is_same_v, unary_parser>) && - std::is_constructible_v - constexpr unary_parser(SubjectT&& subject) - noexcept(std::is_nothrow_constructible_v) - : subject(std::forward(subject)) - {} - - Subject subject; + [[nodiscard]] constexpr Subject& subject() & noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } + [[nodiscard]] constexpr Subject const& subject() const& noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } + [[nodiscard]] constexpr Subject&& subject() && noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } + [[nodiscard]] constexpr Subject const&& subject() const&& noexcept IRIS_LIFETIMEBOUND + { + return static_cast(*this); + } }; -template -struct proxy_parser : unary_parser +template // TODO +struct proxy_parser : unary_parser { using proxy_backend_type = Subject; using attribute_type = parser_traits::attribute_type; @@ -141,11 +245,11 @@ struct proxy_parser : unary_parser template static constexpr bool handles_container = parser_traits::template handles_container; - using unary_parser::unary_parser; + using unary_parser::unary_parser; }; -template -struct binary_parser : parser +template // TODO +struct binary_parser : parser<> { using left_type = Left; using right_type = Right; @@ -163,12 +267,12 @@ struct binary_parser : parser , right(std::forward(right)) {} - // TODO: [MSVC 2022 BUG] "overruns" in constexpr, test case in `lit.cpp` - /*IRIS_NO_UNIQUE_ADDRESS*/ Left left; - /*IRIS_NO_UNIQUE_ADDRESS*/ Right right; + // TODO: EBO + IRIS_NO_UNIQUE_ADDRESS Left left; + IRIS_NO_UNIQUE_ADDRESS Right right; }; -namespace extension { +namespace traits { template struct as_parser; // not defined @@ -176,88 +280,70 @@ struct as_parser; // not defined template<> struct as_parser { - using value_type [[deprecated("Use x4::as_parser_plain_t")]] = unused_type; - template - [[nodiscard]] static constexpr auto&& call(T&& unused_) noexcept + [[nodiscard]] static constexpr auto&& operator()(T&& unused_ IRIS_LIFETIMEBOUND) noexcept { - return std::forward(unused_); + return static_cast(unused_); } }; -template - requires std::is_base_of_v> -struct as_parser -{ - using value_type [[deprecated("Use x4::as_parser_plain_t")]] = std::remove_cvref_t; +//template +// requires std::is_base_of_v> +//struct as_parser +//{ +// template +// [[nodiscard]] static constexpr auto&& call(T&& p) noexcept +// { +// return std::forward(p); +// } +//}; +// +//template +//struct as_parser> +//{ +// template +// [[nodiscard]] static constexpr auto&& call(T&& p) noexcept +// { +// return std::forward(p).derived(); +// } +//}; + +} // traits - template - [[nodiscard]] static constexpr auto&& call(T&& p) noexcept - { - return std::forward(p); - } -}; +namespace detail { -template -struct as_parser> -{ - using value_type [[deprecated("Use x4::as_parser_plain_t")]] = Derived; +template +concept has_parser_base = std::is_base_of_v>; - template - [[nodiscard]] static constexpr auto&& call(T&& p) noexcept - { - return std::forward(p).derived(); - } +template +concept has_custom_as_parser = requires(T&& p) { + { x4::traits::as_parser>{}(std::forward(p)) } -> has_parser_base; }; -} // extension - -namespace detail { - struct as_parser_fn { - template - static void operator()(T&&) = delete; // If you reach here, your specialization of `x4::extension::as_parser` has a wrong signature, or the type is simply incompatible. + //template + //static void operator()(T&&) = delete; // If you reach here, your specialization of `x4::extension::as_parser` has a wrong signature, or the type is simply incompatible. - // catch-all default fallback template - requires std::is_base_of_v< - parser_base, - std::remove_cvref_t>::call(std::declval()))> - > + requires has_custom_as_parser [[nodiscard]] static constexpr decltype(auto) - operator()(T&& x) noexcept(noexcept(extension::as_parser>::call(std::forward(x)))) - { - return extension::as_parser>::call(std::forward(x)); - } - - template - [[nodiscard]] static constexpr auto&& - operator()(parser& p IRIS_LIFETIMEBOUND) noexcept + operator()(T&& x) noexcept(noexcept(traits::as_parser>{}(std::forward(x)))) { - return p.derived(); + static_assert(has_parser_base>{}(std::forward(x)))>); + return traits::as_parser>{}(std::forward(x)); } - template - [[nodiscard]] static constexpr auto&& - operator()(parser const& p IRIS_LIFETIMEBOUND) noexcept - { - return p.derived(); - } - - template + template + requires + (!has_custom_as_parser

) && + has_parser_base

[[nodiscard]] static constexpr auto&& - operator()(parser&& p IRIS_LIFETIMEBOUND) noexcept + operator()(P&& p) noexcept { - return std::move(p).derived(); + return static_cast(p); } - template - [[nodiscard]] static constexpr auto&& - operator()(parser const&& p IRIS_LIFETIMEBOUND) noexcept - { - return std::move(p).derived(); - } }; // as_parser_fn } // detail @@ -309,16 +395,15 @@ constexpr bool is_parser_nothrow_castable_v = is_parser_nothrow_castable::val template concept X4ExplicitSubject = - std::is_base_of_v> && + detail::has_parser_base && std::move_constructible>; // Note: a lambda with a capture has a deleted move assignment operator, // thus requiring move assignable here would make such `x4::action` to // not satisfy this trait; we consider it too strict for now. - //std::is_move_assignable_v> template concept X4ImplicitSubject = - !std::is_base_of_v> && + !detail::has_parser_base && is_parser_castable_v && // `as_parser(t)` is valid? X4ExplicitSubject>; @@ -372,7 +457,19 @@ constexpr bool is_parser_nothrow_constructible_v = is_parser_nothrow_constructib template -struct is_parsable +concept Parsable = requires(Parser const& p) { + { + p.parse( + std::declval(), // first + std::declval(), // last + std::declval(), // context + std::declval() // attr + ) + } -> std::same_as; +}; + +template +struct is_parsable : std::bool_constant> { static_assert(X4ExplicitSubject); static_assert(!std::is_reference_v); @@ -381,38 +478,11 @@ struct is_parsable static_assert(!std::is_reference_v); static_assert(!std::is_reference_v); static_assert(X4Attribute); - - static constexpr bool value = requires(Parser const& p) { // mutable parser use case is currently unknown - { - p.parse( - std::declval(), // first - std::declval(), // last - std::declval(), // context - std::declval() // attr - ) - } -> std::same_as; - }; - - static_assert(!requires(Parser const& p) { - { - p.parse( - std::declval(), // first - std::declval(), // last - std::declval(), // context - std::declval(), // rcontext - std::declval() // attr - ) - } -> std::same_as; - }, "X4 can now determine `RContext` automatically. Remove `RContext` from your parser."); }; template constexpr bool is_parsable_v = is_parsable::value; -template -concept Parsable = is_parsable::value; -// ^^^ this must be concept in order to provide better diagnostics (e.g. on MSVC) - template struct is_nothrow_parsable { @@ -424,7 +494,7 @@ struct is_nothrow_parsable static_assert(!std::is_reference_v); static_assert(X4Attribute); - static constexpr bool value = requires(Parser const& p) { // mutable parser use case is currently unknown + static constexpr bool value = requires(Parser const& p) { { p.parse( std::declval(), // first @@ -434,18 +504,6 @@ struct is_nothrow_parsable ) } noexcept -> std::same_as; }; - - static_assert(!requires(Parser const& p) { - { - p.parse( - std::declval(), // first - std::declval(), // last - std::declval(), // context - std::declval(), // rcontext - std::declval() // attr - ) - } /*noexcept*/ -> std::same_as; - }, "X4 can now determine `RContext` automatically. Remove `RContext` from your parser."); }; template @@ -455,12 +513,12 @@ constexpr bool is_nothrow_parsable_v = is_nothrow_parsable concept X4ExplicitParser = X4ExplicitSubject && - is_parsable_v, It, Se, unused_type, unused_type>; + Parsable, It, Se, unused_type, unused_type>; template concept X4ImplicitParser = X4ImplicitSubject && - is_parsable_v, It, Se, unused_type, unused_type>; + Parsable, It, Se, unused_type, unused_type>; // The primary "parser" concept of X4, applicable in iterator-aware contexts. // diff --git a/include/iris/x4/core/parser_traits.hpp b/include/iris/x4/core/parser_traits.hpp index acb25898f..9c38f741e 100644 --- a/include/iris/x4/core/parser_traits.hpp +++ b/include/iris/x4/core/parser_traits.hpp @@ -9,13 +9,14 @@ 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 +#include // IWYU pragma: keep // If a user-provided program declares an explicit or partial // specialization of any entity defined in this header, the @@ -65,8 +66,8 @@ struct get_handles_container template struct has_attribute : std::bool_constant< - !std::is_same_v::type, unused_type> && - !std::is_same_v::type, unused_container_type> + !std::same_as::type, unused_type> && + !std::same_as::type, unused_container_type> > {}; diff --git a/include/iris/x4/directive/expect.hpp b/include/iris/x4/directive/expect.hpp index 411995dbf..6626344e2 100644 --- a/include/iris/x4/directive/expect.hpp +++ b/include/iris/x4/directive/expect.hpp @@ -36,11 +36,11 @@ struct expect_directive : proxy_parser> "Note that you must also bind the context to your skipper." ); - bool const r = this->subject.parse(first, last, ctx, attr); + 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); + x4::set_expectation_failure(first, this->subject(), ctx); } return r; } @@ -91,11 +91,11 @@ struct parse_into_container_impl> "Note that you must also bind the context to your skipper." ); - bool const r = detail::parse_into_container(parser.subject, first, last, ctx, attr); + bool const r = detail::parse_into_container(parser.subject(), first, last, ctx, attr); // only the first error is needed if (!r && !x4::has_expectation_failure(ctx)) { - x4::set_expectation_failure(first, parser.subject, ctx); + x4::set_expectation_failure(first, parser.subject(), ctx); } return r; } diff --git a/include/iris/x4/directive/lexeme.hpp b/include/iris/x4/directive/lexeme.hpp index c4120d89a..49e192d39 100644 --- a/include/iris/x4/directive/lexeme.hpp +++ b/include/iris/x4/directive/lexeme.hpp @@ -39,7 +39,7 @@ struct lexeme_directive : proxy_parser> auto it = first; x4::skip_over(it, last, ctx); // pre-skip - bool const ok = this->subject.parse( + bool const ok = this->subject().parse( it, last, x4::remove_first_context(ctx), // no skipper attr @@ -50,7 +50,7 @@ struct lexeme_directive : proxy_parser> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format("lexeme[{}]", get_info{}(this->subject)); + return std::format("lexeme[{}]", get_info{}(this->subject())); } }; diff --git a/include/iris/x4/directive/matches.hpp b/include/iris/x4/directive/matches.hpp index 9e7be6218..46724e398 100644 --- a/include/iris/x4/directive/matches.hpp +++ b/include/iris/x4/directive/matches.hpp @@ -24,7 +24,7 @@ namespace iris::x4 { template -struct matches_directive : unary_parser> +struct matches_directive : unary_parser { using attribute_type = bool; @@ -38,7 +38,7 @@ struct matches_directive : unary_parser> noexcept(x4::move_to(std::declval(), attr)) ) { - bool const matched = this->subject.parse(first, last, ctx, unused); + bool const matched = this->subject().parse(first, last, ctx, unused); if constexpr (has_context_v) { if (x4::has_expectation_failure(ctx)) return false; diff --git a/include/iris/x4/directive/no_case.hpp b/include/iris/x4/directive/no_case.hpp index f1dfe4f9b..60986eebe 100644 --- a/include/iris/x4/directive/no_case.hpp +++ b/include/iris/x4/directive/no_case.hpp @@ -28,15 +28,13 @@ struct no_case_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(noexcept( - this->subject.parse( - first, last, - x4::make_context(detail::case_compare_no_case, ctx), - attr - ) - )) + noexcept(is_nothrow_parsable_v< + Subject, It, Se, + decltype(x4::make_context(detail::case_compare_no_case, ctx)), + Attr + >) { - return this->subject.parse( + return this->subject().parse( first, last, x4::make_context(detail::case_compare_no_case, ctx), attr diff --git a/include/iris/x4/directive/no_skip.hpp b/include/iris/x4/directive/no_skip.hpp index 40e48061c..447829739 100644 --- a/include/iris/x4/directive/no_skip.hpp +++ b/include/iris/x4/directive/no_skip.hpp @@ -38,7 +38,7 @@ struct no_skip_directive : proxy_parser> // No pre-skip here, in contrast to `lexeme` // - return this->subject.parse( + return this->subject().parse( first, last, x4::remove_first_context(ctx), attr diff --git a/include/iris/x4/directive/omit.hpp b/include/iris/x4/directive/omit.hpp index d75541614..20053b7d3 100644 --- a/include/iris/x4/directive/omit.hpp +++ b/include/iris/x4/directive/omit.hpp @@ -24,7 +24,7 @@ namespace iris::x4 { // `omit_directive` forces the attribute of subject parser // to be `unused_type` template -struct omit_directive : unary_parser> +struct omit_directive : unary_parser { using attribute_type = unused_type; @@ -35,14 +35,14 @@ struct omit_directive : unary_parser> 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); + return this->subject().parse(first, last, ctx, unused); } [[nodiscard]] constexpr std::string get_x4_info() const { return std::format( "omit[{}]", - get_info{}(this->subject) + get_info{}(this->subject()) ); } }; diff --git a/include/iris/x4/directive/repeat.hpp b/include/iris/x4/directive/repeat.hpp index e5cff7659..a1167de30 100644 --- a/include/iris/x4/directive/repeat.hpp +++ b/include/iris/x4/directive/repeat.hpp @@ -114,7 +114,7 @@ struct repeat_directive : proxy_parsersubject, local_it, last, ctx, chunk_buf)) { + if (detail::parse_into_container(this->subject(), local_it, last, ctx, chunk_buf)) { // We can't merge here; it will lead to partial status } else { return false; @@ -125,7 +125,7 @@ struct repeat_directive : proxy_parsersubject, first, last, ctx, chunk_buf)) { + if (detail::parse_into_container(this->subject(), first, last, ctx, chunk_buf)) { list_like_parser::successful_merge_into(chunk_buf, container_attr); } else { break; @@ -143,7 +143,7 @@ struct repeat_directive : proxy_parsersubject, first, last, ctx, x4::assume_container(unused_attr))) && + 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 ) @@ -151,7 +151,7 @@ struct repeat_directive : proxy_parsersubject, local_it, last, ctx, x4::assume_container(unused_attr))) { + if (!detail::parse_into_container(this->subject(), local_it, last, ctx, x4::assume_container(unused_attr))) { return false; } } @@ -159,7 +159,7 @@ struct repeat_directive : proxy_parsersubject, first, last, ctx, x4::assume_container(unused_attr))) { + if (!detail::parse_into_container(this->subject(), first, last, ctx, x4::assume_container(unused_attr))) { break; } } diff --git a/include/iris/x4/directive/skip.hpp b/include/iris/x4/directive/skip.hpp index fede93a71..31d0affda 100644 --- a/include/iris/x4/directive/skip.hpp +++ b/include/iris/x4/directive/skip.hpp @@ -50,7 +50,7 @@ struct skip_directive : proxy_parser> 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); + return this->subject().parse(first, last, x4::replace_first_context(ctx, skipper_), attr); } [[nodiscard]] constexpr std::string get_x4_info() const @@ -58,7 +58,7 @@ struct skip_directive : proxy_parser> return std::format( "skip({})[{}]", get_info{}(this->skipper_), - get_info{}(this->subject) + get_info{}(this->subject()) ); } @@ -88,7 +88,7 @@ struct builtin_skip_directive : proxy_parsersubject.parse(first, last, ctx, attr); + bool const ok = this->subject().parse(first, last, ctx, attr); skipper_kind = old_skipper_kind; return ok; @@ -109,7 +109,7 @@ struct builtin_skip_directive : proxy_parsersubject.parse(first, last, x4::replace_first_context(ctx, skipper_kind), attr); + return this->subject().parse(first, last, x4::replace_first_context(ctx, skipper_kind), attr); } [[nodiscard]] constexpr std::string get_x4_info() const @@ -117,7 +117,7 @@ struct builtin_skip_directive : proxy_parser::name, - get_info{}(this->subject) + get_info{}(this->subject()) ); } }; diff --git a/include/iris/x4/directive/with.hpp b/include/iris/x4/directive/with.hpp index b658d82e4..8c99426c3 100644 --- a/include/iris/x4/directive/with.hpp +++ b/include/iris/x4/directive/with.hpp @@ -106,12 +106,7 @@ struct with_directive : detail::with_directive_impl using value_type = T; using base_type = detail::with_directive_impl; - template - requires std::is_constructible_v - constexpr with_directive(SubjectT&& subject, U&& val) - noexcept(std::is_nothrow_constructible_v) - : base_type(std::forward(subject), std::forward(val)) - {} + using base_type::base_type; // The internal context type. This can be used to determine the composed // context type used in `x4::parse`/`x4::phrase_parse`. It is required for @@ -124,8 +119,7 @@ struct with_directive : detail::with_directive_impl parse(It& first, Se const& last, Context const& ctx, Attr& attr) const noexcept(is_nothrow_parsable_v, Attr>) { - static_assert(Parsable, Attr>); - return this->subject.parse( + return this->subject().parse( first, last, x4::make_context(this->val_, ctx), attr @@ -134,7 +128,7 @@ struct with_directive : detail::with_directive_impl [[nodiscard]] std::string get_x4_info() const { - return std::format("with<...>[{}]", get_info{}(this->subject)); + return std::format("with<...>[{}]", get_info{}(this->subject())); } private: @@ -246,12 +240,12 @@ struct without_directive : proxy_parser ) { - return this->subject.parse(first, last, x4::remove_all_contexts(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 std::format("without<...>[{}]", get_info{}(this->subject())); } }; diff --git a/include/iris/x4/directive/with_local.hpp b/include/iris/x4/directive/with_local.hpp index 2d09c98a0..83fd021de 100644 --- a/include/iris/x4/directive/with_local.hpp +++ b/include/iris/x4/directive/with_local.hpp @@ -1,4 +1,4 @@ -#ifndef IRIS_ZZ_X4_DIRECTIVE_WITH_LOCAL_HPP +#ifndef IRIS_ZZ_X4_DIRECTIVE_WITH_LOCAL_HPP #define IRIS_ZZ_X4_DIRECTIVE_WITH_LOCAL_HPP /*============================================================================= @@ -77,7 +77,7 @@ struct with_local_directive : proxy_parsersubject.parse(first, last, x4::replace_first_context(ctx, local_var), attr); + return this->subject().parse(first, last, x4::replace_first_context(ctx, local_var), attr); } }; diff --git a/include/iris/x4/numeric/bool.hpp b/include/iris/x4/numeric/bool.hpp index 759ab6957..fcdb45444 100644 --- a/include/iris/x4/numeric/bool.hpp +++ b/include/iris/x4/numeric/bool.hpp @@ -20,6 +20,8 @@ #include +#include + #include #include #include @@ -60,7 +62,7 @@ struct bool_policies }; template> -struct bool_parser : parser> +struct bool_parser : parser<> { static_assert(X4Attribute); static_assert(std::default_initializable); @@ -70,10 +72,6 @@ struct bool_parser : parser> static constexpr bool has_attribute = true; - constexpr bool_parser() = default; - - constexpr bool_parser(Policy const&) = delete; // Policy should be stateless - // Attribute is `T` or `unused_type` template Se, class Context, class U> requires @@ -126,9 +124,11 @@ struct bool_parser : parser> } }; -template> -struct literal_bool_parser : parser> +template> +struct literal_bool_parser : parser<> { + using T = decltype(ExpectedValue); + static_assert(requires { static_cast(ExpectedValue); }); static_assert(X4Attribute); static_assert(std::default_initializable); @@ -136,18 +136,6 @@ struct literal_bool_parser : parser> static constexpr bool has_attribute = true; - template - requires - (!std::is_same_v, literal_bool_parser>) && - std::is_constructible_v - constexpr explicit literal_bool_parser(U&& expected_bool) - noexcept(std::is_nothrow_constructible_v) - : expected_bool_(std::forward(expected_bool)) - {} - - 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 @@ -166,8 +154,8 @@ struct literal_bool_parser : parser> 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)); + bool const ok = (static_cast(ExpectedValue) && Policy::parse_true(it, last, attr, compare)) + || (!static_cast(ExpectedValue) && Policy::parse_false(it, last, attr, compare)); if (ok) first = it; return ok; @@ -196,18 +184,15 @@ struct literal_bool_parser : parser> [[nodiscard]] std::string get_x4_info() const { - return expected_bool_ ? "`true`" : "`false`"; + return static_cast(ExpectedValue) ? "`true`" : "`false`"; } - -private: - T expected_bool_; // TODO: remove this runtime param; make this CTP }; namespace parsers { [[maybe_unused]] inline constexpr bool_parser bool_{}; -[[maybe_unused]] inline constexpr literal_bool_parser true_{true}; -[[maybe_unused]] inline constexpr literal_bool_parser false_{false}; +[[maybe_unused]] inline constexpr literal_bool_parser true_{}; +[[maybe_unused]] inline constexpr literal_bool_parser false_{}; } // parsers diff --git a/include/iris/x4/numeric/int.hpp b/include/iris/x4/numeric/int.hpp index b5f8a2519..167a6219c 100644 --- a/include/iris/x4/numeric/int.hpp +++ b/include/iris/x4/numeric/int.hpp @@ -27,7 +27,7 @@ template< unsigned MinDigits = 1, int MaxDigits = -1 > -struct int_parser : parser> +struct int_parser : parser<> { // check template parameter 'Radix' for validity static_assert( diff --git a/include/iris/x4/numeric/real.hpp b/include/iris/x4/numeric/real.hpp index 57b94a683..f484c72aa 100644 --- a/include/iris/x4/numeric/real.hpp +++ b/include/iris/x4/numeric/real.hpp @@ -207,7 +207,7 @@ struct strict_real_policies : real_policies }; template> -struct real_parser : parser> +struct real_parser : parser<> { static_assert(X4Attribute); static_assert(std::default_initializable); diff --git a/include/iris/x4/numeric/uint.hpp b/include/iris/x4/numeric/uint.hpp index eefd34208..df1956398 100644 --- a/include/iris/x4/numeric/uint.hpp +++ b/include/iris/x4/numeric/uint.hpp @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -29,7 +28,7 @@ template< unsigned MinDigits = 1, int MaxDigits = -1 > -struct uint_parser : parser> +struct uint_parser : parser<> { // check template parameter 'Radix' for validity static_assert((Radix >= 2 && Radix <= 36), "Unsupported Radix"); diff --git a/include/iris/x4/operator/and_predicate.hpp b/include/iris/x4/operator/and_predicate.hpp index 4b03ad795..aad57f66b 100644 --- a/include/iris/x4/operator/and_predicate.hpp +++ b/include/iris/x4/operator/and_predicate.hpp @@ -19,7 +19,7 @@ namespace iris::x4 { template -struct and_predicate : unary_parser> +struct and_predicate : unary_parser { using attribute_type = unused_type; @@ -34,7 +34,7 @@ struct and_predicate : unary_parser> ) { auto it = first; - return this->subject.parse(it, last, ctx, unused); + return this->subject().parse(it, last, ctx, unused); } }; diff --git a/include/iris/x4/operator/kleene.hpp b/include/iris/x4/operator/kleene.hpp index a49bb904a..9c30d614f 100644 --- a/include/iris/x4/operator/kleene.hpp +++ b/include/iris/x4/operator/kleene.hpp @@ -25,7 +25,7 @@ namespace iris::x4 { template -struct kleene : unary_parser> +struct kleene : unary_parser { using attribute_type = traits::default_container::attribute_type>::type; @@ -43,7 +43,7 @@ struct kleene : unary_parser> auto& container_attr = list_like_parser::get_container(attr); list_like_parser::chunk_buffer chunk_buf; - while (detail::parse_into_container(this->subject, first, last, ctx, chunk_buf)) { + while (detail::parse_into_container(this->subject(), first, last, ctx, chunk_buf)) { list_like_parser::successful_merge_into(chunk_buf, container_attr); } @@ -57,9 +57,9 @@ struct kleene : unary_parser> 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)))) + 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))) + while (detail::parse_into_container(this->subject(), first, last, ctx, x4::assume_container(unused_attr))) /* loop */; if constexpr (has_context_v) { @@ -73,7 +73,7 @@ struct kleene : unary_parser> { return std::format( "*{}", - get_info{}(this->subject) + get_info{}(this->subject()) ); } }; diff --git a/include/iris/x4/operator/not_predicate.hpp b/include/iris/x4/operator/not_predicate.hpp index fd9bcb10f..f54ca2d15 100644 --- a/include/iris/x4/operator/not_predicate.hpp +++ b/include/iris/x4/operator/not_predicate.hpp @@ -21,7 +21,7 @@ namespace iris::x4 { template -struct not_predicate : unary_parser> +struct not_predicate : unary_parser { using attribute_type = unused_type; @@ -38,10 +38,10 @@ struct not_predicate : unary_parser> It local_first = first; if constexpr (has_context_v) { - return !this->subject.parse(local_first, last, ctx, unused) && + return !this->subject().parse(local_first, last, ctx, unused) && !x4::has_expectation_failure(ctx); } else { - return !this->subject.parse(local_first, last, ctx, unused); + return !this->subject().parse(local_first, last, ctx, unused); } } }; diff --git a/include/iris/x4/operator/optional.hpp b/include/iris/x4/operator/optional.hpp index cc820f58d..37dcc71b1 100644 --- a/include/iris/x4/operator/optional.hpp +++ b/include/iris/x4/operator/optional.hpp @@ -28,7 +28,7 @@ namespace iris::x4 { template -struct optional : unary_parser> +struct optional : unary_parser { using attribute_type = traits::build_optional::attribute_type>::type; @@ -47,10 +47,8 @@ struct optional : unary_parser> parse(It& first, Se const& last, Context const& ctx, Attr& attr) const noexcept(is_nothrow_parsable_v) { - static_assert(Parsable); - // discard [[nodiscard]] - (void)this->subject.parse(first, last, ctx, attr); + (void)this->subject().parse(first, last, ctx, attr); if constexpr (has_context_v) { return !x4::has_expectation_failure(ctx); @@ -66,10 +64,10 @@ struct optional : unary_parser> > [[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))) + 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); + (void)detail::parse_into_container(this->subject(), first, last, ctx, attr); if constexpr (has_context_v) { return !x4::has_expectation_failure(ctx); @@ -93,7 +91,7 @@ struct optional : unary_parser> { typename traits::optional_value::type val{}; // value-initialize - if (this->subject.parse(first, last, ctx, val)) { + if (this->subject().parse(first, last, ctx, val)) { // assign the parsed value into our attribute x4::move_to(std::move(val), attr); return true; diff --git a/include/iris/x4/operator/plus.hpp b/include/iris/x4/operator/plus.hpp index 4cc72295c..f2b6adde3 100644 --- a/include/iris/x4/operator/plus.hpp +++ b/include/iris/x4/operator/plus.hpp @@ -25,7 +25,7 @@ namespace iris::x4 { template -struct plus : unary_parser> +struct plus : unary_parser { using attribute_type = traits::default_container::attribute_type>::type; @@ -43,13 +43,13 @@ struct plus : unary_parser> auto& container_attr = list_like_parser::get_container(attr); list_like_parser::chunk_buffer chunk_buf; - if (detail::parse_into_container(this->subject, first, last, ctx, chunk_buf)) { + if (detail::parse_into_container(this->subject(), first, last, ctx, chunk_buf)) { list_like_parser::successful_merge_into(chunk_buf, container_attr); } else { return false; } - while (detail::parse_into_container(this->subject, first, last, ctx, chunk_buf)) { + while (detail::parse_into_container(this->subject(), first, last, ctx, chunk_buf)) { list_like_parser::successful_merge_into(chunk_buf, container_attr); } @@ -63,13 +63,13 @@ struct plus : unary_parser> 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)))) + 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))) { + if (!detail::parse_into_container(this->subject(), first, last, ctx, x4::assume_container(unused_attr))) { return false; } - while (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 */; if constexpr (has_context_v) { @@ -83,7 +83,7 @@ struct plus : unary_parser> { return std::format( "+{}", - get_info{}(this->subject) + get_info{}(this->subject()) ); } }; diff --git a/include/iris/x4/operator/sequence.hpp b/include/iris/x4/operator/sequence.hpp index 21d3e942f..a5c42eaf9 100644 --- a/include/iris/x4/operator/sequence.hpp +++ b/include/iris/x4/operator/sequence.hpp @@ -136,7 +136,7 @@ struct sequence : binary_parser> return std::format( "{} > {}", get_info{}(this->left), - get_info{}(this->right.subject) + get_info{}(this->right.subject()) ); } else { return std::format( diff --git a/include/iris/x4/primitive/eoi.hpp b/include/iris/x4/primitive/eoi.hpp index e4549aa88..225d0dbaa 100644 --- a/include/iris/x4/primitive/eoi.hpp +++ b/include/iris/x4/primitive/eoi.hpp @@ -20,7 +20,7 @@ namespace iris::x4 { -struct eoi_parser : parser +struct eoi_parser : parser<> { using attribute_type = unused_type; diff --git a/include/iris/x4/primitive/eol.hpp b/include/iris/x4/primitive/eol.hpp index c43d23443..7c3d685d8 100644 --- a/include/iris/x4/primitive/eol.hpp +++ b/include/iris/x4/primitive/eol.hpp @@ -19,7 +19,7 @@ namespace iris::x4 { -struct eol_parser : parser +struct eol_parser : parser<> { using attribute_type = unused_type; diff --git a/include/iris/x4/primitive/eps.hpp b/include/iris/x4/primitive/eps.hpp index f45c5a324..e3e133a50 100644 --- a/include/iris/x4/primitive/eps.hpp +++ b/include/iris/x4/primitive/eps.hpp @@ -23,43 +23,27 @@ namespace iris::x4 { namespace detail { -struct semantic_predicate : parser +struct semantic_predicate : parser { using attribute_type = unused_type; - constexpr explicit semantic_predicate(bool predicate) noexcept - : predicate_(predicate) - {} - template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr&) const noexcept(noexcept(x4::skip_over(first, last, ctx))) { - if (predicate_) { + if (this->storage()) { x4::skip_over(first, last, ctx); } - return predicate_; + return this->storage(); } - -private: - bool predicate_; }; template -struct lazy_semantic_predicate : parser> +struct lazy_semantic_predicate : parser { using attribute_type = unused_type; - template - requires - (!std::is_same_v, lazy_semantic_predicate>) && - std::is_constructible_v - constexpr explicit lazy_semantic_predicate(F_&& f) - noexcept(std::is_nothrow_constructible_v) - : f_(std::forward(f)) - {} - template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr&) const @@ -69,26 +53,23 @@ struct lazy_semantic_predicate : parser> if constexpr (std::invocable) { static_assert(std::same_as, bool>); - bool const ok = f_(ctx); + bool const ok = this->storage()(ctx); if (ok) first = it; return ok; } else { static_assert(std::invocable); static_assert(std::same_as, bool>); - bool const ok = f_(); + bool const ok = this->storage()(); if (ok) first = it; return ok; } } - -private: - F f_; }; } // detail -struct eps_parser : parser +struct eps_parser : parser<> { using attribute_type = unused_type; diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index f9577a9b6..e63d828bc 100644 --- a/include/iris/x4/rule.hpp +++ b/include/iris/x4/rule.hpp @@ -302,9 +302,9 @@ struct rule_impl }; template -struct rule_definition : parser> +struct rule_definition : parser { - static_assert(!std::is_same_v, unused_container_type>, "`rule_definition` with `unused_container_type` is not supported"); + 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; @@ -319,7 +319,7 @@ struct rule_definition : parser constexpr rule_definition(RHS_T&& rhs, std::string_view name) noexcept(std::is_nothrow_constructible_v) - : rhs(std::forward(rhs)) + : parser(std::forward(rhs)) , name(std::move(name)) {} @@ -330,11 +330,10 @@ struct rule_definition : parser ::template call_rule_definition( - rhs, name, first, last, ctx, attr + this->storage(), name, first, last, ctx, attr ); } - RHS rhs; std::string_view name = "unnamed_rule"; }; @@ -386,7 +385,7 @@ concept RuleAttrCompatible = } // detail template -struct rule : parser> +struct rule : parser<> { // This type MUST be constructible with incomplete types. // Do NOT add `static_assert`s or other constructs that cause eager diff --git a/include/iris/x4/string/literal_string.hpp b/include/iris/x4/string/literal_string.hpp index ca367844c..f88795f7e 100644 --- a/include/iris/x4/string/literal_string.hpp +++ b/include/iris/x4/string/literal_string.hpp @@ -29,7 +29,7 @@ namespace iris::x4 { template> -struct literal_string : parser> +struct literal_string : parser { static_assert( !std::is_pointer_v>, @@ -37,7 +37,7 @@ struct literal_string : parser> ); static_assert(std::is_convertible_v>); - using char_type = typename Encoding::char_type; + using char_type = Encoding::char_type; using encoding = Encoding; using attribute_type = Attr; @@ -45,28 +45,19 @@ struct literal_string : parser> static constexpr bool has_attribute = !std::is_same_v; - template - requires - (!std::is_same_v, literal_string>) && - std::is_constructible_v - constexpr literal_string(T&& val, Rest&&... rest) - noexcept(std::is_nothrow_constructible_v) - : str_(std::forward(val), std::forward(rest)...) - {} - 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 && noexcept(x4::skip_over(first, last, ctx)) && - noexcept(detail::string_parse(str_, first, last, x4::assume_container(attr), x4::get_case_compare(ctx))) + noexcept(detail::string_parse(this->storage(), 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"); 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)); + bool const ok = detail::string_parse(this->storage(), it, last, x4::assume_container(attr), x4::get_case_compare(ctx)); if (ok) first = it; return ok; } @@ -74,11 +65,8 @@ struct literal_string : parser> [[nodiscard]] std::string get_x4_info() const { // TODO: escape quotes - return std::format("\"{}\"", iris::unicode::transcode(this->str_)); + return std::format("\"{}\"", iris::unicode::transcode(this->storage())); } - -private: - String str_; }; } // iris::x4 diff --git a/include/iris/x4/string/string.hpp b/include/iris/x4/string/string.hpp index 73d0934e3..acddd3bf7 100644 --- a/include/iris/x4/string/string.hpp +++ b/include/iris/x4/string/string.hpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -201,64 +202,54 @@ using x4::lit; } // parsers -namespace extension { +namespace traits { template struct as_parser { - using type = literal_string, traits::char_encoding_for, unused_type>; - using value_type = type; - - [[nodiscard]] static constexpr type call(CharT const* s) + [[nodiscard]] static constexpr literal_string, char_encoding_for, unused_type> + operator()(CharT const (&s)[N]) noexcept { - return type(s); + return {s}; } }; -template -struct as_parser : as_parser {}; - template struct as_parser { - using type = literal_string, traits::char_encoding_for, unused_type>; - using value_type = type; - - [[nodiscard]] static constexpr type call(CharT const* s) + [[nodiscard]] static constexpr literal_string, char_encoding_for, unused_type> + operator()(CharT const* s) noexcept { - return type(std::basic_string_view{s}); + return {std::basic_string_view{s}}; } }; template struct as_parser> { - using type = literal_string, traits::char_encoding_for, unused_type>; - using value_type = type; - template - [[nodiscard]] static constexpr type call(T&& str) - noexcept(std::is_nothrow_constructible_v) + [[nodiscard]] static constexpr literal_string, char_encoding_for, unused_type> + operator()(T&& str) + noexcept(std::is_nothrow_constructible_v, char_encoding_for, unused_type>, T>) { - return type(std::forward(str)); + static_assert(std::same_as, std::basic_string>); + return {std::forward(str)}; } }; template struct as_parser> { - using type = literal_string, traits::char_encoding_for, unused_type>; - using value_type = type; - template - [[nodiscard]] static constexpr type call(T&& str) - noexcept(std::is_nothrow_constructible_v) + [[nodiscard]] static constexpr literal_string, char_encoding_for, unused_type> + operator()(T&& str) noexcept { - return type(std::forward(str)); + static_assert(std::same_as, std::basic_string_view>); + return {std::forward(str)}; } }; -} // extension +} // traits } // iris::x4 diff --git a/include/iris/x4/symbols.hpp b/include/iris/x4/symbols.hpp index dd918b28a..4d0ecd7b4 100644 --- a/include/iris/x4/symbols.hpp +++ b/include/iris/x4/symbols.hpp @@ -11,7 +11,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include +#include // IWYU pragma: keep + #include #include #include @@ -49,8 +50,8 @@ namespace iris::x4 { namespace detail { -template -struct symbols_parser_impl : parser +template +struct symbols_parser_impl : parser<> { static_assert(!std::is_same_v, "symbols parser with `unused_container_type` is not supported"); @@ -331,9 +332,9 @@ struct symbols_parser_impl : parser template> struct shared_symbols_parser - : detail::symbols_parser_impl, true, Encoding, T, Lookup> + : detail::symbols_parser_impl { - using base_type = detail::symbols_parser_impl, true, Encoding, T, Lookup>; + using base_type = detail::symbols_parser_impl; using base_type::base_type; using base_type::operator=; }; @@ -349,9 +350,9 @@ symbols_parser : shared_symbols_parser template> struct unique_symbols_parser - : detail::symbols_parser_impl, false, Encoding, T, Lookup> + : detail::symbols_parser_impl { - using base_type = detail::symbols_parser_impl, false, Encoding, T, Lookup>; + using base_type = detail::symbols_parser_impl; using base_type::base_type; using base_type::operator=; }; diff --git a/test/x4/as.cpp b/test/x4/as.cpp index 232dc962c..d03fabf0c 100644 --- a/test/x4/as.cpp +++ b/test/x4/as.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include diff --git a/test/x4/char.cpp b/test/x4/char.cpp index 08ccc53b6..c783c3c1c 100644 --- a/test/x4/char.cpp +++ b/test/x4/char.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/test/x4/char_class.cpp b/test/x4/char_class.cpp index 775268879..28127bcc6 100644 --- a/test/x4/char_class.cpp +++ b/test/x4/char_class.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/test/x4/expect.cpp b/test/x4/expect.cpp index 797816a6e..17bb2b2ec 100644 --- a/test/x4/expect.cpp +++ b/test/x4/expect.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include diff --git a/test/x4/iris_x4_test.hpp b/test/x4/iris_x4_test.hpp index cc54d29a5..0c5c221e1 100644 --- a/test/x4/iris_x4_test.hpp +++ b/test/x4/iris_x4_test.hpp @@ -234,7 +234,7 @@ struct move_only }; template -struct synth_parser : x4::parser> +struct synth_parser : x4::parser<> { using attribute_type = T; diff --git a/test/x4/iterator.cpp b/test/x4/iterator.cpp index 5e8202b23..ac61f9be1 100644 --- a/test/x4/iterator.cpp +++ b/test/x4/iterator.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/test/x4/no_skip.cpp b/test/x4/no_skip.cpp index c5a1b7746..a662f2837 100644 --- a/test/x4/no_skip.cpp +++ b/test/x4/no_skip.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include diff --git a/test/x4/parser.cpp b/test/x4/parser.cpp index 2f6924733..fa1b98ce3 100644 --- a/test/x4/parser.cpp +++ b/test/x4/parser.cpp @@ -18,7 +18,7 @@ namespace { struct minimal_parser - : x4::parser + : x4::parser<> { constexpr minimal_parser() = default; @@ -37,7 +37,7 @@ struct minimal_parser }; struct minimal_unary_parser - : x4::unary_parser + : x4::unary_parser { template Se, class Context, x4::X4Attribute Attr> [[nodiscard]] constexpr bool @@ -59,7 +59,7 @@ struct minimal_binary_parser }; struct minimal_unused_parser - : x4::parser + : x4::parser<> { template Se, class Context> [[nodiscard]] constexpr bool @@ -70,7 +70,7 @@ struct minimal_unused_parser }; struct minimal_unary_unused_parser - : x4::unary_parser + : x4::unary_parser { template Se, class Context> [[nodiscard]] constexpr bool diff --git a/test/x4/rule4.cpp b/test/x4/rule4.cpp index 018c60317..7c82a936d 100644 --- a/test/x4/rule4.cpp +++ b/test/x4/rule4.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include diff --git a/test/x4/sequence.cpp b/test/x4/sequence.cpp index 55ac84c6c..692553030 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -313,8 +312,8 @@ TEST_CASE("sequence") { std::vector v; - auto e = as(*~char_(',')); - auto l = as>(e >> *(',' >> e)); + constexpr auto e = as(*~char_(',')); + constexpr auto l = as>(e >> *(',' >> e)); REQUIRE(parse("abc1,abc2,abc3", l, v)); REQUIRE(v.size() == 3); diff --git a/test/x4/smart_ptr.cpp b/test/x4/smart_ptr.cpp index b6224ba4e..efe3f6d42 100644 --- a/test/x4/smart_ptr.cpp +++ b/test/x4/smart_ptr.cpp @@ -15,7 +15,7 @@ #include template -struct throwing_parser : x4::parser> +struct throwing_parser : x4::parser<> { using attribute_type = T; diff --git a/test/x4/symbols1.cpp b/test/x4/symbols1.cpp index 4fe0f11fb..10676d44b 100644 --- a/test/x4/symbols1.cpp +++ b/test/x4/symbols1.cpp @@ -14,22 +14,6 @@ #include #include -namespace { - -// Custom string type with a C-style string conversion. -struct custom_string_c -{ - custom_string_c(char c) { str[0] = c; str[1] = '\0'; } - - operator char*() { return str; } - operator char const*() const { return str; } - -private: - char str[2]; -}; - -} // anonymous - TEST_CASE("symbols1") { using x4::shared_symbols; diff --git a/test/x4/without.cpp b/test/x4/without.cpp index fd1be30bb..0dfaa7a8f 100644 --- a/test/x4/without.cpp +++ b/test/x4/without.cpp @@ -15,7 +15,7 @@ #include template -struct context_checker : x4::parser> +struct context_checker : x4::parser<> { template Se, class Context, x4::X4Attribute Attr> [[nodiscard]] static constexpr bool parse(It&, Se const&, Context const&, Attr const&) From 54ca7b77a232732a59b69c9aef85cd6fc7c7e9e4 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Sun, 20 Sep 2026 07:56:51 +0900 Subject: [PATCH 3/5] Optimize char/string literal parsers --- include/iris/x4/char.hpp | 15 +- include/iris/x4/char/any_char.hpp | 29 +- include/iris/x4/char/char.hpp | 200 ++---------- include/iris/x4/char/literal_char.hpp | 4 +- include/iris/x4/char_string_literal.hpp | 299 ++++++++++++++++++ include/iris/x4/core/attribute.hpp | 38 +-- include/iris/x4/core/char_traits.hpp | 30 +- .../iris/x4/core/detail/parse_alternative.hpp | 39 +-- include/iris/x4/core/parser.hpp | 145 +++++---- include/iris/x4/core/string_traits.hpp | 50 --- include/iris/x4/core/unused.hpp | 19 +- include/iris/x4/operator/alternative.hpp | 2 +- include/iris/x4/string.hpp | 8 +- .../iris/x4/string/detail/string_parse.hpp | 25 +- include/iris/x4/string/literal_string.hpp | 3 +- include/iris/x4/string/string.hpp | 243 +------------- modules/iris | 2 +- test/x4/alternative.cpp | 4 +- test/x4/as.cpp | 2 +- test/x4/char.cpp | 1 + test/x4/difference.cpp | 2 +- test/x4/expect.cpp | 6 +- test/x4/iterator.cpp | 2 +- test/x4/lit.cpp | 63 ++-- test/x4/no_case.cpp | 23 +- test/x4/omit.cpp | 2 +- test/x4/optional.cpp | 23 +- test/x4/partial_success.cpp | 8 +- test/x4/plus.cpp | 2 +- test/x4/recursive.cpp | 3 +- test/x4/rule4.cpp | 5 +- test/x4/rule_separate_tu_grammar.cpp | 6 +- test/x4/sequence.cpp | 8 +- test/x4/skip.cpp | 2 +- test/x4/symbols1.cpp | 2 +- 35 files changed, 555 insertions(+), 760 deletions(-) create mode 100644 include/iris/x4/char_string_literal.hpp delete mode 100644 include/iris/x4/core/string_traits.hpp diff --git a/include/iris/x4/char.hpp b/include/iris/x4/char.hpp index b7e46fe4e..c9a9d5e93 100644 --- a/include/iris/x4/char.hpp +++ b/include/iris/x4/char.hpp @@ -10,14 +10,17 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include -#include -#include -#include -#include +#include // IWYU pragma: keep + +#include // IWYU pragma: export + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #ifdef IRIS_X4_UNICODE -# include +# include // IWYU pragma: export #endif #endif diff --git a/include/iris/x4/char/any_char.hpp b/include/iris/x4/char/any_char.hpp index 72af3998a..21f1a534e 100644 --- a/include/iris/x4/char/any_char.hpp +++ b/include/iris/x4/char/any_char.hpp @@ -10,7 +10,6 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include #include #include @@ -20,9 +19,9 @@ template struct any_char : char_parser> { using encoding_type = Encoding; - using attribute_type = typename Encoding::char_type; - using char_type = typename Encoding::char_type; - using classify_type = typename Encoding::classify_type; + using attribute_type = Encoding::char_type; + using char_type = Encoding::char_type; + using classify_type = Encoding::classify_type; static constexpr bool has_attribute = true; @@ -33,9 +32,6 @@ struct any_char : char_parser> return encoding_type::ischar(classify_ch); } - static constexpr void - test(auto, auto const& /* ctx */) = delete; // Mixing incompatible char types is not allowed - template CharT> [[nodiscard]] static constexpr literal_char operator()(CharT ch) noexcept @@ -43,26 +39,21 @@ struct any_char : char_parser> return {ch}; } - template CharT> - static constexpr void operator()(CharT) = delete; // Mixing incompatible char types is not allowed - + template CharT> [[nodiscard]] static constexpr literal_char - operator()(char_type const (&ch)[2]) noexcept + operator()(CharT const (&ch)[2]) noexcept { return {ch[0]}; } - template + template CharT, std::size_t N> [[nodiscard]] static constexpr char_set - operator()(char_type const (&ch)[N]) + operator()(CharT const (&ch)[N]) { + static_assert(N >= 3); return char_set{ch}; } - template CharT, std::size_t N> - static constexpr void - operator()(CharT const (&)[N]) = delete; // Mixing incompatible char types is not allowed - template CharT> [[nodiscard]] static constexpr char_range operator()(CharT from, CharT to) noexcept @@ -70,10 +61,6 @@ struct any_char : char_parser> return {from, to}; } - template - requires CharIncompatibleWith || CharIncompatibleWith - static constexpr void operator()(From, To) = delete; // Mixing incompatible char types is not allowed - template static constexpr void operator()(From const (&)[FromN], To const (&)[ToN]) = delete; // Use single character literal to define character range diff --git a/include/iris/x4/char/char.hpp b/include/iris/x4/char/char.hpp index 55cd566bc..ad69154f0 100644 --- a/include/iris/x4/char/char.hpp +++ b/include/iris/x4/char/char.hpp @@ -10,220 +10,62 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include -#include -#include +#include // IWYU pragma: export +#include // IWYU pragma: export -#include - -#ifndef IRIS_X4_NO_STANDARD_WIDE -# include -#endif - -#ifdef IRIS_X4_UNICODE -# include -#endif +#include namespace iris::x4 { -namespace standard { - -[[maybe_unused]] inline constexpr any_char char_{}; +namespace detail { -inline namespace helpers { +template +struct any_char_fn : any_char> +{}; -[[nodiscard]] constexpr literal_char -lit(char ch) noexcept -{ - return {ch}; -} +} // detail -[[nodiscard]] constexpr literal_char -lit(X4VagueArrayOf2Chars auto const& ch) noexcept -{ - return {ch[0]}; -} - -} // helpers - -// If you see "no matching overload" on string literals (e.g. `"foo"`), -// you may need to include `string/literal_string.hpp`. -// If you still see errors after the inclusion, that might be due to -// mixing incompatible string literals. Don't do that. - -constexpr void lit(CharIncompatibleWith auto const*) = delete; // Mixing incompatible character types is not allowed -constexpr void lit(CharIncompatibleWith auto) = delete; // Mixing incompatible character types is not allowed +namespace parsers { +namespace standard { +[[maybe_unused]] inline constexpr x4::detail::any_char_fn char_{}; } // standard - -[[maybe_unused]] inline constexpr auto const& char_ = standard::char_; // TODO: this can't overload other character types - -using standard::helpers::lit; - +using standard::char_; #ifndef IRIS_X4_NO_STANDARD_WIDE namespace standard_wide { - -[[maybe_unused]] inline constexpr any_char char_{}; - -inline namespace helpers { - -[[nodiscard]] constexpr literal_char -lit(wchar_t ch) noexcept -{ - return {ch}; -} - -[[nodiscard]] constexpr literal_char -lit(X4VagueArrayOf2Chars auto const& ch) noexcept -{ - return {ch[0]}; -} - -} // helpers - -constexpr void lit(CharIncompatibleWith auto const*) = delete; // Mixing incompatible character types is not allowed -constexpr void lit(CharIncompatibleWith auto) = delete; // Mixing incompatible character types is not allowed - +[[maybe_unused]] inline constexpr x4::detail::any_char_fn char_{}; } // standard_wide - -using standard_wide::helpers::lit; #endif // IRIS_X4_NO_STANDARD_WIDE - #ifdef IRIS_X4_UNICODE namespace unicode { - -[[maybe_unused]] inline constexpr any_char char_{}; - -inline namespace helpers { - -// TODO: add `char8_t` and `char16_t` overloads - -[[nodiscard]] constexpr literal_char -lit(char32_t ch) noexcept -{ - return {ch}; -} - -[[nodiscard]] constexpr literal_char -lit(X4VagueArrayOf2Chars auto const& ch) noexcept -{ - return {ch[0]}; -} - -} // helpers - -constexpr void lit(CharIncompatibleWith auto const*) = delete; // Mixing incompatible character types is not allowed -constexpr void lit(CharIncompatibleWith auto) = delete; // Mixing incompatible character types is not allowed - +[[maybe_unused]] inline constexpr x4::detail::any_char_fn char_{}; } // unicode - -using unicode::helpers::lit; - #endif // IRIS_X4_UNICODE +} // parsers + +using parsers::char_; -namespace parsers { namespace standard { -using x4::standard::char_; -using x4::standard::lit; +using x4::parsers::standard::char_; } // standard #ifndef IRIS_X4_NO_STANDARD_WIDE namespace standard_wide { -using x4::standard_wide::char_; -using x4::standard_wide::lit; +using x4::parsers::standard_wide::char_; } // standard_wide -#endif +#endif // IRIS_X4_NO_STANDARD_WIDE #ifdef IRIS_X4_UNICODE namespace unicode { -using x4::unicode::char_; -using x4::unicode::lit; +using x4::parsers::unicode::char_; } // unicode -#endif - -using x4::char_; // TODO: make `any_parser` encoding-agnostic -using x4::lit; - -} // parsers - - -namespace traits { - -template<> -struct as_parser -{ - [[nodiscard]] static constexpr literal_char - operator()(char ch) noexcept - { - return {ch}; - } -}; - -#ifndef IRIS_X4_NO_STANDARD_WIDE -template<> -struct as_parser -{ - [[nodiscard]] static constexpr literal_char - operator()(wchar_t ch) noexcept - { - return {ch}; - } -}; -#endif // IRIS_X4_NO_STANDARD_WIDE - -#ifdef IRIS_X4_UNICODE -template<> -struct as_parser -{ - [[nodiscard]] static constexpr literal_char - operator()(char32_t ch) noexcept - { - return {ch}; - } -}; #endif // IRIS_X4_UNICODE -template<> -struct as_parser -{ - [[nodiscard]] static constexpr literal_char - operator()(char const (&ch)[2]) noexcept - { - return {ch[0]}; - } -}; - -#ifndef IRIS_X4_NO_STANDARD_WIDE -template<> -struct as_parser -{ - [[nodiscard]] static constexpr literal_char - operator()(wchar_t const (&ch)[2]) noexcept - { - return {ch[0]}; - } -}; -#endif // IRIS_X4_NO_STANDARD_WIDE - -#ifdef IRIS_X4_UNICODE -template<> -struct as_parser -{ - [[nodiscard]] static constexpr literal_char - operator()(char32_t const (&ch)[2]) noexcept - { - return {ch[0]}; - } -}; -#endif // IRIS_X4_UNICODE - -} // traits - } // iris::x4 #endif diff --git a/include/iris/x4/char/literal_char.hpp b/include/iris/x4/char/literal_char.hpp index 3950a05c8..cf62b044c 100644 --- a/include/iris/x4/char/literal_char.hpp +++ b/include/iris/x4/char/literal_char.hpp @@ -26,8 +26,8 @@ struct literal_char : char_parser> { using encoding_type = Encoding; using attribute_type = Attr; - using char_type = typename Encoding::char_type; - using classify_type = typename Encoding::classify_type; + using char_type = Encoding::char_type; + using classify_type = Encoding::classify_type; static constexpr bool has_attribute = !std::is_same_v; diff --git a/include/iris/x4/char_string_literal.hpp b/include/iris/x4/char_string_literal.hpp new file mode 100644 index 000000000..64b24dfa9 --- /dev/null +++ b/include/iris/x4/char_string_literal.hpp @@ -0,0 +1,299 @@ +#ifndef IRIS_ZZ_X4_CHAR_STRING_LITERAL_HPP +#define IRIS_ZZ_X4_CHAR_STRING_LITERAL_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 +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep + +namespace iris::x4 { + +namespace detail { + +template +concept is_string_parser_sso_eligible = + CharLike && + sizeof(CharT) * (N - 1) // size when directly storing null-omitted chars as an array + < sizeof(std::basic_string_view); + +template class AttrSelectorTT> +struct char_parser_fn +{ + template + [[nodiscard]] static constexpr literal_char< + traits::char_encoding_for, + typename AttrSelectorTT::type + > + operator()(CharT ch) noexcept + { + return {ch}; + } +}; + +template class AttrSelectorTT> +struct char_array_parser_fn +{ + template + [[nodiscard]] static constexpr literal_char< + traits::char_encoding_for, + typename AttrSelectorTT::type + > + operator()(CharT const (&str)[2]) noexcept + { + return {str[0]}; + } + + template + [[nodiscard]] static constexpr literal_string< + std::array, + traits::char_encoding_for, + typename AttrSelectorTT>::type + > + operator()(CharT const (&str)[N]) noexcept + requires (N >= 3) && x4::detail::is_string_parser_sso_eligible + { + std::array arr; + std::ranges::copy_n(str, N - 1, arr.data()); + return {arr}; + } + + template + [[nodiscard]] static constexpr literal_string< + std::basic_string_view, + traits::char_encoding_for, + typename AttrSelectorTT>::type + > + operator()(CharT const (&str)[N]) noexcept + requires (N >= 3) && (!x4::detail::is_string_parser_sso_eligible) + { + static_assert(N >= 2); + return {std::basic_string_view(str, N - 1)}; + } +}; + +template<> +struct char_array_parser_fn +{ + template + [[nodiscard]] static constexpr literal_string< + std::array, + traits::char_encoding_for, + std::basic_string + > + operator()(CharT const (&str)[2]) noexcept + { + return literal_string< + std::array, + traits::char_encoding_for, + std::basic_string + >{std::array{str[0]}}; + } + + template + [[nodiscard]] static constexpr literal_string< + std::array, + traits::char_encoding_for, + std::basic_string + > + operator()(CharT const (&str)[N]) noexcept + requires (N >= 3) && x4::detail::is_string_parser_sso_eligible + { + std::array arr; + std::ranges::copy_n(str, N - 1, arr.data()); + return {arr}; + } + + template + [[nodiscard]] static constexpr literal_string< + std::basic_string_view, + traits::char_encoding_for, + std::basic_string + > + operator()(CharT const (&str)[N]) noexcept + requires (N >= 3) && (!x4::detail::is_string_parser_sso_eligible) + { + static_assert(N >= 2); + return std::basic_string_view(str, N - 1); + } +}; + +//template class AttrSelectorTT> +//struct char_pointer_parser_fn +//{ +// template +// [[nodiscard]] static constexpr literal_string< +// std::basic_string_view, +// traits::char_encoding_for, +// typename AttrSelectorTT>::type +// > +// operator()(CharT const* s) noexcept +// { +// return {std::basic_string_view{s}}; +// } +//}; + +template class AttrSelectorTT> +struct string_parser_fn +{ + template + requires is_ttp_specialization_of_v, std::basic_string> + [[nodiscard]] static constexpr literal_string< + std::basic_string>, + traits::char_encoding_for>, + typename AttrSelectorTT>>::type + > + operator()(StringLikeT&& str) + noexcept(std::is_nothrow_constructible_v< + literal_string< + std::basic_string>, + traits::char_encoding_for>, + typename AttrSelectorTT>>::type + >, + StringLikeT + >) + { + return {std::forward(str)}; + } +}; + +template class AttrSelectorTT> +struct string_view_parser_fn +{ + template + requires is_ttp_specialization_of_v, std::basic_string_view> + [[nodiscard]] static constexpr literal_string< + std::basic_string_view>, + traits::char_encoding_for>, + typename AttrSelectorTT>>::type + > + operator()(StringLikeT&& str) + noexcept(std::is_nothrow_constructible_v< + literal_string< + std::basic_string_view>, + traits::char_encoding_for>, + typename AttrSelectorTT>>::type + >, + StringLikeT + >) + { + return {std::forward(str)}; + } +}; + +} // detail + + +namespace traits { + +template +struct as_parser + : x4::detail::char_parser_fn +{}; + +template<> +struct as_parser + : x4::detail::char_array_parser_fn +{}; + +//template +//struct as_parser +// : x4::detail::char_pointer_parser_fn +//{}; + +template +struct as_parser> + : x4::detail::string_parser_fn +{}; + +template +struct as_parser> + : x4::detail::string_view_parser_fn +{}; + +} // traits + + +namespace detail { + +template +struct lit_string_fn + : char_parser_fn + , char_array_parser_fn + //, char_pointer_parser_fn + , string_parser_fn + , string_view_parser_fn +{ + using char_parser_fn::operator(); + using char_array_parser_fn::operator(); + using string_parser_fn::operator(); + using string_view_parser_fn::operator(); +}; + +template<> +struct lit_string_fn + : char_parser_fn + , char_array_parser_fn + //, char_pointer_parser_fn + , string_parser_fn + , string_view_parser_fn +{ + using char_parser_fn::operator(); + using char_array_parser_fn::operator(); + using string_parser_fn::operator(); + using string_view_parser_fn::operator(); +}; + +} // detail + +namespace parsers { +[[maybe_unused]] inline constexpr detail::lit_string_fn lit{}; +[[maybe_unused]] inline constexpr detail::lit_string_fn string{}; +} // parsers + +using parsers::lit; +using parsers::string; + + +namespace standard { +[[maybe_unused]] inline constexpr auto const& lit [[deprecated("use `x4::lit`")]] = x4::lit; +[[maybe_unused]] inline constexpr auto const& string [[deprecated("use `x4::string`")]] = x4::string; +} // standard + +#ifndef IRIS_X4_NO_STANDARD_WIDE +namespace standard_wide { +[[maybe_unused]] inline constexpr auto const& lit [[deprecated("use `x4::lit`")]] = x4::lit; +[[maybe_unused]] inline constexpr auto const& string [[deprecated("use `x4::string`")]] = x4::string; +} // standard_wide +#endif // IRIS_X4_NO_STANDARD_WIDE + +#ifdef IRIS_X4_UNICODE +namespace unicode { +[[maybe_unused]] inline constexpr auto const& lit [[deprecated("use `x4::lit`")]] = x4::lit; +[[maybe_unused]] inline constexpr auto const& string [[deprecated("use `x4::string`")]] = x4::string; +} // unicode +#endif // IRIS_X4_UNICODE + +} // iris::x4 + +#endif diff --git a/include/iris/x4/core/attribute.hpp b/include/iris/x4/core/attribute.hpp index 74f689ce1..70894fdb1 100644 --- a/include/iris/x4/core/attribute.hpp +++ b/include/iris/x4/core/attribute.hpp @@ -1,4 +1,4 @@ -#ifndef IRIS_ZZ_X4_CORE_ATTRIBUTE_HPP +#ifndef IRIS_ZZ_X4_CORE_ATTRIBUTE_HPP #define IRIS_ZZ_X4_CORE_ATTRIBUTE_HPP /*============================================================================= @@ -9,7 +9,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ -#include +#include // IWYU pragma: keep #include #include @@ -24,6 +24,17 @@ namespace detail { struct parser_base; +template +struct has_parser_base_impl +{ + static constexpr bool value = requires { + requires std::same_as; + }; +}; + +template +concept has_parser_base = has_parser_base_impl>::value; + } // detail template @@ -35,7 +46,7 @@ template concept X4NonUnusedAttribute = !X4UnusedAttribute && std::is_object_v && // implies not reference - !std::is_base_of_v> && + !detail::has_parser_base && std::move_constructible>; // TODO: `fusion::iterator_range` does not satisfy these due to `fusion::vector`'s iterator being a reference type //std::default_initializable> && @@ -46,25 +57,4 @@ concept X4Attribute = X4UnusedAttribute || X4NonUnusedAttribute; } // iris::x4 -namespace iris::x4::traits { - -// Pseudo attribute is a parser attribute whose actual type can only be determined at -// parse time. Such attribute is dependent on at least one of It/Se/Context. -template Se, class Context, class AttrRef> -struct pseudo_attribute -{ - static_assert(X4Attribute>); - static_assert(!std::is_rvalue_reference_v); - - using actual_type = AttrRef; - - [[nodiscard]] static constexpr actual_type - make_actual_type(It&, Se const&, Context const&, AttrRef&& attr_) noexcept // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) - { - return static_cast(attr_); - } -}; - -} // iris::x4::traits - #endif diff --git a/include/iris/x4/core/char_traits.hpp b/include/iris/x4/core/char_traits.hpp index 256b129e1..55aeb6bf9 100644 --- a/include/iris/x4/core/char_traits.hpp +++ b/include/iris/x4/core/char_traits.hpp @@ -13,44 +13,18 @@ #include // IWYU pragma: keep +#include + #include #include namespace iris::x4 { -template -concept CharLike = - std::same_as, char> || - std::same_as, wchar_t> || - std::same_as, char8_t> || - std::same_as, char16_t> || - std::same_as, char32_t>; - template concept CharArray = std::is_array_v> && CharLike>>; -// Spirit has historically converted "c" to 'c'. -// -// While we think it's still useful to retain the conversion, -// we need to avoid further conversion to `std::basic_string_view`, -// which leads to performance overhead. This trait enables -// detection of such arrays. -// -// Note that the status quo introduces ambiguity in determining -// {'c', '\0'} and {'c', 'd'}, but we're not aware of any practical -// usage of non-null-terminated character array in the context of -// DSL on parser combinator. -// -// However, if compelling use cases emerge, we may revise these -// semantics. Versioned as `X4` for forward compatibility. -template -concept X4VagueArrayOf2Chars = - std::same_as>, CharT> && - std::is_bounded_array_v> && - std::extent_v> == 2; - // Mixing incompatible character types is semantically wrong. // Don't do that. It may even lead to security vulnerabilities. template diff --git a/include/iris/x4/core/detail/parse_alternative.hpp b/include/iris/x4/core/detail/parse_alternative.hpp index 4e6d27298..6643d6144 100644 --- a/include/iris/x4/core/detail/parse_alternative.hpp +++ b/include/iris/x4/core/detail/parse_alternative.hpp @@ -10,7 +10,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ -#include +#include // IWYU pragma: keep #include #include @@ -21,7 +21,7 @@ #include #include -#include +#include // IWYU pragma: keep #include #include @@ -111,11 +111,11 @@ template requires traits::is_size_one_sequence_v struct pass_non_variant_attribute { - using attr_type = typename std::remove_reference_t< + using attr_type = std::remove_reference_t< alloy::tuple_element_t<0, Attr> >; using pass = pass_parser_attribute; - using type = typename pass::type; + using type = pass::type; template [[nodiscard]] static constexpr type @@ -156,55 +156,38 @@ struct pass_variant_attribute, Attr> {}; template Se, class Context, X4Attribute Attr> -using parse_alternative_pseudo = traits::pseudo_attribute< - It, Se, Context, typename pass_variant_attribute::type ->; - -template Se, class Context, X4Attribute Attr> - requires std::is_lvalue_reference_v::actual_type> + requires std::is_lvalue_reference_v::type> [[nodiscard]] constexpr bool parse_alternative( Parser const& p, It& first, Se const& last, Context const& ctx, Attr& attribute ) noexcept( - noexcept(parse_alternative_pseudo::make_actual_type( - first, last, ctx, pass_variant_attribute::call(attribute) - )) && is_nothrow_parsable_v< Parser, It, Se, Context, - std::remove_reference_t::actual_type> + std::remove_reference_t::type> > ) { - using pass = pass_variant_attribute; - using pseudo = traits::pseudo_attribute; - auto&& actual_attr = pseudo::make_actual_type(first, last, ctx, pass::call(attribute)); - return p.parse(first, last, ctx, actual_attr); + return p.parse(first, last, ctx, pass_variant_attribute::call(attribute)); } template Se, class Context, X4Attribute Attr> - requires (!std::is_lvalue_reference_v::actual_type>) + requires (!std::is_lvalue_reference_v::type>) [[nodiscard]] constexpr bool parse_alternative( Parser const& p, It& first, Se const& last, Context const& ctx, Attr& attribute ) noexcept( - noexcept(parse_alternative_pseudo::make_actual_type( - first, last, ctx, pass_variant_attribute::call(attribute) - )) && is_nothrow_parsable_v< - Parser, It, Se, Context, std::remove_reference_t::actual_type> + Parser, It, Se, Context, std::remove_reference_t::type> > && noexcept(x4::move_to( - std::declval::actual_type>(), + std::declval::type>(), attribute )) ) { - using pass = pass_variant_attribute; - using pseudo = traits::pseudo_attribute; - auto&& actual_attr = pseudo::make_actual_type(first, last, ctx, pass::call(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); return true; diff --git a/include/iris/x4/core/parser.hpp b/include/iris/x4/core/parser.hpp index 5ef6b274a..06daf6129 100644 --- a/include/iris/x4/core/parser.hpp +++ b/include/iris/x4/core/parser.hpp @@ -11,12 +11,14 @@ 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 + #include #include #include @@ -27,6 +29,8 @@ #include #endif +#include // IWYU pragma: keep + namespace iris::x4 { template @@ -35,16 +39,18 @@ struct action; namespace detail { struct parser_base {}; -struct parser_id; + +template +concept ebo_eligible = std::is_empty_v && !std::is_final_v; template struct parser_storage - : private parser_base - , protected Storage // needs to be non-private because we want to expose static public members { - // `Storage` is a class type - - using Storage::Storage; + // `Storage` is some non-EBO-eligible type: + // - Non-class type, or + // - Class type that is + // - `final`, or + // - has non-zero size template requires @@ -53,34 +59,39 @@ struct parser_storage constexpr explicit(!std::convertible_to) parser_storage(StorageT&& storage) noexcept(std::is_nothrow_constructible_v) - : Storage(std::forward(storage)) + : storage_(std::forward(storage)) {} protected: [[nodiscard]] constexpr Storage& storage() & noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(storage_); } [[nodiscard]] constexpr Storage const& storage() const& noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(storage_); } [[nodiscard]] constexpr Storage&& storage() && noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(storage_); } [[nodiscard]] constexpr Storage const&& storage() const&& noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(storage_); } + +private: + IRIS_NO_UNIQUE_ADDRESS Storage storage_; }; template - requires (!std::is_class_v) + requires ebo_eligible struct parser_storage - : private parser_base + : public Storage // needs to be public because we want to expose static public members { - // `Storage` is some non-class type (can't inherit from it; no EBO needed) + // `Storage` is a EBO-eligible class type + + using Storage::Storage; template requires @@ -89,37 +100,34 @@ struct parser_storage constexpr explicit(!std::convertible_to) parser_storage(StorageT&& storage) noexcept(std::is_nothrow_constructible_v) - : storage_(std::forward(storage)) + : Storage(std::forward(storage)) {} protected: [[nodiscard]] constexpr Storage& storage() & noexcept IRIS_LIFETIMEBOUND { - return static_cast(storage_); + return static_cast(*this); } [[nodiscard]] constexpr Storage const& storage() const& noexcept IRIS_LIFETIMEBOUND { - return static_cast(storage_); + return static_cast(*this); } [[nodiscard]] constexpr Storage&& storage() && noexcept IRIS_LIFETIMEBOUND { - return static_cast(storage_); + return static_cast(*this); } [[nodiscard]] constexpr Storage const&& storage() const&& noexcept IRIS_LIFETIMEBOUND { - return static_cast(storage_); + return static_cast(*this); } - -private: - Storage storage_; }; template requires - (!std::is_void_v) && - std::is_base_of_v + ebo_eligible && + has_parser_base struct parser_storage - : protected Storage // needs to be non-private because we want to expose static public members + : public Storage // needs to be public because we want to expose static public members { // `Storage` is some X4 parser class (possibly a `Subject`) // (which means we must not double-inherit from `parser_base`) @@ -157,7 +165,6 @@ struct parser_storage template<> struct parser_storage - : private parser_base {}; } // detail @@ -203,6 +210,10 @@ struct parser : detail::parser_storage { return std::forward(self).on_match(std::forward(f)); } + +private: + template friend struct detail::has_parser_base_impl; + using x4_parser_base_type = detail::parser_base; }; template @@ -217,20 +228,23 @@ struct unary_parser : parser [[nodiscard]] constexpr Subject& subject() & noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(this->storage()); } [[nodiscard]] constexpr Subject const& subject() const& noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(this->storage()); } [[nodiscard]] constexpr Subject&& subject() && noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(this->storage()); } [[nodiscard]] constexpr Subject const&& subject() const&& noexcept IRIS_LIFETIMEBOUND { - return static_cast(*this); + return static_cast(this->storage()); } + +private: + using detail::parser_storage::storage; }; template // TODO @@ -249,7 +263,7 @@ struct proxy_parser : unary_parser }; template // TODO -struct binary_parser : parser<> +struct binary_parser // : parser<> { using left_type = Left; using right_type = Right; @@ -267,9 +281,30 @@ struct binary_parser : parser<> , right(std::forward(right)) {} - // TODO: EBO + static constexpr bool requires_exact_attribute_type = false; + + template + requires std::constructible_from< + action, std::remove_cvref_t>, + Self, Action + > + [[nodiscard]] + constexpr action, std::remove_cvref_t> + on_match(this Self&& self, Action&& f) + noexcept(std::is_nothrow_constructible_v< + action, std::remove_cvref_t>, + Self, Action + >) + { + return {std::forward(self), std::forward(f)}; + } + IRIS_NO_UNIQUE_ADDRESS Left left; IRIS_NO_UNIQUE_ADDRESS Right right; + +private: + template friend struct detail::has_parser_base_impl; + using x4_parser_base_type = detail::parser_base; }; namespace traits { @@ -287,51 +322,37 @@ struct as_parser } }; -//template -// requires std::is_base_of_v> -//struct as_parser -//{ -// template -// [[nodiscard]] static constexpr auto&& call(T&& p) noexcept -// { -// return std::forward(p); -// } -//}; -// -//template -//struct as_parser> -//{ -// template -// [[nodiscard]] static constexpr auto&& call(T&& p) noexcept -// { -// return std::forward(p).derived(); -// } -//}; - } // traits namespace detail { +struct as_parser_char_array_tag {}; + template -concept has_parser_base = std::is_base_of_v>; +struct as_parser_plain_type +{ + using type = std::remove_cvref_t; +}; +template +struct as_parser_plain_type +{ + using type = as_parser_char_array_tag; +}; template concept has_custom_as_parser = requires(T&& p) { - { x4::traits::as_parser>{}(std::forward(p)) } -> has_parser_base; + { x4::traits::as_parser::type>{}(std::forward(p)) } -> has_parser_base; }; struct as_parser_fn { - //template - //static void operator()(T&&) = delete; // If you reach here, your specialization of `x4::extension::as_parser` has a wrong signature, or the type is simply incompatible. - template requires has_custom_as_parser [[nodiscard]] static constexpr decltype(auto) - operator()(T&& x) noexcept(noexcept(traits::as_parser>{}(std::forward(x)))) + operator()(T&& x) noexcept(noexcept(traits::as_parser::type>{}(std::forward(x)))) { - static_assert(has_parser_base>{}(std::forward(x)))>); - return traits::as_parser>{}(std::forward(x)); + static_assert(has_parser_base::type>{}(std::forward(x)))>); + return traits::as_parser::type>{}(std::forward(x)); } template diff --git a/include/iris/x4/core/string_traits.hpp b/include/iris/x4/core/string_traits.hpp deleted file mode 100644 index 5952626d8..000000000 --- a/include/iris/x4/core/string_traits.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef IRIS_ZZ_X4_CORE_STRING_TRAITS_HPP -#define IRIS_ZZ_X4_CORE_STRING_TRAITS_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 - -#include - -#include -#include -#include -#include - -namespace iris::x4 { - -// Mixing incompatible character types is semantically wrong. -// Don't do that. It may even lead to security vulnerabilities. -template -concept StringLikeIncompatibleWith = - CharLike>>> && - !std::convertible_to>; - -// Main utility to guide `char_`, `lit` and `string` to be -// resolved into either `x4::literal_char` or `x4::literal_string`. -// -// This may also be used in other codes which require the same -// semantics. -template -concept CppStringLike = - // This avoids converting `CharT[2]` to `std::basic_string_view`. - (!X4VagueArrayOf2Chars) && - // All other types that are *naturally* convertible to `std::basic_string_view`. - std::convertible_to, std::basic_string_view>; - -template -using maybe_owning_string = std::conditional_t< - std::is_pointer_v>, - std::basic_string_view>>>, - std::remove_cvref_t ->; - -} // iris::x4 - -#endif diff --git a/include/iris/x4/core/unused.hpp b/include/iris/x4/core/unused.hpp index 99ac567fb..b17efaef8 100644 --- a/include/iris/x4/core/unused.hpp +++ b/include/iris/x4/core/unused.hpp @@ -11,7 +11,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include +#include // IWYU pragma: keep #include @@ -65,6 +65,23 @@ assume_container(unused_type const&) noexcept return unused_container; } + +namespace detail { + +template +struct attribute_unused_switcher +{ + using type = unused_type; +}; + +template +struct attribute_identity_switcher +{ + using type = T; +}; + +} // detail + } // iris::x4 #endif diff --git a/include/iris/x4/operator/alternative.hpp b/include/iris/x4/operator/alternative.hpp index a8185bc30..be07acbdc 100644 --- a/include/iris/x4/operator/alternative.hpp +++ b/include/iris/x4/operator/alternative.hpp @@ -20,7 +20,7 @@ #include #include -#include +#include // IWYU pragma: keep #include #include diff --git a/include/iris/x4/string.hpp b/include/iris/x4/string.hpp index 35bb8b486..0b8462b24 100644 --- a/include/iris/x4/string.hpp +++ b/include/iris/x4/string.hpp @@ -10,8 +10,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include -#include -#include +#include // IWYU pragma: keep + +#include // IWYU pragma: export + +#include // IWYU pragma: export #endif diff --git a/include/iris/x4/string/detail/string_parse.hpp b/include/iris/x4/string/detail/string_parse.hpp index 9f138c3ff..a82dcc36e 100644 --- a/include/iris/x4/string/detail/string_parse.hpp +++ b/include/iris/x4/string/detail/string_parse.hpp @@ -15,21 +15,26 @@ #include #include +#include #include +#include #include #include #include namespace iris::x4::detail { -template Se, X4Attribute Attr, class CaseCompareFunc> +template Se, X4Attribute Attr, class CaseCompareFunc> [[nodiscard]] constexpr bool string_parse( - std::basic_string_view const str, + StrR const& str, It& first, Se const& last, Attr& attr, CaseCompareFunc const& compare ) noexcept(std::same_as, unused_container_type>) { + static_assert(!std::is_array_v); + using CharT = std::ranges::range_value_t; + using synthesized_value_type = traits::synthesized_value_t; static_assert(std::same_as, traits::container_attr>); using value_type = traits::container_value::type; @@ -37,8 +42,8 @@ string_parse( static_assert(!CharIncompatibleWith, CharT>, "Mixing incompatible char types is not allowed"); It it = first; - auto stri = str.begin(); - auto str_last = str.end(); + auto stri = std::ranges::begin(str); + auto str_last = std::ranges::end(str); for (; stri != str_last; ++stri, ++it) { if (it == last || compare(*stri, *it) != 0) { @@ -51,23 +56,23 @@ string_parse( return true; } -template Se, class CaseCompareFunc> +template Se, class CaseCompareFunc> constexpr void string_parse( - std::basic_string_view const, + StrR const&, It&, Se const&, unused_type const&, CaseCompareFunc const& ) = delete; // The call site is lacking `x4::assume_container(attr)` -template Se, X4Attribute Attr, class CaseCompareFunc> +template Se, X4Attribute Attr, class CaseCompareFunc> [[nodiscard]] constexpr bool string_parse( - std::basic_string const& str, + CharT const (&str)[N], It& first, Se const& last, Attr& attr, CaseCompareFunc const& compare -) noexcept(noexcept(detail::string_parse(std::basic_string_view{str}, first, last, attr, compare))) +) noexcept(noexcept(detail::string_parse(std::basic_string_view{str, N - 1}, first, last, attr, compare))) { - return detail::string_parse(std::basic_string_view{str}, first, last, attr, compare); + return detail::string_parse(std::basic_string_view{str, N - 1}, first, last, attr, compare); } template Se, X4Attribute Attr> diff --git a/include/iris/x4/string/literal_string.hpp b/include/iris/x4/string/literal_string.hpp index f88795f7e..2193e005c 100644 --- a/include/iris/x4/string/literal_string.hpp +++ b/include/iris/x4/string/literal_string.hpp @@ -35,7 +35,6 @@ struct literal_string : parser !std::is_pointer_v>, "`literal_string` for raw character pointer/array is banned; it has an undetectable risk of holding a dangling pointer." ); - static_assert(std::is_convertible_v>); using char_type = Encoding::char_type; using encoding = Encoding; @@ -65,7 +64,7 @@ struct literal_string : parser [[nodiscard]] std::string get_x4_info() const { // TODO: escape quotes - return std::format("\"{}\"", iris::unicode::transcode(this->storage())); + return std::format("\"{}\"", iris::unicode::transcode(std::basic_string_view{this->storage()})); } }; diff --git a/include/iris/x4/string/string.hpp b/include/iris/x4/string/string.hpp index acddd3bf7..583918ac9 100644 --- a/include/iris/x4/string/string.hpp +++ b/include/iris/x4/string/string.hpp @@ -10,247 +10,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include -#include +#warning "This header is deprecated; please #include " -#include // required for "c" -> 'c' optimization -#include - -#include - -#ifndef IRIS_X4_NO_STANDARD_WIDE -# include -#endif - -#ifdef IRIS_X4_UNICODE -# include -#endif - -#include -#include -#include -#include -#include - -namespace iris::x4 { - -namespace standard { - -inline namespace helpers { - -template T> -[[nodiscard]] constexpr literal_string, char_encoding::standard> -string(T&& string_like) - noexcept(std::is_nothrow_constructible_v, char_encoding::standard>, T>) -{ - return {std::forward(string_like)}; -} - -// Optimize `literal_string{'c'}` into `literal_char{'c'}` -[[nodiscard]] constexpr literal_char> -string(char ch) noexcept -{ - return {ch}; -} - -// Optimize `literal_string{"c"}` into `literal_char{'c'}` -[[nodiscard]] constexpr literal_char> -string(X4VagueArrayOf2Chars auto const& ch) noexcept -{ - return {ch[0]}; -} - -template T> -[[nodiscard]] constexpr literal_string, char_encoding::standard, unused_type> -lit(T&& string_like) - noexcept(std::is_nothrow_constructible_v, char_encoding::standard, unused_type>, T>) -{ - return {std::forward(string_like)}; -} - -} // helpers - -template - requires CharIncompatibleWith || StringLikeIncompatibleWith -constexpr void string(T&&) = delete; // Mixing incompatible character types is not allowed - -} // standard - -using standard::helpers::string; -using standard::helpers::lit; - -#ifndef IRIS_X4_NO_STANDARD_WIDE -namespace standard_wide { - -inline namespace helpers { - -template T> -[[nodiscard]] constexpr literal_string, char_encoding::standard_wide> -string(T&& string_like) - noexcept(std::is_nothrow_constructible_v, char_encoding::standard_wide>, T>) -{ - return {std::forward(string_like)}; -} - -// Optimize `literal_string{'c'}` into `literal_char{'c'}` -[[nodiscard]] constexpr literal_char> -string(wchar_t ch) noexcept -{ - return {ch}; -} - -// Optimize `literal_string{L"c"}` into `literal_char{L'c'}` -[[nodiscard]] constexpr literal_char> -string(X4VagueArrayOf2Chars auto const& ch) noexcept -{ - return {ch[0]}; -} - -template T> -[[nodiscard]] constexpr literal_string, char_encoding::standard_wide, unused_type> -lit(T&& string_like) - noexcept(std::is_nothrow_constructible_v, char_encoding::standard_wide, unused_type>, T>) -{ - return {std::forward(string_like)}; -} - -} // helpers - -template - requires CharIncompatibleWith || StringLikeIncompatibleWith -constexpr void string(T&&) = delete; // Mixing incompatible character types is not allowed - -} // standard_wide - -using standard_wide::helpers::string; -using standard_wide::helpers::lit; -#endif - -#ifdef IRIS_X4_UNICODE -namespace unicode { - -inline namespace helpers { - -// TODO: add `char8_t` and `char16_t` overloads -template T> -[[nodiscard]] constexpr literal_string, char_encoding::unicode> -string(T&& string_like) - noexcept(std::is_nothrow_constructible_v, char_encoding::unicode>, T>) -{ - return {std::forward(string_like)}; -} - -// Optimize `literal_string{'c'}` into `literal_char{'c'}` -[[nodiscard]] constexpr literal_char> -string(char32_t ch) noexcept -{ - return {ch}; -} - -// Optimize `literal_string{U"c"}` into `literal_char{U'c'}` -[[nodiscard]] constexpr literal_char> -string(X4VagueArrayOf2Chars auto const& ch) noexcept -{ - return {ch[0]}; -} - -template T> -[[nodiscard]] constexpr literal_string, char_encoding::unicode, unused_type> -lit(T&& string_like) - noexcept(std::is_nothrow_constructible_v, char_encoding::unicode, unused_type>, T>) -{ - return {std::forward(string_like)}; -} - -} // helpers - -template - requires CharIncompatibleWith || StringLikeIncompatibleWith -constexpr void string(T&&) = delete; // Mixing incompatible character types is not allowed - -} // unicode - -using unicode::helpers::string; -using unicode::helpers::lit; -#endif - - -namespace parsers { - -namespace standard { -using x4::standard::string; -using x4::standard::lit; -} // standard - -#ifndef IRIS_X4_NO_STANDARD_WIDE -namespace standard_wide { -using x4::standard_wide::string; -using x4::standard_wide::lit; -} // standard_wide -#endif - -#ifdef IRIS_X4_UNICODE -namespace unicode { -using x4::unicode::string; -using x4::unicode::lit; -} // standard_wide -#endif - -using x4::string; -using x4::lit; - -} // parsers - - -namespace traits { - -template -struct as_parser -{ - [[nodiscard]] static constexpr literal_string, char_encoding_for, unused_type> - operator()(CharT const (&s)[N]) noexcept - { - return {s}; - } -}; - -template -struct as_parser -{ - [[nodiscard]] static constexpr literal_string, char_encoding_for, unused_type> - operator()(CharT const* s) noexcept - { - return {std::basic_string_view{s}}; - } -}; - -template -struct as_parser> -{ - template - [[nodiscard]] static constexpr literal_string, char_encoding_for, unused_type> - operator()(T&& str) - noexcept(std::is_nothrow_constructible_v, char_encoding_for, unused_type>, T>) - { - static_assert(std::same_as, std::basic_string>); - return {std::forward(str)}; - } -}; - -template -struct as_parser> -{ - template - [[nodiscard]] static constexpr literal_string, char_encoding_for, unused_type> - operator()(T&& str) noexcept - { - static_assert(std::same_as, std::basic_string_view>); - return {std::forward(str)}; - } -}; - -} // traits - -} // iris::x4 +#include #endif diff --git a/modules/iris b/modules/iris index 4df793a55..b29919bfc 160000 --- a/modules/iris +++ b/modules/iris @@ -1 +1 @@ -Subproject commit 4df793a5528afabddbe91038712a5fd4593d9596 +Subproject commit b29919bfca9288b035e27b62df0d52c3b8747b71 diff --git a/test/x4/alternative.cpp b/test/x4/alternative.cpp index 2fc315f64..1c85a13c6 100644 --- a/test/x4/alternative.cpp +++ b/test/x4/alternative.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include @@ -66,7 +66,7 @@ struct undefined {}; TEST_CASE("alternative") { using x4::standard::char_; - using x4::standard::lit; + using x4::lit; using x4::fixed_value; using x4::int_; using x4::unused; diff --git a/test/x4/as.cpp b/test/x4/as.cpp index d03fabf0c..9b036f9e8 100644 --- a/test/x4/as.cpp +++ b/test/x4/as.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include diff --git a/test/x4/char.cpp b/test/x4/char.cpp index c783c3c1c..8474180b0 100644 --- a/test/x4/char.cpp +++ b/test/x4/char.cpp @@ -13,6 +13,7 @@ #include "iris_x4_test.hpp" +#include #include #include #include diff --git a/test/x4/difference.cpp b/test/x4/difference.cpp index c4dd601f1..1899f954c 100644 --- a/test/x4/difference.cpp +++ b/test/x4/difference.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/x4/expect.cpp b/test/x4/expect.cpp index 17bb2b2ec..a238b7b72 100644 --- a/test/x4/expect.cpp +++ b/test/x4/expect.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include @@ -283,8 +283,8 @@ TEST_CASE("expect") using x4::standard::space; using x4::standard::blank; using x4::standard::char_; - using x4::standard::string; - using x4::standard::lit; + using x4::string; + using x4::lit; // using x4::lit; using x4::expect; diff --git a/test/x4/iterator.cpp b/test/x4/iterator.cpp index ac61f9be1..cfb2693d8 100644 --- a/test/x4/iterator.cpp +++ b/test/x4/iterator.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include diff --git a/test/x4/lit.cpp b/test/x4/lit.cpp index ba370320a..743ef7901 100644 --- a/test/x4/lit.cpp +++ b/test/x4/lit.cpp @@ -13,7 +13,7 @@ #include "iris_x4_test.hpp" #include -#include +#include #include #include @@ -27,13 +27,7 @@ TEST_CASE("lit") (void)x4::lit('f'); (void)x4::lit("f"); (void)x4::lit("foo"); - (void)x4::standard::lit('f'); - (void)x4::standard::lit("f"); - (void)x4::standard::lit("foo"); - (void)x4::char_('f'); - (void)x4::char_("f"); - (void)x4::char_("foo"); (void)x4::standard::char_('f'); (void)x4::standard::char_("f"); (void)x4::standard::char_("foo"); @@ -41,9 +35,6 @@ TEST_CASE("lit") (void)x4::string('f'); (void)x4::string("f"); (void)x4::string("foo"); - (void)x4::standard::string('f'); - (void)x4::standard::string("f"); - (void)x4::standard::string("foo"); } // standard_wide @@ -51,9 +42,6 @@ TEST_CASE("lit") (void)x4::lit(L'f'); (void)x4::lit(L"f"); (void)x4::lit(L"foo"); - (void)x4::standard_wide::lit(L'f'); - (void)x4::standard_wide::lit(L"f"); - (void)x4::standard_wide::lit(L"foo"); (void)x4::standard_wide::char_(L'f'); (void)x4::standard_wide::char_(L"f"); @@ -62,36 +50,33 @@ TEST_CASE("lit") (void)x4::string(L'f'); (void)x4::string(L"f"); (void)x4::string(L"foo"); - (void)x4::standard_wide::string(L'f'); - (void)x4::standard_wide::string(L"f"); - (void)x4::standard_wide::string(L"foo"); } // unicode { - (void)x4::unicode::lit(U'f'); - (void)x4::unicode::lit(U"f"); - (void)x4::unicode::lit(U"foo"); + (void)x4::lit(U'f'); + (void)x4::lit(U"f"); + (void)x4::lit(U"foo"); (void)x4::unicode::char_(U'f'); (void)x4::unicode::char_(U"f"); (void)x4::unicode::char_(U"foo"); - (void)x4::unicode::string(U'f'); - (void)x4::unicode::string(U"f"); - (void)x4::unicode::string(U"foo"); + (void)x4::string(U'f'); + (void)x4::string(U"f"); + (void)x4::string(U"foo"); } { std::string attr; - constexpr auto p = x4::standard::char_ >> x4::standard::lit("\n"); // TODO: MSVC 2022 bug, [[no_unique_address]] on binary_parser "overruns" + constexpr auto p = x4::standard::char_ >> x4::lit("\n"); // TODO: MSVC 2022 bug, [[no_unique_address]] on binary_parser "overruns" REQUIRE(parse("A\n", p, attr)); CHECK(attr == "A"); } { std::wstring attr; - constexpr auto p = x4::standard_wide::char_ >> x4::standard_wide::lit(L"\n"); + constexpr auto p = x4::standard_wide::char_ >> x4::lit(L"\n"); REQUIRE(parse(L"É\n", p, attr)); CHECK(attr == L"É"); } @@ -99,57 +84,57 @@ TEST_CASE("lit") // ------------------------------------------------- { - CHECK(parse("kimpo", x4::standard::lit("kimpo"))); + CHECK(parse("kimpo", x4::lit("kimpo"))); std::basic_string s("kimpo"); - CHECK(parse("kimpo", x4::standard::lit(s))); + CHECK(parse("kimpo", x4::lit(s))); std::basic_string ws(L"kimpo"); - CHECK(parse(L"kimpo", x4::standard_wide::lit(ws))); + CHECK(parse(L"kimpo", x4::lit(ws))); } { std::basic_string s("kimpo"); std::basic_string ws(L"kimpo"); - CHECK(parse("kimpo", x4::standard::lit(s))); - CHECK(parse(L"kimpo", x4::standard_wide::lit(ws))); + CHECK(parse("kimpo", x4::lit(s))); + CHECK(parse(L"kimpo", x4::lit(ws))); } // ------------------------------------------------- { CHECK(parse("kimpo", "kimpo")); - CHECK(parse("kimpo", x4::standard::string("kimpo"))); + CHECK(parse("kimpo", x4::string("kimpo"))); - CHECK(parse("x", x4::standard::string("x"))); - CHECK(parse(L"x", x4::standard_wide::string(L"x"))); + CHECK(parse("x", x4::string("x"))); + CHECK(parse(L"x", x4::string(L"x"))); std::basic_string s("kimpo"); std::basic_string ws(L"kimpo"); CHECK(parse("kimpo", s)); CHECK(parse(L"kimpo", ws)); - CHECK(parse("kimpo", x4::standard::string(s))); - CHECK(parse(L"kimpo", x4::standard_wide::string(ws))); + CHECK(parse("kimpo", x4::string(s))); + CHECK(parse(L"kimpo", x4::string(ws))); } { CHECK(parse(L"kimpo", L"kimpo")); - CHECK(parse(L"kimpo", x4::standard_wide::string(L"kimpo"))); - CHECK(parse(L"x", x4::standard_wide::string(L"x"))); + CHECK(parse(L"kimpo", x4::string(L"kimpo"))); + CHECK(parse(L"x", x4::string(L"x"))); } { std::basic_string s("kimpo"); - CHECK(parse("kimpo", x4::standard::string(s))); + CHECK(parse("kimpo", x4::string(s))); std::basic_string ws(L"kimpo"); - CHECK(parse(L"kimpo", x4::standard_wide::string(ws))); + CHECK(parse(L"kimpo", x4::string(ws))); } { // single-element tuple tests alloy::tuple s; - REQUIRE(parse("kimpo", x4::standard::string("kimpo"), s)); + REQUIRE(parse("kimpo", x4::string("kimpo"), s)); CHECK(alloy::get<0>(s) == "kimpo"); } } diff --git a/test/x4/no_case.cpp b/test/x4/no_case.cpp index 74301f6b2..c249f2fe1 100644 --- a/test/x4/no_case.cpp +++ b/test/x4/no_case.cpp @@ -12,16 +12,17 @@ #include #include #include -#include +#include TEST_CASE("no_case") { using x4::no_case; + using x4::lit; + using x4::char_; IRIS_X4_ASSERT_CONSTEXPR_CTORS(no_case['x']); { - using namespace x4::standard; CHECK(parse("x", no_case[char_])); CHECK(parse("X", no_case[char_('x')])); CHECK(parse("X", no_case[char_('X')])); @@ -35,7 +36,6 @@ TEST_CASE("no_case") CHECK(!parse("z", no_case[char_('a', 'y')])); } { - using namespace x4::standard; CHECK(parse("X", no_case['x'])); CHECK(parse("X", no_case['X'])); CHECK(parse("x", no_case['X'])); @@ -45,21 +45,17 @@ TEST_CASE("no_case") } { - using namespace x4::standard; CHECK(parse("X", no_case[char_("a-z")])); CHECK(!parse("1", no_case[char_("a-z")])); } { - using namespace x4::standard; CHECK(parse("Bochi Bochi", no_case[lit("bochi bochi")])); CHECK(parse("BOCHI BOCHI", no_case[lit("bochi bochi")])); CHECK(!parse("Vavoo", no_case[lit("bochi bochi")])); } { - // should work! - using namespace x4::standard; CHECK(parse("x", no_case[no_case[char_]])); CHECK(parse("x", no_case[no_case[char_('x')]])); CHECK(parse("yabadabadoo", no_case[no_case[lit("Yabadabadoo")]])); @@ -81,21 +77,16 @@ TEST_CASE("no_case") { // chsets - namespace standard = x4::standard; - namespace standard_wide = x4::standard_wide; - CHECK(parse("x", no_case[standard::char_("a-z")])); - CHECK(parse("X", no_case[standard::char_("a-z")])); - CHECK(parse(L"X", no_case[standard_wide::char_(L"a-z")])); - CHECK(parse(L"X", no_case[standard_wide::char_(L"X")])); + CHECK(parse("x", no_case[x4::standard::char_("a-z")])); + CHECK(parse("X", no_case[x4::standard::char_("a-z")])); + CHECK(parse(L"X", no_case[x4::standard_wide::char_(L"a-z")])); + CHECK(parse(L"X", no_case[x4::standard_wide::char_(L"X")])); } { - using namespace x4::standard; std::string s("bochi bochi"); - CHECK(parse("Bochi Bochi", no_case[lit(s.c_str())])); CHECK(parse("Bochi Bochi", no_case[lit(s)])); - CHECK(parse("Bochi Bochi", no_case[s.c_str()])); CHECK(parse("Bochi Bochi", no_case[s])); } diff --git a/test/x4/omit.cpp b/test/x4/omit.cpp index 822ebbd40..6fb9181ad 100644 --- a/test/x4/omit.cpp +++ b/test/x4/omit.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/test/x4/optional.cpp b/test/x4/optional.cpp index a781d7ef0..1bdf7797f 100644 --- a/test/x4/optional.cpp +++ b/test/x4/optional.cpp @@ -10,7 +10,7 @@ #include "iris_x4_test.hpp" #include -#include +#include #include #include #include @@ -42,19 +42,6 @@ struct alloy::adaptor using getters_list = iris::constant_list<&adata::a, &adata::b>; }; -namespace { - -struct test_attribute_type -{ - template - void operator()(Context&& ctx) const - { - CHECK(typeid(decltype(x4::_attr(ctx))).name() == typeid(std::optional).name()); - } -}; - -} // anonymous - TEST_CASE("optional") { static_assert(x4::traits::is_optional_v>); @@ -62,7 +49,7 @@ TEST_CASE("optional") using x4::int_; using x4::omit; using x4::lit; - using x4::standard::char_; + using x4::char_; using x4::_attr; IRIS_X4_ASSERT_CONSTEXPR_CTORS(-int_); @@ -147,9 +134,10 @@ TEST_CASE("optional") } { - // test action std::optional n = 0; - REQUIRE(parse("1234", (-int_).on_match(test_attribute_type()), n)); + REQUIRE(parse("1234", (-int_).on_match([](auto&& ctx) { + CHECK(typeid(decltype(x4::_attr(ctx))).name() == typeid(std::optional).name()); + }), n)); CHECK(*n == 1234); } @@ -184,7 +172,6 @@ TEST_CASE("optional") } { - // test move only types std::optional o; REQUIRE(parse("s", -x4_test::synth_move_only, o)); CHECK(o.has_value()); diff --git a/test/x4/partial_success.cpp b/test/x4/partial_success.cpp index c04fca916..913c4812d 100644 --- a/test/x4/partial_success.cpp +++ b/test/x4/partial_success.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -243,8 +243,8 @@ TEST_CASE("partial success (list-like)") using x4::char_encoding::standard; using x4::standard::char_; - using x4::standard::string; - using x4::standard::lit; + using x4::lit; + using x4::string; using x4::repeat; constexpr auto a = char_('a'); @@ -354,7 +354,7 @@ TEST_CASE("partial success (list-like)") using Subject = x4::sequence< x4::sequence< x4::literal_char, - x4::literal_string + x4::literal_string, standard> >, x4::literal_char >; diff --git a/test/x4/plus.cpp b/test/x4/plus.cpp index 36019941e..e0f950fd7 100644 --- a/test/x4/plus.cpp +++ b/test/x4/plus.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/x4/recursive.cpp b/test/x4/recursive.cpp index fae38e9ac..0aa61b259 100644 --- a/test/x4/recursive.cpp +++ b/test/x4/recursive.cpp @@ -7,8 +7,7 @@ #include "iris_x4_test.hpp" -#include -#include +#include #include #include diff --git a/test/x4/rule4.cpp b/test/x4/rule4.cpp index 7c82a936d..96957d738 100644 --- a/test/x4/rule4.cpp +++ b/test/x4/rule4.cpp @@ -23,7 +23,6 @@ #include #include -#include #include namespace { @@ -67,10 +66,10 @@ struct on_success_gets_preskipped_iterator TEST_CASE("rule4") { - using namespace x4::standard; using x4::rule; using x4::int_; using x4::lit; + using x4::char_; // show that ra = rb and ra %= rb works as expected { @@ -135,7 +134,7 @@ TEST_CASE("rule4") // error handling { auto r = rule{} - = '(' > int_ > ',' > int_ > ')'; + = '(' > int_ > ',' > int_ > ')'; // NOLINT(bugprone-chained-comparison) CHECK(parse("(123,456)", r)); CHECK(!parse("(abc,def)", r)); diff --git a/test/x4/rule_separate_tu_grammar.cpp b/test/x4/rule_separate_tu_grammar.cpp index f427a8825..1564bf043 100644 --- a/test/x4/rule_separate_tu_grammar.cpp +++ b/test/x4/rule_separate_tu_grammar.cpp @@ -18,17 +18,17 @@ namespace unused_attr { using iterator_type = std::string_view::const_iterator; -constexpr auto skipper_def = x4::standard::lit('*'); +constexpr auto skipper_def = x4::lit('*'); IRIS_X4_DEFINE(skipper) IRIS_X4_INSTANTIATE(skipper_type, iterator_type, x4::parse_context_for) IRIS_X4_INSTANTIATE(skipper_type, iterator_type, x4::skipper_parse_context_for) -constexpr auto skipper2_def = x4::standard::lit('#'); +constexpr auto skipper2_def = x4::lit('#'); IRIS_X4_DEFINE(skipper2) IRIS_X4_INSTANTIATE(skipper2_type, iterator_type, x4::parse_context_for) IRIS_X4_INSTANTIATE(skipper2_type, iterator_type, x4::skipper_parse_context_for) -constexpr auto grammar_def = *x4::standard::lit('='); +constexpr auto grammar_def = *x4::lit('='); IRIS_X4_DEFINE(grammar) IRIS_X4_INSTANTIATE(grammar_type, iterator_type, x4::parse_context_for) diff --git a/test/x4/sequence.cpp b/test/x4/sequence.cpp index 692553030..11512cd8e 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -14,11 +14,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -41,11 +41,11 @@ TEST_CASE("sequence") { namespace traits = x4::traits; - using x4::standard::char_; using x4::standard::space; - using x4::standard::string; - using x4::standard::lit; using x4::standard::alnum; + using x4::standard::char_; + using x4::string; + using x4::lit; using x4::fixed_value; using x4::omit; using x4::unused; diff --git a/test/x4/skip.cpp b/test/x4/skip.cpp index b41d0eb9d..8fef066a9 100644 --- a/test/x4/skip.cpp +++ b/test/x4/skip.cpp @@ -18,8 +18,8 @@ TEST_CASE("skip") { using x4::standard::space; - using x4::standard::char_; using x4::standard::alpha; + using x4::char_; using x4::skip; using x4::lit; diff --git a/test/x4/symbols1.cpp b/test/x4/symbols1.cpp index 10676d44b..16499ca79 100644 --- a/test/x4/symbols1.cpp +++ b/test/x4/symbols1.cpp @@ -9,7 +9,7 @@ #include "iris_x4_test.hpp" -#include +#include #include #include #include From 05e408a16dfcac0782517344e573e9afc0d3d540 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Sun, 20 Sep 2026 08:00:50 +0900 Subject: [PATCH 4/5] Fix return type --- include/iris/x4/char_string_literal.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/iris/x4/char_string_literal.hpp b/include/iris/x4/char_string_literal.hpp index 64b24dfa9..5a9072f3c 100644 --- a/include/iris/x4/char_string_literal.hpp +++ b/include/iris/x4/char_string_literal.hpp @@ -134,7 +134,7 @@ struct char_array_parser_fn requires (N >= 3) && (!x4::detail::is_string_parser_sso_eligible) { static_assert(N >= 2); - return std::basic_string_view(str, N - 1); + return {std::basic_string_view(str, N - 1)}; } }; From 077ddd99c5553e878dc4fc14c4ce61f628f3c270 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Mon, 21 Sep 2026 03:30:44 +0900 Subject: [PATCH 5/5] Optimize class layout --- include/iris/x4/attribute.hpp | 4 +- include/iris/x4/attribute/as.hpp | 20 +- include/iris/x4/attribute/smart_ptr.hpp | 30 +- include/iris/x4/attribute/value.hpp | 52 ++-- include/iris/x4/char/any_char.hpp | 2 +- include/iris/x4/char/char_class.hpp | 6 +- include/iris/x4/char/char_parser.hpp | 8 +- include/iris/x4/char/char_set.hpp | 8 +- include/iris/x4/char/literal_char.hpp | 4 +- include/iris/x4/char/unicode_char_class.hpp | 4 +- include/iris/x4/char_string_literal.hpp | 1 - include/iris/x4/core/action.hpp | 10 +- include/iris/x4/core/attribute.hpp | 11 +- .../iris/x4/core/detail/parse_alternative.hpp | 8 +- include/iris/x4/core/parser.hpp | 263 +++++------------- include/iris/x4/directive.hpp | 20 +- include/iris/x4/directive/expect.hpp | 14 +- include/iris/x4/directive/lexeme.hpp | 10 +- include/iris/x4/directive/matches.hpp | 8 +- include/iris/x4/directive/no_case.hpp | 8 +- include/iris/x4/directive/no_skip.hpp | 8 +- include/iris/x4/directive/omit.hpp | 10 +- include/iris/x4/directive/repeat.hpp | 14 +- include/iris/x4/directive/skip.hpp | 20 +- include/iris/x4/directive/with.hpp | 24 +- include/iris/x4/directive/with_local.hpp | 6 +- include/iris/x4/numeric.hpp | 8 +- include/iris/x4/numeric/bool.hpp | 8 +- include/iris/x4/numeric/int.hpp | 2 +- include/iris/x4/numeric/real.hpp | 2 +- include/iris/x4/numeric/uint.hpp | 2 +- include/iris/x4/operator.hpp | 18 +- include/iris/x4/operator/alternative.hpp | 4 +- include/iris/x4/operator/and_predicate.hpp | 8 +- include/iris/x4/operator/delimited_list.hpp | 4 +- include/iris/x4/operator/difference.hpp | 4 +- include/iris/x4/operator/kleene.hpp | 14 +- include/iris/x4/operator/not_predicate.hpp | 10 +- include/iris/x4/operator/optional.hpp | 14 +- include/iris/x4/operator/plus.hpp | 18 +- include/iris/x4/operator/sequence.hpp | 6 +- include/iris/x4/parse.hpp | 3 +- include/iris/x4/parse_debug.hpp | 3 +- include/iris/x4/parse_result.hpp | 3 +- include/iris/x4/primitive.hpp | 7 +- include/iris/x4/primitive/eoi.hpp | 2 +- include/iris/x4/primitive/eol.hpp | 2 +- include/iris/x4/primitive/eps.hpp | 46 ++- include/iris/x4/rule.hpp | 14 +- include/iris/x4/string/literal_string.hpp | 28 +- include/iris/x4/symbols.hpp | 2 +- test/x4/iris_x4_test.hpp | 2 +- test/x4/parser.cpp | 12 +- test/x4/smart_ptr.cpp | 2 +- test/x4/without.cpp | 2 +- 55 files changed, 378 insertions(+), 445 deletions(-) diff --git a/include/iris/x4/attribute.hpp b/include/iris/x4/attribute.hpp index 0ffbb29f9..97d68441d 100644 --- a/include/iris/x4/attribute.hpp +++ b/include/iris/x4/attribute.hpp @@ -9,8 +9,8 @@ ==============================================================================*/ #include -#include +#include // IWYU pragma: export // #include // excluded -#include +#include // IWYU pragma: export #endif diff --git a/include/iris/x4/attribute/as.hpp b/include/iris/x4/attribute/as.hpp index beb7ab98d..04fca9894 100644 --- a/include/iris/x4/attribute/as.hpp +++ b/include/iris/x4/attribute/as.hpp @@ -47,7 +47,7 @@ struct as_type_parser_ctx_impl // to be `T`. When `T` is `unused_type`, this is equivalent to // `omit_directive`. template -struct as_type_parser : unary_parser +struct as_type_parser : unary_parser, Subject> { static_assert(!std::is_const_v); // Forbid const `unused_type` static_assert(!std::same_as); // Unknown use case, not supported for now @@ -65,7 +65,7 @@ struct as_type_parser : unary_parser // `parser_traits>::handles_container` must transparently // handle this case. - using unary_parser::unary_parser; + using unary_parser::unary_parser; private: template @@ -82,9 +82,9 @@ struct as_type_parser : unary_parser 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); + return this->subject.parse(first, last, x4::replace_first_context(ctx, outer_attr), unused); } else { - return this->subject().parse(first, last, ctx, outer_attr); + return this->subject.parse(first, last, ctx, outer_attr); } } @@ -97,9 +97,9 @@ struct as_type_parser : unary_parser 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); + return this->subject.parse(first, last, x4::replace_first_context(ctx, unused), unused); } else { - return this->subject().parse(first, last, ctx, unused); + return this->subject.parse(first, last, ctx, unused); } } @@ -124,9 +124,9 @@ struct as_type_parser : unary_parser T attr_{}; // value-initialize if constexpr (Subject::has_action) { - if (!this->subject().parse(first, last, x4::replace_first_context(ctx, attr_), unused)) return false; + if (!this->subject.parse(first, last, x4::replace_first_context(ctx, attr_), unused)) return false; } else { - if (!this->subject().parse(first, last, ctx, attr_)) return false; + if (!this->subject.parse(first, last, ctx, attr_)) return false; } x4::move_to(std::move(attr_), outer_attr); @@ -138,7 +138,7 @@ struct as_type_parser : unary_parser return std::format( "as<{}>({})", typeid(T).name(), - get_info{}(this->subject()) + get_info{}(this->subject) ); } }; @@ -153,7 +153,7 @@ struct as_fn operator()(Subject&& subject) noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {std::forward(subject)}; + return as_type_parser>{std::forward(subject)}; } }; diff --git a/include/iris/x4/attribute/smart_ptr.hpp b/include/iris/x4/attribute/smart_ptr.hpp index d1d7d850e..ee0f47d31 100644 --- a/include/iris/x4/attribute/smart_ptr.hpp +++ b/include/iris/x4/attribute/smart_ptr.hpp @@ -45,9 +45,9 @@ struct [[nodiscard]] smart_ptr_rollback_guard template struct unique_ptr_parser_base - : proxy_parser + : proxy_parser { - using base_type = proxy_parser; + using base_type = proxy_parser; // https://eel.is/c++draft/unique.ptr.single.ctor @@ -105,19 +105,19 @@ struct unique_ptr_parser_base smart_ptr_rollback_guard> guard{parse_ok, old_ptr, ptr}; - parse_ok = this->subject().parse(it, se, ctx, *ptr); + parse_ok = this->subject.parse(it, se, ctx, *ptr); return parse_ok; } private: - DeleterT deleter_{}; + IRIS_NO_UNIQUE_ADDRESS DeleterT deleter_{}; }; template struct unique_ptr_parser_base> - : proxy_parser + : proxy_parser { - using proxy_parser::proxy_parser; + using proxy_parser::proxy_parser; template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool @@ -132,7 +132,7 @@ struct unique_ptr_parser_base> smart_ptr_rollback_guard>> guard{parse_ok, old_ptr, ptr}; - parse_ok = this->subject().parse(it, se, ctx, *ptr); + parse_ok = this->subject.parse(it, se, ctx, *ptr); return parse_ok; } }; @@ -158,7 +158,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 std::format("unique_ptr({})", get_info{}(this->subject)); } }; @@ -210,9 +210,9 @@ namespace detail { template struct shared_ptr_parser_base - : proxy_parser + : proxy_parser { - using base_type = proxy_parser; + using base_type = proxy_parser; // https://eel.is/c++draft/util.smartptr.shared.const @@ -268,7 +268,7 @@ struct shared_ptr_parser_base smart_ptr_rollback_guard> guard{parse_ok, old_ptr, ptr}; - parse_ok = this->subject().parse(it, se, ctx, *ptr); + parse_ok = this->subject.parse(it, se, ctx, *ptr); return parse_ok; } @@ -278,9 +278,9 @@ struct shared_ptr_parser_base template struct shared_ptr_parser_base> - : proxy_parser + : proxy_parser { - using base_type = proxy_parser; + using base_type = proxy_parser; template requires @@ -302,7 +302,7 @@ struct shared_ptr_parser_base> smart_ptr_rollback_guard> guard{parse_ok, old_ptr, ptr}; - parse_ok = this->subject().parse(it, se, ctx, *ptr); + parse_ok = this->subject.parse(it, se, ctx, *ptr); return parse_ok; } }; @@ -328,7 +328,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 std::format("shared_ptr({})", get_info{}(this->subject)); } }; diff --git a/include/iris/x4/attribute/value.hpp b/include/iris/x4/attribute/value.hpp index ec01a3d3a..396fdab7b 100644 --- a/include/iris/x4/attribute/value.hpp +++ b/include/iris/x4/attribute/value.hpp @@ -23,51 +23,69 @@ #include #include #include +#include namespace iris::x4 { // `fixed_value(...)` template -struct fixed_value_parser : parser +struct fixed_value_parser : parser> { static_assert(X4Attribute); static_assert(!X4UnusedAttribute, "fixed_value_parser with `unused_type` is meaningless"); + static_assert(X4Movable); // `HeldValueT` is almost always equal to `T`. // // The most notable situation where they differ is when `fixed_value_parser` is initialized // by `char const (&)[N]`. In such case, `fixed_value_parser` must hold the value by // `std::string_view`, instead of `std::string`, to be constexpr. - - static_assert(X4Movable); - using attribute_type = T; using held_value_type = HeldValueT; - using parser::parser; + constexpr fixed_value_parser() = default; + + template + requires + // This exclusion is mandatory because `HeldValueT` can be a weakly constrained + // type such as `std::any` + (!std::is_base_of_v>) && + std::is_constructible_v + constexpr explicit fixed_value_parser(U&& arg, Rest&&... rest) + noexcept(std::is_nothrow_constructible_v) + : held_value_(std::forward(arg), std::forward(rest)...) + {} + + template + requires std::is_constructible_v&, Args...> + constexpr explicit fixed_value_parser(std::initializer_list il, Args&&... args) + noexcept(std::is_nothrow_constructible_v&, Args...>) + : held_value_(il, std::forward(args)...) + {} template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool - parse(It&, Se const&, Context const&, Attr& attr_) const - noexcept(noexcept(x4::move_to(std::declval(), attr_))) + parse(It&, Se const&, Context const&, Attr& exposed_attr) const + noexcept(noexcept(x4::move_to(std::declval(), exposed_attr))) { // Always copy (need reuse in repetitive invocations) - x4::move_to(this->storage(), attr_); + x4::move_to(this->held_value_, exposed_attr); return true; } + +private: + IRIS_NO_UNIQUE_ADDRESS HeldValueT held_value_{}; }; -// `reset_value` +// aka `reset_value` template -struct fixed_value_parser : parser<> +struct fixed_value_parser : parser> { static_assert(X4Attribute); static_assert(!X4UnusedAttribute, "fixed_value_parser with `unused_type` is meaningless"); using attribute_type = T; - using parser<>::parser; - template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] static constexpr bool parse(It&, Se const&, Context const&, UnusedAttr const&) noexcept @@ -78,19 +96,19 @@ struct fixed_value_parser : parser<> template Se, class Context, X4NonUnusedAttribute ContainerAttr> requires traits::CategorizedAttr [[nodiscard]] static constexpr bool - parse(It&, Se const&, Context const&, ContainerAttr& container_attr) noexcept + parse(It&, Se const&, Context const&, ContainerAttr& exposed_attr) noexcept { - traits::clear(container_attr); + traits::clear(exposed_attr); return true; } template Se, class Context, X4NonUnusedAttribute Attr> requires (!traits::CategorizedAttr) [[nodiscard]] static constexpr bool - parse(It&, Se const&, Context const&, Attr& attr_) - noexcept(noexcept(attr_ = Attr{})) + parse(It&, Se const&, Context const&, Attr& exposed_attr) + noexcept(noexcept(exposed_attr = Attr{})) { - attr_ = Attr{}; + exposed_attr = Attr{}; return true; } }; diff --git a/include/iris/x4/char/any_char.hpp b/include/iris/x4/char/any_char.hpp index 21f1a534e..63f8da02a 100644 --- a/include/iris/x4/char/any_char.hpp +++ b/include/iris/x4/char/any_char.hpp @@ -16,7 +16,7 @@ namespace iris::x4 { template -struct any_char : char_parser> +struct any_char : char_parser, Encoding> { using encoding_type = Encoding; using attribute_type = Encoding::char_type; diff --git a/include/iris/x4/char/char_class.hpp b/include/iris/x4/char/char_class.hpp index 4df79cc2e..b87b9b9b9 100644 --- a/include/iris/x4/char/char_class.hpp +++ b/include/iris/x4/char/char_class.hpp @@ -39,7 +39,7 @@ namespace detail { template struct char_class_base { - using classify_type = typename Encoding::classify_type; + using classify_type = Encoding::classify_type; #define IRIS_X4_CLASSIFY(name) \ template \ @@ -70,11 +70,11 @@ struct char_class_base } // detail template -struct char_class_parser : char_parser> +struct char_class_parser : char_parser, Encoding> { using encoding_type = Encoding; using tag = Tag; - using char_type = typename Encoding::char_type; + using char_type = Encoding::char_type; using attribute_type = char_type; static constexpr bool has_attribute = true; diff --git a/include/iris/x4/char/char_parser.hpp b/include/iris/x4/char/char_parser.hpp index 744b8efab..01fa3fa6b 100644 --- a/include/iris/x4/char/char_parser.hpp +++ b/include/iris/x4/char/char_parser.hpp @@ -22,12 +22,12 @@ namespace iris::x4 { -template +template struct char_parser; // `negated_char_parser` handles `~cp`, where `cp` is a `char_parser` template -struct negated_char_parser : char_parser> +struct negated_char_parser : char_parser, typename Positive::encoding_type> { static_assert(X4ExplicitSubject); @@ -62,8 +62,8 @@ struct negated_char_parser : char_parser -struct char_parser : parser<> +template +struct char_parser : parser> { using encoding_type = Encoding; using char_type = Encoding::char_type; diff --git a/include/iris/x4/char/char_set.hpp b/include/iris/x4/char/char_set.hpp index e29e18c50..b577f7c37 100644 --- a/include/iris/x4/char/char_set.hpp +++ b/include/iris/x4/char/char_set.hpp @@ -26,9 +26,9 @@ namespace iris::x4 { // Parser for a character range template -struct char_range : char_parser> +struct char_range : char_parser, Encoding> { - using char_type = typename Encoding::char_type; + using char_type = Encoding::char_type; using encoding_type = Encoding; using attribute_type = Attr; @@ -65,9 +65,9 @@ struct char_range : char_parser> // Parser for a character set template -struct char_set : char_parser> +struct char_set : char_parser, Encoding> { - using char_type = typename Encoding::char_type; + using char_type = Encoding::char_type; using encoding_type = Encoding; using attribute_type = Attr; diff --git a/include/iris/x4/char/literal_char.hpp b/include/iris/x4/char/literal_char.hpp index cf62b044c..fce367ff4 100644 --- a/include/iris/x4/char/literal_char.hpp +++ b/include/iris/x4/char/literal_char.hpp @@ -22,7 +22,7 @@ namespace iris::x4 { template -struct literal_char : char_parser> +struct literal_char : char_parser, Encoding> { using encoding_type = Encoding; using attribute_type = Attr; @@ -35,7 +35,7 @@ struct literal_char : char_parser> requires (!std::is_same_v, literal_char>) && std::convertible_to - constexpr literal_char(Char ch) noexcept + constexpr /*explicit*/ literal_char(Char ch) noexcept : classify_ch_(static_cast(ch)) { static_assert(std::same_as, "Mixing incompatible character types is not allowed"); diff --git a/include/iris/x4/char/unicode_char_class.hpp b/include/iris/x4/char/unicode_char_class.hpp index cb413b683..74b57edae 100644 --- a/include/iris/x4/char/unicode_char_class.hpp +++ b/include/iris/x4/char/unicode_char_class.hpp @@ -537,11 +537,11 @@ struct unicode_char_class_base } // detail template -struct unicode_char_class : char_parser> +struct unicode_char_class : char_parser, char_encoding::unicode> { using encoding_type = char_encoding::unicode; using tag = Tag; - using char_type = typename encoding_type::char_type; + using char_type = encoding_type::char_type; using attribute_type = char_type; static constexpr bool has_attribute = true; diff --git a/include/iris/x4/char_string_literal.hpp b/include/iris/x4/char_string_literal.hpp index 5a9072f3c..d57ce292a 100644 --- a/include/iris/x4/char_string_literal.hpp +++ b/include/iris/x4/char_string_literal.hpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include diff --git a/include/iris/x4/core/action.hpp b/include/iris/x4/core/action.hpp index b1e44a559..d5f08b8cb 100644 --- a/include/iris/x4/core/action.hpp +++ b/include/iris/x4/core/action.hpp @@ -59,14 +59,14 @@ struct action_context // holds exact specific type provided to the entry point (`x4::parse`). template -struct action : proxy_parser> +struct action : proxy_parser, Subject> { static_assert( !std::is_reference_v, "Reference type is disallowed for semantic action functor to prevent dangling reference" ); - using base_type = proxy_parser; + using base_type = proxy_parser; static constexpr bool has_action = true; static constexpr bool need_rcontext = true; @@ -75,7 +75,7 @@ struct action : proxy_parser> ActionF f; template - requires /*std::is_constructible_v &&*/ std::is_constructible_v + requires std::is_constructible_v && std::is_constructible_v constexpr action(SubjectT&& subject, ActionT&& f) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) : base_type(std::forward(subject)) @@ -146,7 +146,7 @@ struct action : proxy_parser> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format("{}[f]", get_info{}(this->subject())); + return std::format("{}[f]", get_info{}(this->subject)); } private: @@ -237,7 +237,7 @@ struct action : proxy_parser> ) { It const saved_first = first; - if (!this->subject().parse(first, last, ctx, attr)) return false; + if (!this->subject.parse(first, last, ctx, attr)) return false; if (this->call_action(ctx, attr)) { return true; diff --git a/include/iris/x4/core/attribute.hpp b/include/iris/x4/core/attribute.hpp index 70894fdb1..336be6250 100644 --- a/include/iris/x4/core/attribute.hpp +++ b/include/iris/x4/core/attribute.hpp @@ -12,7 +12,6 @@ #include // IWYU pragma: keep #include -#include #include namespace iris::x4 { @@ -25,15 +24,7 @@ namespace detail { struct parser_base; template -struct has_parser_base_impl -{ - static constexpr bool value = requires { - requires std::same_as; - }; -}; - -template -concept has_parser_base = has_parser_base_impl>::value; +concept has_parser_base = std::is_same_v::x4_parser_base_type, parser_base>; } // detail diff --git a/include/iris/x4/core/detail/parse_alternative.hpp b/include/iris/x4/core/detail/parse_alternative.hpp index 6643d6144..9e08eea66 100644 --- a/include/iris/x4/core/detail/parse_alternative.hpp +++ b/include/iris/x4/core/detail/parse_alternative.hpp @@ -194,16 +194,16 @@ parse_alternative( } template -struct alternative_helper : proxy_parser> +struct alternative_helper : proxy_parser, Subject> { - using proxy_parser::proxy_parser; + using proxy_parser::proxy_parser; template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const - noexcept(noexcept(detail::parse_alternative(this->subject(), first, last, ctx, attr))) + noexcept(noexcept(detail::parse_alternative(this->subject, first, last, ctx, attr))) { - return detail::parse_alternative(this->subject(), first, last, ctx, attr); + return detail::parse_alternative(this->subject, first, last, ctx, attr); } }; diff --git a/include/iris/x4/core/parser.hpp b/include/iris/x4/core/parser.hpp index 06daf6129..04bc827e7 100644 --- a/include/iris/x4/core/parser.hpp +++ b/include/iris/x4/core/parser.hpp @@ -36,149 +36,35 @@ namespace iris::x4 { template struct action; -namespace detail { - -struct parser_base {}; - -template -concept ebo_eligible = std::is_empty_v && !std::is_final_v; - -template -struct parser_storage -{ - // `Storage` is some non-EBO-eligible type: - // - Non-class type, or - // - Class type that is - // - `final`, or - // - has non-zero size - - template - requires - (!std::same_as) && - std::constructible_from - constexpr explicit(!std::convertible_to) - parser_storage(StorageT&& storage) - noexcept(std::is_nothrow_constructible_v) - : storage_(std::forward(storage)) - {} - -protected: - [[nodiscard]] constexpr Storage& storage() & noexcept IRIS_LIFETIMEBOUND - { - return static_cast(storage_); - } - [[nodiscard]] constexpr Storage const& storage() const& noexcept IRIS_LIFETIMEBOUND - { - return static_cast(storage_); - } - [[nodiscard]] constexpr Storage&& storage() && noexcept IRIS_LIFETIMEBOUND - { - return static_cast(storage_); - } - [[nodiscard]] constexpr Storage const&& storage() const&& noexcept IRIS_LIFETIMEBOUND - { - return static_cast(storage_); - } - -private: - IRIS_NO_UNIQUE_ADDRESS Storage storage_; -}; - -template - requires ebo_eligible -struct parser_storage - : public Storage // needs to be public because we want to expose static public members -{ - // `Storage` is a EBO-eligible class type - - using Storage::Storage; - - template - requires - (!std::same_as) && - std::constructible_from - constexpr explicit(!std::convertible_to) - parser_storage(StorageT&& storage) - noexcept(std::is_nothrow_constructible_v) - : Storage(std::forward(storage)) - {} - -protected: - [[nodiscard]] constexpr Storage& storage() & noexcept IRIS_LIFETIMEBOUND - { - return static_cast(*this); - } - [[nodiscard]] constexpr Storage const& storage() const& noexcept IRIS_LIFETIMEBOUND - { - return static_cast(*this); - } - [[nodiscard]] constexpr Storage&& storage() && noexcept IRIS_LIFETIMEBOUND - { - return static_cast(*this); - } - [[nodiscard]] constexpr Storage const&& storage() const&& noexcept IRIS_LIFETIMEBOUND - { - return static_cast(*this); - } -}; - -template - requires - ebo_eligible && - has_parser_base -struct parser_storage - : public Storage // needs to be public because we want to expose static public members +template< + // Although this parameter is largely unnecessary now that C++23 provides explicit object + // parameters ("deducing this"), omitting it would cause virtually all parser classes to + // inherit from the same empty base type, significantly increasing their size. + // + // In practice, shared-empty-base design would make a composite parser instance nearly + // 3 to 8 times as large as one using the optimized layout. While the increased size would + // have virtually no runtime impact (as the access to redundant empty instances would be + // optimized-away anyway), it would still affect the compilation time. + // + // In other words, we currently require this parameter solely for making the base type + // `parser<...>` distinct for each derived class. + // + // Note: The above describes the current design. Future changes may introduce an actual + // dependency on `Derived`, regardless of the emptiness of the actual type. + // + // Caveat: Directly referring to `Derived` inside this base class (without using "deducing + // this") is almost always wrong, as it is not guaranteed to point to the *most* derived + // type when a class further derives from `Derived`. + class Derived +> +struct parser { - // `Storage` is some X4 parser class (possibly a `Subject`) - // (which means we must not double-inherit from `parser_base`) - - using Storage::Storage; - - template - requires - (!std::same_as) && - std::constructible_from - constexpr explicit(!std::convertible_to) - parser_storage(StorageT&& storage) - noexcept(std::is_nothrow_constructible_v) - : Storage(std::forward(storage)) - {} - -protected: - [[nodiscard]] constexpr Storage& storage() & noexcept IRIS_LIFETIMEBOUND - { - return static_cast(*this); - } - [[nodiscard]] constexpr Storage const& storage() const& noexcept IRIS_LIFETIMEBOUND - { - return static_cast(*this); - } - [[nodiscard]] constexpr Storage&& storage() && noexcept IRIS_LIFETIMEBOUND - { - return static_cast(*this); - } - [[nodiscard]] constexpr Storage const&& storage() const&& noexcept IRIS_LIFETIMEBOUND - { - return static_cast(*this); - } -}; - -template<> -struct parser_storage -{}; - -} // detail - + using x4_parser_base_type = detail::parser_base; -template -struct parser : detail::parser_storage -{ static constexpr bool has_action = false; static constexpr bool need_rcontext = false; static constexpr bool requires_exact_attribute_type = false; - using detail::parser_storage::parser_storage; - template requires std::constructible_from< action, std::remove_cvref_t>, @@ -210,45 +96,46 @@ struct parser : detail::parser_storage { return std::forward(self).on_match(std::forward(f)); } - -private: - template friend struct detail::has_parser_base_impl; - using x4_parser_base_type = detail::parser_base; }; -template -struct unary_parser : parser +template +struct unary_parser : parser { using subject_type = Subject; static constexpr bool has_action = Subject::has_action; static constexpr bool need_rcontext = Subject::need_rcontext; - using parser::parser; + constexpr unary_parser() = default; - [[nodiscard]] constexpr Subject& subject() & noexcept IRIS_LIFETIMEBOUND - { - return static_cast(this->storage()); - } - [[nodiscard]] constexpr Subject const& subject() const& noexcept IRIS_LIFETIMEBOUND - { - return static_cast(this->storage()); - } - [[nodiscard]] constexpr Subject&& subject() && noexcept IRIS_LIFETIMEBOUND - { - return static_cast(this->storage()); - } - [[nodiscard]] constexpr Subject const&& subject() const&& noexcept IRIS_LIFETIMEBOUND - { - return static_cast(this->storage()); - } + template + requires std::same_as, Subject> + constexpr explicit unary_parser(SubjectT&& subject) + noexcept(std::is_nothrow_constructible_v) + : subject(std::forward(subject)) + {} -private: - using detail::parser_storage::storage; + // Empty instance elimination technique: + // + // For the background, please read the comment on the `Derived` parameter of the base + // `parser<...>` class first. + // + // In theory, we could go further and eliminate all empty parser instances, including + // those stored in `unary_parser` and `binary_parser`, by omitting member variables + // whose types are empty classes. However, doing so would require one of the following: + // + // (a) Return a value-initialized instance on access: + // e.g. `binary_p.left() -> Empty` + // + // (b) Return a reference to a static const instance: + // e.g. `binary_p.left() -> Empty const&` + // + // We benchmarked both approaches and found that each *increased* compilation time. + IRIS_NO_UNIQUE_ADDRESS Subject subject; }; -template // TODO -struct proxy_parser : unary_parser +template +struct proxy_parser : unary_parser { using proxy_backend_type = Subject; using attribute_type = parser_traits::attribute_type; @@ -259,52 +146,31 @@ struct proxy_parser : unary_parser template static constexpr bool handles_container = parser_traits::template handles_container; - using unary_parser::unary_parser; + using unary_parser::unary_parser; }; -template // TODO -struct binary_parser // : parser<> +template +struct binary_parser : parser { using left_type = Left; using right_type = Right; - static constexpr bool has_action = left_type::has_action || right_type::has_action; - static constexpr bool need_rcontext = left_type::need_rcontext || right_type::need_rcontext; + static constexpr bool has_action = Left::has_action || Right::has_action; + static constexpr bool need_rcontext = Left::need_rcontext || Right::need_rcontext; constexpr binary_parser() = default; template - requires std::is_constructible_v && std::is_constructible_v + requires std::same_as, Left> && std::same_as, Right> constexpr binary_parser(LeftT&& left, RightT&& right) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) : left(std::forward(left)) , right(std::forward(right)) {} - static constexpr bool requires_exact_attribute_type = false; - - template - requires std::constructible_from< - action, std::remove_cvref_t>, - Self, Action - > - [[nodiscard]] - constexpr action, std::remove_cvref_t> - on_match(this Self&& self, Action&& f) - noexcept(std::is_nothrow_constructible_v< - action, std::remove_cvref_t>, - Self, Action - >) - { - return {std::forward(self), std::forward(f)}; - } - + // Empty instance elimination technique: please read the comment on `unary_parser`. IRIS_NO_UNIQUE_ADDRESS Left left; IRIS_NO_UNIQUE_ADDRESS Right right; - -private: - template friend struct detail::has_parser_base_impl; - using x4_parser_base_type = detail::parser_base; }; namespace traits { @@ -449,14 +315,14 @@ concept X4Subject = X4ExplicitSubject || X4ImplicitSubject; // This interface can only be used to check whether `Parser`'s single-parameter // constructor is available. For multi-parameter construction, manually combine // `is_parser_castable` with `std::is_constructible`. -template +template struct is_parser_constructible : std::false_type {}; template requires std::is_constructible_v> struct is_parser_constructible : std::true_type {}; -template +template constexpr bool is_parser_constructible_v = is_parser_constructible::value; // Checks whether `Parser(as_parser(t))` is noexcept. @@ -464,7 +330,7 @@ constexpr bool is_parser_constructible_v = is_parser_constructible::v // This interface can only be used to check whether `Parser`'s single-parameter // constructor is available. For multi-parameter construction, manually combine // `is_parser_nothrow_castable` with `std::is_nothrow_constructible`. -template +template struct is_parser_nothrow_constructible : std::false_type {}; template @@ -473,7 +339,7 @@ template std::is_nothrow_constructible_v> struct is_parser_nothrow_constructible : std::true_type {}; -template +template constexpr bool is_parser_nothrow_constructible_v = is_parser_nothrow_constructible::value; @@ -587,9 +453,6 @@ struct get_info namespace detail { -// "what" is an extremely common identifier that can be defined in many user-specific -// namespaces. We should avoid ADL usage for such generic names in the first place. -// (Note: CPO inhibits ADL in general.) struct what_fn { template @@ -603,7 +466,7 @@ struct what_fn inline namespace cpos { -[[maybe_unused]] inline constexpr detail::what_fn what{}; // no ADL +[[maybe_unused]] inline constexpr detail::what_fn what{}; } // cpos diff --git a/include/iris/x4/directive.hpp b/include/iris/x4/directive.hpp index 33c3bcc76..42086d084 100644 --- a/include/iris/x4/directive.hpp +++ b/include/iris/x4/directive.hpp @@ -11,15 +11,15 @@ ==============================================================================*/ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif diff --git a/include/iris/x4/directive/expect.hpp b/include/iris/x4/directive/expect.hpp index 6626344e2..d80d5dd1a 100644 --- a/include/iris/x4/directive/expect.hpp +++ b/include/iris/x4/directive/expect.hpp @@ -22,8 +22,10 @@ namespace iris::x4 { template -struct expect_directive : proxy_parser> +struct expect_directive : proxy_parser, Subject> { + using proxy_parser::proxy_parser; + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -36,11 +38,11 @@ struct expect_directive : proxy_parser> "Note that you must also bind the context to your skipper." ); - bool const r = this->subject().parse(first, last, ctx, attr); + 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); + x4::set_expectation_failure(first, this->subject, ctx); } return r; } @@ -55,7 +57,7 @@ struct expect_gen operator[](Subject&& subject) const noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return expect_directive>{as_parser(std::forward(subject))}; } }; @@ -91,11 +93,11 @@ struct parse_into_container_impl> "Note that you must also bind the context to your skipper." ); - bool const r = detail::parse_into_container(parser.subject(), first, last, ctx, attr); + bool const r = detail::parse_into_container(parser.subject, first, last, ctx, attr); // only the first error is needed if (!r && !x4::has_expectation_failure(ctx)) { - x4::set_expectation_failure(first, parser.subject(), ctx); + x4::set_expectation_failure(first, parser.subject, ctx); } return r; } diff --git a/include/iris/x4/directive/lexeme.hpp b/include/iris/x4/directive/lexeme.hpp index 49e192d39..708c35f02 100644 --- a/include/iris/x4/directive/lexeme.hpp +++ b/include/iris/x4/directive/lexeme.hpp @@ -22,8 +22,10 @@ namespace iris::x4 { template -struct lexeme_directive : proxy_parser> +struct lexeme_directive : proxy_parser, Subject> { + using proxy_parser::proxy_parser; + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -39,7 +41,7 @@ struct lexeme_directive : proxy_parser> auto it = first; x4::skip_over(it, last, ctx); // pre-skip - bool const ok = this->subject().parse( + bool const ok = this->subject.parse( it, last, x4::remove_first_context(ctx), // no skipper attr @@ -50,7 +52,7 @@ struct lexeme_directive : proxy_parser> [[nodiscard]] constexpr std::string get_x4_info() const { - return std::format("lexeme[{}]", get_info{}(this->subject())); + return std::format("lexeme[{}]", get_info{}(this->subject)); } }; @@ -63,7 +65,7 @@ struct lexeme_gen operator[](Subject&& subject) const noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return lexeme_directive>{as_parser(std::forward(subject))}; } }; diff --git a/include/iris/x4/directive/matches.hpp b/include/iris/x4/directive/matches.hpp index 46724e398..395ac7f63 100644 --- a/include/iris/x4/directive/matches.hpp +++ b/include/iris/x4/directive/matches.hpp @@ -24,12 +24,14 @@ namespace iris::x4 { template -struct matches_directive : unary_parser +struct matches_directive : unary_parser, Subject> { using attribute_type = bool; static constexpr bool has_attribute = true; + using unary_parser::unary_parser; + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -38,7 +40,7 @@ struct matches_directive : unary_parser noexcept(x4::move_to(std::declval(), attr)) ) { - bool const matched = this->subject().parse(first, last, ctx, unused); + bool const matched = this->subject.parse(first, last, ctx, unused); if constexpr (has_context_v) { if (x4::has_expectation_failure(ctx)) return false; @@ -58,7 +60,7 @@ struct matches_gen operator[](Subject&& subject) const noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return matches_directive>{as_parser(std::forward(subject))}; } }; diff --git a/include/iris/x4/directive/no_case.hpp b/include/iris/x4/directive/no_case.hpp index 60986eebe..89df4d2bb 100644 --- a/include/iris/x4/directive/no_case.hpp +++ b/include/iris/x4/directive/no_case.hpp @@ -23,8 +23,10 @@ namespace iris::x4 { // propagate no_case information through the context template -struct no_case_directive : proxy_parser> +struct no_case_directive : proxy_parser, Subject> { + using proxy_parser::proxy_parser; + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -34,7 +36,7 @@ struct no_case_directive : proxy_parser> Attr >) { - return this->subject().parse( + return this->subject.parse( first, last, x4::make_context(detail::case_compare_no_case, ctx), attr @@ -51,7 +53,7 @@ struct no_case_gen operator[](Subject&& subject) const noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return no_case_directive>{as_parser(std::forward(subject))}; } }; diff --git a/include/iris/x4/directive/no_skip.hpp b/include/iris/x4/directive/no_skip.hpp index 447829739..a64aa0147 100644 --- a/include/iris/x4/directive/no_skip.hpp +++ b/include/iris/x4/directive/no_skip.hpp @@ -23,8 +23,10 @@ namespace iris::x4 { // Same as `lexeme[...]`, but does not pre-skip template -struct no_skip_directive : proxy_parser> +struct no_skip_directive : proxy_parser, Subject> { + using proxy_parser::proxy_parser; + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -38,7 +40,7 @@ struct no_skip_directive : proxy_parser> // No pre-skip here, in contrast to `lexeme` // - return this->subject().parse( + return this->subject.parse( first, last, x4::remove_first_context(ctx), attr @@ -55,7 +57,7 @@ struct no_skip_gen operator[](Subject&& subject) const // TODO: MSVC can't handle static operator[] noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return no_skip_directive>{as_parser(std::forward(subject))}; } }; diff --git a/include/iris/x4/directive/omit.hpp b/include/iris/x4/directive/omit.hpp index 20053b7d3..9a271afa5 100644 --- a/include/iris/x4/directive/omit.hpp +++ b/include/iris/x4/directive/omit.hpp @@ -24,25 +24,27 @@ namespace iris::x4 { // `omit_directive` forces the attribute of subject parser // to be `unused_type` template -struct omit_directive : unary_parser +struct omit_directive : unary_parser, Subject> { using attribute_type = unused_type; static constexpr bool has_attribute = false; + using unary_parser::unary_parser; + 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); + return this->subject.parse(first, last, ctx, unused); } [[nodiscard]] constexpr std::string get_x4_info() const { return std::format( "omit[{}]", - get_info{}(this->subject()) + get_info{}(this->subject) ); } }; @@ -56,7 +58,7 @@ struct omit_gen operator[](Subject&& subject) const noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return omit_directive>{as_parser(std::forward(subject))}; } }; diff --git a/include/iris/x4/directive/repeat.hpp b/include/iris/x4/directive/repeat.hpp index a1167de30..07e5a7832 100644 --- a/include/iris/x4/directive/repeat.hpp +++ b/include/iris/x4/directive/repeat.hpp @@ -84,9 +84,9 @@ inline constexpr detail::repeat_inf_type inf{}; } // cpos template -struct repeat_directive : proxy_parser> +struct repeat_directive : proxy_parser, Subject> { - using base_type = proxy_parser; + using base_type = proxy_parser; using attribute_type = traits::default_container::attribute_type>::type; template @@ -114,7 +114,7 @@ struct repeat_directive : proxy_parsersubject(), local_it, last, ctx, chunk_buf)) { + if (detail::parse_into_container(this->subject, local_it, last, ctx, chunk_buf)) { // We can't merge here; it will lead to partial status } else { return false; @@ -125,7 +125,7 @@ struct repeat_directive : proxy_parsersubject(), first, last, ctx, chunk_buf)) { + if (detail::parse_into_container(this->subject, first, last, ctx, chunk_buf)) { list_like_parser::successful_merge_into(chunk_buf, container_attr); } else { break; @@ -143,7 +143,7 @@ struct repeat_directive : proxy_parsersubject(), first, last, ctx, x4::assume_container(unused_attr))) && + 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 ) @@ -151,7 +151,7 @@ struct repeat_directive : proxy_parsersubject(), local_it, last, ctx, x4::assume_container(unused_attr))) { + if (!detail::parse_into_container(this->subject, local_it, last, ctx, x4::assume_container(unused_attr))) { return false; } } @@ -159,7 +159,7 @@ struct repeat_directive : proxy_parsersubject(), first, last, ctx, x4::assume_container(unused_attr))) { + if (!detail::parse_into_container(this->subject, first, last, ctx, x4::assume_container(unused_attr))) { break; } } diff --git a/include/iris/x4/directive/skip.hpp b/include/iris/x4/directive/skip.hpp index 31d0affda..0e7af5643 100644 --- a/include/iris/x4/directive/skip.hpp +++ b/include/iris/x4/directive/skip.hpp @@ -33,9 +33,9 @@ struct unicode_char_class; #endif template -struct skip_directive : proxy_parser> +struct skip_directive : proxy_parser, Subject> { - using base_type = proxy_parser; + using base_type = proxy_parser; template requires std::is_constructible_v && std::is_constructible_v @@ -50,7 +50,7 @@ struct skip_directive : proxy_parser> 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); + return this->subject.parse(first, last, x4::replace_first_context(ctx, skipper_), attr); } [[nodiscard]] constexpr std::string get_x4_info() const @@ -58,7 +58,7 @@ struct skip_directive : proxy_parser> return std::format( "skip({})[{}]", get_info{}(this->skipper_), - get_info{}(this->subject()) + get_info{}(this->subject) ); } @@ -73,9 +73,9 @@ struct skip_directive : proxy_parser> template -struct builtin_skip_directive : proxy_parser> +struct builtin_skip_directive : proxy_parser, Subject> { - using base_type = proxy_parser; + using base_type = proxy_parser; using base_type::base_type; // Has existing builtin skipper @@ -88,7 +88,7 @@ struct builtin_skip_directive : proxy_parsersubject().parse(first, last, ctx, attr); + bool const ok = this->subject.parse(first, last, ctx, attr); skipper_kind = old_skipper_kind; return ok; @@ -109,7 +109,7 @@ struct builtin_skip_directive : proxy_parsersubject().parse(first, last, x4::replace_first_context(ctx, skipper_kind), attr); + return this->subject.parse(first, last, x4::replace_first_context(ctx, skipper_kind), attr); } [[nodiscard]] constexpr std::string get_x4_info() const @@ -117,7 +117,7 @@ struct builtin_skip_directive : proxy_parser::name, - get_info{}(this->subject()) + get_info{}(this->subject) ); } }; @@ -175,7 +175,7 @@ struct builtin_skip_gen_impl > ) { - return {as_parser(std::forward(subject))}; + return builtin_skip_directive>{as_parser(std::forward(subject))}; } }; diff --git a/include/iris/x4/directive/with.hpp b/include/iris/x4/directive/with.hpp index 8c99426c3..64c6c1dfd 100644 --- a/include/iris/x4/directive/with.hpp +++ b/include/iris/x4/directive/with.hpp @@ -26,9 +26,9 @@ namespace detail { template struct with_directive_impl - : proxy_parser> + : proxy_parser, Subject> { - using base_type = proxy_parser>; + using base_type = proxy_parser, Subject>; mutable T val_; template @@ -47,9 +47,9 @@ struct with_directive_impl template struct with_directive_impl - : proxy_parser> + : proxy_parser, Subject> { - using base_type = proxy_parser>; + using base_type = proxy_parser, Subject>; /* not mutable */ T const val_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) template @@ -68,9 +68,9 @@ struct with_directive_impl template struct with_directive_impl - : proxy_parser> + : proxy_parser, Subject> { - using base_type = proxy_parser>; + using base_type = proxy_parser, Subject>; T& val_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) template @@ -119,7 +119,7 @@ struct with_directive : detail::with_directive_impl parse(It& first, Se const& last, Context const& ctx, Attr& attr) const noexcept(is_nothrow_parsable_v, Attr>) { - return this->subject().parse( + return this->subject.parse( first, last, x4::make_context(this->val_, ctx), attr @@ -128,7 +128,7 @@ struct with_directive : detail::with_directive_impl [[nodiscard]] std::string get_x4_info() const { - return std::format("with<...>[{}]", get_info{}(this->subject())); + return std::format("with<...>[{}]", get_info{}(this->subject)); } private: @@ -225,10 +225,12 @@ using parsers::directive::with; template -struct without_directive : proxy_parser> +struct without_directive : proxy_parser, Subject> { static_assert(sizeof...(IDs) > 0); + using proxy_parser::proxy_parser; + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -240,12 +242,12 @@ struct without_directive : proxy_parser ) { - return this->subject().parse(first, last, x4::remove_all_contexts(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 std::format("without<...>[{}]", get_info{}(this->subject)); } }; diff --git a/include/iris/x4/directive/with_local.hpp b/include/iris/x4/directive/with_local.hpp index 83fd021de..61dcdade0 100644 --- a/include/iris/x4/directive/with_local.hpp +++ b/include/iris/x4/directive/with_local.hpp @@ -55,13 +55,15 @@ inline namespace cpos { template -struct with_local_directive : proxy_parser> +struct with_local_directive : proxy_parser, Subject> { static_assert(std::default_initializable); using id_type = ID; using value_type = T; + using proxy_parser::proxy_parser; + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -77,7 +79,7 @@ struct with_local_directive : proxy_parsersubject().parse(first, last, x4::replace_first_context(ctx, local_var), attr); + return this->subject.parse(first, last, x4::replace_first_context(ctx, local_var), attr); } }; diff --git a/include/iris/x4/numeric.hpp b/include/iris/x4/numeric.hpp index 46b28b3da..6dac3e75e 100644 --- a/include/iris/x4/numeric.hpp +++ b/include/iris/x4/numeric.hpp @@ -11,9 +11,9 @@ ==============================================================================*/ #include -#include -#include -#include -#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif diff --git a/include/iris/x4/numeric/bool.hpp b/include/iris/x4/numeric/bool.hpp index fcdb45444..85b611a69 100644 --- a/include/iris/x4/numeric/bool.hpp +++ b/include/iris/x4/numeric/bool.hpp @@ -20,8 +20,6 @@ #include -#include - #include #include #include @@ -30,7 +28,6 @@ namespace iris::x4 { -// Default boolean policies template struct bool_policies { @@ -62,7 +59,7 @@ struct bool_policies }; template> -struct bool_parser : parser<> +struct bool_parser : parser> { static_assert(X4Attribute); static_assert(std::default_initializable); @@ -109,7 +106,6 @@ struct bool_parser : parser<> { static_assert(X4NonUnusedAttribute); - // this case is called when Attribute is not T T attr_{}; if (bool_parser::parse(first, last, ctx, attr_)) { x4::move_to(std::move(attr_), attr); @@ -125,7 +121,7 @@ struct bool_parser : parser<> }; template> -struct literal_bool_parser : parser<> +struct literal_bool_parser : parser> { using T = decltype(ExpectedValue); static_assert(requires { static_cast(ExpectedValue); }); diff --git a/include/iris/x4/numeric/int.hpp b/include/iris/x4/numeric/int.hpp index 167a6219c..b5f8a2519 100644 --- a/include/iris/x4/numeric/int.hpp +++ b/include/iris/x4/numeric/int.hpp @@ -27,7 +27,7 @@ template< unsigned MinDigits = 1, int MaxDigits = -1 > -struct int_parser : parser<> +struct int_parser : parser> { // check template parameter 'Radix' for validity static_assert( diff --git a/include/iris/x4/numeric/real.hpp b/include/iris/x4/numeric/real.hpp index f484c72aa..57b94a683 100644 --- a/include/iris/x4/numeric/real.hpp +++ b/include/iris/x4/numeric/real.hpp @@ -207,7 +207,7 @@ struct strict_real_policies : real_policies }; template> -struct real_parser : parser<> +struct real_parser : parser> { static_assert(X4Attribute); static_assert(std::default_initializable); diff --git a/include/iris/x4/numeric/uint.hpp b/include/iris/x4/numeric/uint.hpp index df1956398..761aaaeec 100644 --- a/include/iris/x4/numeric/uint.hpp +++ b/include/iris/x4/numeric/uint.hpp @@ -28,7 +28,7 @@ template< unsigned MinDigits = 1, int MaxDigits = -1 > -struct uint_parser : parser<> +struct uint_parser : parser> { // check template parameter 'Radix' for validity static_assert((Radix >= 2 && Radix <= 36), "Unsupported Radix"); diff --git a/include/iris/x4/operator.hpp b/include/iris/x4/operator.hpp index 96224a9a6..096dbd13c 100644 --- a/include/iris/x4/operator.hpp +++ b/include/iris/x4/operator.hpp @@ -11,14 +11,14 @@ ==============================================================================*/ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif diff --git a/include/iris/x4/operator/alternative.hpp b/include/iris/x4/operator/alternative.hpp index be07acbdc..ffcbdd1bc 100644 --- a/include/iris/x4/operator/alternative.hpp +++ b/include/iris/x4/operator/alternative.hpp @@ -52,7 +52,7 @@ struct alternative_attribute_impl } // detail template -struct alternative : binary_parser> +struct alternative : binary_parser, Left, Right> { private: static constexpr bool is_both_same_attribute = std::is_same_v< @@ -95,7 +95,7 @@ struct alternative : binary_parser> >; public: - using binary_parser::binary_parser; + using binary_parser::binary_parser; // unused_type attribute template Se, class Context, X4UnusedAttribute UnusedAttr> diff --git a/include/iris/x4/operator/and_predicate.hpp b/include/iris/x4/operator/and_predicate.hpp index aad57f66b..301a22413 100644 --- a/include/iris/x4/operator/and_predicate.hpp +++ b/include/iris/x4/operator/and_predicate.hpp @@ -19,12 +19,14 @@ namespace iris::x4 { template -struct and_predicate : unary_parser +struct and_predicate : unary_parser, Subject> { using attribute_type = unused_type; static constexpr bool has_attribute = false; + using unary_parser::unary_parser; + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& /*attr*/) const @@ -34,7 +36,7 @@ struct and_predicate : unary_parser ) { auto it = first; - return this->subject().parse(it, last, ctx, unused); + return this->subject.parse(it, last, ctx, unused); } }; @@ -43,7 +45,7 @@ template operator&(Subject&& subject) noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return and_predicate>{as_parser(std::forward(subject))}; } } // iris::x4 diff --git a/include/iris/x4/operator/delimited_list.hpp b/include/iris/x4/operator/delimited_list.hpp index d9df58eaa..6fc1ca606 100644 --- a/include/iris/x4/operator/delimited_list.hpp +++ b/include/iris/x4/operator/delimited_list.hpp @@ -24,7 +24,7 @@ namespace iris::x4 { template -struct delimited_list : binary_parser> +struct delimited_list : binary_parser, Left, Right> { using attribute_type = traits::default_container::attribute_type>::type; @@ -34,7 +34,7 @@ struct delimited_list : binary_parser> traits::can_hold::attribute_type, typename traits::container_value::type> >; - using binary_parser::binary_parser; + using binary_parser::binary_parser; template Se, class Context, X4NonUnusedAttribute Attr> [[nodiscard]] constexpr bool diff --git a/include/iris/x4/operator/difference.hpp b/include/iris/x4/operator/difference.hpp index 7744f9988..be2734138 100644 --- a/include/iris/x4/operator/difference.hpp +++ b/include/iris/x4/operator/difference.hpp @@ -21,11 +21,11 @@ namespace iris::x4 { template -struct difference : binary_parser> +struct difference : binary_parser, Left, Right> { using attribute_type = parser_traits::attribute_type; - using binary_parser::binary_parser; + using binary_parser::binary_parser; template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool diff --git a/include/iris/x4/operator/kleene.hpp b/include/iris/x4/operator/kleene.hpp index 9c30d614f..cf45fb26d 100644 --- a/include/iris/x4/operator/kleene.hpp +++ b/include/iris/x4/operator/kleene.hpp @@ -25,7 +25,7 @@ namespace iris::x4 { template -struct kleene : unary_parser +struct kleene : unary_parser, Subject> { using attribute_type = traits::default_container::attribute_type>::type; @@ -35,6 +35,8 @@ struct kleene : unary_parser traits::can_hold::attribute_type, typename traits::container_value::type> >; + using unary_parser::unary_parser; + template Se, class Context, X4NonUnusedAttribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -43,7 +45,7 @@ struct kleene : unary_parser auto& container_attr = list_like_parser::get_container(attr); list_like_parser::chunk_buffer chunk_buf; - while (detail::parse_into_container(this->subject(), first, last, ctx, chunk_buf)) { + while (detail::parse_into_container(this->subject, first, last, ctx, chunk_buf)) { list_like_parser::successful_merge_into(chunk_buf, container_attr); } @@ -57,9 +59,9 @@ struct kleene : unary_parser 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)))) + 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))) + while (detail::parse_into_container(this->subject, first, last, ctx, x4::assume_container(unused_attr))) /* loop */; if constexpr (has_context_v) { @@ -73,7 +75,7 @@ struct kleene : unary_parser { return std::format( "*{}", - get_info{}(this->subject()) + get_info{}(this->subject) ); } }; @@ -83,7 +85,7 @@ template operator*(Subject&& subject) noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return kleene>{as_parser(std::forward(subject))}; } } // iris::x4 diff --git a/include/iris/x4/operator/not_predicate.hpp b/include/iris/x4/operator/not_predicate.hpp index f54ca2d15..ea26fd31f 100644 --- a/include/iris/x4/operator/not_predicate.hpp +++ b/include/iris/x4/operator/not_predicate.hpp @@ -21,12 +21,14 @@ namespace iris::x4 { template -struct not_predicate : unary_parser +struct not_predicate : unary_parser, Subject> { using attribute_type = unused_type; static constexpr bool has_attribute = false; + using unary_parser::unary_parser; + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& /*attr*/) const @@ -38,10 +40,10 @@ struct not_predicate : unary_parser It local_first = first; if constexpr (has_context_v) { - return !this->subject().parse(local_first, last, ctx, unused) && + return !this->subject.parse(local_first, last, ctx, unused) && !x4::has_expectation_failure(ctx); } else { - return !this->subject().parse(local_first, last, ctx, unused); + return !this->subject.parse(local_first, last, ctx, unused); } } }; @@ -51,7 +53,7 @@ template operator!(Subject&& subject) noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return not_predicate>{as_parser(std::forward(subject))}; } } // iris::x4 diff --git a/include/iris/x4/operator/optional.hpp b/include/iris/x4/operator/optional.hpp index 37dcc71b1..e0fc7c140 100644 --- a/include/iris/x4/operator/optional.hpp +++ b/include/iris/x4/operator/optional.hpp @@ -28,7 +28,7 @@ namespace iris::x4 { template -struct optional : unary_parser +struct optional : unary_parser, Subject> { using attribute_type = traits::build_optional::attribute_type>::type; @@ -38,6 +38,8 @@ struct optional : unary_parser traits::can_hold::attribute_type, typename traits::container_value::type> >; + using unary_parser::unary_parser; + // catch-all overload template< std::forward_iterator It, std::sentinel_for Se, class Context, @@ -48,7 +50,7 @@ struct optional : unary_parser noexcept(is_nothrow_parsable_v) { // discard [[nodiscard]] - (void)this->subject().parse(first, last, ctx, attr); + (void)this->subject.parse(first, last, ctx, attr); if constexpr (has_context_v) { return !x4::has_expectation_failure(ctx); @@ -64,10 +66,10 @@ struct optional : unary_parser > [[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))) + 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); + (void)detail::parse_into_container(this->subject, first, last, ctx, attr); if constexpr (has_context_v) { return !x4::has_expectation_failure(ctx); @@ -91,7 +93,7 @@ struct optional : unary_parser { typename traits::optional_value::type val{}; // value-initialize - if (this->subject().parse(first, last, ctx, val)) { + if (this->subject.parse(first, last, ctx, val)) { // assign the parsed value into our attribute x4::move_to(std::move(val), attr); return true; @@ -110,7 +112,7 @@ template operator-(Subject&& subject) noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return optional>{as_parser(std::forward(subject))}; } } // iris::x4 diff --git a/include/iris/x4/operator/plus.hpp b/include/iris/x4/operator/plus.hpp index f2b6adde3..0f618ae8a 100644 --- a/include/iris/x4/operator/plus.hpp +++ b/include/iris/x4/operator/plus.hpp @@ -25,7 +25,7 @@ namespace iris::x4 { template -struct plus : unary_parser +struct plus : unary_parser, Subject> { using attribute_type = traits::default_container::attribute_type>::type; @@ -35,6 +35,8 @@ struct plus : unary_parser traits::can_hold::attribute_type, typename traits::container_value::type> >; + using unary_parser::unary_parser; + template Se, class Context, X4NonUnusedAttribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr& attr) const @@ -43,13 +45,13 @@ struct plus : unary_parser auto& container_attr = list_like_parser::get_container(attr); list_like_parser::chunk_buffer chunk_buf; - if (detail::parse_into_container(this->subject(), first, last, ctx, chunk_buf)) { + if (detail::parse_into_container(this->subject, first, last, ctx, chunk_buf)) { list_like_parser::successful_merge_into(chunk_buf, container_attr); } else { return false; } - while (detail::parse_into_container(this->subject(), first, last, ctx, chunk_buf)) { + while (detail::parse_into_container(this->subject, first, last, ctx, chunk_buf)) { list_like_parser::successful_merge_into(chunk_buf, container_attr); } @@ -63,13 +65,13 @@ struct plus : unary_parser 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)))) + 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))) { + if (!detail::parse_into_container(this->subject, first, last, ctx, x4::assume_container(unused_attr))) { return false; } - while (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 */; if constexpr (has_context_v) { @@ -83,7 +85,7 @@ struct plus : unary_parser { return std::format( "+{}", - get_info{}(this->subject()) + get_info{}(this->subject) ); } }; @@ -93,7 +95,7 @@ template operator+(Subject&& subject) noexcept(is_parser_nothrow_constructible_v>, Subject>) { - return {as_parser(std::forward(subject))}; + return plus>{as_parser(std::forward(subject))}; } } // iris::x4 diff --git a/include/iris/x4/operator/sequence.hpp b/include/iris/x4/operator/sequence.hpp index a5c42eaf9..e05f1fe3a 100644 --- a/include/iris/x4/operator/sequence.hpp +++ b/include/iris/x4/operator/sequence.hpp @@ -77,7 +77,7 @@ struct container_can_hold_sequence> } // detail template -struct sequence : binary_parser> +struct sequence : binary_parser, Left, Right> { using attribute_type = traits::detail::attribute_of_sequence::type; @@ -92,7 +92,7 @@ struct sequence : binary_parser> ) || detail::container_can_hold_sequence::value; - using binary_parser::binary_parser; + using binary_parser::binary_parser; template Se, class Context, X4UnusedAttribute UnusedAttr> [[nodiscard]] constexpr bool @@ -136,7 +136,7 @@ struct sequence : binary_parser> return std::format( "{} > {}", get_info{}(this->left), - get_info{}(this->right.subject()) + get_info{}(this->right.subject) ); } else { return std::format( diff --git a/include/iris/x4/parse.hpp b/include/iris/x4/parse.hpp index abdbf885c..2a2b11ce3 100644 --- a/include/iris/x4/parse.hpp +++ b/include/iris/x4/parse.hpp @@ -10,7 +10,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ -#include +#include // IWYU pragma: keep + #include #include // for user-defined semantic action QoL #include diff --git a/include/iris/x4/parse_debug.hpp b/include/iris/x4/parse_debug.hpp index 82a62fddd..a8bd8fc64 100644 --- a/include/iris/x4/parse_debug.hpp +++ b/include/iris/x4/parse_debug.hpp @@ -1,7 +1,8 @@ #ifndef IRIS_X4_PARSE_DEBUG_HPP #define IRIS_X4_PARSE_DEBUG_HPP -#include +#include // IWYU pragma: keep + #include #include #include diff --git a/include/iris/x4/parse_result.hpp b/include/iris/x4/parse_result.hpp index 1409b3da1..b13eae6ce 100644 --- a/include/iris/x4/parse_result.hpp +++ b/include/iris/x4/parse_result.hpp @@ -12,7 +12,8 @@ // This header is intended for inclusion by user-facing, non-parser headers. // Do not add includes specific to X4's parser implementation here. -#include +#include // IWYU pragma: keep + #include #include diff --git a/include/iris/x4/primitive.hpp b/include/iris/x4/primitive.hpp index 87dd63852..05943bf35 100644 --- a/include/iris/x4/primitive.hpp +++ b/include/iris/x4/primitive.hpp @@ -12,8 +12,9 @@ ==============================================================================*/ #include -#include -#include -#include + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif diff --git a/include/iris/x4/primitive/eoi.hpp b/include/iris/x4/primitive/eoi.hpp index 225d0dbaa..e4549aa88 100644 --- a/include/iris/x4/primitive/eoi.hpp +++ b/include/iris/x4/primitive/eoi.hpp @@ -20,7 +20,7 @@ namespace iris::x4 { -struct eoi_parser : parser<> +struct eoi_parser : parser { using attribute_type = unused_type; diff --git a/include/iris/x4/primitive/eol.hpp b/include/iris/x4/primitive/eol.hpp index 7c3d685d8..c43d23443 100644 --- a/include/iris/x4/primitive/eol.hpp +++ b/include/iris/x4/primitive/eol.hpp @@ -19,7 +19,7 @@ namespace iris::x4 { -struct eol_parser : parser<> +struct eol_parser : parser { using attribute_type = unused_type; diff --git a/include/iris/x4/primitive/eps.hpp b/include/iris/x4/primitive/eps.hpp index e3e133a50..48c61d605 100644 --- a/include/iris/x4/primitive/eps.hpp +++ b/include/iris/x4/primitive/eps.hpp @@ -23,27 +23,45 @@ namespace iris::x4 { namespace detail { -struct semantic_predicate : parser +struct semantic_predicate : parser { using attribute_type = unused_type; + constexpr explicit semantic_predicate(bool cond) noexcept + : cond_(cond) + {} + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr&) const noexcept(noexcept(x4::skip_over(first, last, ctx))) { - if (this->storage()) { + if (this->cond_) { x4::skip_over(first, last, ctx); } - return this->storage(); + return this->cond_; } + +private: + bool cond_{}; }; template -struct lazy_semantic_predicate : parser +struct lazy_semantic_predicate : parser> { using attribute_type = unused_type; + constexpr lazy_semantic_predicate() = default; + + template + requires + (!std::same_as, lazy_semantic_predicate>) && + std::is_constructible_v + constexpr explicit lazy_semantic_predicate(T&& f) + noexcept(std::is_nothrow_constructible_v) + : f_(std::forward(f)) + {} + template Se, class Context, X4Attribute Attr> [[nodiscard]] constexpr bool parse(It& first, Se const& last, Context const& ctx, Attr&) const @@ -51,25 +69,27 @@ struct lazy_semantic_predicate : parser auto it = first; x4::skip_over(it, last, ctx); - if constexpr (std::invocable) { - static_assert(std::same_as, bool>); - bool const ok = this->storage()(ctx); + if constexpr (requires { this->f_(ctx); }) { + static_assert(std::same_asf_(ctx)), bool>); + bool const ok = this->f_(ctx); if (ok) first = it; return ok; } else { - static_assert(std::invocable); - static_assert(std::same_as, bool>); - bool const ok = this->storage()(); + static_assert(std::same_asf_()), bool>); + bool const ok = this->f_(); if (ok) first = it; return ok; } } + +private: + F f_{}; }; } // detail -struct eps_parser : parser<> +struct eps_parser : parser { using attribute_type = unused_type; @@ -83,9 +103,9 @@ struct eps_parser : parser<> } [[nodiscard]] static constexpr detail::semantic_predicate - operator()(bool predicate) noexcept + operator()(bool cond) noexcept { - return detail::semantic_predicate{predicate}; + return detail::semantic_predicate{cond}; } template diff --git a/include/iris/x4/rule.hpp b/include/iris/x4/rule.hpp index e63d828bc..f965aa4db 100644 --- a/include/iris/x4/rule.hpp +++ b/include/iris/x4/rule.hpp @@ -11,7 +11,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include +#include // IWYU pragma: keep #include #include @@ -302,7 +302,7 @@ struct rule_impl }; template -struct rule_definition : parser +struct rule_definition : parser> { static_assert(!std::same_as, unused_container_type>, "`rule_definition` with `unused_container_type` is not supported"); @@ -319,7 +319,7 @@ struct rule_definition : parser requires std::is_constructible_v constexpr rule_definition(RHS_T&& rhs, std::string_view name) noexcept(std::is_nothrow_constructible_v) - : parser(std::forward(rhs)) + : rhs_(std::forward(rhs)) , name(std::move(name)) {} @@ -330,10 +330,14 @@ struct rule_definition : parser { return rule_impl ::template call_rule_definition( - this->storage(), name, first, last, ctx, attr + this->rhs_, this->name, first, last, ctx, attr ); } +private: + RHS rhs_; + +public: std::string_view name = "unnamed_rule"; }; @@ -385,7 +389,7 @@ concept RuleAttrCompatible = } // detail template -struct rule : parser<> +struct rule : parser> { // This type MUST be constructible with incomplete types. // Do NOT add `static_assert`s or other constructs that cause eager diff --git a/include/iris/x4/string/literal_string.hpp b/include/iris/x4/string/literal_string.hpp index 2193e005c..71839ec22 100644 --- a/include/iris/x4/string/literal_string.hpp +++ b/include/iris/x4/string/literal_string.hpp @@ -28,11 +28,11 @@ namespace iris::x4 { -template> -struct literal_string : parser +template> +struct literal_string : parser> { static_assert( - !std::is_pointer_v>, + !std::is_pointer_v>, "`literal_string` for raw character pointer/array is banned; it has an undetectable risk of holding a dangling pointer." ); @@ -44,19 +44,28 @@ struct literal_string : parser static constexpr bool has_attribute = !std::is_same_v; - template Se, class Context, X4Attribute Attr_> + constexpr literal_string() = default; + + template + requires std::is_constructible_v + constexpr /*explicit*/ literal_string(Args&&... args) + noexcept(std::is_nothrow_constructible_v) + : str_(std::forward(args)...) + {} + + template Se, class Context, X4Attribute ExposedAttr> [[nodiscard]] constexpr bool - parse(It& first, Se const& last, Context const& ctx, Attr_& attr) const + parse(It& first, Se const& last, Context const& ctx, ExposedAttr& exposed_attr) const noexcept( std::is_nothrow_copy_assignable_v && noexcept(x4::skip_over(first, last, ctx)) && - noexcept(detail::string_parse(this->storage(), first, last, x4::assume_container(attr), x4::get_case_compare(ctx))) + noexcept(detail::string_parse(this->str_, first, last, x4::assume_container(exposed_attr), x4::get_case_compare(ctx))) ) { static_assert(std::same_as, char_type>, "Mixing incompatible char types is not allowed"); auto it = first; x4::skip_over(it, last, ctx); - bool const ok = detail::string_parse(this->storage(), it, last, x4::assume_container(attr), x4::get_case_compare(ctx)); + bool const ok = detail::string_parse(this->str_, it, last, x4::assume_container(exposed_attr), x4::get_case_compare(ctx)); if (ok) first = it; return ok; } @@ -64,8 +73,11 @@ 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->storage()})); + return std::format("\"{}\"", iris::unicode::transcode(std::basic_string_view{this->str_})); } + +private: + StoredStringT str_{}; }; } // iris::x4 diff --git a/include/iris/x4/symbols.hpp b/include/iris/x4/symbols.hpp index 4d0ecd7b4..3fdc92fbd 100644 --- a/include/iris/x4/symbols.hpp +++ b/include/iris/x4/symbols.hpp @@ -51,7 +51,7 @@ namespace iris::x4 { namespace detail { template -struct symbols_parser_impl : parser<> +struct symbols_parser_impl : parser> { static_assert(!std::is_same_v, "symbols parser with `unused_container_type` is not supported"); diff --git a/test/x4/iris_x4_test.hpp b/test/x4/iris_x4_test.hpp index 0c5c221e1..cc54d29a5 100644 --- a/test/x4/iris_x4_test.hpp +++ b/test/x4/iris_x4_test.hpp @@ -234,7 +234,7 @@ struct move_only }; template -struct synth_parser : x4::parser<> +struct synth_parser : x4::parser> { using attribute_type = T; diff --git a/test/x4/parser.cpp b/test/x4/parser.cpp index fa1b98ce3..0f0fddfc2 100644 --- a/test/x4/parser.cpp +++ b/test/x4/parser.cpp @@ -18,7 +18,7 @@ namespace { struct minimal_parser - : x4::parser<> + : x4::parser { constexpr minimal_parser() = default; @@ -37,7 +37,7 @@ struct minimal_parser }; struct minimal_unary_parser - : x4::unary_parser + : x4::unary_parser { template Se, class Context, x4::X4Attribute Attr> [[nodiscard]] constexpr bool @@ -48,7 +48,7 @@ struct minimal_unary_parser }; struct minimal_binary_parser - : x4::binary_parser + : x4::binary_parser { template Se, class Context, x4::X4Attribute Attr> [[nodiscard]] constexpr bool @@ -59,7 +59,7 @@ struct minimal_binary_parser }; struct minimal_unused_parser - : x4::parser<> + : x4::parser { template Se, class Context> [[nodiscard]] constexpr bool @@ -70,7 +70,7 @@ struct minimal_unused_parser }; struct minimal_unary_unused_parser - : x4::unary_parser + : x4::unary_parser { template Se, class Context> [[nodiscard]] constexpr bool @@ -81,7 +81,7 @@ struct minimal_unary_unused_parser }; struct minimal_binary_unused_parser - : x4::binary_parser + : x4::binary_parser { template Se, class Context> [[nodiscard]] constexpr bool diff --git a/test/x4/smart_ptr.cpp b/test/x4/smart_ptr.cpp index efe3f6d42..b6224ba4e 100644 --- a/test/x4/smart_ptr.cpp +++ b/test/x4/smart_ptr.cpp @@ -15,7 +15,7 @@ #include template -struct throwing_parser : x4::parser<> +struct throwing_parser : x4::parser> { using attribute_type = T; diff --git a/test/x4/without.cpp b/test/x4/without.cpp index 0dfaa7a8f..fd1be30bb 100644 --- a/test/x4/without.cpp +++ b/test/x4/without.cpp @@ -15,7 +15,7 @@ #include template -struct context_checker : x4::parser<> +struct context_checker : x4::parser> { template Se, class Context, x4::X4Attribute Attr> [[nodiscard]] static constexpr bool parse(It&, Se const&, Context const&, Attr const&)