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 8c9aa32b3..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,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< @@ -151,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 4cd6ed234..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 @@ -115,18 +115,9 @@ struct unique_ptr_parser_base template struct unique_ptr_parser_base> - : proxy_parser + : 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 @@ -219,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 @@ -287,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 diff --git a/include/iris/x4/attribute/value.hpp b/include/iris/x4/attribute/value.hpp index 2f89cdeee..396fdab7b 100644 --- a/include/iris/x4/attribute/value.hpp +++ b/include/iris/x4/attribute/value.hpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace iris::x4 { @@ -32,42 +33,51 @@ 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; - template + constexpr fixed_value_parser() = default; + + 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)) + // 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::as_const(held_value_), 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(std::as_const(held_value_), attr_); + x4::move_to(this->held_value_, exposed_attr); return true; } private: - HeldValueT held_value_; + IRIS_NO_UNIQUE_ADDRESS HeldValueT held_value_{}; }; -// `reset_value` +// aka `reset_value` template struct fixed_value_parser : parser> { @@ -86,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.hpp b/include/iris/x4/char.hpp index 8e1773114..c9a9d5e93 100644 --- a/include/iris/x4/char.hpp +++ b/include/iris/x4/char.hpp @@ -10,15 +10,17 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include -#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..63f8da02a 100644 --- a/include/iris/x4/char/any_char.hpp +++ b/include/iris/x4/char/any_char.hpp @@ -10,19 +10,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ -#include #include #include namespace iris::x4 { template -struct any_char : char_parser> +struct any_char : char_parser, Encoding> { 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 867959364..ad69154f0 100644 --- a/include/iris/x4/char/char.hpp +++ b/include/iris/x4/char/char.hpp @@ -10,232 +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 extension { - -template<> -struct as_parser -{ - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(char ch) noexcept - { - return {ch}; - } -}; - -#ifndef IRIS_X4_NO_STANDARD_WIDE -template<> -struct as_parser -{ - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(wchar_t ch) noexcept - { - return {ch}; - } -}; -#endif // IRIS_X4_NO_STANDARD_WIDE - -#ifdef IRIS_X4_UNICODE -template<> -struct as_parser -{ - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(char32_t ch) noexcept - { - return {ch}; - } -}; #endif // IRIS_X4_UNICODE -template<> -struct as_parser -{ - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(char const ch[]) noexcept - { - return {ch[0]}; - } -}; - -#ifndef IRIS_X4_NO_STANDARD_WIDE -template<> -struct as_parser -{ - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(wchar_t const ch[]) noexcept - { - return {ch[0]}; - } -}; -#endif // IRIS_X4_NO_STANDARD_WIDE - -#ifdef IRIS_X4_UNICODE -template<> -struct as_parser -{ - using type = literal_char; - using value_type = type; - - [[nodiscard]] static constexpr type call(char32_t const ch[]) noexcept - { - return {ch[0]}; - } -}; -#endif // IRIS_X4_UNICODE - -} // extension - } // iris::x4 #endif 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 2f6e1ef4c..01fa3fa6b 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 +template +struct char_parser; + +// `negated_char_parser` handles `~cp`, where `cp` is a `char_parser` +template +struct negated_char_parser : char_parser, typename Positive::encoding_type> +{ + 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/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 3950a05c8..fce367ff4 100644 --- a/include/iris/x4/char/literal_char.hpp +++ b/include/iris/x4/char/literal_char.hpp @@ -22,12 +22,12 @@ namespace iris::x4 { template -struct literal_char : char_parser> +struct literal_char : char_parser, Encoding> { 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; @@ -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/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/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 new file mode 100644 index 000000000..d57ce292a --- /dev/null +++ b/include/iris/x4/char_string_literal.hpp @@ -0,0 +1,298 @@ +#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 // 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/action.hpp b/include/iris/x4/core/action.hpp index 6341b4a0d..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; diff --git a/include/iris/x4/core/attribute.hpp b/include/iris/x4/core/attribute.hpp index 74f689ce1..336be6250 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,10 +9,9 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ -#include +#include // IWYU pragma: keep #include -#include #include namespace iris::x4 { @@ -24,6 +23,9 @@ namespace detail { struct parser_base; +template +concept has_parser_base = std::is_same_v::x4_parser_base_type, parser_base>; + } // detail template @@ -35,7 +37,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 +48,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 5176df180..9e08eea66 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,64 +156,47 @@ 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; } 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 diff --git a/include/iris/x4/core/parser.hpp b/include/iris/x4/core/parser.hpp index 08c1f6a7b..04bc827e7 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,87 +29,76 @@ #include #endif +#include // IWYU pragma: keep + namespace iris::x4 { template struct action; -namespace detail { - -struct parser_base {}; -struct parser_id; - -} // detail - - -template -struct parser : private detail::parser_base +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 { - static_assert(!std::is_reference_v); - using derived_type = Derived; + using x4_parser_base_type = detail::parser_base; static constexpr bool has_action = false; static constexpr bool need_rcontext = false; static constexpr bool requires_exact_attribute_type = false; - [[nodiscard]] constexpr Derived& derived() & noexcept - { - return static_cast(*this); - } - - [[nodiscard]] constexpr Derived const& derived() const& noexcept - { - return static_cast(*this); - } - - [[nodiscard]] constexpr Derived&& derived() && noexcept - { - return static_cast(*this); - } - - [[nodiscard]] constexpr Derived const&& derived() const&& noexcept - { - return static_cast(*this); - } - 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 +template struct unary_parser : parser { using subject_type = Subject; @@ -118,19 +109,33 @@ struct unary_parser : parser constexpr unary_parser() = default; template - requires - (!std::is_same_v, unary_parser>) && - std::is_constructible_v - constexpr unary_parser(SubjectT&& subject) + requires std::same_as, Subject> + constexpr explicit unary_parser(SubjectT&& subject) noexcept(std::is_nothrow_constructible_v) : subject(std::forward(subject)) {} - Subject subject; + // 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 -struct proxy_parser : unary_parser +template +struct proxy_parser : unary_parser { using proxy_backend_type = Subject; using attribute_type = parser_traits::attribute_type; @@ -141,34 +146,34 @@ 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 +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)) {} - // TODO: [MSVC 2022 BUG] "overruns" in constexpr, test case in `lit.cpp` - /*IRIS_NO_UNIQUE_ADDRESS*/ Left left; - /*IRIS_NO_UNIQUE_ADDRESS*/ Right right; + // Empty instance elimination technique: please read the comment on `unary_parser`. + IRIS_NO_UNIQUE_ADDRESS Left left; + IRIS_NO_UNIQUE_ADDRESS Right right; }; -namespace extension { +namespace traits { template struct as_parser; // not defined @@ -176,88 +181,56 @@ 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; +} // 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; +struct as_parser_char_array_tag {}; - template - [[nodiscard]] static constexpr auto&& call(T&& p) noexcept - { - return std::forward(p).derived(); - } +template +struct as_parser_plain_type +{ + using type = std::remove_cvref_t; +}; +template +struct as_parser_plain_type +{ + using type = as_parser_char_array_tag; }; -} // extension - -namespace detail { +template +concept has_custom_as_parser = requires(T&& p) { + { 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. - - // 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 - { - return p.derived(); - } - - template - [[nodiscard]] static constexpr auto&& - operator()(parser const& p IRIS_LIFETIMEBOUND) noexcept + operator()(T&& x) noexcept(noexcept(traits::as_parser::type>{}(std::forward(x)))) { - return p.derived(); + static_assert(has_parser_base::type>{}(std::forward(x)))>); + return traits::as_parser::type>{}(std::forward(x)); } - 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 +282,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>; @@ -343,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. @@ -358,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 @@ -367,12 +339,24 @@ 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; 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 +365,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 +381,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 +391,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 +400,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. // @@ -508,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 @@ -524,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/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/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/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 411995dbf..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 @@ -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))}; } }; diff --git a/include/iris/x4/directive/lexeme.hpp b/include/iris/x4/directive/lexeme.hpp index c4120d89a..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 @@ -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 9e7be6218..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 @@ -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 f1dfe4f9b..89df4d2bb 100644 --- a/include/iris/x4/directive/no_case.hpp +++ b/include/iris/x4/directive/no_case.hpp @@ -23,18 +23,18 @@ 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 - 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( first, last, @@ -53,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 40e48061c..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 @@ -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 d75541614..9a271afa5 100644 --- a/include/iris/x4/directive/omit.hpp +++ b/include/iris/x4/directive/omit.hpp @@ -24,12 +24,14 @@ 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 @@ -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 e5cff7659..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 diff --git a/include/iris/x4/directive/skip.hpp b/include/iris/x4/directive/skip.hpp index fede93a71..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 @@ -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 @@ -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 b658d82e4..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 @@ -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,7 +119,6 @@ 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( first, last, x4::make_context(this->val_, ctx), @@ -231,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 diff --git a/include/iris/x4/directive/with_local.hpp b/include/iris/x4/directive/with_local.hpp index 2d09c98a0..61dcdade0 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 /*============================================================================= @@ -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 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 759ab6957..85b611a69 100644 --- a/include/iris/x4/numeric/bool.hpp +++ b/include/iris/x4/numeric/bool.hpp @@ -28,7 +28,6 @@ namespace iris::x4 { -// Default boolean policies template struct bool_policies { @@ -70,10 +69,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 @@ -111,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); @@ -126,9 +120,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 +132,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 +150,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 +180,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/uint.hpp b/include/iris/x4/numeric/uint.hpp index eefd34208..761aaaeec 100644 --- a/include/iris/x4/numeric/uint.hpp +++ b/include/iris/x4/numeric/uint.hpp @@ -15,7 +15,6 @@ #include #include -#include #include #include 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 a8185bc30..ffcbdd1bc 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 @@ -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 4b03ad795..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 @@ -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 a49bb904a..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 @@ -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 fd9bcb10f..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 @@ -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 cc820f58d..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, @@ -47,8 +49,6 @@ 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); @@ -112,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 4cc72295c..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 @@ -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 21d3e942f..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 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/eps.hpp b/include/iris/x4/primitive/eps.hpp index f45c5a324..48c61d605 100644 --- a/include/iris/x4/primitive/eps.hpp +++ b/include/iris/x4/primitive/eps.hpp @@ -27,8 +27,8 @@ struct semantic_predicate : parser { using attribute_type = unused_type; - constexpr explicit semantic_predicate(bool predicate) noexcept - : predicate_(predicate) + constexpr explicit semantic_predicate(bool cond) noexcept + : cond_(cond) {} template Se, class Context, X4Attribute Attr> @@ -36,14 +36,14 @@ struct semantic_predicate : parser parse(It& first, Se const& last, Context const& ctx, Attr&) const noexcept(noexcept(x4::skip_over(first, last, ctx))) { - if (predicate_) { + if (this->cond_) { x4::skip_over(first, last, ctx); } - return predicate_; + return this->cond_; } private: - bool predicate_; + bool cond_{}; }; template @@ -51,13 +51,15 @@ struct lazy_semantic_predicate : parser> { using attribute_type = unused_type; - template + constexpr lazy_semantic_predicate() = default; + + 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)) + (!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> @@ -67,23 +69,22 @@ 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 = f_(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 = f_(); + static_assert(std::same_asf_()), bool>); + bool const ok = this->f_(); if (ok) first = it; return ok; } } private: - F f_; + F f_{}; }; } // detail @@ -102,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 f9577a9b6..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 @@ -304,7 +304,7 @@ struct rule_impl template 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)) + : rhs_(std::forward(rhs)) , name(std::move(name)) {} @@ -330,11 +330,14 @@ struct rule_definition : parser ::template call_rule_definition( - rhs, name, first, last, ctx, attr + this->rhs_, this->name, first, last, ctx, attr ); } - RHS rhs; +private: + RHS rhs_; + +public: std::string_view name = "unnamed_rule"; }; 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 ca367844c..71839ec22 100644 --- a/include/iris/x4/string/literal_string.hpp +++ b/include/iris/x4/string/literal_string.hpp @@ -28,16 +28,15 @@ 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." ); - 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 +44,28 @@ 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)...) + 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 Attr_> + 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(str_, 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(str_, 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; } @@ -74,11 +73,11 @@ 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(std::basic_string_view{this->str_})); } private: - String str_; + StoredStringT str_{}; }; } // iris::x4 diff --git a/include/iris/x4/string/string.hpp b/include/iris/x4/string/string.hpp index 73d0934e3..583918ac9 100644 --- a/include/iris/x4/string/string.hpp +++ b/include/iris/x4/string/string.hpp @@ -10,256 +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 - -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 extension { - -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) - { - return type(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) - { - return type(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) - { - return type(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) - { - return type(std::forward(str)); - } -}; - -} // extension - -} // iris::x4 +#include #endif diff --git a/include/iris/x4/symbols.hpp b/include/iris/x4/symbols.hpp index dd918b28a..3fdc92fbd 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/modules/iris b/modules/iris index ba5c2bdea..b29919bfc 160000 --- a/modules/iris +++ b/modules/iris @@ -1 +1 @@ -Subproject commit ba5c2bdea2fef9e86a17d5c189fd6af67a0aa4a5 +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 232dc962c..9b036f9e8 100644 --- a/test/x4/as.cpp +++ b/test/x4/as.cpp @@ -16,8 +16,7 @@ #include #include -#include -#include +#include #include #include diff --git a/test/x4/char.cpp b/test/x4/char.cpp index 08ccc53b6..8474180b0 100644 --- a/test/x4/char.cpp +++ b/test/x4/char.cpp @@ -13,10 +13,10 @@ #include "iris_x4_test.hpp" +#include #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/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 797816a6e..a238b7b72 100644 --- a/test/x4/expect.cpp +++ b/test/x4/expect.cpp @@ -26,8 +26,7 @@ #include #include -#include -#include +#include #include #include @@ -284,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 5e8202b23..cfb2693d8 100644 --- a/test/x4/iterator.cpp +++ b/test/x4/iterator.cpp @@ -22,8 +22,7 @@ #include #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/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/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/parser.cpp b/test/x4/parser.cpp index 2f6924733..0f0fddfc2 100644 --- a/test/x4/parser.cpp +++ b/test/x4/parser.cpp @@ -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 @@ -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/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 018c60317..96957d738 100644 --- a/test/x4/rule4.cpp +++ b/test/x4/rule4.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -24,7 +23,6 @@ #include #include -#include #include namespace { @@ -68,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 { @@ -136,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 55ac84c6c..11512cd8e 100644 --- a/test/x4/sequence.cpp +++ b/test/x4/sequence.cpp @@ -14,12 +14,11 @@ #include #include #include +#include #include -#include #include #include #include -#include #include #include @@ -42,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; @@ -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/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 4fe0f11fb..16499ca79 100644 --- a/test/x4/symbols1.cpp +++ b/test/x4/symbols1.cpp @@ -9,27 +9,11 @@ #include "iris_x4_test.hpp" -#include +#include #include #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;