mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
`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.
103 lines
3.4 KiB
C++
103 lines
3.4 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 <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>
|
|
|
|
namespace x3 = boost::spirit::x3;
|
|
|
|
struct error_handler_base
|
|
{
|
|
template <std::forward_iterator It, std::sentinel_for<It> Se, typename Exception, typename Context>
|
|
x3::error_handler_result on_error(
|
|
It const&, Se 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();
|
|
error_handler(x.where(), message);
|
|
return x3::error_handler_result::fail;
|
|
}
|
|
};
|
|
|
|
struct test_inner_rule_class;
|
|
struct test_rule_class : x3::annotate_on_success, error_handler_base {};
|
|
|
|
x3::rule<test_inner_rule_class> const test_inner_rule = "\"bar\"";
|
|
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)
|
|
|
|
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};
|
|
|
|
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);
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
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::space);
|
|
|
|
BOOST_TEST_EQ(stream.str(), "In line 2:\nError! Expecting: \"bar\" here:\nfoo foo\n_____^_\n");
|
|
}
|
|
|
|
int main()
|
|
{
|
|
test("\n");
|
|
test("\r");
|
|
test("\r\n");
|
|
|
|
test_line_break_first("\n");
|
|
test_line_break_first("\r");
|
|
test_line_break_first("\r\n");
|
|
|
|
|
|
return boost::report_errors();
|
|
}
|