mirror of
https://github.com/boostorg/spirit_x4.git
synced 2026-01-29 20:12:07 +00:00
210 lines
6.3 KiB
C++
210 lines
6.3 KiB
C++
/*=============================================================================
|
|
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/x4/char/char.hpp>
|
|
#include <boost/spirit/x4/rule.hpp>
|
|
#include <boost/spirit/x4/directive/with.hpp>
|
|
#include <boost/spirit/x4/numeric/int.hpp>
|
|
#include <boost/spirit/x4/operator/sequence.hpp>
|
|
#include <boost/spirit/x4/operator/list.hpp>
|
|
|
|
#include <vector>
|
|
#include <concepts>
|
|
#include <iterator>
|
|
#include <utility>
|
|
#include <type_traits>
|
|
|
|
namespace {
|
|
|
|
struct my_tag;
|
|
|
|
template<x4::X4Attribute ExpectedAttr>
|
|
struct match_counter_rule_id
|
|
{
|
|
template<std::forward_iterator It, std::sentinel_for<It> Se, class Failure, class Context>
|
|
void on_error(It const&, Se const&, Context const& ctx, Failure const&)
|
|
{
|
|
STATIC_CHECK(x4::has_context_of_v<Context, x4::contexts::rule_var, ExpectedAttr>);
|
|
++x4::get<my_tag>(ctx);
|
|
}
|
|
|
|
template<std::forward_iterator It, std::sentinel_for<It> Se, class Context, x4::X4Attribute Attr>
|
|
void on_success(It const&, Se const&, Context const& ctx, Attr&)
|
|
{
|
|
STATIC_CHECK(x4::has_context_of_v<Context, x4::contexts::rule_var, ExpectedAttr>);
|
|
++x4::get<my_tag>(ctx);
|
|
}
|
|
};
|
|
|
|
using x4::rule;
|
|
using x4::int_;
|
|
using x4::with;
|
|
using x4::_attr;
|
|
|
|
template<class T>
|
|
constexpr auto value_equals = int_[([](auto&& ctx) {
|
|
auto&& with_val = x4::get<my_tag>(ctx);
|
|
static_assert(std::same_as<decltype(with_val), T>);
|
|
return with_val == _attr(ctx);
|
|
})];
|
|
|
|
} // anonymous
|
|
|
|
TEST_CASE("with")
|
|
{
|
|
BOOST_SPIRIT_X4_ASSERT_CONSTEXPR_CTORS(with<my_tag>(0)['x']);
|
|
|
|
{
|
|
constexpr int i = 0;
|
|
BOOST_SPIRIT_X4_ASSERT_CONSTEXPR_CTORS(with<my_tag>(i)['x']);
|
|
}
|
|
|
|
// check various value categories
|
|
{
|
|
{
|
|
int i = 42;
|
|
CHECK(parse("42", with<my_tag>(i)[value_equals<int&>]));
|
|
}
|
|
{
|
|
int const i = 42;
|
|
CHECK(parse("42", with<my_tag>(i)[value_equals<int const&>]));
|
|
}
|
|
{
|
|
int i = 42;
|
|
CHECK(parse("42", with<my_tag>(std::move(i))[value_equals<int&>]));
|
|
}
|
|
{
|
|
int const i = 42;
|
|
CHECK(parse("42", with<my_tag>(std::move(i))[value_equals<int const&>]));
|
|
}
|
|
|
|
{
|
|
int i = 42;
|
|
auto with_gen = with<my_tag>(i);
|
|
CHECK(parse("42", with_gen[value_equals<int&>]));
|
|
}
|
|
{
|
|
int const i = 42;
|
|
auto with_gen = with<my_tag>(i);
|
|
CHECK(parse("42", with_gen[value_equals<int const&>]));
|
|
}
|
|
{
|
|
int i = 42;
|
|
auto with_gen = with<my_tag>(std::move(i));
|
|
CHECK(parse("42", with_gen[value_equals<int&>]));
|
|
}
|
|
{
|
|
int const i = 42;
|
|
auto with_gen = with<my_tag>(std::move(i));
|
|
CHECK(parse("42", with_gen[value_equals<int const&>]));
|
|
}
|
|
|
|
// lvalue `move_only`
|
|
{
|
|
spirit_test::move_only mo;
|
|
(void)with<my_tag>(mo)[int_];
|
|
}
|
|
{
|
|
spirit_test::move_only mo;
|
|
auto with_gen = with<my_tag>(mo); // passed-by-reference
|
|
(void)with_gen[int_]; // permitted, never copies
|
|
(void)std::move(with_gen)[int_];
|
|
}
|
|
|
|
// rvalue `move_only`
|
|
{
|
|
(void)with<my_tag>(spirit_test::move_only{})[int_];
|
|
}
|
|
{
|
|
auto with_gen = with<my_tag>(spirit_test::move_only{});
|
|
// (void)with_gen[int_]; // requires copy-constructible
|
|
(void)std::move(with_gen)[int_];
|
|
}
|
|
}
|
|
|
|
{
|
|
// injecting data into the context in the grammar
|
|
int matched_count = 0;
|
|
auto r = rule<match_counter_rule_id<std::vector<int>>, std::vector<int>>{} =
|
|
'(' > int_ > ',' > int_ > ')'; // NOLINT(bugprone-chained-comparison)
|
|
|
|
auto start = with<my_tag>(std::ref(matched_count))[r];
|
|
std::vector<int> ints;
|
|
|
|
REQUIRE(parse("(123,456)", start, ints));
|
|
REQUIRE(!parse("(abc,def)", start, ints));
|
|
CHECK(matched_count == 2);
|
|
CHECK(ints == std::vector<int>{123, 456});
|
|
}
|
|
|
|
{
|
|
// injecting non-const lvalue into the context
|
|
int val = 0;
|
|
auto const r = int_[([](auto&& ctx){
|
|
x4::get<my_tag>(ctx) += x4::_attr(ctx);
|
|
})];
|
|
REQUIRE(parse("123,456", with<my_tag>(val)[r % ',']));
|
|
CHECK(val == 579);
|
|
}
|
|
|
|
{
|
|
// injecting rvalue into the context
|
|
auto const r1 = int_[([](auto&& ctx){
|
|
x4::get<my_tag>(ctx) += x4::_attr(ctx);
|
|
})];
|
|
auto const r2 = rule<struct my_rvalue_rule_class, int>() =
|
|
x4::lit('(') >> (r1 % ',') >> x4::lit(')')[([](auto&& ctx){
|
|
x4::_rule_var(ctx) = x4::get<my_tag>(ctx);
|
|
})];
|
|
int attr = 0;
|
|
REQUIRE(parse("(1,2,3)", with<my_tag>(100)[r2], attr));
|
|
CHECK(attr == 106);
|
|
}
|
|
|
|
{
|
|
// injecting const/non-const lvalue and rvalue into the context
|
|
struct functor
|
|
{
|
|
int operator()(int& val)
|
|
{
|
|
return val * 10; // non-const ref returns 10 * injected val
|
|
}
|
|
int operator()(int const& val)
|
|
{
|
|
return val; // const ref returns injected val
|
|
}
|
|
};
|
|
|
|
auto f = [](auto&& ctx){
|
|
x4::_rule_var(ctx) = x4::_attr(ctx) + functor()(x4::get<my_tag>(ctx));
|
|
};
|
|
auto const r = rule<struct my_rule_class2, int>() = int_[f];
|
|
|
|
{
|
|
int attr = 0;
|
|
int const cval = 10;
|
|
REQUIRE(parse("5", with<my_tag>(cval)[r], attr));
|
|
CHECK(attr == 15); // x4::get returns const ref to cval
|
|
}
|
|
{
|
|
int attr = 0;
|
|
int val = 10;
|
|
REQUIRE(parse("5", with<my_tag>(val)[r], attr));
|
|
CHECK(attr == 105); // x4::get returns ref to val
|
|
}
|
|
{
|
|
int attr = 0;
|
|
REQUIRE(parse("5", with<my_tag>(10)[r], attr));
|
|
// x4::get returns ref to member variable of with_directive
|
|
CHECK(attr == 105);
|
|
}
|
|
}
|
|
}
|