2
0
mirror of https://github.com/boostorg/spirit.git synced 2026-01-19 04:42:11 +00:00
Files
spirit/test/x3/rule4.cpp
Nana Sakisaka 47945d1e81 Modernize rule, guard, on_error and on_success
`rule` now resolves the parse function using concepts, providing
significantly faster compile time & better errors.

`core/error_handler_types.hpp`: New header to separate enum
definition.

`annotate_on_success`: Modernized.

`on_error` and `on_success` now only accepts const iterators.
This is a breaking change, but we should apply this immediately
due to the reasons described below.

Historically, Spirit has passed mutable lvalue references of the
*internal* iterators to the `on_success`/`on_error` handlers. This
behavior was semantically 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 had never provided any
kind of guarantee on how those variables are processed in X3's
implementation details.

In other words, I consider the old behavior as a serious BUG
that involves undefined behavior which may even lead to
security issues.

`BOOST_SPIRIT_DECLARE`: Deprecated regarding compile-time slowness
of `BOOST_PP_SEQ_FOR_EACH`.

`BOOST_SPIRIT_DEFINE`: Ditto.

`BOOST_SPIRIT_INSTANTIATE`: Deprecated because the name was not
correctly prefixed with `X3_`.

`BOOST_SPIRIT_X3_DECLARE`: New macro with correctly prefixed name.

`BOOST_SPIRIT_X3_DEFINE`: Ditto.

`BOOST_SPIRIT_X3_INSTANTIATE`: Ditto.
2025-09-07 07:29:17 +09:00

194 lines
5.3 KiB
C++

/*=============================================================================
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)
=============================================================================*/
#include "test.hpp"
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/vector.hpp>
#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>
x3::error_handler_result
on_error(It const&, Se const& last, Exception const& x, Context const&)
{
std::cout
<< "Error! Expecting: "
<< x.which()
<< ", got: \""
<< std::string(x.where(), last)
<< "\""
<< std::endl
;
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&)
{
++got_it;
}
};
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&)
{
bool const before_was_b = 'b' == *before;
ok = before_was_b && (++before == after);
}
};
bool on_success_gets_preskipped_iterator::ok = false;
int main()
{
using spirit_test::test_attr;
using spirit_test::test;
using namespace boost::spirit::x3::standard;
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
{
rule<class a, int> ra;
rule<class b, int> rb;
int attr;
auto ra_def = (ra %= int_);
BOOST_TEST(test_attr("123", ra_def, attr));
BOOST_TEST(attr == 123);
auto rb_def = (rb %= ra_def);
BOOST_TEST(test_attr("123", rb_def, attr));
BOOST_TEST(attr == 123);
auto rb_def2 = (rb = ra_def);
BOOST_TEST(test_attr("123", rb_def2, attr));
BOOST_TEST(attr == 123);
}
// show that ra %= rb works as expected with semantic actions
{
rule<class a, int> ra;
rule<class b, int> rb;
int attr;
auto f = [](auto&){};
auto ra_def = (ra %= int_[f]);
BOOST_TEST(test_attr("123", ra_def, attr));
BOOST_TEST(attr == 123);
auto ra_def2 = (rb = (ra %= int_[f]));
BOOST_TEST(test_attr("123", ra_def2, attr));
BOOST_TEST(attr == 123);
}
// std::string as container attribute with auto rules
{
std::string attr;
// test deduced auto rule behavior
auto text = rule<class text_id, std::string>()
= +(!char_(')') >> !char_('>') >> char_);
attr.clear();
BOOST_TEST(test_attr("x", text, attr));
BOOST_TEST(attr == "x");
}
// error handling
{
auto r = rule<my_rule_class, char const*>()
= '(' > int_ > ',' > int_ > ')';
BOOST_TEST(test("(123,456)", r));
BOOST_TEST(!test("(abc,def)", r));
BOOST_TEST(!test("(123,456]", r));
BOOST_TEST(!test("(123;456)", r));
BOOST_TEST(!test("[123,456]", r));
BOOST_TEST(got_it == 1);
}
// 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);
}
{
using v_type = boost::variant<double, int>;
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>;
auto r2 = rule<class r2_id, ov_type>()
= int_;
ov_type ov;
BOOST_TEST(test_attr("1", r2, ov) && ov && boost::get<int>(ov) == 1);
}
// test handling of single element fusion sequences
{
using boost::fusion::vector;
using boost::fusion::at_c;
auto r = rule<class r_id, vector<int>>()
= int_;
vector<int> v(0);
BOOST_TEST(test_attr("1", r, v) && at_c<0>(v) == 1);
}
// attribute compatibility test
{
constexpr auto expr = int_;
long long i = 0;
BOOST_TEST(test_attr("1", expr, i) && i == 1);
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 j = 0;
BOOST_TEST(test_attr("1", start, j) && j == 1);
}
return boost::report_errors();
}