mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
Merge pull request #821 from saki7/modernize-lexeme
Modernize `x3::lexeme` and `x3::skip_over` Use concepts in `x3::lexeme` and `x3::skip_over`. `x3::lexeme` is now a CPO that inhibits ADL.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
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)
|
||||
@@ -13,61 +13,86 @@
|
||||
#include <boost/spirit/home/x3/support/unused.hpp>
|
||||
#include <boost/spirit/home/x3/support/context.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_category.hpp>
|
||||
#include <boost/mpl/bool.hpp>
|
||||
#include <boost/mpl/not.hpp>
|
||||
#include <boost/type_traits/remove_cv.hpp>
|
||||
#include <boost/type_traits/remove_reference.hpp>
|
||||
#include <boost/utility/declval.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost::spirit::x3
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Tag used to find the skipper from the context
|
||||
struct skipper_tag;
|
||||
|
||||
// Move the /first/ iterator to the first non-matching position
|
||||
// given a skip-parser. The function is a no-op if unused_type or
|
||||
// unused_skipper is passed as the skip-parser.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
template <typename Skipper>
|
||||
struct unused_skipper : unused_type
|
||||
template <X3Subject Skipper>
|
||||
struct [[nodiscard]] unused_skipper
|
||||
{
|
||||
unused_skipper(Skipper const& skipper)
|
||||
: skipper(skipper) {}
|
||||
constexpr unused_skipper(Skipper const& skipper) noexcept
|
||||
: skipper(skipper)
|
||||
{}
|
||||
|
||||
Skipper const& skipper;
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
using unused_skipper_t = unused_skipper<std::remove_cvref_t<decltype(x3::get<skipper_tag>(std::declval<Context>()))>>;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename Skipper>
|
||||
struct is_unused_skipper
|
||||
: mpl::false_ {};
|
||||
struct is_unused_skipper : std::false_type
|
||||
{
|
||||
static_assert(X3Subject<Skipper>);
|
||||
};
|
||||
|
||||
template <typename Skipper>
|
||||
struct is_unused_skipper<unused_skipper<Skipper>>
|
||||
: mpl::true_ {};
|
||||
struct is_unused_skipper<unused_skipper<Skipper>> : std::true_type {};
|
||||
|
||||
template <>
|
||||
struct is_unused_skipper<unused_type>
|
||||
: mpl::true_ {};
|
||||
struct is_unused_skipper<unused_type> : std::true_type {};
|
||||
|
||||
template <typename Skipper>
|
||||
inline Skipper const&
|
||||
get_unused_skipper(Skipper const& skipper)
|
||||
[[nodiscard]] constexpr Skipper const&
|
||||
get_unused_skipper(Skipper const& skipper) noexcept
|
||||
{
|
||||
static_assert(X3Subject<Skipper>);
|
||||
return skipper;
|
||||
}
|
||||
|
||||
template <typename Skipper>
|
||||
inline Skipper const&
|
||||
get_unused_skipper(unused_skipper<Skipper> const& unused_skipper)
|
||||
[[nodiscard]] constexpr Skipper const&
|
||||
get_unused_skipper(unused_skipper<Skipper> const& unused_skipper) noexcept
|
||||
{
|
||||
static_assert(X3Subject<Skipper>);
|
||||
return unused_skipper.skipper;
|
||||
}
|
||||
|
||||
template <typename Iterator, typename Context, typename Skipper>
|
||||
inline void skip_over(
|
||||
Iterator& first, Iterator const& last, Context& context, Skipper const& skipper)
|
||||
template <typename Context>
|
||||
struct skip_over_context
|
||||
{
|
||||
using type = unused_type;
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
requires (!std::is_same_v<expectation_failure_t<Context>, unused_type>)
|
||||
struct skip_over_context<Context>
|
||||
{
|
||||
using type = std::remove_cvref_t<decltype(x3::make_context<expectation_failure_tag>(
|
||||
x3::get<expectation_failure_tag>(std::declval<Context const&>())
|
||||
))>;
|
||||
};
|
||||
|
||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, X3Subject Skipper>
|
||||
constexpr void skip_over(
|
||||
It& first, Se const& last, Context& context, Skipper const& skipper
|
||||
)
|
||||
noexcept(is_nothrow_parsable_v<Skipper, It, Se, typename skip_over_context<Context>::type, unused_type, unused_type>)
|
||||
{
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
boost::ignore_unused(context);
|
||||
(void)context;
|
||||
while (skipper.parse(first, last, unused, unused, unused))
|
||||
/* loop */;
|
||||
#else
|
||||
@@ -117,7 +142,7 @@ namespace boost { namespace spirit { namespace x3
|
||||
// For this reason we're going to cherry-pick the reference
|
||||
// and repack it into a brand new context.
|
||||
|
||||
auto const local_ctx = make_context<expectation_failure_tag>(
|
||||
auto const local_ctx = x3::make_context<expectation_failure_tag>(
|
||||
x3::get<expectation_failure_tag>(context));
|
||||
|
||||
while (skipper.parse(first, last, local_ctx, unused, unused))
|
||||
@@ -126,35 +151,33 @@ namespace boost { namespace spirit { namespace x3
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Iterator, typename Context>
|
||||
inline void skip_over(Iterator&, Iterator const&, Context&, unused_type)
|
||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context>
|
||||
constexpr void skip_over(It&, Se const&, Context&, unused_type) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Iterator, typename Context, typename Skipper>
|
||||
inline void skip_over(
|
||||
Iterator&, Iterator const&, Context&, unused_skipper<Skipper> const&)
|
||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename Skipper>
|
||||
constexpr void skip_over(It&, Se const&, Context&, unused_skipper<Skipper> const&) noexcept
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// this tag is used to find the skipper from the context
|
||||
struct skipper_tag;
|
||||
|
||||
template <typename Context>
|
||||
struct has_skipper
|
||||
: mpl::not_<detail::is_unused_skipper<
|
||||
typename remove_cv<typename remove_reference<
|
||||
decltype(x3::get<skipper_tag>(boost::declval<Context>()))
|
||||
>::type>::type
|
||||
>> {};
|
||||
: std::bool_constant<!detail::is_unused_skipper<
|
||||
std::remove_cvref_t<decltype(x3::get<skipper_tag>(std::declval<Context>()))>
|
||||
>::value>
|
||||
{};
|
||||
|
||||
template <typename Iterator, typename Context>
|
||||
inline void skip_over(
|
||||
Iterator& first, Iterator const& last, Context& context)
|
||||
template <typename Context>
|
||||
constexpr bool has_skipper_v = has_skipper<Context>::value;
|
||||
|
||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context>
|
||||
constexpr void skip_over(It& first, Se const& last, Context& context)
|
||||
noexcept(noexcept(detail::skip_over(first, last, context, x3::get<skipper_tag>(context))))
|
||||
{
|
||||
detail::skip_over(first, last, context, x3::get<skipper_tag>(context));
|
||||
}
|
||||
}}}
|
||||
} // boost::spirit::x3
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 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)
|
||||
@@ -11,68 +12,85 @@
|
||||
#include <boost/spirit/home/x3/support/unused.hpp>
|
||||
#include <boost/spirit/home/x3/core/skip_over.hpp>
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/type_traits/remove_reference.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>
|
||||
struct lexeme_directive : unary_parser<Subject, lexeme_directive<Subject>>
|
||||
{
|
||||
typedef unary_parser<Subject, lexeme_directive<Subject> > base_type;
|
||||
static bool const is_pass_through_unary = true;
|
||||
static bool const handles_container = Subject::handles_container;
|
||||
using base_type = unary_parser<Subject, lexeme_directive<Subject>>;
|
||||
static constexpr bool is_pass_through_unary = true;
|
||||
static constexpr bool handles_container = Subject::handles_container;
|
||||
|
||||
constexpr lexeme_directive(Subject const& subject)
|
||||
: base_type(subject) {}
|
||||
template <typename SubjectT>
|
||||
requires std::is_constructible_v<Subject, SubjectT>
|
||||
constexpr lexeme_directive(SubjectT&& subject)
|
||||
noexcept(std::is_nothrow_constructible_v<base_type, SubjectT>)
|
||||
: base_type(std::forward<SubjectT>(subject))
|
||||
{}
|
||||
|
||||
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
|
||||
template <typename Context>
|
||||
using pre_skip_context_t = std::remove_cvref_t<decltype(
|
||||
x3::make_context<skipper_tag>(std::declval<unused_skipper_t<Context>&>(), std::declval<Context const&>())
|
||||
)>;
|
||||
|
||||
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
|
||||
noexcept(
|
||||
noexcept(x3::skip_over(first, last, context)) &&
|
||||
is_nothrow_parsable_v<Subject, It, Se, pre_skip_context_t<Context>, RContext, Attribute>
|
||||
)
|
||||
{
|
||||
x3::skip_over(first, last, context);
|
||||
auto const& skipper = x3::get<skipper_tag>(context);
|
||||
|
||||
typedef unused_skipper<
|
||||
typename remove_reference<decltype(skipper)>::type>
|
||||
unused_skipper_type;
|
||||
unused_skipper_type unused_skipper(skipper);
|
||||
auto const& skipper = x3::get<skipper_tag>(context);
|
||||
unused_skipper_t<Context> unused_skipper(skipper);
|
||||
|
||||
return this->subject.parse(
|
||||
first, last
|
||||
, make_context<skipper_tag>(unused_skipper, context)
|
||||
, rcontext
|
||||
, attr);
|
||||
first, last,
|
||||
x3::make_context<skipper_tag>(unused_skipper, context),
|
||||
rcontext,
|
||||
attr
|
||||
);
|
||||
}
|
||||
|
||||
template <typename Iterator, typename Context
|
||||
, typename RContext, typename Attribute>
|
||||
typename disable_if<has_skipper<Context>, bool>::type
|
||||
parse(Iterator& first, Iterator const& last
|
||||
, Context const& context, RContext& rcontext, Attribute& attr) const
|
||||
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
|
||||
noexcept(is_nothrow_parsable_v<Subject, It, Se, Context, RContext, Attribute>)
|
||||
{
|
||||
// no need to pre-skip if skipper is unused
|
||||
return this->subject.parse(
|
||||
first, last
|
||||
, context
|
||||
, rcontext
|
||||
, attr);
|
||||
return this->subject.parse(first, last, context, rcontext, attr);
|
||||
}
|
||||
};
|
||||
|
||||
struct lexeme_gen
|
||||
namespace detail
|
||||
{
|
||||
template <typename Subject>
|
||||
constexpr lexeme_directive<typename extension::as_parser<Subject>::value_type>
|
||||
operator[](Subject const& subject) const
|
||||
struct lexeme_gen
|
||||
{
|
||||
return { as_parser(subject) };
|
||||
}
|
||||
};
|
||||
template <X3Subject Subject>
|
||||
[[nodiscard]] constexpr lexeme_directive<as_parser_plain_t<Subject>>
|
||||
operator[](Subject&& subject) const
|
||||
noexcept(is_parser_nothrow_constructible_v<lexeme_directive<as_parser_plain_t<Subject>>, Subject>)
|
||||
{
|
||||
return { as_parser(std::forward<Subject>(subject)) };
|
||||
}
|
||||
};
|
||||
} // detail
|
||||
|
||||
constexpr auto lexeme = lexeme_gen{};
|
||||
}}}
|
||||
inline namespace cpos
|
||||
{
|
||||
inline constexpr detail::lexeme_gen lexeme{};
|
||||
}
|
||||
} // boost::spirit::x3
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2015 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 "test.hpp"
|
||||
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include "test.hpp"
|
||||
|
||||
int
|
||||
main()
|
||||
int main()
|
||||
{
|
||||
using spirit_test::test;
|
||||
using boost::spirit::x3::ascii::space;
|
||||
using boost::spirit::x3::ascii::space_type;
|
||||
using boost::spirit::x3::ascii::digit;
|
||||
using boost::spirit::x3::standard::space;
|
||||
using boost::spirit::x3::standard::space_type;
|
||||
using boost::spirit::x3::standard::digit;
|
||||
using boost::spirit::x3::lexeme;
|
||||
using boost::spirit::x3::rule;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user