From 0fd058fdd7f3daaf71f90a1c7b36fd33ebf72c50 Mon Sep 17 00:00:00 2001 From: Nana Sakisaka <1901813+saki7@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:37:35 +0900 Subject: [PATCH] Modernize `x3::repeat` --- .../boost/spirit/home/x3/directive/repeat.hpp | 234 +++++++++++------- test/x3/repeat.cpp | 46 ++-- test/x3/utils.hpp | 24 +- 3 files changed, 175 insertions(+), 129 deletions(-) diff --git a/include/boost/spirit/home/x3/directive/repeat.hpp b/include/boost/spirit/home/x3/directive/repeat.hpp index d19ea6a52..d0dbfd905 100644 --- a/include/boost/spirit/home/x3/directive/repeat.hpp +++ b/include/boost/spirit/home/x3/directive/repeat.hpp @@ -3,7 +3,7 @@ Copyright (c) 2001-2011 Hartmut Kaiser Copyright (c) 2014 Thomas Bernard Copyright (c) 2017 wanghan02 - Copyright (c) 2024 Nana Sakisaka + Copyright (c) 2024-2025 Nana Sakisaka 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) @@ -15,144 +15,192 @@ #include #include -namespace boost { namespace spirit { namespace x3 { namespace detail +#include +#include +#include +#include + +namespace boost::spirit::x3::detail { - template + template struct exact_count // handles repeat(exact)[p] { - typedef T type; - bool got_max(T i) const { return i >= exact_value; } - bool got_min(T i) const { return i >= exact_value; } + using value_type = T; + [[nodiscard]] constexpr bool got_max(T i) const noexcept { return i >= exact_value; } + [[nodiscard]] constexpr bool got_min(T i) const noexcept { return i >= exact_value; } - T const exact_value; + T exact_value; }; - template + template struct finite_count // handles repeat(min, max)[p] { - typedef T type; - bool got_max(T i) const { return i >= max_value; } - bool got_min(T i) const { return i >= min_value; } + using value_type = T; + [[nodiscard]] constexpr bool got_max(T i) const noexcept { return i >= max_value; } + [[nodiscard]] constexpr bool got_min(T i) const noexcept { return i >= min_value; } - T const min_value; - T const max_value; + T min_value; + T max_value; }; - template + template struct infinite_count // handles repeat(min, inf)[p] { - typedef T type; - bool got_max(T /*i*/) const { return false; } - bool got_min(T i) const { return i >= min_value; } + using value_type = T; + [[nodiscard]] constexpr bool got_max(T /*i*/) const noexcept { return false; } + [[nodiscard]] constexpr bool got_min(T i) const noexcept { return i >= min_value; } - T const min_value; + T min_value; }; -}}}} +} // boost::spirit::x3::detail -namespace boost { namespace spirit { namespace x3 +namespace boost::spirit::x3 { - template - struct repeat_directive : unary_parser> + namespace detail { - typedef unary_parser> base_type; - static bool const is_pass_through_unary = true; - static bool const handles_container = true; + template + concept RepeatBounds = requires(std::remove_cvref_t const& bounds) { + typename std::remove_cvref_t::value_type; + { bounds.got_max(std::declval::value_type>()) } -> std::same_as; + { bounds.got_min(std::declval::value_type>()) } -> std::same_as; + }; + } // detail - constexpr repeat_directive(Subject const& subject, RepeatCountLimit const& repeat_limit_) - : base_type(subject) - , repeat_limit(repeat_limit_) + template + struct repeat_directive : unary_parser> + { + using base_type = unary_parser>; + static constexpr bool is_pass_through_unary = true; + static constexpr bool handles_container = true; + + template + requires std::is_constructible_v && std::is_constructible_v + constexpr repeat_directive(SubjectT&& subject, BoundsT&& bounds) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : base_type(std::forward(subject)) + , bounds_(std::forward(bounds)) {} - template - bool parse( - Iterator& first, Iterator const& last - , Context const& context, RContext& rcontext, Attribute& attr) const + template Se, typename Context, typename RContext, typename Attribute> + [[nodiscard]] constexpr bool + parse(It& first, Se const& last, Context const& context, RContext& rcontext, Attribute& attr) const + // never noexcept (requires container insertion) { - Iterator local_iterator = first; - typename RepeatCountLimit::type i{}; - for (/**/; !repeat_limit.got_min(i); ++i) + It local_it = first; + typename Bounds::value_type i{}; + for (; !bounds_.got_min(i); ++i) { - if (!detail::parse_into_container( - this->subject, local_iterator, last, context, rcontext, attr)) + if (!detail::parse_into_container(this->subject, local_it, last, context, rcontext, attr)) + { return false; + } } - first = local_iterator; + first = local_it; // parse some more up to the maximum specified - for (/**/; !repeat_limit.got_max(i); ++i) + for (; !bounds_.got_max(i); ++i) { - if (!detail::parse_into_container( - this->subject, first, last, context, rcontext, attr)) + if (!detail::parse_into_container(this->subject, first, last, context, rcontext, attr)) + { break; + } } - return !has_expectation_failure(context); + return !x3::has_expectation_failure(context); } - RepeatCountLimit repeat_limit; + private: + Bounds bounds_; }; - // Infinite loop tag type - struct inf_type {}; - constexpr inf_type inf = inf_type(); - - struct repeat_gen + namespace detail { - template - constexpr auto operator[](Subject const& subject) const + // Infinite loop tag type + struct repeat_inf_type { - return *as_parser(subject); - } + constexpr explicit repeat_inf_type() noexcept = default; + }; + } // detail - template - struct repeat_gen_lvl1 + inline namespace cpos + { + // Infinite loop tag type + [[deprecated("Use `x3::repeat_inf`")]] + inline constexpr detail::repeat_inf_type inf{}; + + // Infinite loop tag type + inline constexpr detail::repeat_inf_type repeat_inf{}; + } + + namespace detail + { + struct repeat_gen { - constexpr repeat_gen_lvl1(T&& repeat_limit_) - : repeat_limit(repeat_limit_) - {} - - template - constexpr repeat_directive< typename extension::as_parser::value_type, T> - operator[](Subject const& subject) const + template + [[nodiscard, deprecated("`repeat[p]` has the exact same meaning as `*p`. Use `*p` instead.")]] + constexpr auto operator[](Subject&& subject) const + noexcept(noexcept(*as_parser(std::forward(subject)))) { - return { as_parser(subject),repeat_limit }; + return *as_parser(std::forward(subject)); } - T repeat_limit; + template + struct [[nodiscard]] repeat_gen_impl + { + template + [[nodiscard]] constexpr repeat_directive, Bounds> + operator[](Subject&& subject) const + noexcept( + is_parser_nothrow_castable_v && + std::is_nothrow_constructible_v< + repeat_directive, Bounds>, + as_parser_t, + Bounds const& + > + ) + { + return { as_parser(std::forward(subject)), bounds }; + } + + Bounds bounds; + }; + + template + static constexpr repeat_gen_impl> + operator()(T const exact) noexcept + { + return { exact_count{exact} }; + } + + template + static constexpr repeat_gen_impl> + operator()(T const min_val, T const max_val) noexcept + { + return { finite_count{min_val, max_val} }; + } + + template + static constexpr repeat_gen_impl> + operator()(T const min_val, repeat_inf_type const&) noexcept + { + return { infinite_count{min_val} }; + } }; + } // detail - template - constexpr repeat_gen_lvl1> - operator()(T const exact) const - { - return { detail::exact_count{exact} }; - } + inline namespace cpos + { + inline constexpr detail::repeat_gen repeat{}; + } - template - constexpr repeat_gen_lvl1> - operator()(T const min_val, T const max_val) const - { - return { detail::finite_count{min_val,max_val} }; - } +} // boost::spirit::x3 - template - constexpr repeat_gen_lvl1> - operator()(T const min_val, inf_type const &) const - { - return { detail::infinite_count{min_val} }; - } - }; - - constexpr auto repeat = repeat_gen{}; -}}} - -namespace boost { namespace spirit { namespace x3 { namespace traits +namespace boost::spirit::x3::traits { - template - struct attribute_of, Context> - : build_container::type> {}; -}}}} - + template + struct attribute_of, Context> + : build_container> + {}; +} // boost::spirit::x3::traits #endif diff --git a/test/x3/repeat.cpp b/test/x3/repeat.cpp index 4468c2e66..7c397dde4 100644 --- a/test/x3/repeat.cpp +++ b/test/x3/repeat.cpp @@ -1,33 +1,27 @@ /*============================================================================= Copyright (c) 2001-2011 Joel de Guzman + Copyright (c) 2025 Nana Sakisaka 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 - -#if defined(__GNUC__) && (__GNUC__ >= 8) -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92539 -# pragma GCC diagnostic ignored "-Warray-bounds" -#endif -#include -#include -#include #include "test.hpp" #include "utils.hpp" -int -main() +#include + +#include +#include +#include + +int main() { using spirit_test::test_attr; using spirit_test::test; - using namespace boost::spirit::x3::ascii; + using namespace boost::spirit::x3::standard; using boost::spirit::x3::repeat; - using boost::spirit::x3::inf; + using boost::spirit::x3::repeat_inf; using boost::spirit::x3::omit; using boost::spirit::x3::int_; using boost::spirit::x3::lexeme; @@ -36,7 +30,7 @@ main() BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(repeat['x']); BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(repeat(3)['x']); BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(repeat(3, 5)['x']); - BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(repeat(3, inf)['x']); + BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(repeat(3, repeat_inf)['x']); { BOOST_TEST(test("aaaaaaaa", repeat[char_])); // kleene synonym @@ -47,10 +41,10 @@ main() BOOST_TEST(!test("aaaaaa", repeat(3, 5)[char_])); BOOST_TEST(!test("aa", repeat(3, 5)[char_])); - BOOST_TEST(test("aaa", repeat(3, inf)[char_])); - BOOST_TEST(test("aaaaa", repeat(3, inf)[char_])); - BOOST_TEST(test("aaaaaa", repeat(3, inf)[char_])); - BOOST_TEST(!test("aa", repeat(3, inf)[char_])); + BOOST_TEST(test("aaa", repeat(3, repeat_inf)[char_])); + BOOST_TEST(test("aaaaa", repeat(3, repeat_inf)[char_])); + BOOST_TEST(test("aaaaaa", repeat(3, repeat_inf)[char_])); + BOOST_TEST(!test("aa", repeat(3, repeat_inf)[char_])); } { std::string s; @@ -76,18 +70,18 @@ main() BOOST_TEST(!test("a", repeat(1, 3)[char_ >> char_])); s.clear(); - BOOST_TEST(test_attr("aaaa", repeat(2, inf)[char_ >> char_], s)); + BOOST_TEST(test_attr("aaaa", repeat(2, repeat_inf)[char_ >> char_], s)); BOOST_TEST(s == "aaaa"); s.clear(); - BOOST_TEST(test_attr("aaaaaa", repeat(2, inf)[char_ >> char_], s)); + BOOST_TEST(test_attr("aaaaaa", repeat(2, repeat_inf)[char_ >> char_], s)); BOOST_TEST(s == "aaaaaa"); - BOOST_TEST(!test("aa", repeat(2, inf)[char_ >> char_])); + BOOST_TEST(!test("aa", repeat(2, repeat_inf)[char_ >> char_])); } { // from classic spirit tests - BOOST_TEST(test("", repeat(0, inf)['x'])); + BOOST_TEST(test("", repeat(0, repeat_inf)['x'])); // repeat exact 8 #define rep8 repeat(8)[alpha] >> 'X' @@ -105,7 +99,7 @@ main() BOOST_TEST(!test("a*", rep28, false)); // repeat 2 or more - #define rep2_ repeat(2, inf)[alpha] >> '+' + #define rep2_ repeat(2, repeat_inf)[alpha] >> '+' BOOST_TEST(test("abcdefg+", rep2_)); BOOST_TEST(test("abcdefgh+", rep2_)); BOOST_TEST(test("abcdefghi+", rep2_)); diff --git a/test/x3/utils.hpp b/test/x3/utils.hpp index 588f60dec..0b31b9f98 100644 --- a/test/x3/utils.hpp +++ b/test/x3/utils.hpp @@ -1,5 +1,6 @@ /*============================================================================= Copyright (c) 2019 Nikita Kniazev + Copyright (c) 2025 Nana Sakisaka Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,6 +10,9 @@ #define BOOST_SPIRIT_TEST_X3_UTILS_HPP #include +#include + +#include struct move_only { @@ -17,19 +21,19 @@ struct move_only move_only& operator=(move_only&&) = default; }; - template struct synth_parser : boost::spirit::x3::parser> { - typedef T attribute_type; + using attribute_type = T; - static bool const has_attribute = true; - static bool const handles_container = false; + static constexpr bool has_attribute = true; + static constexpr bool handles_container = false; - template - bool parse(Iterator& iter, Iterator const& last, Context const&, - RuleContext&, Attribute& attr) const + template Se, typename Context, typename RContext, typename Attribute> + [[nodiscard]] constexpr bool parse( + It& iter, Se const& last, Context const&, + RContext&, Attribute& attr + ) const { if (iter != last && *iter == 's') { ++iter; @@ -41,8 +45,8 @@ struct synth_parser : boost::spirit::x3::parser> }; template -synth_parser synth{}; +constexpr synth_parser synth{}; -synth_parser const synth_move_only{}; +constexpr synth_parser synth_move_only{}; #endif