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

Merge pull request #827 from saki7/modernize-skip

Modernize `x3::skip`

Use concepts in `x3::skip`.
Make `x3::skip` a CPO that inhibits ADL.
`x3::reskip[...]`: new API that has the same effect as `x3::skip[...]`.
`x3::skip[...]`: deprecated in favor of `x3::reskip[...]`.
This commit is contained in:
Nana Sakisaka
2025-09-11 15:50:52 +09:00
committed by GitHub
2 changed files with 160 additions and 77 deletions

View File

@@ -1,7 +1,7 @@
/*============================================================================= /*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2013 Agustin Berge Copyright (c) 2013 Agustin Berge
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)
@@ -14,67 +14,94 @@
#include <boost/spirit/home/x3/support/expectation.hpp> #include <boost/spirit/home/x3/support/expectation.hpp>
#include <boost/spirit/home/x3/core/skip_over.hpp> #include <boost/spirit/home/x3/core/skip_over.hpp>
#include <boost/spirit/home/x3/core/parser.hpp> #include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/utility/enable_if.hpp>
namespace boost { namespace spirit { namespace x3 #include <iterator>
#include <type_traits>
#include <utility>
namespace boost::spirit::x3
{ {
template <typename Subject> template <typename Subject>
struct reskip_directive : unary_parser<Subject, reskip_directive<Subject>> struct reskip_directive : unary_parser<Subject, reskip_directive<Subject>>
{ {
typedef unary_parser<Subject, reskip_directive<Subject>> base_type; using base_type = unary_parser<Subject, reskip_directive<Subject>>;
static bool const is_pass_through_unary = true; static constexpr bool is_pass_through_unary = true;
static bool const handles_container = Subject::handles_container; static constexpr bool handles_container = Subject::handles_container;
constexpr reskip_directive(Subject const& subject) template <typename SubjectT>
: base_type(subject) {} requires
(!std::is_same_v<std::remove_cvref_t<SubjectT>, reskip_directive>) &&
std::is_constructible_v<Subject, SubjectT>
constexpr reskip_directive(SubjectT&& subject)
noexcept(std::is_nothrow_constructible_v<Subject, SubjectT>)
: base_type(std::forward<SubjectT>(subject))
{}
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> requires has_skipper_v<Context>
typename disable_if<has_skipper<Context>, bool>::type [[nodiscard]] constexpr bool
parse(Iterator& first, Iterator const& last parse(It& first, Se const& last, Context const& context, RContext& rcontext, Attribute& attr) const
, Context& context, RContext& rcontext, Attribute& attr) const noexcept(is_nothrow_parsable_v<Subject, It, Se, Context, RContext, Attribute>)
{ {
auto const& skipper = static_assert(Parsable<Subject, It, Se, Context, RContext, Attribute>);
detail::get_unused_skipper(x3::get<skipper_tag>(context)); return this->subject.parse(first, last, context, rcontext, attr);
}
auto const local_ctx = make_context<skipper_tag>(skipper, context); private:
template <typename Context>
using context_t = x3::context<
skipper_tag,
decltype(detail::get_unused_skipper(x3::get<skipper_tag>(std::declval<Context const&>()))),
Context
>;
public:
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext, typename Attribute>
requires (!has_skipper_v<Context>)
[[nodiscard]] constexpr bool
parse(It& first, Se const& last, Context const& context, RContext& rcontext, Attribute& attr) const
// never noexcept (requires expectation failure modification)
{
static_assert(Parsable<Subject, It, Se, context_t<Context>, RContext, Attribute>);
auto const& skipper = detail::get_unused_skipper(x3::get<skipper_tag>(context));
auto const local_ctx = x3::make_context<skipper_tag>(skipper, context);
bool const r = this->subject.parse(first, last, local_ctx, rcontext, attr); bool const r = this->subject.parse(first, last, local_ctx, rcontext, attr);
#if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE #if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
if (has_expectation_failure(local_ctx)) if (x3::has_expectation_failure(local_ctx))
{ {
set_expectation_failure(get_expectation_failure(local_ctx), context); x3::set_expectation_failure(x3::get_expectation_failure(local_ctx), context);
} }
#endif #endif
return r; return r;
} }
template <typename Iterator, typename Context
, typename RContext, typename Attribute>
typename enable_if<has_skipper<Context>, bool>::type
parse(Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr) const
{
return this->subject.parse(first, last, context, rcontext, attr);
}
}; };
template <typename Subject, typename Skipper> template <typename Subject, typename Skipper>
struct skip_directive : unary_parser<Subject, skip_directive<Subject, Skipper>> struct skip_directive : unary_parser<Subject, skip_directive<Subject, Skipper>>
{ {
typedef unary_parser<Subject, skip_directive<Subject, Skipper>> base_type; using base_type = unary_parser<Subject, skip_directive<Subject, Skipper>>;
static bool const is_pass_through_unary = true; static constexpr bool is_pass_through_unary = true;
static bool const handles_container = Subject::handles_container; static constexpr bool handles_container = Subject::handles_container;
constexpr skip_directive(Subject const& subject, Skipper const& skipper) template <typename SubjectT, typename SkipperT>
: base_type(subject) requires std::is_constructible_v<Subject, SubjectT> && std::is_constructible_v<Skipper, SkipperT>
, skipper(skipper) constexpr skip_directive(SubjectT&& subject, SkipperT&& skipper)
noexcept(std::is_nothrow_constructible_v<Subject, SubjectT> && std::is_nothrow_constructible_v<Skipper, SkipperT>)
: base_type(std::forward<SubjectT>(subject))
, skipper_(skipper)
{} {}
template <typename Iterator, typename RContext, typename Attribute> template <std::forward_iterator It, std::sentinel_for<It> Se, typename RContext, typename Attribute>
bool parse(Iterator& first, Iterator const& last [[nodiscard]] constexpr bool
, unused_type const&, RContext& rcontext, Attribute& attr) const parse(It& first, Se const& last, unused_type const&, RContext& rcontext, Attribute& attr) const
noexcept(is_nothrow_parsable_v<Subject, It, Se, x3::context<skipper_tag, Skipper>, RContext, Attribute>)
{ {
static_assert(Parsable<Subject, It, Se, x3::context<skipper_tag, Skipper>, RContext, Attribute>);
// It is perfectly fine to omit the expectation_failure context // It is perfectly fine to omit the expectation_failure context
// even in non-throwing mode if and only if the skipper itself // even in non-throwing mode if and only if the skipper itself
// is expectation-less. // is expectation-less.
@@ -94,73 +121,126 @@ namespace boost { namespace spirit { namespace x3
// Anyways, we don't need to repack the expectation context // Anyways, we don't need to repack the expectation context
// into our brand new skipper context, in contrast to the // into our brand new skipper context, in contrast to the
// repacking process done in `x3::skip_over`. // repacking process done in `x3::skip_over`.
return this->subject.parse(first, last, return this->subject.parse(
make_context<skipper_tag>(skipper), rcontext, attr); first, last, x3::make_context<skipper_tag>(skipper_), rcontext, attr
);
} }
template <typename Iterator, typename Context, typename RContext, typename Attribute> template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext, typename Attribute>
bool parse(Iterator& first, Iterator const& last [[nodiscard]] constexpr bool
, Context const& context, RContext& rcontext, Attribute& attr) const parse(It& first, Se const& last, Context const& context, RContext& rcontext, Attribute& attr) const
// never noexcept (requires expectation failure modification)
{ {
static_assert(Parsable<Subject, It, Se, x3::context<skipper_tag, Context>, RContext, Attribute>);
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE #if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
return this->subject.parse(first, last, make_context<skipper_tag>(skipper, context), rcontext, attr); return this->subject.parse(first, last, x3::make_context<skipper_tag>(skipper_, context), rcontext, attr);
#else #else
static_assert( static_assert(
!std::is_same_v<expectation_failure_t<Context>, unused_type>, !std::is_same_v<expectation_failure_t<Context>, unused_type>,
"Context type was not specified for x3::expectation_failure_tag. " "Context type was not specified for x3::expectation_failure_tag. "
"You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. " "You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
"Note that you must also bind the context to your skipper."); "Note that you must also bind the context to your skipper."
);
// This logic is heavily related to the instantiation chain; // This logic is heavily related to the instantiation chain;
// see `x3::skip_over` for details. // see `x3::skip_over` for details.
auto const local_ctx = make_context<skipper_tag>(skipper, context); auto const local_ctx = x3::make_context<skipper_tag>(skipper_, context);
bool const r = this->subject.parse(first, last, local_ctx, rcontext, attr); bool const r = this->subject.parse(first, last, local_ctx, rcontext, attr);
if (has_expectation_failure(local_ctx)) if (x3::has_expectation_failure(local_ctx))
{ {
set_expectation_failure(get_expectation_failure(local_ctx), context); x3::set_expectation_failure(x3::get_expectation_failure(local_ctx), context);
} }
return r; return r;
#endif #endif
} }
Skipper const skipper; private:
Skipper skipper_;
}; };
struct reskip_gen namespace detail
{ {
template <typename Skipper> template <X3Subject Skipper>
struct skip_gen struct skip_gen_impl
{ {
constexpr skip_gen(Skipper const& skipper) // Unreference rvalue reference, but hold lvalue reference as-is
: skipper_(skipper) {} using skipper_type = std::conditional_t<
std::is_rvalue_reference_v<Skipper>,
std::remove_reference_t<Skipper>,
Skipper
>;
template <typename Subject> template <typename SkipperT>
constexpr skip_directive<typename extension::as_parser<Subject>::value_type, Skipper> requires std::is_same_v<std::remove_cvref_t<SkipperT>, std::remove_cvref_t<Skipper>>
operator[](Subject const& subject) const constexpr skip_gen_impl(SkipperT&& skipper)
noexcept(std::is_nothrow_constructible_v<skipper_type, SkipperT>)
: skipper_(std::forward<SkipperT>(skipper))
{}
template <X3Subject Subject>
[[nodiscard]] constexpr skip_directive<as_parser_plain_t<Subject>, std::remove_cvref_t<Skipper>>
operator[](Subject&& subject) const
noexcept(
is_parser_nothrow_castable_v<Subject> &&
std::is_nothrow_constructible_v<
skip_directive<as_parser_plain_t<Subject>, std::remove_cvref_t<Skipper>>,
as_parser_t<Subject>,
skipper_type const&
>
)
{ {
return { as_parser(subject), skipper_ }; return { as_parser(std::forward<Subject>(subject)), skipper_ };
} }
Skipper skipper_; private:
skipper_type skipper_;
}; };
template <typename Skipper> struct skip_gen
constexpr skip_gen<Skipper> const operator()(Skipper const& skipper) const
{ {
return { skipper }; template <X3Subject Skipper>
} [[nodiscard]]
static constexpr skip_gen_impl<as_parser_t<Skipper>>
operator()(Skipper&& skipper)
noexcept(
is_parser_nothrow_castable_v<Skipper> &&
std::is_nothrow_constructible_v<skip_gen_impl<as_parser_t<Skipper>>, as_parser_t<Skipper>>
)
{
return { as_parser(std::forward<Skipper>(skipper)) };
}
template <typename Subject> template <typename Subject>
constexpr reskip_directive<typename extension::as_parser<Subject>::value_type> [[nodiscard, deprecated("Use `x3::reskip[p]`.")]]
operator[](Subject const& subject) const /* static */ constexpr reskip_directive<as_parser_plain_t<Subject>>
operator[](Subject&& subject) const // MSVC 2022 bug: cannot define `static operator[]` even in C++26 mode
noexcept(is_parser_nothrow_constructible_v<reskip_directive<as_parser_plain_t<Subject>>, Subject>)
{
return { as_parser(std::forward<Subject>(subject)) };
}
};
struct reskip_gen
{ {
return { as_parser(subject) }; template <X3Subject Subject>
} [[nodiscard]]
}; /* static */ constexpr reskip_directive<as_parser_plain_t<Subject>>
operator[](Subject&& subject) const // MSVC 2022 bug: cannot define `static operator[]` even in C++26 mode
noexcept(is_parser_nothrow_constructible_v<reskip_directive<as_parser_plain_t<Subject>>, Subject>)
{
return { as_parser(std::forward<Subject>(subject)) };
}
};
} // detail
constexpr auto skip = reskip_gen{}; inline namespace cpos
}}} {
inline constexpr detail::skip_gen skip{};
inline constexpr detail::reskip_gen reskip{};
}
} // boost::spirit::x3
#endif #endif

View File

@@ -1,24 +1,25 @@
/*============================================================================= /*=============================================================================
Copyright (c) 2001-2015 Joel de Guzman Copyright (c) 2001-2015 Joel de Guzman
Copyright (c) 2013 Agustin Berge Copyright (c) 2013 Agustin Berge
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 "test.hpp"
#include <boost/spirit/home/x3.hpp> #include <boost/spirit/home/x3.hpp>
#include <iostream> #include <iostream>
#include "test.hpp"
int int main()
main()
{ {
using spirit_test::test; using spirit_test::test;
using spirit_test::test_attr; using spirit_test::test_attr;
using boost::spirit::x3::ascii::space; using boost::spirit::x3::standard::space;
using boost::spirit::x3::ascii::space_type; using boost::spirit::x3::standard::space_type;
using boost::spirit::x3::ascii::char_; using boost::spirit::x3::standard::char_;
using boost::spirit::x3::ascii::alpha; using boost::spirit::x3::standard::alpha;
using boost::spirit::x3::lexeme; using boost::spirit::x3::lexeme;
using boost::spirit::x3::skip; using boost::spirit::x3::skip;
using boost::spirit::x3::lit; using boost::spirit::x3::lit;
@@ -29,13 +30,15 @@ main()
BOOST_TEST((test("a b c d", skip(space)[*char_]))); BOOST_TEST((test("a b c d", skip(space)[*char_])));
} }
{ // test attribute {
// test attribute
std::string s; std::string s;
BOOST_TEST((test_attr("a b c d", skip(space)[*char_], s))); BOOST_TEST((test_attr("a b c d", skip(space)[*char_], s)));
BOOST_TEST(s == "abcd"); BOOST_TEST(s == "abcd");
} }
{ // reskip {
// reskip
BOOST_TEST((test("ab c d", lexeme[lit('a') >> 'b' >> skip[lit('c') >> 'd']], space))); BOOST_TEST((test("ab c d", lexeme[lit('a') >> 'b' >> skip[lit('c') >> 'd']], space)));
BOOST_TEST((test("abcd", lexeme[lit('a') >> 'b' >> skip[lit('c') >> 'd']], space))); BOOST_TEST((test("abcd", lexeme[lit('a') >> 'b' >> skip[lit('c') >> 'd']], space)));
BOOST_TEST(!(test("a bcd", lexeme[lit('a') >> 'b' >> skip[lit('c') >> 'd']], space))); BOOST_TEST(!(test("a bcd", lexeme[lit('a') >> 'b' >> skip[lit('c') >> 'd']], space)));