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 commitbae393d159, reversing changes made toc64a9146fc.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
370
include/boost/spirit/home/x3/nonterminal/detail/rule.hpp
Normal file
370
include/boost/spirit/home/x3/nonterminal/detail/rule.hpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user