mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
@@ -1,6 +1,5 @@
|
|||||||
/*=============================================================================
|
/*=============================================================================
|
||||||
Copyright (c) 2001-2014 Joel de Guzman
|
Copyright (c) 2001-2014 Joel de Guzman
|
||||||
Copyright (c) 2025 Nana Sakisaka
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@@ -10,243 +9,102 @@
|
|||||||
|
|
||||||
#include <boost/spirit/home/x3/support/context.hpp>
|
#include <boost/spirit/home/x3/support/context.hpp>
|
||||||
#include <boost/spirit/home/x3/support/traits/attribute_of.hpp>
|
#include <boost/spirit/home/x3/support/traits/attribute_of.hpp>
|
||||||
#include <boost/spirit/home/x3/nonterminal/detail/transform_attribute.hpp>
|
|
||||||
#include <boost/spirit/home/x3/core/call.hpp>
|
#include <boost/spirit/home/x3/core/call.hpp>
|
||||||
|
#include <boost/spirit/home/x3/nonterminal/detail/transform_attribute.hpp>
|
||||||
|
#include <boost/range/iterator_range_core.hpp>
|
||||||
|
|
||||||
#ifndef BOOST_SPIRIT_X3_NO_BOOST_ITERATOR_RANGE
|
namespace boost { namespace spirit { namespace x3
|
||||||
# pragma message("Use of `boost::iterator_range` is deprecated in X3. #define BOOST_SPIRIT_X3_NO_BOOST_ITERATOR_RANGE")
|
|
||||||
# include <boost/range/iterator_range_core.hpp>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ranges>
|
|
||||||
#include <iterator>
|
|
||||||
#include <concepts>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace boost::spirit::x3
|
|
||||||
{
|
{
|
||||||
struct raw_attribute_type; // TODO: move this to detail
|
struct raw_attribute_type;
|
||||||
|
struct parse_pass_context_tag;
|
||||||
|
|
||||||
// Ideally we should have a context-agnostic concept that can be used
|
template <typename Context>
|
||||||
// like `X3ActionFunctor<F>`, but we technically can't.
|
inline bool& _pass(Context const& context)
|
||||||
//
|
{
|
||||||
// In order to check `std::invocable`, we need to know the actual context
|
return x3::get<parse_pass_context_tag>(context);
|
||||||
// type passed to the `.parse(...)` function but it is unknown until
|
}
|
||||||
// runtime.
|
|
||||||
//
|
|
||||||
// Even if we make up the most trivial context type (i.e. `unused_type`),
|
|
||||||
// such concept will be useless because a user-provided functor always
|
|
||||||
// operates on user-specific precondition that assumes the context
|
|
||||||
// holds exact specific type provided to the entry point (`x3::parse`).
|
|
||||||
|
|
||||||
template <typename Subject, typename Action>
|
template <typename Subject, typename Action>
|
||||||
struct action : unary_parser<Subject, action<Subject, Action>>
|
struct action : unary_parser<Subject, action<Subject, Action>>
|
||||||
{
|
{
|
||||||
static_assert(
|
typedef unary_parser<Subject, action<Subject, Action>> base_type;
|
||||||
!std::is_reference_v<Action>,
|
static bool const is_pass_through_unary = true;
|
||||||
"Reference type is disallowed for semantic action functor to prevent dangling reference"
|
static bool const has_action = true;
|
||||||
);
|
|
||||||
|
|
||||||
using base_type = unary_parser<Subject, action<Subject, Action>>;
|
constexpr action(Subject const& subject, Action f)
|
||||||
static constexpr bool is_pass_through_unary = true;
|
: base_type(subject), f(f) {}
|
||||||
static constexpr bool has_action = true;
|
|
||||||
|
|
||||||
Action f;
|
template <typename Iterator, typename Context, typename RuleContext, typename Attribute>
|
||||||
|
bool call_action(
|
||||||
template <typename SubjectT, typename ActionT>
|
Iterator& first, Iterator const& last
|
||||||
requires std::is_constructible_v<base_type, SubjectT> && std::is_constructible_v<Action, ActionT>
|
, Context const& context, RuleContext& rcontext, Attribute& attr) const
|
||||||
constexpr action(SubjectT&& subject, ActionT&& f)
|
|
||||||
noexcept(std::is_nothrow_constructible_v<base_type, SubjectT> && std::is_nothrow_constructible_v<Action, ActionT>)
|
|
||||||
: base_type(std::forward<SubjectT>(subject))
|
|
||||||
, f(std::forward<ActionT>(f))
|
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
// attr==unused, action wants attribute
|
|
||||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext>
|
|
||||||
[[nodiscard]] constexpr bool
|
|
||||||
parse(
|
|
||||||
It& first, Se const& last, Context const& context, RContext& rcontext, unused_type
|
|
||||||
) const noexcept(
|
|
||||||
std::is_nothrow_default_constructible_v<traits::attribute_of_t<action<Subject, Action>, Context>> &&
|
|
||||||
noexcept(this->parse_main(first, last, context, rcontext, std::declval<traits::attribute_of_t<action<Subject, Action>, Context>&>()))
|
|
||||||
)
|
|
||||||
{
|
|
||||||
using attribute_type = traits::attribute_of_t<action<Subject, Action>, Context>;
|
|
||||||
|
|
||||||
// Synthesize the attribute since one is not supplied
|
|
||||||
attribute_type attribute; // default-initialize
|
|
||||||
return this->parse_main(first, last, context, rcontext, attribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Catch-all overload for non-unused_type attribute
|
|
||||||
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 noexcept(noexcept(this->parse_main(first, last, context, rcontext, attr)))
|
|
||||||
{
|
|
||||||
return this->parse_main(first, last, context, rcontext, attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Compose attr(where(val(pass(context))))
|
|
||||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext, typename Attribute>
|
|
||||||
using composed_context_t = x3::context<
|
|
||||||
attr_context_tag,
|
|
||||||
Attribute,
|
|
||||||
x3::context<
|
|
||||||
where_context_tag,
|
|
||||||
#ifdef BOOST_SPIRIT_X3_NO_BOOST_ITERATOR_RANGE
|
|
||||||
std::ranges::subrange<It, Se> const,
|
|
||||||
#else
|
|
||||||
boost::iterator_range<It> const,
|
|
||||||
#endif
|
|
||||||
x3::context<
|
|
||||||
rule_val_context_tag,
|
|
||||||
RContext,
|
|
||||||
x3::context<
|
|
||||||
parse_pass_context_tag,
|
|
||||||
bool,
|
|
||||||
Context
|
|
||||||
>
|
|
||||||
>
|
|
||||||
>
|
|
||||||
>;
|
|
||||||
|
|
||||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext, typename Attribute>
|
|
||||||
requires std::invocable<Action const&, composed_context_t<It, Se, Context, RContext, Attribute> const&>
|
|
||||||
[[nodiscard]] constexpr bool
|
|
||||||
call_action(
|
|
||||||
It& first, Se const& last,
|
|
||||||
Context const& context, RContext& rcontext, Attribute& attr
|
|
||||||
) const noexcept(false) // construction of `subrange` is never noexcept as per the standard
|
|
||||||
{
|
|
||||||
#ifdef BOOST_SPIRIT_X3_NO_BOOST_ITERATOR_RANGE
|
|
||||||
using where_range_t = std::ranges::subrange<It, Se>;
|
|
||||||
#else
|
|
||||||
using where_range_t = boost::iterator_range<It>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static_assert(
|
|
||||||
std::is_void_v<std::invoke_result_t<Action const&, composed_context_t<It, Se, Context, RContext, Attribute> const&>>,
|
|
||||||
"Semantic action should not return value. Check your function signature."
|
|
||||||
);
|
|
||||||
|
|
||||||
bool pass = true;
|
bool pass = true;
|
||||||
auto const pass_context = x3::make_context<parse_pass_context_tag>(pass, context);
|
auto action_context = make_context<parse_pass_context_tag>(pass, context);
|
||||||
|
call(f, first, last, action_context, rcontext, attr);
|
||||||
auto const val_context = x3::make_context<rule_val_context_tag>(rcontext, pass_context);
|
|
||||||
|
|
||||||
// TODO: Provide some trait to detect whether this is actually needed for
|
|
||||||
// each semantic actions.
|
|
||||||
//
|
|
||||||
// Although this can be assumed to be eliminated in optimized code,
|
|
||||||
// this still may introduce compile time overhead (and also runtime
|
|
||||||
// overhead, as constructing `subrange` is never noexcept).
|
|
||||||
where_range_t const where_rng(first, last);
|
|
||||||
auto const where_context = x3::make_context<where_context_tag>(where_rng, val_context);
|
|
||||||
|
|
||||||
auto const attr_context = x3::make_context<attr_context_tag>(attr, where_context);
|
|
||||||
|
|
||||||
// Sanity check (internal check to detect implementation divergence)
|
|
||||||
static_assert(std::same_as<
|
|
||||||
std::remove_cvref_t<decltype(attr_context)>,
|
|
||||||
composed_context_t<It, Se, Context, RContext, Attribute>
|
|
||||||
>);
|
|
||||||
|
|
||||||
this->f(attr_context);
|
|
||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext, typename Attribute>
|
template <typename Iterator, typename Context
|
||||||
requires (!std::invocable<Action const&, composed_context_t<It, Se, Context, RContext, Attribute> const&>)
|
, typename RuleContext, typename Attribute>
|
||||||
[[nodiscard]] constexpr bool
|
bool parse_main(Iterator& first, Iterator const& last
|
||||||
call_action(
|
, Context const& context, RuleContext& rcontext, Attribute& attr) const
|
||||||
It&, Se const&,
|
|
||||||
Context const&, RContext&, Attribute&
|
|
||||||
) const noexcept(std::is_nothrow_invocable_v<Action const&>)
|
|
||||||
{
|
{
|
||||||
// Explicitly make this hard error instead of emitting "no matching overload".
|
Iterator save = first;
|
||||||
// This provides much more human-friendly errors.
|
|
||||||
static_assert(
|
|
||||||
std::invocable<Action const&>,
|
|
||||||
"Neither `f(ctx)` nor `f()` is well-formed for your semantic action. "
|
|
||||||
"Check your function signature. Note that some functors might need "
|
|
||||||
"`const` qualifier to satisfy the constraints."
|
|
||||||
);
|
|
||||||
|
|
||||||
static_assert(
|
|
||||||
std::is_void_v<std::invoke_result_t<Action const&>>,
|
|
||||||
"Semantic action should not return value. Check your function signature."
|
|
||||||
);
|
|
||||||
this->f();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext, typename Attribute>
|
|
||||||
[[nodiscard]] constexpr bool
|
|
||||||
parse_main(
|
|
||||||
It& first, Se const& last, Context const& context, RContext& rcontext, Attribute& attr
|
|
||||||
) const noexcept(
|
|
||||||
std::is_copy_assignable_v<It> &&
|
|
||||||
is_nothrow_parsable_v<Subject, It, Se, Context, RContext, Attribute> &&
|
|
||||||
noexcept(this->call_action(first, last, context, rcontext, attr))
|
|
||||||
)
|
|
||||||
{
|
|
||||||
It const saved_first = first;
|
|
||||||
if (this->subject.parse(first, last, context, rcontext, attr))
|
if (this->subject.parse(first, last, context, rcontext, attr))
|
||||||
{
|
{
|
||||||
if (this->call_action(first, last, context, rcontext, attr))
|
if (call_action(first, last, context, rcontext, attr))
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
// reset iterators if semantic action failed the match
|
// reset iterators if semantic action failed the match
|
||||||
// retrospectively
|
// retrospectively
|
||||||
first = saved_first;
|
first = save;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// attr==raw_attribute_type, action wants iterator_range (see raw.hpp)
|
// attr==raw_attribute_type, action wants iterator_range (see raw.hpp)
|
||||||
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Context, typename RContext>
|
template <typename Iterator, typename Context, typename RuleContext>
|
||||||
[[nodiscard]] constexpr bool
|
bool parse_main(Iterator& first, Iterator const& last
|
||||||
parse_main(
|
, Context const& context, RuleContext& rcontext, raw_attribute_type&) const
|
||||||
It& first, Se const& last, Context const& context, RContext& rcontext, raw_attribute_type&
|
|
||||||
) const noexcept(false) // construction of `subrange` is never noexcept as per the standard
|
|
||||||
{
|
{
|
||||||
#ifdef BOOST_SPIRIT_X3_NO_BOOST_ITERATOR_RANGE
|
boost::iterator_range<Iterator> rng;
|
||||||
std::ranges::subrange<It, Se> rng;
|
|
||||||
#else
|
|
||||||
boost::iterator_range<It> rng;
|
|
||||||
#endif
|
|
||||||
// synthesize the attribute since one is not supplied
|
// synthesize the attribute since one is not supplied
|
||||||
return this->parse_main(first, last, context, rcontext, rng);
|
return parse_main(first, last, context, rcontext, rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// attr==unused, action wants attribute
|
||||||
|
template <typename Iterator, typename Context, typename RuleContext>
|
||||||
|
bool parse(Iterator& first, Iterator const& last
|
||||||
|
, Context const& context, RuleContext& rcontext, unused_type) const
|
||||||
|
{
|
||||||
|
typedef typename
|
||||||
|
traits::attribute_of<action<Subject, Action>, Context>::type
|
||||||
|
attribute_type;
|
||||||
|
|
||||||
|
// synthesize the attribute since one is not supplied
|
||||||
|
attribute_type attribute{};
|
||||||
|
return parse_main(first, last, context, rcontext, attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
// main parse function
|
||||||
|
template <typename Iterator, typename Context
|
||||||
|
, typename RuleContext, typename Attribute>
|
||||||
|
bool parse(Iterator& first, Iterator const& last
|
||||||
|
, Context const& context, RuleContext& rcontext, Attribute& attr) const
|
||||||
|
{
|
||||||
|
return parse_main(first, last, context, rcontext, attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Action f;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <X3Subject Subject, typename Action>
|
template <typename P, typename Action>
|
||||||
[[nodiscard, deprecated(
|
constexpr action<typename extension::as_parser<P>::value_type, Action>
|
||||||
"Use `operator[]` instead. The symbol `/` normally means \"ordered choice\" "
|
operator/(P const& p, Action f)
|
||||||
"in PEG, and is irrelevant to semantic actions. Furthermore, using C++'s "
|
|
||||||
"`operator/` for this purpose may introduce surprising behavior when it's "
|
|
||||||
"mixed with ordinary PEG operators, for instance, the unary `operator+`, "
|
|
||||||
"due to precedence."
|
|
||||||
)]]
|
|
||||||
constexpr action<as_parser_plain_t<Subject>, std::remove_cvref_t<Action>>
|
|
||||||
operator/(Subject&& p, Action&& f)
|
|
||||||
noexcept(
|
|
||||||
is_parser_nothrow_castable_v<Subject> &&
|
|
||||||
std::is_nothrow_constructible_v<
|
|
||||||
action<as_parser_plain_t<Subject>, std::remove_cvref_t<Action>>,
|
|
||||||
as_parser_t<Subject>, Action
|
|
||||||
>
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
return { as_parser(std::forward<Subject>(p)), std::forward<Action>(f) };
|
return { as_parser(p), f };
|
||||||
}
|
}
|
||||||
} // boost::spirit::x3
|
}}}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/*=============================================================================
|
/*=============================================================================
|
||||||
Copyright (c) 2001-2014 Joel de Guzman
|
Copyright (c) 2001-2014 Joel de Guzman
|
||||||
Copyright (c) 2025 Nana Sakisaka
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@@ -8,38 +7,70 @@
|
|||||||
#if !defined(BOOST_SPIRIT_X3_CALL_CONTEXT_MAY_26_2014_0234PM)
|
#if !defined(BOOST_SPIRIT_X3_CALL_CONTEXT_MAY_26_2014_0234PM)
|
||||||
#define BOOST_SPIRIT_X3_CALL_CONTEXT_MAY_26_2014_0234PM
|
#define BOOST_SPIRIT_X3_CALL_CONTEXT_MAY_26_2014_0234PM
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include <boost/spirit/home/x3/support/context.hpp>
|
#include <boost/spirit/home/x3/support/context.hpp>
|
||||||
|
#include <boost/spirit/home/x3/support/utility/is_callable.hpp>
|
||||||
|
#include <boost/range/iterator_range_core.hpp>
|
||||||
|
|
||||||
namespace boost::spirit::x3
|
namespace boost { namespace spirit { namespace x3
|
||||||
{
|
{
|
||||||
struct parse_pass_context_tag; // _pass
|
////////////////////////////////////////////////////////////////////////////
|
||||||
struct rule_val_context_tag; // _val
|
struct rule_val_context_tag;
|
||||||
struct where_context_tag; // _where
|
|
||||||
struct attr_context_tag; // _attr
|
|
||||||
|
|
||||||
template <typename Context>
|
template <typename Context>
|
||||||
[[nodiscard]] constexpr bool& _pass(Context const& context) noexcept
|
inline decltype(auto) _val(Context const& context)
|
||||||
{
|
|
||||||
return x3::get<parse_pass_context_tag>(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Context>
|
|
||||||
[[nodiscard]] constexpr auto&& _val(Context const& context) noexcept
|
|
||||||
{
|
{
|
||||||
return x3::get<rule_val_context_tag>(context);
|
return x3::get<rule_val_context_tag>(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
struct where_context_tag;
|
||||||
|
|
||||||
template <typename Context>
|
template <typename Context>
|
||||||
[[nodiscard]] constexpr auto&& _where(Context const& context) noexcept
|
inline decltype(auto) _where(Context const& context)
|
||||||
{
|
{
|
||||||
return x3::get<where_context_tag>(context);
|
return x3::get<where_context_tag>(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
struct attr_context_tag;
|
||||||
|
|
||||||
template <typename Context>
|
template <typename Context>
|
||||||
[[nodiscard]] constexpr auto&& _attr(Context const& context) noexcept
|
inline decltype(auto) _attr(Context const& context)
|
||||||
{
|
{
|
||||||
return x3::get<attr_context_tag>(context);
|
return x3::get<attr_context_tag>(context);
|
||||||
}
|
}
|
||||||
} // boost::spirit::x3
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template <typename F, typename Context>
|
||||||
|
auto call(F f, Context const& context, mpl::true_)
|
||||||
|
{
|
||||||
|
return f(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F, typename Context>
|
||||||
|
auto call(F f, Context const& /* context */, mpl::false_)
|
||||||
|
{
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename F, typename Iterator
|
||||||
|
, typename Context, typename RuleContext, typename Attribute>
|
||||||
|
auto call(
|
||||||
|
F f, Iterator& first, Iterator const& last
|
||||||
|
, Context const& context, RuleContext& rcontext, Attribute& attr)
|
||||||
|
{
|
||||||
|
boost::iterator_range<Iterator> rng(first, last);
|
||||||
|
auto val_context = make_context<rule_val_context_tag>(rcontext, context);
|
||||||
|
auto where_context = make_context<where_context_tag>(rng, val_context);
|
||||||
|
auto attr_context = make_context<attr_context_tag>(attr, where_context);
|
||||||
|
return detail::call(f, attr_context, is_callable<F(decltype(attr_context) const&)>());
|
||||||
|
}
|
||||||
|
}}}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include <boost/fusion/include/std_pair.hpp>
|
#include <boost/fusion/include/std_pair.hpp>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -109,14 +108,15 @@ int main()
|
|||||||
using spirit_test::test_attr;
|
using spirit_test::test_attr;
|
||||||
using spirit_test::test;
|
using spirit_test::test;
|
||||||
|
|
||||||
using namespace boost::spirit::x3::standard;
|
using namespace boost::spirit::x3::ascii;
|
||||||
using boost::spirit::x3::rule;
|
using boost::spirit::x3::rule;
|
||||||
using boost::spirit::x3::lit;
|
using boost::spirit::x3::lit;
|
||||||
using boost::spirit::x3::eps;
|
using boost::spirit::x3::eps;
|
||||||
using boost::spirit::x3::unused_type;
|
using boost::spirit::x3::unused_type;
|
||||||
|
|
||||||
// synth attribute value-init
|
|
||||||
{
|
{ // synth attribute value-init
|
||||||
|
|
||||||
std::string s;
|
std::string s;
|
||||||
typedef rule<class r, std::string> rule_type;
|
typedef rule<class r, std::string> rule_type;
|
||||||
|
|
||||||
@@ -128,15 +128,17 @@ int main()
|
|||||||
BOOST_TEST(s == "abcdef");
|
BOOST_TEST(s == "abcdef");
|
||||||
}
|
}
|
||||||
|
|
||||||
// synth attribute value-init
|
{ // synth attribute value-init
|
||||||
{
|
|
||||||
std::string s;
|
std::string s;
|
||||||
typedef rule<class r, std::string> rule_type;
|
typedef rule<class r, std::string> rule_type;
|
||||||
|
|
||||||
auto rdef = rule_type() =
|
auto rdef = rule_type() =
|
||||||
alpha[([](auto& ctx) {
|
alpha /
|
||||||
_val(ctx) += _attr(ctx);
|
[](auto& ctx)
|
||||||
})]
|
{
|
||||||
|
_val(ctx) += _attr(ctx);
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
BOOST_TEST(test_attr("abcdef", +rdef, s));
|
BOOST_TEST(test_attr("abcdef", +rdef, s));
|
||||||
@@ -146,16 +148,13 @@ int main()
|
|||||||
{
|
{
|
||||||
auto r = rule<class r_id, int>{} = eps[([] (auto& ctx) {
|
auto r = rule<class r_id, int>{} = eps[([] (auto& ctx) {
|
||||||
using boost::spirit::x3::_val;
|
using boost::spirit::x3::_val;
|
||||||
static_assert(
|
static_assert(std::is_same<std::decay_t<decltype(_val(ctx))>, unused_type>::value,
|
||||||
std::is_same_v<std::decay_t<decltype(_val(ctx))>, unused_type>,
|
"Attribute must not be synthesized");
|
||||||
"Attribute must not be synthesized"
|
|
||||||
);
|
|
||||||
})];
|
})];
|
||||||
BOOST_TEST(test("", r));
|
BOOST_TEST(test("", r));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure no unneeded synthesization, copying and moving occurred
|
{ // ensure no unneeded synthesization, copying and moving occurred
|
||||||
{
|
|
||||||
stationary st { 0 };
|
stationary st { 0 };
|
||||||
BOOST_TEST(test_attr("{42}", check_stationary::b, st));
|
BOOST_TEST(test_attr("{42}", check_stationary::b, st));
|
||||||
BOOST_TEST_EQ(st.val, 42);
|
BOOST_TEST_EQ(st.val, 42);
|
||||||
|
|||||||
Reference in New Issue
Block a user