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

Revert "Merge pull request #813 from saki7/modernize-rule-parser"

This reverts commit bae393d159, reversing
changes made to c64a9146fc.
This commit is contained in:
Nana Sakisaka
2025-09-13 08:57:05 +09:00
parent 569c52220e
commit e4d3fce09f
21 changed files with 738 additions and 982 deletions

View File

@@ -1,6 +1,5 @@
[/==============================================================================
Copyright (C) 2001-2018 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)
@@ -129,10 +128,9 @@ our `on_success` handler:
struct annotate_position
{
template <std::forward_iterator It, std::sentinel_for<It> Se, typename T, typename Context>
void on_success(
It const& first, Se const& last, T& ast, Context const& context
) const
template <typename T, typename Iterator, typename Context>
inline void on_success(Iterator const& first, Iterator const& last
, T& ast, Context const& context)
{
auto& position_cache = x3::get<position_cache_tag>(context).get();
position_cache.annotate(ast, first, last);

View File

@@ -1,6 +1,6 @@
[/==============================================================================
Copyright (C) 2001-2018 Joel de Guzman
Copyright (C) 2024-2025 Nana Sakisaka
Copyright (C) 2024 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)
@@ -129,12 +129,10 @@ Here's our `on_error` handler:
struct error_handler
{
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Exception, typename Context>
[[nodiscard]] x3::error_handler_result
on_error(
It const& first, Se const& last,
Exception const& x, Context const& context
)
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(
Iterator& first, Iterator const& last
, Exception const& x, Context const& context)
{
auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
std::string message = "Error! Expecting: " + x3::which(x) + " here:";

View File

@@ -1,7 +1,7 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2017 wanghan02
Copyright (c) 2024-2025 Nana Sakisaka
Copyright (c) 2024 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)
@@ -12,79 +12,57 @@
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/support/expectation.hpp>
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/core/error_handler_types.hpp>
#include <iterator>
#include <type_traits>
#include <utility>
namespace boost::spirit::x3
namespace boost { namespace spirit { namespace x3
{
enum class error_handler_result
{
fail
, retry
, accept
, rethrow // see BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE for alternative behaviors
};
template <typename Subject, typename Handler>
struct guard : unary_parser<Subject, guard<Subject, Handler>>
{
static_assert(
!std::is_reference_v<Handler>,
"Reference type is disallowed for `Handler` to prevent dangling reference"
);
typedef unary_parser<Subject, guard<Subject, Handler>> base_type;
static bool const is_pass_through_unary = true;
using base_type = unary_parser<Subject, guard<Subject, Handler>>;
static constexpr bool is_pass_through_unary = true;
constexpr guard(Subject const& subject, Handler handler)
: base_type(subject), handler(handler) {}
Handler handler;
template <typename SubjectT, typename HandlerT>
requires std::is_constructible_v<Subject, SubjectT> && std::is_constructible_v<Handler, HandlerT>
constexpr guard(SubjectT&& subject, HandlerT&& handler)
noexcept(std::is_nothrow_constructible_v<Subject, SubjectT> && std::is_nothrow_constructible_v<Handler, HandlerT>)
: base_type(std::forward<SubjectT>(subject))
, handler(std::forward<HandlerT>(handler))
{}
template <std::forward_iterator It, std::sentinel_for<It> 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 complex implementation details
template <typename Iterator, typename Context
, typename RuleContext, typename Attribute>
bool parse(Iterator& first, Iterator const& last
, Context const& context, RuleContext& rcontext, Attribute& attr) const
{
static_assert(Parsable<Subject, It, Se, Context, RContext, Attribute>);
while (true)
for (;;)
{
It saved_it = first;
Iterator i = first;
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
try
#endif
{
if (this->subject.parse(saved_it, last, context, rcontext, attr))
if (this->subject.parse(i, last, context, rcontext, attr))
{
first = saved_it;
first = i;
return true;
}
}
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
catch (expectation_failure<It> const& x) {
catch (expectation_failure<Iterator> const& x) {
#else
if (x3::has_expectation_failure(context)) {
auto const& x = x3::get_expectation_failure(context);
if (has_expectation_failure(context)) {
auto& x = get_expectation_failure(context);
#endif
static_assert(
std::is_invocable_r_v<
error_handler_result,
Handler const&,
It const&, Se const&,
std::remove_cvref_t<decltype(x)> const&,
Context const&
>,
"x3::guard: `Handler`'s signature is wrong"
);
// X3 developer note: don't forget to sync this implementation with x3::detail::rule_parser
switch (handler(std::as_const(first), std::as_const(last), x, context))
switch (handler(first, last, x, context))
{
case error_handler_result::fail:
x3::clear_expectation_failure(context);
clear_expectation_failure(context);
return false;
case error_handler_result::retry:
@@ -104,7 +82,9 @@ namespace boost::spirit::x3
return false;
}
}
Handler handler;
};
} // boost::spirit::x3
}}}
#endif

View File

@@ -1,28 +0,0 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
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)
=============================================================================*/
#ifndef BOOST_SPIRIT_X3_CORE_ERROR_HANDLER_TYPES_HPP
#define BOOST_SPIRIT_X3_CORE_ERROR_HANDLER_TYPES_HPP
namespace boost::spirit::x3
{
// Enum type used in `on_error`.
enum class error_handler_result
{
fail,
retry,
accept,
rethrow, // see BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE for alternative behaviors
};
// Note for X3 developers:
// You must at least sync the implementation of `rule_parser` and `guard`
// when you modify the semantics of error handlers.
} // boost::spirit::x3
#endif

View File

@@ -0,0 +1,370 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2017 wanghan02
Copyright (c) 2024 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)
==============================================================================*/
#if !defined(BOOST_SPIRIT_X3_DETAIL_RULE_JAN_08_2012_0326PM)
#define BOOST_SPIRIT_X3_DETAIL_RULE_JAN_08_2012_0326PM
#include <boost/core/ignore_unused.hpp>
#include <boost/spirit/home/x3/auxiliary/guard.hpp>
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/core/skip_over.hpp>
#include <boost/spirit/home/x3/support/expectation.hpp>
#include <boost/spirit/home/x3/directive/expect.hpp>
#include <boost/spirit/home/x3/nonterminal/detail/transform_attribute.hpp>
#include <boost/utility/addressof.hpp>
#if defined(BOOST_SPIRIT_X3_DEBUG)
#include <boost/spirit/home/x3/nonterminal/simple_trace.hpp>
#endif
#include <type_traits>
namespace boost { namespace spirit { namespace x3
{
template <typename ID>
struct identity;
template <typename ID, typename Attribute = unused_type, bool force_attribute = false>
struct rule;
struct parse_pass_context_tag;
namespace detail
{
template <typename ID>
struct rule_id {};
// we use this so we can detect if the default parse_rule
// is the being called.
struct default_parse_rule_result
{
default_parse_rule_result(bool r)
: r(r) {}
operator bool() const { return r; }
bool r;
};
}
// default parse_rule implementation
template <typename ID, typename Iterator
, typename Context, typename ActualAttribute>
inline detail::default_parse_rule_result
parse_rule(
detail::rule_id<ID>
, Iterator& first, Iterator const& last
, Context const& context, ActualAttribute& attr);
}}}
namespace boost { namespace spirit { namespace x3 { namespace detail
{
#if defined(BOOST_SPIRIT_X3_DEBUG)
template <typename Iterator, typename Attribute>
struct context_debug
{
context_debug(
char const* rule_name
, Iterator const& first, Iterator const& last
, Attribute const& attr
, bool const& ok_parse //was parse successful?
)
: ok_parse(ok_parse), rule_name(rule_name)
, first(first), last(last)
, attr(attr)
, f(detail::get_simple_trace())
{
f(first, last, attr, pre_parse, rule_name);
}
~context_debug()
{
auto status = ok_parse ? successful_parse : failed_parse ;
f(first, last, attr, status, rule_name);
}
bool const& ok_parse;
char const* rule_name;
Iterator const& first;
Iterator const& last;
Attribute const& attr;
detail::simple_trace_type& f;
};
#endif
template <typename ID, typename Iterator, typename Context, typename Enable = void>
struct has_on_error : mpl::false_ {};
template <typename ID, typename Iterator, typename Context>
struct has_on_error<ID, Iterator, Context,
decltype(void(
std::declval<ID>().on_error(
std::declval<Iterator&>()
, std::declval<Iterator>()
, std::declval<expectation_failure<Iterator>>()
, std::declval<Context>()
)
))
>
: mpl::true_
{};
template <typename ID, typename Iterator, typename Attribute, typename Context, typename Enable = void>
struct has_on_success : mpl::false_ {};
template <typename ID, typename Iterator, typename Attribute, typename Context>
struct has_on_success<ID, Iterator, Context, Attribute,
decltype(void(
std::declval<ID>().on_success(
std::declval<Iterator&>()
, std::declval<Iterator&>()
, std::declval<Attribute&>()
, std::declval<Context>()
)
))
>
: mpl::true_
{};
template <typename ID>
struct make_id
{
typedef identity<ID> type;
};
template <typename ID>
struct make_id<identity<ID>>
{
typedef identity<ID> type;
};
template <typename ID, typename RHS, typename Context>
Context const&
make_rule_context(RHS const& /* rhs */, Context const& context
, mpl::false_ /* is_default_parse_rule */)
{
return context;
}
template <typename ID, typename RHS, typename Context>
auto make_rule_context(RHS const& rhs, Context const& context
, mpl::true_ /* is_default_parse_rule */ )
{
return make_unique_context<ID>(rhs, context);
}
template <typename Attribute, typename ID, bool skip_definition_injection = false>
struct rule_parser
{
template <typename Iterator, typename Context, typename ActualAttribute>
static bool call_on_success(
Iterator& /* before */, Iterator& /* after */
, Context const& /* context */, ActualAttribute& /* attr */
, mpl::false_ /* No on_success handler */ )
{
return true;
}
template <typename Iterator, typename Context, typename ActualAttribute>
static bool call_on_success(
Iterator& before, Iterator& after
, Context const& context, ActualAttribute& attr
, mpl::true_ /* Has on_success handler */)
{
x3::skip_over(before, after, context);
bool pass = true;
ID().on_success(
before
, after
, attr
, make_context<parse_pass_context_tag>(pass, context)
);
return pass;
}
template <typename RHS, typename Iterator, typename Context
, typename RContext, typename ActualAttribute>
static bool parse_rhs_main(
RHS const& rhs
, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, ActualAttribute& attr
, mpl::false_)
{
// see if the user has a BOOST_SPIRIT_DEFINE for this rule
typedef
decltype(parse_rule(
detail::rule_id<ID>{}, first, last
, make_unique_context<ID>(rhs, context), std::declval<Attribute&>()))
parse_rule_result;
// If there is no BOOST_SPIRIT_DEFINE for this rule,
// we'll make a context for this rule tagged by its ID
// so we can extract the rule later on in the default
// (generic) parse_rule function.
typedef
is_same<parse_rule_result, default_parse_rule_result>
is_default_parse_rule;
Iterator start = first;
bool r = rhs.parse(
first
, last
, make_rule_context<ID>(rhs, context, std::conditional_t<skip_definition_injection, mpl::false_, is_default_parse_rule>())
, rcontext
, attr
);
if (r)
{
r = call_on_success(start, first, context, attr
, has_on_success<ID, Iterator, Context, ActualAttribute>());
}
return r;
}
template <typename RHS, typename Iterator, typename Context
, typename RContext, typename ActualAttribute>
static bool parse_rhs_main(
RHS const& rhs
, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, ActualAttribute& attr
, mpl::true_ /* on_error is found */)
{
for (;;)
{
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
try
#endif
{
if (parse_rhs_main(
rhs, first, last, context, rcontext, attr, mpl::false_()))
{
return true;
}
}
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
catch (expectation_failure<Iterator> const& x) {
#else
if (has_expectation_failure(context)) {
auto& x = get_expectation_failure(context);
#endif
// X3 developer note: don't forget to sync this implementation with x3::guard
switch (ID().on_error(first, last, x, context))
{
case error_handler_result::fail:
clear_expectation_failure(context);
return false;
case error_handler_result::retry:
continue;
case error_handler_result::accept:
return true;
case error_handler_result::rethrow:
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
throw;
#else
return false; // TODO: design decision required
#endif
}
}
return false;
}
}
template <typename RHS, typename Iterator
, typename Context, typename RContext, typename ActualAttribute>
static bool parse_rhs_main(
RHS const& rhs
, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, ActualAttribute& attr)
{
return parse_rhs_main(
rhs, first, last, context, rcontext, attr
, has_on_error<ID, Iterator, Context>()
);
}
template <typename RHS, typename Iterator
, typename Context, typename RContext, typename ActualAttribute>
static bool parse_rhs(
RHS const& rhs
, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, ActualAttribute& attr
, mpl::false_)
{
return parse_rhs_main(rhs, first, last, context, rcontext, attr);
}
template <typename RHS, typename Iterator
, typename Context, typename RContext, typename ActualAttribute>
static bool parse_rhs(
RHS const& rhs
, Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, ActualAttribute& /* attr */
, mpl::true_)
{
return parse_rhs_main(rhs, first, last, context, rcontext, unused);
}
template <typename RHS, typename Iterator, typename Context
, typename ActualAttribute, typename ExplicitAttrPropagation>
static bool call_rule_definition(
RHS const& rhs
, char const* rule_name
, Iterator& first, Iterator const& last
, Context const& context, ActualAttribute& attr
, ExplicitAttrPropagation)
{
boost::ignore_unused(rule_name);
// do down-stream transformation, provides attribute for
// rhs parser
typedef traits::transform_attribute<
ActualAttribute, Attribute, parser_id>
transform;
typedef typename transform::type transform_attr;
transform_attr attr_ = transform::pre(attr);
bool ok_parse
//Creates a place to hold the result of parse_rhs
//called inside the following scope.
;
{
// Create a scope to cause the dbg variable below (within
// the #if...#endif) to call it's DTOR before any
// modifications are made to the attribute, attr_ passed
// to parse_rhs (such as might be done in
// transform::post when, for example,
// ActualAttribute is a recursive variant).
#if defined(BOOST_SPIRIT_X3_DEBUG)
context_debug<Iterator, transform_attr>
dbg(rule_name, first, last, attr_, ok_parse);
#endif
ok_parse = parse_rhs(rhs, first, last, context, attr_, attr_
, mpl::bool_
< ( RHS::has_action
&& !ExplicitAttrPropagation::value
)
>()
);
}
if (ok_parse)
{
// do up-stream transformation, this integrates the results
// back into the original attribute value, if appropriate
transform::post(attr, std::forward<transform_attr>(attr_));
}
return ok_parse;
}
};
}}}}
#endif

View File

@@ -1,454 +0,0 @@
/*=============================================================================
Copyright (c) 2001-2014 Joel de Guzman
Copyright (c) 2017 wanghan02
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)
==============================================================================*/
#if !defined(BOOST_SPIRIT_X3_DETAIL_RULE_JAN_08_2012_0326PM)
#define BOOST_SPIRIT_X3_DETAIL_RULE_JAN_08_2012_0326PM
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/core/skip_over.hpp>
#include <boost/spirit/home/x3/core/error_handler_types.hpp>
#include <boost/spirit/home/x3/support/expectation.hpp>
#include <boost/spirit/home/x3/directive/expect.hpp>
#include <boost/spirit/home/x3/nonterminal/detail/transform_attribute.hpp>
#if defined(BOOST_SPIRIT_X3_DEBUG)
#include <boost/spirit/home/x3/nonterminal/simple_trace.hpp>
#endif
#include <iterator>
#include <type_traits>
#include <concepts>
#include <utility>
namespace boost::spirit::x3
{
template <typename ID, typename Attribute = unused_type, bool force_attribute = false>
struct rule;
struct parse_pass_context_tag;
namespace detail
{
template <typename ID>
struct rule_id {};
// Placeholder type to detect whether the default `parse_rule(...)` is called
enum struct default_parse_rule_result : bool {};
template <typename ID, typename Context>
constexpr bool context_has_rule_id = !std::is_same_v<
std::remove_cvref_t<decltype(x3::get<ID>(std::declval<Context const&>()))>,
unused_type
>;
} // detail
// The default `parse_rule` definition.
//
// This overload will only be selected when there exists no user-defined
// definition for `parse_rule`.
//
// When a user invokes `BOOST_SPIRIT_X3_DEFINE_`, an unconstrained overload
// is generated at the user's namespace scope. It will never conflict with
// this (vvvvv) overload, as the generated one is never directly called with
// a context containing `ID`.
template <typename ID, std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename Attribute>
requires detail::context_has_rule_id<ID, Context>
[[nodiscard]] constexpr detail::default_parse_rule_result
parse_rule(
detail::rule_id<ID>,
It& first, Se const& last,
Context const& context, Attribute& attr
)
{
auto&& rule_ = x3::get<ID>(context);
return static_cast<detail::default_parse_rule_result>(
rule_.parse(first, last, context, unused, attr)
);
}
// This overload is selected only when the user *declares* their `parse_rule`
// in the user's namespace scope AND the function definition is not found.
template <typename ID, std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename Attribute>
requires (!detail::context_has_rule_id<ID, Context>)
constexpr void
parse_rule(
detail::rule_id<ID>,
It&, Se const&,
Context const&, Attribute&
) = delete; // BOOST_SPIRIT_X3_DEFINE undefined for this rule
} // boost::spirit::x3
namespace boost::spirit::x3::detail
{
#if defined(BOOST_SPIRIT_X3_DEBUG)
// TODO: This should be customizable by users
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Attribute>
struct scoped_debug
{
scoped_debug(
char const* rule_name,
It const& first, Se const& last,
Attribute const& attr,
bool const& parse_ok
)
: parse_ok(parse_ok)
, rule_name(rule_name)
, first(first)
, last(last)
, attr(attr)
, f(detail::get_simple_trace())
{
f(first, last, attr, pre_parse, rule_name);
}
~scoped_debug()
{
f(first, last, attr, parse_ok ? successful_parse : failed_parse, rule_name);
}
bool const& parse_ok;
char const* rule_name = nullptr;
It const& first;
Se const& last;
Attribute const& attr;
detail::simple_trace_type& f;
};
#endif
template <typename ID, typename It, typename Se, typename Context>
concept HasImmutableOnErrorOverload =
std::forward_iterator<It> &&
std::sentinel_for<Se, It> &&
requires(ID& id) { // Note: `ID` should be non-const
id.on_error(
std::declval<It const&>(),
std::declval<Se const&>(),
std::declval<expectation_failure<It> const&>(),
std::declval<Context const&>()
);
};
template <typename ID, typename It, typename Se, typename Context>
concept HasMutableOnErrorOverload =
std::forward_iterator<It> &&
std::sentinel_for<Se, It> &&
requires(ID& id) { // Note: `ID` should be non-const
id.on_error(
std::declval<It&>(),
std::declval<Se&>(),
std::declval<expectation_failure<It> const&>(),
std::declval<Context const&>()
);
};
template <typename ID, std::forward_iterator It, std::sentinel_for<It> Se, typename Context>
struct has_on_error : std::false_type {};
template <typename ID, std::forward_iterator It, std::sentinel_for<It> Se, typename Context>
requires HasImmutableOnErrorOverload<ID, It, Se, Context>
struct has_on_error<ID, It, Se, Context> : std::true_type
{
// We intentionally make this hard error to prevent error-prone definition
static_assert(
std::convertible_to<
decltype(std::declval<ID&>().on_error(
std::declval<It const&>(),
std::declval<Se const&>(),
std::declval<expectation_failure<It> const&>(),
std::declval<Context const&>()
)),
x3::error_handler_result
>,
"The return type of `on_error` should be convertible to `x3::error_handler_result`."
);
};
template <typename ID, std::forward_iterator It, std::sentinel_for<It> Se, typename Context>
requires
(!HasImmutableOnErrorOverload<ID, It, Se, Context>) &&
HasMutableOnErrorOverload<ID, It, Se, Context>
struct has_on_error<ID, It, Se, Context> : std::false_type
{
// Historically, Spirit has passed mutable lvalue references of the iterators *as-is*
// to the `on_success`/`on_error` handlers. This behavior was simply a mistake, because:
// (1) `on_success`/`on_error` mechanism was designed to be grammar-agnostic, and
// (2) it does not make sense to modify the grammar-specific iterator on the
// grammar-agnostic callback.
//
// Furthermore, any modification to X3's internal iterator variables may invoke
// undefined behavior, since we never provide any kind of guarantee on how the
// intermediate iterator variables are processed in X3's implementation details.
static_assert(
false,
"The `on_error` callback should only accept const reference or passed-by-value iterators."
);
};
template <typename ID, typename It, typename Se, typename Attribute, typename Context>
concept HasImmutableOnSuccessOverload =
std::forward_iterator<It> &&
std::sentinel_for<Se, It> &&
requires(ID& id) { // Note: `ID` should be non-const
id.on_success(
std::declval<It const&>(),
std::declval<Se const&>(),
std::declval<Attribute&>(),
std::declval<Context const&>()
);
};
template <typename ID, typename It, typename Se, typename Attribute, typename Context>
concept HasMutableOnSuccessOverload =
std::forward_iterator<It> &&
std::sentinel_for<Se, It> &&
requires(ID& id) { // Note: `ID` should be non-const
id.on_success(
std::declval<It&>(),
std::declval<Se&>(),
std::declval<Attribute&>(),
std::declval<Context const&>()
);
};
template <typename ID, std::forward_iterator It, std::sentinel_for<It> Se, typename Attribute, typename Context>
struct has_on_success : std::false_type {};
template <typename ID, std::forward_iterator It, std::sentinel_for<It> Se, typename Attribute, typename Context>
requires HasImmutableOnSuccessOverload<ID, It, Se, Attribute, Context>
struct has_on_success<ID, It, Se, Attribute, Context> : std::true_type
{
// We intentionally make this hard error to prevent error-prone definition
static_assert(
std::is_void_v<
decltype(std::declval<ID&>().on_success(
std::declval<It const&>(),
std::declval<Se const&>(),
std::declval<Attribute&>(),
std::declval<Context const&>()
))
>,
"The return type of `on_success` should be `void`."
);
};
template <typename ID, std::forward_iterator It, std::sentinel_for<It> Se, typename Attribute, typename Context>
requires
(!HasImmutableOnSuccessOverload<ID, It, Se, Attribute, Context>) &&
HasMutableOnSuccessOverload<ID, It, Se, Attribute, Context>
struct has_on_success<ID, It, Se, Attribute, Context> : std::false_type
{
// For details, see the comments on `has_on_error`.
static_assert(
false,
"The `on_success` callback should only accept const reference or passed-by-value iterators."
);
};
template <typename Attribute, typename ID, bool SkipDefinitionInjection = false>
struct rule_parser
{
template <
typename RHS, std::forward_iterator It, std::sentinel_for<It> Se,
typename Context, typename RContext, typename ActualAttribute
>
[[nodiscard]] static constexpr bool
parse_rhs(
RHS const& rhs, It& first, Se const& last,
Context const& context, RContext& rcontext, ActualAttribute& attr
) // never noexcept; requires complex handling
{
It start = first;
// See if the user has a BOOST_SPIRIT_DEFINE for this rule
using parse_rule_result_type = decltype(
parse_rule( // ADL
std::declval<detail::rule_id<ID>>(), first, last,
std::declval<decltype(x3::make_unique_context<ID>(rhs, context))>(),
std::declval<Attribute&>()
)
);
constexpr bool is_default_parse_rule = std::is_same_v<
parse_rule_result_type, default_parse_rule_result
>;
bool ok;
if constexpr (SkipDefinitionInjection || !is_default_parse_rule)
{
ok = rhs.parse(first, last, context, rcontext, attr);
}
else
{
// If there is no BOOST_SPIRIT_DEFINE for this rule,
// we'll make a context for this rule tagged by its ID
// so we can extract the rule later on in the default
// parse_rule function.
auto rule_id_context = x3::make_unique_context<ID>(rhs, context);
ok = rhs.parse(first, last, rule_id_context, rcontext, attr);
}
// Note: this check uses `It, It` because the value is actually iterator-iterator pair
if constexpr (has_on_success<ID, It, It, ActualAttribute, Context>::value)
{
if (!ok) return false;
x3::skip_over(start, first, context);
bool pass = true;
ID().on_success(
std::as_const(start), std::as_const(first), attr,
x3::make_context<parse_pass_context_tag>(pass, context)
);
return pass;
}
else
{
return ok;
}
}
template <
typename RHS, std::forward_iterator It, std::sentinel_for<It> Se,
typename Context, typename RContext, typename ActualAttribute
>
[[nodiscard]] static constexpr bool
parse_rhs_with_on_error(
RHS const& rhs, It& first, Se const& last,
Context const& context, RContext& rcontext, ActualAttribute& attr
) // never noexcept; requires complex handling
{
while (true)
{
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
try
#endif
{
if (rule_parser::parse_rhs(rhs, first, last, context, rcontext, attr))
{
return true;
}
}
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
catch (expectation_failure<It> const& x) {
#else
if (x3::has_expectation_failure(context)) {
auto const& x = x3::get_expectation_failure(context);
#endif
// X3 developer note: don't forget to sync this implementation with x3::guard
switch (ID{}.on_error(std::as_const(first), std::as_const(last), x, context))
{
case error_handler_result::fail:
x3::clear_expectation_failure(context);
return false;
case error_handler_result::retry:
continue;
case error_handler_result::accept:
return true;
case error_handler_result::rethrow:
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
throw;
#else
return false; // TODO: design decision required
#endif
}
}
return false;
}
}
template <
bool ForceAttribute,
typename RHS, std::forward_iterator It, std::sentinel_for<It> Se,
typename Context, typename ActualAttribute
>
[[nodiscard]] static constexpr bool
call_rule_definition(
RHS const& rhs, char const* rule_name,
It& first, Se const& last,
Context const& context, ActualAttribute& attr
)
{
// Do down-stream transformation, provides attribute for rhs parser
using transform = traits::transform_attribute<ActualAttribute, Attribute, parser_id>;
using transform_attr = typename transform::type;
transform_attr attr_ = transform::pre(attr);
// Creates a place to hold the result of parse_rhs
// called inside the following scope.
bool parse_ok;
{
#ifdef BOOST_SPIRIT_X3_DEBUG
// Create a scope to cause the dbg variable below (within
// the #if...#endif) to call it's DTOR before any
// modifications are made to the attribute, attr_ passed
// to parse_rhs (such as might be done in
// transform::post when, for example,
// ActualAttribute is a recursive variant).
scoped_debug<It, Se, transform_attr>
dbg(rule_name, first, last, attr_, parse_ok);
#else
(void)rule_name;
#endif
// In theory, these calls can be overloaded using tag dispatches.
// However we should get over those legacy technique already, as they
// lead to enormous amount of call stack, generating unreadable
// compilation errors. Even when we have a single layer of tag dispatch,
// we should generally avoid it because the "true_type/false_type"
// argument placed at the very last param of the overload is virtually
// indistinguishable in messages and has serious QoL issue in debuggers.
constexpr bool rhs_has_on_error = has_on_error<ID, It, Se, Context>::value;
// The existence of semantic action inhibits attribute materialization
// _unless_ it is explicitly required by the user (primarily via `%=`).
if constexpr (RHS::has_action && !ForceAttribute)
{
if constexpr (rhs_has_on_error)
{
parse_ok = rule_parser::parse_rhs_with_on_error(
rhs, first, last, context, attr_ /* rcontext */, unused
);
}
else
{
parse_ok = rule_parser::parse_rhs(
rhs, first, last, context, attr_ /* rcontext */, unused
);
}
}
else // attribute is required
{
if constexpr (rhs_has_on_error)
{
parse_ok = rule_parser::parse_rhs_with_on_error(
rhs, first, last, context, attr_ /* rcontext */, attr_
);
}
else
{
parse_ok = rule_parser::parse_rhs(
rhs, first, last, context, attr_ /* rcontext */, attr_
);
}
}
}
if (parse_ok)
{
// do up-stream transformation, this integrates the results
// back into the original attribute value, if appropriate
transform::post(attr, std::forward<transform_attr>(attr_));
}
return parse_ok;
}
};
} // boost::spirit::x3::detail
#endif

View File

@@ -8,16 +8,13 @@
#if !defined(BOOST_SPIRIT_X3_RULE_JAN_08_2012_0326PM)
#define BOOST_SPIRIT_X3_RULE_JAN_08_2012_0326PM
#include <boost/spirit/home/x3/nonterminal/detail/rule_parser.hpp>
#include <boost/spirit/home/x3/nonterminal/detail/rule.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/preprocessor/variadic/elem.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/config/helper_macros.hpp>
#include <iterator>
#include <type_traits>
#include <utility>
#if !defined(BOOST_SPIRIT_X3_NO_RTTI)
#include <typeinfo>
@@ -25,313 +22,240 @@
namespace boost::spirit::x3
{
template <typename ID, X3Subject RHS, typename Attribute, bool ForceAttribute, bool SkipDefinitionInjection = false>
struct rule_definition : parser<rule_definition<ID, RHS, Attribute, ForceAttribute, SkipDefinitionInjection>>
// default parse_rule implementation
template <typename ID, typename Iterator
, typename Context, typename ActualAttribute>
inline detail::default_parse_rule_result
parse_rule(
detail::rule_id<ID>
, Iterator& first, Iterator const& last
, Context const& context, ActualAttribute& attr)
{
using this_type = rule_definition<ID, RHS, Attribute, ForceAttribute, SkipDefinitionInjection>;
using id = ID;
using rhs_type = RHS;
using lhs_type = rule<ID, Attribute, ForceAttribute>;
using attribute_type = Attribute;
static_assert(!is_same<decltype(x3::get<ID>(context)), unused_type>::value,
"BOOST_SPIRIT_DEFINE undefined for this rule.");
return x3::get<ID>(context).parse(first, last, context, unused, attr);
}
static constexpr bool has_attribute = !std::is_same_v<Attribute, unused_type>;
static constexpr bool handles_container = traits::is_container<Attribute>::value;
static constexpr bool force_attribute = ForceAttribute;
template <typename ID, typename RHS, typename Attribute, bool force_attribute_, bool skip_definition_injection = false>
struct rule_definition : parser<rule_definition<ID, RHS, Attribute, force_attribute_, skip_definition_injection>>
{
typedef rule_definition<ID, RHS, Attribute, force_attribute_, skip_definition_injection> this_type;
typedef ID id;
typedef RHS rhs_type;
typedef rule<ID, Attribute, force_attribute_> lhs_type;
typedef Attribute attribute_type;
template <typename RHS_T>
requires std::is_constructible_v<RHS, RHS_T>
constexpr rule_definition(RHS_T&& rhs, char const* name)
noexcept(std::is_nothrow_constructible_v<RHS, RHS_T>)
: rhs(std::forward<RHS_T>(rhs))
, name(name)
{}
static bool const has_attribute =
!is_same<Attribute, unused_type>::value;
static bool const handles_container =
traits::is_container<Attribute>::value;
static bool const force_attribute =
force_attribute_;
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename Attribute_>
[[nodiscard]] constexpr bool
parse(It& first, Se const& last, Context const& context, unused_type, Attribute_& attr) const
// never noexcept; requires very complex implementation details
constexpr rule_definition(RHS const& rhs, char const* name)
: rhs(rhs), name(name) {}
template <typename Iterator, typename Context, typename Attribute_>
bool parse(Iterator& first, Iterator const& last
, Context const& context, unused_type, Attribute_& attr) const
{
return detail::rule_parser<attribute_type, ID, SkipDefinitionInjection>
::template call_rule_definition<ForceAttribute>(
rhs, name, first, last, context, attr
);
return detail::rule_parser<attribute_type, ID, skip_definition_injection>
::call_rule_definition(
rhs, name, first, last
, context
, attr
, mpl::bool_<force_attribute>());
}
RHS rhs;
char const* name = "unnamed";
char const* name;
};
template <typename ID, typename Attribute, bool ForceAttribute>
struct rule : parser<rule<ID, Attribute, ForceAttribute>>
template <typename ID, typename Attribute, bool force_attribute_>
struct rule : parser<rule<ID, Attribute, force_attribute_>>
{
static_assert(!std::is_reference_v<Attribute>, "Reference qualifier on rule attribute type is meaningless");
static_assert(!std::is_reference<Attribute>::value,
"Reference qualifier on rule attribute type is meaningless");
using id = ID;
using attribute_type = Attribute;
static constexpr bool has_attribute = !std::is_same_v<std::remove_const_t<Attribute>, unused_type>;
static constexpr bool handles_container = traits::is_container<Attribute>::value;
static constexpr bool force_attribute = ForceAttribute;
typedef ID id;
typedef Attribute attribute_type;
static bool const has_attribute =
!std::is_same<std::remove_const_t<Attribute>, unused_type>::value;
static bool const handles_container =
traits::is_container<Attribute>::value;
static bool const force_attribute = force_attribute_;
#if !defined(BOOST_SPIRIT_X3_NO_RTTI)
rule() : name(typeid(rule).name()) {}
#else
constexpr rule() noexcept : name("unnamed") {}
constexpr rule() : name("unnamed") {}
#endif
constexpr rule(char const* name) noexcept
: name(name)
{}
constexpr rule(char const* name)
: name(name) {}
constexpr rule(rule const& r) noexcept
: name(r.name)
constexpr rule(rule const& r)
: name(r.name)
{
// Assert that we are not copying an unitialized static rule. If
// the static is in another TU, it may be initialized after we copy
// it. If so, its name member will be nullptr.
BOOST_ASSERT_MSG(r.name != nullptr, "uninitialized rule"); // static initialization order fiasco
BOOST_ASSERT_MSG(r.name, "uninitialized rule"); // static initialization order fiasco
}
template <X3Subject RHS>
[[nodiscard]] constexpr rule_definition<ID, as_parser_plain_t<RHS>, Attribute, ForceAttribute>
operator=(RHS&& rhs) const&
noexcept(
is_parser_nothrow_castable_v<RHS> &&
std::is_nothrow_constructible_v<
rule_definition<ID, as_parser_plain_t<RHS>, Attribute, ForceAttribute>,
as_parser_t<RHS>, char const*
>
)
template <typename RHS>
constexpr rule_definition<
ID, typename extension::as_parser<RHS>::value_type, Attribute, force_attribute_>
operator=(RHS const& rhs) const&
{
return { as_parser(std::forward<RHS>(rhs)), name };
return { as_parser(rhs), name };
}
template <X3Subject RHS>
[[nodiscard]] constexpr rule_definition<ID, as_parser_plain_t<RHS>, Attribute, true>
operator%=(RHS&& rhs) const&
noexcept(
is_parser_nothrow_castable_v<RHS> &&
std::is_nothrow_constructible_v<
rule_definition<ID, as_parser_plain_t<RHS>, Attribute, true>,
as_parser_t<RHS>, char const*
>
)
template <typename RHS>
constexpr rule_definition<
ID, typename extension::as_parser<RHS>::value_type, Attribute, true>
operator%=(RHS const& rhs) const&
{
return { as_parser(std::forward<RHS>(rhs)), name };
return { as_parser(rhs), name };
}
// When a rule placeholder constructed and immediately consumed it cannot be used recursively,
// that's why the rule definition injection into a parser context can be skipped.
// This optimization has a huge impact on compile times because immediate rules are commonly
// used to cast an attribute like `as`/`attr_cast` does in Qi.
template <X3Subject RHS>
[[nodiscard]] constexpr rule_definition<ID, as_parser_plain_t<RHS>, Attribute, ForceAttribute, true>
operator=(RHS&& rhs) const&&
noexcept(
is_parser_nothrow_castable_v<RHS> &&
std::is_nothrow_constructible_v<
rule_definition<ID, as_parser_plain_t<RHS>, Attribute, ForceAttribute, true>,
as_parser_t<RHS>, char const*
>
)
template <typename RHS>
constexpr rule_definition<
ID, typename extension::as_parser<RHS>::value_type, Attribute, force_attribute_, true>
operator=(RHS const& rhs) const&&
{
return { as_parser(std::forward<RHS>(rhs)), name };
return { as_parser(rhs), name };
}
template <X3Subject RHS>
[[nodiscard]] constexpr rule_definition<ID, as_parser_plain_t<RHS>, Attribute, true, true>
operator%=(RHS&& rhs) const&&
noexcept(
is_parser_nothrow_castable_v<RHS> &&
std::is_nothrow_constructible_v<
rule_definition<ID, as_parser_plain_t<RHS>, Attribute, true, true>,
as_parser_t<RHS>, char const*
>
)
template <typename RHS>
constexpr rule_definition<
ID, typename extension::as_parser<RHS>::value_type, Attribute, true, true>
operator%=(RHS const& rhs) const&&
{
return { as_parser(std::forward<RHS>(rhs)), name };
return { as_parser(rhs), name };
}
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename Exposed>
requires (!std::is_same_v<std::remove_const_t<Exposed>, unused_type>)
[[nodiscard]] constexpr bool
parse(It& first, Se const& last, Context const& context, unused_type, Exposed& exposed_attr) const
// never noexcept; requires very complex implementation details
template <typename Iterator, typename Context, typename Attribute_>
bool parse(Iterator& first, Iterator const& last
, Context const& context, unused_type, Attribute_& attr) const
{
static_assert(has_attribute, "A rule must have an attribute. Check your rule definition.");
static_assert(has_attribute,
"The rule does not have an attribute. Check your parser.");
static_assert(
traits::Transformable<Exposed, Attribute, detail::parser_id>,
"Attribute type mismatch; the rule's attribute is not assignable to "
"the exposed attribute, and no eligible specialization of "
"`x3::traits::transform_attribute` was found."
);
using transform = traits::transform_attribute<
Attribute_, attribute_type, detail::parser_id>;
using transform = traits::transform_attribute<Exposed, Attribute, detail::parser_id>;
using transformed_type = typename transform::type;
transformed_type transformed_attr = transform::pre(exposed_attr);
using transform_attr = typename transform::type;
transform_attr attr_ = transform::pre(attr);
// ADL
if (static_cast<bool>(parse_rule(detail::rule_id<ID>{}, first, last, context, transformed_attr))) {
transform::post(exposed_attr, std::forward<transformed_type>(transformed_attr));
if (parse_rule(detail::rule_id<ID>{}, first, last, context, attr_)) {
transform::post(attr, std::forward<transform_attr>(attr_));
return true;
}
return false;
}
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context>
[[nodiscard]] constexpr bool
parse(It& first, Se const& last, Context const& context, unused_type, unused_type) const
// never noexcept; requires very complex implementation details
template <typename Iterator, typename Context>
bool parse(Iterator& first, Iterator const& last
, Context const& context, unused_type, unused_type) const
{
// make sure we pass exactly the rule attribute type
attribute_type no_attr; // default-initialize
// ADL
return static_cast<bool>(parse_rule(detail::rule_id<ID>{}, first, last, context, no_attr));
attribute_type no_attr{};
return parse_rule(detail::rule_id<ID>{}, first, last, context, no_attr);
}
char const* name = "unnamed";
char const* name;
};
namespace traits
{
template <typename T, typename Enable = void>
struct is_rule : std::false_type {};
struct is_rule : mpl::false_ {};
template <typename T>
constexpr bool is_rule_v = is_rule<T>::value;
template <typename ID, typename Attribute, bool force_attribute>
struct is_rule<rule<ID, Attribute, force_attribute>> : mpl::true_ {};
template <typename ID, typename Attribute, bool ForceAttribute>
struct is_rule<rule<ID, Attribute, ForceAttribute>> : std::true_type {};
template <typename ID, typename Attribute, typename RHS, bool ForceAttribute, bool SkipDefinitionInjection>
struct is_rule<rule_definition<ID, RHS, Attribute, ForceAttribute, SkipDefinitionInjection>> : std::true_type {};
template <typename ID, typename Attribute, typename RHS, bool force_attribute, bool skip_definition_injection>
struct is_rule<rule_definition<ID, RHS, Attribute, force_attribute, skip_definition_injection>> : mpl::true_ {};
}
template <typename T>
requires traits::is_rule_v<T>
struct get_info<T>
struct get_info<T, typename enable_if<traits::is_rule<T>>::type>
{
using result_type = std::string;
[[nodiscard]] std::string operator()(T const& r) const
typedef std::string result_type;
std::string operator()(T const& r) const
{
BOOST_ASSERT_MSG(r.name, "uninitialized rule"); // static initialization order fiasco
return r.name? r.name : "uninitialized";
}
};
// -------------------------------------------------------------
#define BOOST_SPIRIT_DECLARE_(r, data, rule_type) \
template <typename Iterator, typename Context> \
bool parse_rule( \
::boost::spirit::x3::detail::rule_id<rule_type::id> \
, Iterator& first, Iterator const& last \
, Context const& context, rule_type::attribute_type& attr); \
/***/
#define BOOST_SPIRIT_X3_DEPRECATED_MACRO_WARN_I(x) _Pragma(#x)
#define BOOST_SPIRIT_X3_DEPRECATED_MACRO_WARN(msg) BOOST_SPIRIT_X3_DEPRECATED_MACRO_WARN_I(message(msg))
#define BOOST_SPIRIT_DECLARE(...) BOOST_PP_SEQ_FOR_EACH( \
BOOST_SPIRIT_DECLARE_, _, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
/***/
#define BOOST_SPIRIT_X3_DECLARE_(r, constexpr_, rule_type) \
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context> \
[[nodiscard]] constexpr_ bool \
parse_rule( \
::boost::spirit::x3::detail::rule_id<typename std::remove_cvref_t<rule_type>::id>, \
It& first, Se const& last, \
Context const& context, typename std::remove_cvref_t<rule_type>::attribute_type& attr \
);
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
#define BOOST_SPIRIT_DEFINE_(r, data, rule_name) \
using BOOST_PP_CAT(rule_name, _synonym) = decltype(rule_name); \
template <typename Iterator, typename Context> \
inline bool parse_rule( \
::boost::spirit::x3::detail::rule_id<BOOST_PP_CAT(rule_name, _synonym)::id> \
, Iterator& first, Iterator const& last \
, Context const& context, BOOST_PP_CAT(rule_name, _synonym)::attribute_type& attr) \
{ \
using rule_t = BOOST_JOIN(rule_name, _synonym); \
return ::boost::spirit::x3::detail \
::rule_parser<typename rule_t::attribute_type, rule_t::id, true> \
::call_rule_definition( \
BOOST_JOIN(rule_name, _def), rule_name.name \
, first, last, context, attr \
, ::boost::mpl::bool_<rule_t::force_attribute>()); \
} \
/***/
#else
#define BOOST_SPIRIT_DEFINE_(r, data, rule_name) \
template <typename Iterator, typename Context> \
inline bool parse_rule( \
::boost::spirit::x3::detail::rule_id<decltype(rule_name)::id> \
, Iterator& first, Iterator const& last \
, Context const& context, decltype(rule_name)::attribute_type& attr) \
{ \
using rule_t = decltype(rule_name); \
return ::boost::spirit::x3::detail \
::rule_parser<typename rule_t::attribute_type, rule_t::id, true> \
::call_rule_definition( \
BOOST_JOIN(rule_name, _def), rule_name.name \
, first, last, context, attr \
, ::boost::mpl::bool_<rule_t::force_attribute>()); \
} \
/***/
#endif
#define BOOST_SPIRIT_X3_DECLARE(rule_type) BOOST_SPIRIT_X3_DECLARE_(,, rule_type)
#define BOOST_SPIRIT_X3_DECLARE_CONSTEXPR(rule_type) BOOST_SPIRIT_X3_DECLARE_(, constexpr, rule_type)
#define BOOST_SPIRIT_DEFINE(...) BOOST_PP_SEQ_FOR_EACH( \
BOOST_SPIRIT_DEFINE_, _, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
/***/
// NB: This can't be `constexpr`, because a constexpr declaration
// cannot be used with explicit template instantiation. We simply
// can't drop (legit) use cases of `BOOST_SPIRIT_INSTANTIATE`, so
// this is a pure technical limitation. If you need `constexpr`
// support in your rule, use `BOOST_SPIRIT_X3_DECLARE_CONSTEXPR`.
#define BOOST_SPIRIT_DECLARE(...) \
BOOST_SPIRIT_X3_DEPRECATED_MACRO_WARN( \
"Use of variadic arguments with `BOOST_SPIRIT_DECLARE` is deprecated due to the heavy compile-time cost of " \
"`BOOST_PP_SEQ_*`. Just apply `BOOST_SPIRIT_X3_DECLARE` for each of your rules." \
) \
BOOST_PP_SEQ_FOR_EACH(BOOST_SPIRIT_X3_DECLARE_,, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
#define BOOST_SPIRIT_INSTANTIATE(rule_type, Iterator, Context) \
template bool parse_rule<Iterator, Context>( \
::boost::spirit::x3::detail::rule_id<rule_type::id> \
, Iterator& first, Iterator const& last \
, Context const& context, rule_type::attribute_type&); \
/***/
#define BOOST_SPIRIT_X3_DEFINE_(r, constexpr_, rule_name) \
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context> \
[[nodiscard]] constexpr_ bool \
parse_rule( \
::boost::spirit::x3::detail::rule_id<std::remove_cvref_t<decltype(rule_name)>::id>, \
It& first, Se const& last, \
Context const& context, \
std::remove_cvref_t<decltype(rule_name)>::attribute_type& attr \
) { \
using rule_t = std::remove_cvref_t<decltype(rule_name)>; \
return ::boost::spirit::x3::detail::rule_parser< \
typename rule_t::attribute_type, typename rule_t::id, true \
>::call_rule_definition<rule_t::force_attribute>( \
BOOST_JOIN(rule_name, _def), rule_name.name, \
first, last, context, attr \
); \
}
#define BOOST_SPIRIT_X3_DEFINE(rule_type) BOOST_SPIRIT_X3_DEFINE_(,, rule_type)
#define BOOST_SPIRIT_X3_DEFINE_CONSTEXPR(rule_type) BOOST_SPIRIT_X3_DEFINE_(, constexpr, rule_type)
// NB: This can't be `constexpr`, because a constexpr declaration
// cannot be used with explicit template instantiation. We simply
// can't drop (legit) use cases of `BOOST_SPIRIT_INSTANTIATE`, so
// this is a pure technical limitation. If you need `constexpr`
// support in your rule, use `BOOST_SPIRIT_X3_DEFINE_CONSTEXPR`.
#define BOOST_SPIRIT_DEFINE(...) \
BOOST_SPIRIT_X3_DEPRECATED_MACRO_WARN( \
"Use of variadic arguments with `BOOST_SPIRIT_DEFINE` is deprecated due to the heavy compile-time cost of " \
"`BOOST_PP_SEQ_*`. Just apply `BOOST_SPIRIT_X3_DEFINE` for each of your rules." \
) \
BOOST_PP_SEQ_FOR_EACH(BOOST_SPIRIT_X3_DEFINE_,, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
namespace detail
{
template <typename RuleT, std::forward_iterator It, typename A, typename B = void>
struct instantiate_macro_helper
{
using rule_type = RuleT;
using iterator_type = It;
// Old API:
// A => Context
// B => void
// New API:
// A => Se
// B => Context
using sentinel_type = std::conditional_t<std::is_void_v<B>, It, A>;
static_assert(std::sentinel_for<sentinel_type, It>);
using context_type = std::conditional_t<std::is_void_v<B>, A, B>;
};
} // detail
#define BOOST_SPIRIT_X3_INSTANTIATE_(rule_type, It, Se, Context) \
template bool parse_rule<It, Se, Context>( \
::boost::spirit::x3::detail::rule_id<typename std::remove_cvref_t<rule_type>::id>, \
It&, Se const&, Context const&, \
typename std::remove_cvref_t<rule_type>::attribute_type& \
);
#define BOOST_SPIRIT_X3_INSTANTIATE_WRAP(...) __VA_ARGS__
// NB: This can't be `constexpr`, because a constexpr declaration
// cannot be used with explicit template instantiation.
#define BOOST_SPIRIT_X3_INSTANTIATE(...) \
BOOST_SPIRIT_X3_INSTANTIATE_( \
BOOST_SPIRIT_X3_INSTANTIATE_WRAP(typename ::boost::spirit::x3::detail::instantiate_macro_helper<__VA_ARGS__>::rule_type), \
BOOST_SPIRIT_X3_INSTANTIATE_WRAP(typename ::boost::spirit::x3::detail::instantiate_macro_helper<__VA_ARGS__>::iterator_type), \
BOOST_SPIRIT_X3_INSTANTIATE_WRAP(typename ::boost::spirit::x3::detail::instantiate_macro_helper<__VA_ARGS__>::sentinel_type), \
BOOST_SPIRIT_X3_INSTANTIATE_WRAP(typename ::boost::spirit::x3::detail::instantiate_macro_helper<__VA_ARGS__>::context_type) \
)
#define BOOST_SPIRIT_INSTANTIATE(...) \
BOOST_SPIRIT_X3_DEPRECATED_MACRO_WARN( \
"Use `BOOST_SPIRIT_X3_INSTANTIATE`. `BOOST_SPIRIT_INSTANTIATE` is deprecated because " \
"the name was not correctly prefixed with `X3`." \
) \
BOOST_SPIRIT_X3_INSTANTIATE(__VA_ARGS__)
} // boost::spirit::x3
#endif

View File

@@ -1,6 +1,5 @@
/*=============================================================================
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)
@@ -8,18 +7,15 @@
#if !defined(BOOST_SPIRIT_X3__ANNOTATE_ON_SUCCESS_HPP)
#define BOOST_SPIRIT_X3__ANNOTATE_ON_SUCCESS_HPP
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/support/unused.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
#include <boost/spirit/home/x3/support/utility/lambda_visitor.hpp>
#include <boost/spirit/home/x3/support/traits/is_variant.hpp>
#include <iterator>
#include <type_traits>
namespace boost::spirit::x3
namespace boost { namespace spirit { namespace x3
{
struct error_handler_tag;
///////////////////////////////////////////////////////////////////////////
// The on_success handler tags the AST with the iterator position
// for error handling.
//
@@ -29,37 +25,13 @@ namespace boost::spirit::x3
// x3/support/ast.
//
// We'll ask the X3's error_handler utility to do these.
///////////////////////////////////////////////////////////////////////////
struct annotate_on_success
{
// Catch-all default overload
template <std::forward_iterator It, std::sentinel_for<It> Se, typename T, typename Context>
constexpr void
on_success(It const& first, Se const& last, T& ast, Context const& context)
{
auto&& error_handler_ref = x3::get<error_handler_tag>(context);
static_assert(
!std::is_same_v<std::remove_cvref_t<decltype(error_handler_ref)>, unused_type>,
"This rule is derived from `x3::annotate_on_success`, but no reference was bound to "
"`x3::error_handler_tag`. You must provide a viable error handler via `x3::with`."
);
// unwrap `reference_wrapper` if neccessary
if constexpr (requires {
error_handler_ref.get().tag(ast, first, last);
})
{
error_handler_ref.get().tag(ast, first, last);
}
else
{
error_handler_ref.tag(ast, first, last);
}
}
template <std::forward_iterator It, std::sentinel_for<It> Se, typename... Types, typename Context>
constexpr void
on_success(It const& first, Se const& last, x3::variant<Types...>& ast, Context const& context)
template <typename Iterator, typename Context, typename... Types>
inline void on_success(Iterator const& first, Iterator const& last
, variant<Types...>& ast, Context const& context)
{
ast.apply_visitor(x3::make_lambda_visitor<void>([&](auto& node)
{
@@ -67,13 +39,21 @@ namespace boost::spirit::x3
}));
}
template <std::forward_iterator It, std::sentinel_for<It> Se, typename T, typename Context>
constexpr void
on_success(It const& first, Se const& last, forward_ast<T>& ast, Context const& context)
template <typename T, typename Iterator, typename Context>
inline void on_success(Iterator const& first, Iterator const& last
, forward_ast<T>& ast, Context const& context)
{
this->on_success(first, last, ast.get(), context);
}
template <typename T, typename Iterator, typename Context>
inline typename disable_if<traits::is_variant<T>>::type on_success(Iterator const& first, Iterator const& last
, T& ast, Context const& context)
{
auto& error_handler = x3::get<error_handler_tag>(context).get();
error_handler.tag(ast, first, last);
}
};
} // boost::spirit::x3
}}}
#endif

View File

@@ -1,7 +1,6 @@
/*=============================================================================
Copyright (c) 2001-2015 Joel de Guzman
Copyright (c) 2001-2011 Hartmut Kaiser
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)
@@ -59,8 +58,7 @@ x3::rule<class string_rule, std::string> const string_rule("string");
auto const pair_rule_def = string_rule > x3::lit('=') > string_rule;
auto const string_rule_def = x3::lexeme[*x3::alnum];
BOOST_SPIRIT_X3_DEFINE(pair_rule)
BOOST_SPIRIT_X3_DEFINE(string_rule)
BOOST_SPIRIT_DEFINE(pair_rule, string_rule)
template <typename Container>
void test_map_support()

View File

@@ -1,29 +1,26 @@
/*=============================================================================
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)
=============================================================================*/
#define BOOST_SPIRIT_X3_DEBUG
#include "test.hpp"
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/vector.hpp>
#include <iterator>
#include <vector>
#include <string>
#include <cstring>
#include <iostream>
#include "test.hpp"
struct my_error_handler
{
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Exception, typename Context>
template <typename Iterator, typename Exception, typename Context>
boost::spirit::x3::error_handler_result
operator()(It const&, Se const& last, Exception const& x, Context const&) const
operator()(Iterator&, Iterator const& last, Exception const& x, Context const&) const
{
std::cout
<< "Error! Expecting: "
@@ -49,22 +46,24 @@ struct my_attribute
alive = false;
}
friend std::ostream& operator<<(std::ostream & os, my_attribute const & attr)
friend std::ostream & operator << (std::ostream & os, my_attribute const & attr)
{
attr.access();
return os << "my_attribute";
}
};
int main()
int
main()
{
using spirit_test::test_attr;
using spirit_test::test;
using namespace boost::spirit::x3::standard;
using namespace boost::spirit::x3::ascii;
using boost::spirit::x3::rule;
using boost::spirit::x3::symbols;
using boost::spirit::x3::int_;
using boost::spirit::x3::alpha;
{ // basic tests

View File

@@ -1,6 +1,5 @@
/*=============================================================================
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)
@@ -8,11 +7,8 @@
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp>
#include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
#include <boost/core/lightweight_test.hpp>
#include <iterator>
#include <string>
#include <sstream>
@@ -20,11 +16,10 @@ namespace x3 = boost::spirit::x3;
struct error_handler_base
{
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Exception, typename Context>
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(
It const&, Se const&,
Exception const& x, Context const& context
) const
Iterator&, Iterator const&
, Exception const& x, Context const& context) const
{
std::string message = "Error! Expecting: " + x.which() + " here:";
auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
@@ -41,39 +36,35 @@ x3::rule<test_rule_class> const test_rule;
auto const test_inner_rule_def = x3::lit("bar");
auto const test_rule_def = x3::lit("foo") > test_inner_rule > x3::lit("git");
BOOST_SPIRIT_X3_DEFINE(test_inner_rule)
BOOST_SPIRIT_X3_DEFINE(test_rule)
BOOST_SPIRIT_DEFINE(test_inner_rule, test_rule)
void test(std::string const& line_break)
{
void test(std::string const& line_break) {
std::string const input("foo" + line_break + " foo" + line_break + "git");
auto const begin = std::begin(input);
auto const end = std::end(input);
{
std::stringstream stream;
x3::error_handler<std::string::const_iterator> error_handler{begin, end, stream};
{
std::stringstream stream;
x3::error_handler<std::string::const_iterator> error_handler{begin, end, stream};
auto const parser = x3::with<x3::error_handler_tag>(std::ref(error_handler))[test_rule];
(void)x3::phrase_parse(begin, end, parser, x3::standard::space);
auto const parser = x3::with<x3::error_handler_tag>(std::ref(error_handler))[test_rule];
x3::phrase_parse(begin, end, parser, x3::space);
BOOST_TEST_EQ(stream.str(), "In line 2:\nError! Expecting: \"bar\" here:\n foo\n__^_\n");
}
{
// TODO: cleanup when error_handler is reenterable
std::stringstream stream;
x3::error_handler<std::string::const_iterator> error_handler{ begin, end, stream };
auto const parser = x3::with<x3::error_handler_tag>(std::ref(error_handler))[test_rule];
(void)x3::parse(begin, end, parser);
BOOST_TEST_CSTR_EQ(stream.str().c_str(), "In line 1:\nError! Expecting: \"bar\" here:\nfoo\n___^_\n");
}
BOOST_TEST_EQ(stream.str(), "In line 2:\nError! Expecting: \"bar\" here:\n foo\n__^_\n");
}
void test_line_break_first(std::string const& line_break)
{
{ // TODO: cleanup when error_handler is reenterable
std::stringstream stream;
x3::error_handler<std::string::const_iterator> error_handler{ begin, end, stream };
auto const parser = x3::with<x3::error_handler_tag>(std::ref(error_handler))[test_rule];
x3::parse(begin, end, parser);
BOOST_TEST_CSTR_EQ(stream.str().c_str(), "In line 1:\nError! Expecting: \"bar\" here:\nfoo\n___^_\n");
}
}
void test_line_break_first(std::string const& line_break) {
std::string const input(line_break + "foo foo" + line_break + "git");
auto const begin = std::begin(input);
auto const end = std::end(input);
@@ -82,13 +73,12 @@ void test_line_break_first(std::string const& line_break)
x3::error_handler<std::string::const_iterator> error_handler{begin, end, stream};
auto const parser = x3::with<x3::error_handler_tag>(std::ref(error_handler))[test_rule];
(void)x3::phrase_parse(begin, end, parser, x3::space);
x3::phrase_parse(begin, end, parser, x3::space);
BOOST_TEST_EQ(stream.str(), "In line 2:\nError! Expecting: \"bar\" here:\nfoo foo\n_____^_\n");
}
int main()
{
int main() {
test("\n");
test("\r");
test("\r\n");

View File

@@ -1,16 +1,6 @@
/*=============================================================================
Copyright (c) 2019 Joel de Guzman
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
http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#include "grammar.hpp"
auto const grammar_def = x3::int_;
BOOST_SPIRIT_X3_DEFINE(grammar)
BOOST_SPIRIT_X3_INSTANTIATE(grammar_type, char const*, x3::unused_type)
BOOST_SPIRIT_DEFINE(grammar)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, x3::unused_type)

View File

@@ -1,17 +1,7 @@
/*=============================================================================
Copyright (c) 2019 Joel de Guzman
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
http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
x3::rule<struct grammar_r, int> const grammar;
using grammar_type = decltype(grammar);
BOOST_SPIRIT_X3_DECLARE(grammar_type)
BOOST_SPIRIT_DECLARE(grammar_type)

View File

@@ -1,6 +1,5 @@
/*=============================================================================
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)
@@ -21,10 +20,10 @@ rule<class indirect_rule, int> indirect_rule = "indirect_rule";
auto const direct_rule_def = boost::spirit::x3::int_;
auto const indirect_rule_def = direct_rule;
BOOST_SPIRIT_X3_DEFINE(direct_rule)
BOOST_SPIRIT_X3_DEFINE(indirect_rule)
BOOST_SPIRIT_DEFINE(direct_rule, indirect_rule)
int main()
int
main()
{
using namespace boost::spirit::x3::ascii;
using boost::spirit::x3::omit;

View File

@@ -22,8 +22,7 @@ rule<class indirect_rule, int> indirect_rule = "indirect_rule";
auto const direct_rule_def = boost::spirit::x3::int_;
auto const indirect_rule_def = direct_rule;
BOOST_SPIRIT_X3_DEFINE(direct_rule)
BOOST_SPIRIT_X3_DEFINE(indirect_rule)
BOOST_SPIRIT_DEFINE(direct_rule, indirect_rule)
int main()
{
@@ -84,7 +83,7 @@ int main()
boost::variant<int, range> attr;
std::string str("test");
(void)parse(str.begin(), str.end(), (int_ | raw[*char_]), attr);
parse(str.begin(), str.end(), (int_ | raw[*char_]), attr);
auto rng = boost::get<range>(attr);
BOOST_TEST(std::string(rng.begin(), rng.end()) == "test");
@@ -93,7 +92,7 @@ int main()
{
std::vector<boost::iterator_range<std::string::iterator>> attr;
std::string str("123abcd");
(void)parse(str.begin(), str.end()
parse(str.begin(), str.end()
, (raw[int_] >> raw[*char_])
, attr
);
@@ -105,7 +104,7 @@ int main()
{
std::pair<int, boost::iterator_range<std::string::iterator>> attr;
std::string str("123abcd");
(void)parse(str.begin(), str.end()
parse(str.begin(), str.end()
, (int_ >> raw[*char_])
, attr
);

View File

@@ -54,8 +54,7 @@ boost::spirit::x3::rule<class b_r, stationary> const b;
auto const a_def = '{' >> boost::spirit::x3::int_ >> '}';
auto const b_def = a;
BOOST_SPIRIT_X3_DEFINE(a)
BOOST_SPIRIT_X3_DEFINE(b)
BOOST_SPIRIT_DEFINE(a, b)
}
@@ -70,7 +69,7 @@ boost::spirit::x3::rule<class grammar_r, node_t> const grammar;
auto const grammar_def = '[' >> grammar % ',' >> ']' | boost::spirit::x3::int_;
BOOST_SPIRIT_X3_DEFINE(grammar)
BOOST_SPIRIT_DEFINE(grammar)
}
@@ -96,9 +95,9 @@ namespace check_recursive_tuple {
x3::rule<class grammar_r, recursive_tuple> const grammar;
auto const grammar_def = x3::int_ >> ('{' >> grammar % ',' >> '}' | x3::eps);
BOOST_SPIRIT_X3_DEFINE(grammar)
BOOST_SPIRIT_DEFINE(grammar)
BOOST_SPIRIT_X3_INSTANTIATE(decltype(grammar), char const*, x3::unused_type)
BOOST_SPIRIT_INSTANTIATE(decltype(grammar), char const*, x3::unused_type)
}

View File

@@ -13,25 +13,19 @@
#include <boost/fusion/include/at.hpp>
#include <boost/variant.hpp>
#include <iterator>
#include <string>
#include <cstring>
#include <iostream>
namespace x3 = boost::spirit::x3;
namespace {
int got_it = 0;
}
struct my_rule_class
{
//template <std::forward_iterator It, std::sentinel_for<It> Se, typename Exception, typename Context>
//x3::error_handler_result
//on_error(It const&, Se const& last, Exception const& x, Context const&)
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Exception, typename Context>
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result
on_error(It const&, Se const& last, Exception const& x, Context const&)
on_error(Iterator&, Iterator const& last, Exception const& x, Context const&)
{
std::cout
<< "Error! Expecting: "
@@ -44,9 +38,9 @@ struct my_rule_class
return x3::error_handler_result::fail;
}
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Attribute, typename Context>
void
on_success(It const&, Se const&, Attribute&, Context const&)
template <typename Iterator, typename Attribute, typename Context>
inline void
on_success(Iterator const&, Iterator const&, Attribute&, Context const&)
{
++got_it;
}
@@ -56,28 +50,51 @@ struct on_success_gets_preskipped_iterator
{
static bool ok;
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Attribute, typename Context>
void on_success(It before, Se const& after, Attribute&, Context const&)
template <typename Iterator, typename Attribute, typename Context>
void on_success(Iterator before, Iterator& after, Attribute&, Context const&)
{
bool const before_was_b = 'b' == *before;
ok = before_was_b && (++before == after);
ok = ('b' == *before) && (++before == after);
}
};
bool on_success_gets_preskipped_iterator::ok = false;
struct on_success_advance_iterator
{
template <typename Iterator, typename Attribute, typename Context>
void on_success(Iterator const&, Iterator& after, Attribute&, Context const&)
{
++after;
}
};
struct on_success_advance_iterator_mutref
{
template <typename Iterator, typename Attribute, typename Context>
void on_success(Iterator&, Iterator& after, Attribute&, Context const&)
{
++after;
}
};
struct on_success_advance_iterator_byval
{
template <typename Iterator, typename Attribute, typename Context>
void on_success(Iterator, Iterator& after, Attribute&, Context const&)
{
++after;
}
};
int main()
int
main()
{
using spirit_test::test_attr;
using spirit_test::test;
using namespace boost::spirit::x3::standard;
using namespace boost::spirit::x3::ascii;
using boost::spirit::x3::rule;
using boost::spirit::x3::int_;
using boost::spirit::x3::lit;
// show that ra = rb and ra %= rb works as expected
{
{ // show that ra = rb and ra %= rb works as expected
rule<class a, int> ra;
rule<class b, int> rb;
int attr;
@@ -95,8 +112,7 @@ int main()
BOOST_TEST(attr == 123);
}
// show that ra %= rb works as expected with semantic actions
{
{ // show that ra %= rb works as expected with semantic actions
rule<class a, int> ra;
rule<class b, int> rb;
int attr;
@@ -112,8 +128,8 @@ int main()
}
// std::string as container attribute with auto rules
{
{ // std::string as container attribute with auto rules
std::string attr;
// test deduced auto rule behavior
@@ -126,8 +142,8 @@ int main()
BOOST_TEST(attr == "x");
}
// error handling
{
{ // error handling
auto r = rule<my_rule_class, char const*>()
= '(' > int_ > ',' > int_ > ')';
@@ -140,23 +156,34 @@ int main()
BOOST_TEST(got_it == 1);
}
// on_success gets pre-skipped iterator
{
{ // on_success gets pre-skipped iterator
auto r = rule<on_success_gets_preskipped_iterator, char const*>()
= lit("b");
BOOST_TEST(test("a b", 'a' >> r, lit(' ')));
BOOST_TEST(on_success_gets_preskipped_iterator::ok);
}
{ // on_success handler mutable 'after' iterator
auto r1 = rule<on_success_advance_iterator, char const*>()
= lit("ab");
BOOST_TEST(test("abc", r1));
auto r2 = rule<on_success_advance_iterator_mutref, char const*>()
= lit("ab");
BOOST_TEST(test("abc", r2));
auto r3 = rule<on_success_advance_iterator_byval, char const*>()
= lit("ab");
BOOST_TEST(test("abc", r3));
}
{
using v_type = boost::variant<double, int>;
typedef boost::variant<double, int> v_type;
auto r1 = rule<class r1_id, v_type>()
= int_;
v_type v;
BOOST_TEST(test_attr("1", r1, v) && v.which() == 1 &&
boost::get<int>(v) == 1);
using ov_type = boost::optional<int>;
typedef boost::optional<int> ov_type;
auto r2 = rule<class r2_id, ov_type>()
= int_;
ov_type ov;
@@ -174,19 +201,21 @@ int main()
BOOST_TEST(test_attr("1", r, v) && at_c<0>(v) == 1);
}
// attribute compatibility test
{
constexpr auto expr = int_;
{ // attribute compatibility test
using boost::spirit::x3::rule;
using boost::spirit::x3::int_;
long long i = 0;
BOOST_TEST(test_attr("1", expr, i) && i == 1);
auto const expr = int_;
constexpr rule<class int_rule, int> int_rule("int_rule");
constexpr auto int_rule_def = int_;
constexpr auto start = int_rule = int_rule_def;
long long i;
BOOST_TEST(test_attr("1", expr, i) && i == 1); // ok
long long j = 0;
BOOST_TEST(test_attr("1", start, j) && j == 1);
const rule< class int_rule, int > int_rule( "int_rule" );
auto const int_rule_def = int_;
auto const start = int_rule = int_rule_def;
long long j;
BOOST_TEST(test_attr("1", start, j) && j == 1); // error
}
return boost::report_errors();

View File

@@ -1,6 +1,5 @@
/*=============================================================================
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
@@ -20,19 +19,19 @@ auto nop = [](auto const&){};
x3::rule<class used_attr1_r, int> used_attr1;
auto const used_attr1_def = used_attr::grammar[nop];
BOOST_SPIRIT_X3_DEFINE(used_attr1);
BOOST_SPIRIT_DEFINE(used_attr1);
x3::rule<class used_attr2_r, int> used_attr2;
auto const used_attr2_def = unused_attr::grammar[nop];
BOOST_SPIRIT_X3_DEFINE(used_attr2);
BOOST_SPIRIT_DEFINE(used_attr2);
x3::rule<class unused_attr1_r> unused_attr1;
auto const unused_attr1_def = used_attr::grammar[nop];
BOOST_SPIRIT_X3_DEFINE(unused_attr1);
BOOST_SPIRIT_DEFINE(unused_attr1);
x3::rule<class unused_attr2_r> unused_attr2;
auto const unused_attr2_def = unused_attr::grammar[nop];
BOOST_SPIRIT_X3_DEFINE(unused_attr2);
BOOST_SPIRIT_DEFINE(unused_attr2);
}

View File

@@ -14,36 +14,36 @@
namespace unused_attr {
auto const skipper_def = x3::standard::lit('*');
BOOST_SPIRIT_X3_DEFINE(skipper)
BOOST_SPIRIT_X3_INSTANTIATE(skipper_type, char const*, x3::unused_type)
BOOST_SPIRIT_DEFINE(skipper)
BOOST_SPIRIT_INSTANTIATE(skipper_type, char const*, x3::unused_type)
auto const skipper2_def = x3::standard::lit('#');
BOOST_SPIRIT_X3_DEFINE(skipper2)
BOOST_SPIRIT_X3_INSTANTIATE(skipper2_type, char const*, x3::unused_type)
BOOST_SPIRIT_DEFINE(skipper2)
BOOST_SPIRIT_INSTANTIATE(skipper2_type, char const*, x3::unused_type)
auto const grammar_def = *x3::standard::lit('=');
BOOST_SPIRIT_X3_DEFINE(grammar)
BOOST_SPIRIT_X3_INSTANTIATE(grammar_type, char const*, x3::unused_type)
BOOST_SPIRIT_DEFINE(grammar)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, x3::unused_type)
using skipper_context_type = typename x3::phrase_parse_context<skipper_type, char const*>::type;
BOOST_SPIRIT_X3_INSTANTIATE(grammar_type, char const*, skipper_context_type)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, skipper_context_type)
using skipper2_context_type = typename x3::phrase_parse_context<skipper2_type, char const*>::type;
BOOST_SPIRIT_X3_INSTANTIATE(grammar_type, char const*, skipper2_context_type)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, skipper2_context_type)
}
namespace used_attr {
auto const skipper_def = x3::standard::space;
BOOST_SPIRIT_X3_DEFINE(skipper)
BOOST_SPIRIT_X3_INSTANTIATE(skipper_type, char const*, x3::unused_type)
BOOST_SPIRIT_DEFINE(skipper)
BOOST_SPIRIT_INSTANTIATE(skipper_type, char const*, x3::unused_type)
auto const grammar_def = x3::int_;
BOOST_SPIRIT_X3_DEFINE(grammar)
BOOST_SPIRIT_X3_INSTANTIATE(grammar_type, char const*, x3::unused_type)
BOOST_SPIRIT_DEFINE(grammar)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, x3::unused_type)
using skipper_context_type = typename x3::phrase_parse_context<skipper_type, char const*>::type;
BOOST_SPIRIT_X3_INSTANTIATE(grammar_type, char const*, skipper_context_type)
BOOST_SPIRIT_INSTANTIATE(grammar_type, char const*, skipper_context_type)
}

View File

@@ -1,6 +1,5 @@
/*=============================================================================
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,7 +8,7 @@
#include <boost/spirit/home/x3.hpp>
// Check that `BOOST_SPIRIT_X3_INSTANTIATE` instantiates `parse_rule` with proper
// Check that `BOOST_SPIRIT_INSTANTIATE` instantiates `parse_rule` with proper
// types when a rule has no attribute.
namespace unused_attr {
@@ -19,17 +18,17 @@ namespace x3 = boost::spirit::x3;
// skipper must has no attribute, checks `parse` and `skip_over`
using skipper_type = x3::rule<class skipper_r>;
const skipper_type skipper;
BOOST_SPIRIT_X3_DECLARE(skipper_type)
BOOST_SPIRIT_DECLARE(skipper_type)
// the `unused_type const` must have the same effect as no attribute
using skipper2_type = x3::rule<class skipper2_r, x3::unused_type const>;
const skipper2_type skipper2;
BOOST_SPIRIT_X3_DECLARE(skipper2_type)
BOOST_SPIRIT_DECLARE(skipper2_type)
// grammar must has no attribute, checks `parse` and `phrase_parse`
using grammar_type = x3::rule<class grammar_r>;
const grammar_type grammar;
BOOST_SPIRIT_X3_DECLARE(grammar_type)
BOOST_SPIRIT_DECLARE(grammar_type)
}
@@ -41,10 +40,10 @@ namespace x3 = boost::spirit::x3;
using skipper_type = x3::rule<class skipper_r>;
const skipper_type skipper;
BOOST_SPIRIT_X3_DECLARE(skipper_type)
BOOST_SPIRIT_DECLARE(skipper_type)
using grammar_type = x3::rule<class grammar_r, int, true>;
const grammar_type grammar;
BOOST_SPIRIT_X3_DECLARE(grammar_type)
BOOST_SPIRIT_DECLARE(grammar_type)
}

View File

@@ -1,15 +1,11 @@
/*=============================================================================
Copyright (c) 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 <iterator>
#include "test.hpp"
namespace x3 = boost::spirit::x3;
@@ -17,23 +13,24 @@ struct my_tag;
struct my_rule_class
{
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Exception, typename Context>
[[nodiscard]] x3::error_handler_result
on_error(It const&, Se const&, Exception const&, Context const& context)
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result
on_error(Iterator&, Iterator const&, Exception const&, Context const& context)
{
++x3::get<my_tag>(context);
x3::get<my_tag>(context)++;
return x3::error_handler_result::fail;
}
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Attribute, typename Context>
void
on_success(It const&, Se const&, Attribute&, Context const& context)
template <typename Iterator, typename Attribute, typename Context>
inline void
on_success(Iterator const&, Iterator const&, Attribute&, Context const& context)
{
++x3::get<my_tag>(context);
x3::get<my_tag>(context)++;
}
};
int main()
int
main()
{
using spirit_test::test_attr;
using spirit_test::test;