2
0
mirror of https://github.com/boostorg/spirit.git synced 2026-01-19 04:42:11 +00:00

Merge pull request #826 from saki7/modernize-repeat

Use concepts in `x3::repeat`.
`x3::repeat` is now a CPO that inhibits ADL.
`x3::inf` is deprecated regarding overly generic name; use `x3::repeat_inf` instead.

`x3::repeat[p]` is deprecated since it has the exact same meaning as `*p`. It is
generally discouraged to provide multiple ways to achieve same the
functionality in modern C++ library design.
This commit is contained in:
Nana Sakisaka
2025-09-10 11:50:50 +09:00
committed by GitHub
3 changed files with 175 additions and 129 deletions

View File

@@ -3,7 +3,7 @@
Copyright (c) 2001-2011 Hartmut Kaiser Copyright (c) 2001-2011 Hartmut Kaiser
Copyright (c) 2014 Thomas Bernard Copyright (c) 2014 Thomas Bernard
Copyright (c) 2017 wanghan02 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 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) file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,144 +15,192 @@
#include <boost/spirit/home/x3/operator/kleene.hpp> #include <boost/spirit/home/x3/operator/kleene.hpp>
#include <boost/spirit/home/x3/support/expectation.hpp> #include <boost/spirit/home/x3/support/expectation.hpp>
namespace boost { namespace spirit { namespace x3 { namespace detail #include <iterator>
#include <type_traits>
#include <concepts>
#include <utility>
namespace boost::spirit::x3::detail
{ {
template <typename T> template <std::integral T>
struct exact_count // handles repeat(exact)[p] struct exact_count // handles repeat(exact)[p]
{ {
typedef T type; using value_type = T;
bool got_max(T i) const { return i >= exact_value; } [[nodiscard]] constexpr bool got_max(T i) const noexcept { return i >= exact_value; }
bool got_min(T i) const { 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 <typename T> template <std::integral T>
struct finite_count // handles repeat(min, max)[p] struct finite_count // handles repeat(min, max)[p]
{ {
typedef T type; using value_type = T;
bool got_max(T i) const { return i >= max_value; } [[nodiscard]] constexpr bool got_max(T i) const noexcept { return i >= max_value; }
bool got_min(T i) const { return i >= min_value; } [[nodiscard]] constexpr bool got_min(T i) const noexcept { return i >= min_value; }
T const min_value; T min_value;
T const max_value; T max_value;
}; };
template <typename T> template <std::integral T>
struct infinite_count // handles repeat(min, inf)[p] struct infinite_count // handles repeat(min, inf)[p]
{ {
typedef T type; using value_type = T;
bool got_max(T /*i*/) const { return false; } [[nodiscard]] constexpr bool got_max(T /*i*/) const noexcept { return false; }
bool got_min(T i) const { return i >= min_value; } [[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<typename Subject, typename RepeatCountLimit> namespace detail
struct repeat_directive : unary_parser<Subject, repeat_directive<Subject,RepeatCountLimit>>
{ {
typedef unary_parser<Subject, repeat_directive<Subject,RepeatCountLimit>> base_type; template <typename Bounds>
static bool const is_pass_through_unary = true; concept RepeatBounds = requires(std::remove_cvref_t<Bounds> const& bounds) {
static bool const handles_container = true; typename std::remove_cvref_t<Bounds>::value_type;
{ bounds.got_max(std::declval<typename std::remove_cvref_t<Bounds>::value_type>()) } -> std::same_as<bool>;
{ bounds.got_min(std::declval<typename std::remove_cvref_t<Bounds>::value_type>()) } -> std::same_as<bool>;
};
} // detail
constexpr repeat_directive(Subject const& subject, RepeatCountLimit const& repeat_limit_) template <typename Subject, detail::RepeatBounds Bounds>
: base_type(subject) struct repeat_directive : unary_parser<Subject, repeat_directive<Subject, Bounds>>
, repeat_limit(repeat_limit_) {
using base_type = unary_parser<Subject, repeat_directive<Subject, Bounds>>;
static constexpr bool is_pass_through_unary = true;
static constexpr bool handles_container = true;
template <typename SubjectT, detail::RepeatBounds BoundsT>
requires std::is_constructible_v<base_type, SubjectT> && std::is_constructible_v<Bounds, BoundsT>
constexpr repeat_directive(SubjectT&& subject, BoundsT&& bounds)
noexcept(std::is_nothrow_constructible_v<base_type, SubjectT> && std::is_nothrow_constructible_v<Bounds, BoundsT>)
: base_type(std::forward<SubjectT>(subject))
, bounds_(std::forward<BoundsT>(bounds))
{} {}
template<typename Iterator, typename Context template<std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext, typename Attribute>
, typename RContext, typename Attribute> [[nodiscard]] constexpr bool
bool parse( parse(It& first, Se const& last, Context const& context, RContext& rcontext, Attribute& attr) const
Iterator& first, Iterator const& last // never noexcept (requires container insertion)
, Context const& context, RContext& rcontext, Attribute& attr) const
{ {
Iterator local_iterator = first; It local_it = first;
typename RepeatCountLimit::type i{}; typename Bounds::value_type i{};
for (/**/; !repeat_limit.got_min(i); ++i) for (; !bounds_.got_min(i); ++i)
{ {
if (!detail::parse_into_container( if (!detail::parse_into_container(this->subject, local_it, last, context, rcontext, attr))
this->subject, local_iterator, last, context, rcontext, attr)) {
return false; return false;
}
} }
first = local_iterator; first = local_it;
// parse some more up to the maximum specified // 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( if (!detail::parse_into_container(this->subject, first, last, context, rcontext, attr))
this->subject, first, last, context, rcontext, attr)) {
break; break;
}
} }
return !has_expectation_failure(context); return !x3::has_expectation_failure(context);
} }
RepeatCountLimit repeat_limit; private:
Bounds bounds_;
}; };
// Infinite loop tag type namespace detail
struct inf_type {};
constexpr inf_type inf = inf_type();
struct repeat_gen
{ {
template<typename Subject> // Infinite loop tag type
constexpr auto operator[](Subject const& subject) const struct repeat_inf_type
{ {
return *as_parser(subject); constexpr explicit repeat_inf_type() noexcept = default;
} };
} // detail
template <typename T> inline namespace cpos
struct repeat_gen_lvl1 {
// 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_) template <X3Subject Subject>
: repeat_limit(repeat_limit_) [[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>(subject))))
template<typename Subject>
constexpr repeat_directive< typename extension::as_parser<Subject>::value_type, T>
operator[](Subject const& subject) const
{ {
return { as_parser(subject),repeat_limit }; return *as_parser(std::forward<Subject>(subject));
} }
T repeat_limit; template <RepeatBounds Bounds>
struct [[nodiscard]] repeat_gen_impl
{
template <X3Subject Subject>
[[nodiscard]] constexpr repeat_directive<as_parser_plain_t<Subject>, Bounds>
operator[](Subject&& subject) const
noexcept(
is_parser_nothrow_castable_v<Subject> &&
std::is_nothrow_constructible_v<
repeat_directive<as_parser_plain_t<Subject>, Bounds>,
as_parser_t<Subject>,
Bounds const&
>
)
{
return { as_parser(std::forward<Subject>(subject)), bounds };
}
Bounds bounds;
};
template <std::integral T>
static constexpr repeat_gen_impl<exact_count<T>>
operator()(T const exact) noexcept
{
return { exact_count<T>{exact} };
}
template <std::integral T>
static constexpr repeat_gen_impl<finite_count<T>>
operator()(T const min_val, T const max_val) noexcept
{
return { finite_count<T>{min_val, max_val} };
}
template <std::integral T>
static constexpr repeat_gen_impl<infinite_count<T>>
operator()(T const min_val, repeat_inf_type const&) noexcept
{
return { infinite_count<T>{min_val} };
}
}; };
} // detail
template <typename T> inline namespace cpos
constexpr repeat_gen_lvl1<detail::exact_count<T>> {
operator()(T const exact) const inline constexpr detail::repeat_gen repeat{};
{ }
return { detail::exact_count<T>{exact} };
}
template <typename T> } // boost::spirit::x3
constexpr repeat_gen_lvl1<detail::finite_count<T>>
operator()(T const min_val, T const max_val) const
{
return { detail::finite_count<T>{min_val,max_val} };
}
template <typename T> namespace boost::spirit::x3::traits
constexpr repeat_gen_lvl1<detail::infinite_count<T>>
operator()(T const min_val, inf_type const &) const
{
return { detail::infinite_count<T>{min_val} };
}
};
constexpr auto repeat = repeat_gen{};
}}}
namespace boost { namespace spirit { namespace x3 { namespace traits
{ {
template <typename Subject, typename RepeatCountLimit, typename Context> template <typename Subject, typename Bounds, typename Context>
struct attribute_of<x3::repeat_directive<Subject,RepeatCountLimit>, Context> struct attribute_of<x3::repeat_directive<Subject, Bounds>, Context>
: build_container<typename attribute_of<Subject, Context>::type> {}; : build_container<attribute_of_t<Subject, Context>>
}}}} {};
} // boost::spirit::x3::traits
#endif #endif

View File

@@ -1,33 +1,27 @@
/*============================================================================= /*=============================================================================
Copyright (c) 2001-2011 Joel de Guzman Copyright (c) 2001-2011 Joel de Guzman
Copyright (c) 2025 Nana Sakisaka
Distributed under the Boost Software License, Version 1.0. (See accompanying 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) file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/ =============================================================================*/
#include <string>
#include <vector>
#include <boost/utility/enable_if.hpp>
#if defined(__GNUC__) && (__GNUC__ >= 8)
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92539
# pragma GCC diagnostic ignored "-Warray-bounds"
#endif
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <iostream>
#include "test.hpp" #include "test.hpp"
#include "utils.hpp" #include "utils.hpp"
int #include <boost/spirit/home/x3.hpp>
main()
#include <vector>
#include <string>
#include <iostream>
int main()
{ {
using spirit_test::test_attr; using spirit_test::test_attr;
using spirit_test::test; 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::repeat;
using boost::spirit::x3::inf; using boost::spirit::x3::repeat_inf;
using boost::spirit::x3::omit; using boost::spirit::x3::omit;
using boost::spirit::x3::int_; using boost::spirit::x3::int_;
using boost::spirit::x3::lexeme; using boost::spirit::x3::lexeme;
@@ -36,7 +30,7 @@ main()
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(repeat['x']); BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(repeat['x']);
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(repeat(3)['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, 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 BOOST_TEST(test("aaaaaaaa", repeat[char_])); // kleene synonym
@@ -47,10 +41,10 @@ main()
BOOST_TEST(!test("aaaaaa", repeat(3, 5)[char_])); BOOST_TEST(!test("aaaaaa", repeat(3, 5)[char_]));
BOOST_TEST(!test("aa", repeat(3, 5)[char_])); BOOST_TEST(!test("aa", repeat(3, 5)[char_]));
BOOST_TEST(test("aaa", repeat(3, inf)[char_])); BOOST_TEST(test("aaa", repeat(3, repeat_inf)[char_]));
BOOST_TEST(test("aaaaa", repeat(3, inf)[char_])); BOOST_TEST(test("aaaaa", repeat(3, repeat_inf)[char_]));
BOOST_TEST(test("aaaaaa", repeat(3, inf)[char_])); BOOST_TEST(test("aaaaaa", repeat(3, repeat_inf)[char_]));
BOOST_TEST(!test("aa", repeat(3, inf)[char_])); BOOST_TEST(!test("aa", repeat(3, repeat_inf)[char_]));
} }
{ {
std::string s; std::string s;
@@ -76,18 +70,18 @@ main()
BOOST_TEST(!test("a", repeat(1, 3)[char_ >> char_])); BOOST_TEST(!test("a", repeat(1, 3)[char_ >> char_]));
s.clear(); 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"); BOOST_TEST(s == "aaaa");
s.clear(); 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(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 { // from classic spirit tests
BOOST_TEST(test("", repeat(0, inf)['x'])); BOOST_TEST(test("", repeat(0, repeat_inf)['x']));
// repeat exact 8 // repeat exact 8
#define rep8 repeat(8)[alpha] >> 'X' #define rep8 repeat(8)[alpha] >> 'X'
@@ -105,7 +99,7 @@ main()
BOOST_TEST(!test("a*", rep28, false)); BOOST_TEST(!test("a*", rep28, false));
// repeat 2 or more // 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("abcdefg+", rep2_));
BOOST_TEST(test("abcdefgh+", rep2_)); BOOST_TEST(test("abcdefgh+", rep2_));
BOOST_TEST(test("abcdefghi+", rep2_)); BOOST_TEST(test("abcdefghi+", rep2_));

View File

@@ -1,5 +1,6 @@
/*============================================================================= /*=============================================================================
Copyright (c) 2019 Nikita Kniazev Copyright (c) 2019 Nikita Kniazev
Copyright (c) 2025 Nana Sakisaka
Use, modification and distribution is subject to the Boost Software Use, modification and distribution is subject to the Boost Software
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 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 #define BOOST_SPIRIT_TEST_X3_UTILS_HPP
#include <boost/spirit/home/x3/core/parser.hpp> #include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/support/traits/move_to.hpp>
#include <iterator>
struct move_only struct move_only
{ {
@@ -17,19 +21,19 @@ struct move_only
move_only& operator=(move_only&&) = default; move_only& operator=(move_only&&) = default;
}; };
template <typename T> template <typename T>
struct synth_parser : boost::spirit::x3::parser<synth_parser<T>> struct synth_parser : boost::spirit::x3::parser<synth_parser<T>>
{ {
typedef T attribute_type; using attribute_type = T;
static bool const has_attribute = true; static constexpr bool has_attribute = true;
static bool const handles_container = false; static constexpr bool handles_container = false;
template <typename Iterator, typename Context, template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext, typename Attribute>
typename RuleContext, typename Attribute> [[nodiscard]] constexpr bool parse(
bool parse(Iterator& iter, Iterator const& last, Context const&, It& iter, Se const& last, Context const&,
RuleContext&, Attribute& attr) const RContext&, Attribute& attr
) const
{ {
if (iter != last && *iter == 's') { if (iter != last && *iter == 's') {
++iter; ++iter;
@@ -41,8 +45,8 @@ struct synth_parser : boost::spirit::x3::parser<synth_parser<T>>
}; };
template <typename T> template <typename T>
synth_parser<T> synth{}; constexpr synth_parser<T> synth{};
synth_parser<move_only> const synth_move_only{}; constexpr synth_parser<move_only> synth_move_only{};
#endif #endif