Files
spirit_x4/test/x4/with.cpp
2025-10-21 10:31:17 +09:00

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);
}
}
}