mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
Sync from upstream.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
[/==============================================================================
|
||||
Copyright (C) 2001-2015 Joel de Guzman
|
||||
Copyright (C) 2001-2011 Hartmut Kaiser
|
||||
Copyright (C) 2024 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)
|
||||
@@ -237,6 +238,7 @@ Supported compilers will be:
|
||||
[include tutorial/annotation.qbk]
|
||||
[include tutorial/rexpr.qbk]
|
||||
[include tutorial/error_handling.qbk]
|
||||
[include tutorial/non_throwing_expectations.qbk]
|
||||
[endsect]
|
||||
|
||||
[section Quick Reference]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[/==============================================================================
|
||||
Copyright (C) 2001-2018 Joel de Guzman
|
||||
Copyright (C) 2024 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)
|
||||
@@ -94,6 +95,10 @@ matches the input or an exception is emitted. Using on_error(), that
|
||||
exception can be handled by calling a handler with the context at which the
|
||||
parsing failed can be reported.
|
||||
|
||||
[tip Check the [link spirit_x3.tutorials.non_throwing_expectations Non-throwing Expectations Tutorial]
|
||||
for information on switching to more efficient error handling without using C++ exceptions.
|
||||
]
|
||||
|
||||
[heading on_error]
|
||||
|
||||
`on_error` is the counterpart of `on_success`, as discussed in the
|
||||
@@ -104,6 +109,7 @@ that are executed by the parser when an __x3_expectation_failure__ is thrown
|
||||
via the expect operator or directive. `on_error` handlers have access to the
|
||||
iterators, the context and the exception that was thrown.
|
||||
|
||||
[#__tutorial_error_handling__]
|
||||
[heading Error Handling]
|
||||
|
||||
Before we proceed, let me introduce a helper class, the
|
||||
@@ -129,8 +135,8 @@ Here's our `on_error` handler:
|
||||
, Exception const& x, Context const& context)
|
||||
{
|
||||
auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
|
||||
std::string message = "Error! Expecting: " + x.which() + " here:";
|
||||
error_handler(x.where(), message);
|
||||
std::string message = "Error! Expecting: " + x3::which(x) + " here:";
|
||||
error_handler(x3::where(x), message);
|
||||
return x3::error_handler_result::fail;
|
||||
}
|
||||
};
|
||||
@@ -146,7 +152,7 @@ determining the line number and actual column position, and formatting the
|
||||
error message printed. All we have to do is provide the actual error string
|
||||
which we extract from the __x3_expectation_failure__ exception:
|
||||
|
||||
std::string message = "Error! Expecting: " + x.which() + " here:";
|
||||
std::string message = "Error! Expecting: " + x3::which(x) + " here:";
|
||||
|
||||
Then, we return `x3::error_handler_result::fail` to tell X3 that we want to
|
||||
fail the parse when such an event is caught. You can return one of:
|
||||
|
||||
97
doc/x3/tutorial/non_throwing_expectations.qbk
Normal file
97
doc/x3/tutorial/non_throwing_expectations.qbk
Normal file
@@ -0,0 +1,97 @@
|
||||
[/==============================================================================
|
||||
Copyright (C) 2024 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)
|
||||
===============================================================================/]
|
||||
|
||||
[section:non_throwing_expectations Non-throwing Expectations]
|
||||
|
||||
By default, X3 throws __x3_expectation_failure__ when an expectation failure occurs.
|
||||
While C++ exceptions are straightforward, they come with significant overhead
|
||||
that can drastically impact your application's processing speed, especially if
|
||||
your parser is called within a performance-critical loop.
|
||||
|
||||
In short, even the simplest grammar, like the one below, would throw exceptions
|
||||
100,000 times if invoked 100,000 times with mismatched input.
|
||||
|
||||
x3::lit('a') > 'b'
|
||||
|
||||
You can change this behavior to store the error in a user-provided variable
|
||||
instead of throwing an exception.
|
||||
|
||||
Non-throwing mode can be up to *1-90 times faster* than the traditional mode,
|
||||
depending on the complexity of your grammar.
|
||||
|
||||
[tip The performance improvement is capped by the overhead of C++ exceptions,
|
||||
meaning the reduction in parse time is limited by the overhead that
|
||||
exceptions would have introduced.
|
||||
]
|
||||
|
||||
[heading Migration Guide]
|
||||
|
||||
To switch to non-throwing mode, define the macro `BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE`
|
||||
as `0` before including X3 headers, and then make a few modifications to your
|
||||
parser's entry point.
|
||||
|
||||
Here's an example of a parser in its default (throwing) mode:
|
||||
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
void do_parse()
|
||||
{
|
||||
// ... setup your variables here...
|
||||
|
||||
try
|
||||
{
|
||||
bool const ok = x3::parse(first, last, parser);
|
||||
if (!ok)
|
||||
{
|
||||
// error handling
|
||||
}
|
||||
}
|
||||
catch (x3::expectation_failure<Iterator> const& failure)
|
||||
{
|
||||
// error handling
|
||||
}
|
||||
}
|
||||
|
||||
Next, adjust your code as follows to switch to non-throwing mode:
|
||||
|
||||
#define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 0
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
void do_parse()
|
||||
{
|
||||
// ... setup your variables here...
|
||||
|
||||
// convenient helper, defaults to `boost::optional<x3::expectation_failure<Iterator>>`
|
||||
x3::expectation_failure_optional<Iterator> failure;
|
||||
|
||||
bool const ok = x3::parse(
|
||||
first, last, x3::with<x3::expectation_failure_tag>(failure)[parser]);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
if (failure.has_value())
|
||||
{
|
||||
// error handling
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[tip You can also inspect the variable within [link __tutorial_error_handling__ `on_error` handler].
|
||||
]
|
||||
|
||||
That's it! All X3 parsers will behave semantically the same as before,
|
||||
except that expectation failures will be stored in the variable instead of
|
||||
being thrown as C++ exceptions.
|
||||
|
||||
The following types are supported for the context value:
|
||||
|
||||
* `bool`
|
||||
* `std::optional<x3::expectation_failure<Iterator>>`
|
||||
* `boost::optional<x3::expectation_failure<Iterator>>`
|
||||
* `std::reference_wrapper` of optional types
|
||||
|
||||
[endsect]
|
||||
@@ -1,5 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -8,7 +10,8 @@
|
||||
#define BOOST_SPIRIT_X3_GUARD_FERBRUARY_02_2013_0649PM
|
||||
|
||||
#include <boost/spirit/home/x3/support/context.hpp>
|
||||
#include <boost/spirit/home/x3/directive/expect.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
@@ -17,7 +20,7 @@ namespace boost { namespace spirit { namespace x3
|
||||
fail
|
||||
, retry
|
||||
, accept
|
||||
, rethrow
|
||||
, rethrow // see BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE for alternative behaviors
|
||||
};
|
||||
|
||||
template <typename Subject, typename Handler>
|
||||
@@ -36,30 +39,48 @@ namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
Iterator i = first;
|
||||
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
try
|
||||
#endif
|
||||
{
|
||||
Iterator i = first;
|
||||
bool r = this->subject.parse(i, last, context, rcontext, attr);
|
||||
if (r)
|
||||
if (this->subject.parse(i, last, context, rcontext, attr))
|
||||
{
|
||||
first = i;
|
||||
return r;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (expectation_failure<Iterator> const& x)
|
||||
{
|
||||
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
catch (expectation_failure<Iterator> const& x) {
|
||||
#else
|
||||
if (has_expectation_failure(context)) {
|
||||
auto& x = get_expectation_failure(context);
|
||||
#endif
|
||||
// X3 developer note: don't forget to sync this implementation with x3::detail::rule_parser
|
||||
switch (handler(first, last, x, context))
|
||||
{
|
||||
case error_handler_result::fail:
|
||||
clear_expectation_failure(context);
|
||||
return false;
|
||||
|
||||
case error_handler_result::retry:
|
||||
continue;
|
||||
|
||||
case error_handler_result::accept:
|
||||
return true;
|
||||
|
||||
case error_handler_result::rethrow:
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
throw;
|
||||
#else
|
||||
return false; // TODO: design decision required
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Handler handler;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -7,6 +9,7 @@
|
||||
#if !defined(BOOST_SPIRIT_X3_PROXY_FEBRUARY_1_2013_0211PM)
|
||||
#define BOOST_SPIRIT_X3_PROXY_FEBRUARY_1_2013_0211PM
|
||||
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_category.hpp>
|
||||
@@ -29,7 +32,7 @@ namespace boost { namespace spirit { namespace x3
|
||||
, Context const& context, RuleContext& rcontext, Attribute& attr, Category) const
|
||||
{
|
||||
this->subject.parse(first, last, context, rcontext, attr);
|
||||
return true;
|
||||
return !has_expectation_failure(context);
|
||||
}
|
||||
|
||||
// Main entry point.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -7,6 +9,7 @@
|
||||
#if !defined(BOOST_SPIRIT_X3_SKIP_APRIL_16_2006_0625PM)
|
||||
#define BOOST_SPIRIT_X3_SKIP_APRIL_16_2006_0625PM
|
||||
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/support/unused.hpp>
|
||||
#include <boost/spirit/home/x3/support/context.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_category.hpp>
|
||||
@@ -15,6 +18,7 @@
|
||||
#include <boost/type_traits/remove_cv.hpp>
|
||||
#include <boost/type_traits/remove_reference.hpp>
|
||||
#include <boost/utility/declval.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
@@ -41,7 +45,7 @@ namespace boost { namespace spirit { namespace x3
|
||||
struct is_unused_skipper<unused_skipper<Skipper>>
|
||||
: mpl::true_ {};
|
||||
|
||||
template <>
|
||||
template <>
|
||||
struct is_unused_skipper<unused_type>
|
||||
: mpl::true_ {};
|
||||
|
||||
@@ -58,29 +62,85 @@ namespace boost { namespace spirit { namespace x3
|
||||
return unused_skipper.skipper;
|
||||
}
|
||||
|
||||
template <typename Iterator, typename Skipper>
|
||||
template <typename Iterator, typename Context, typename Skipper>
|
||||
inline void skip_over(
|
||||
Iterator& first, Iterator const& last, Skipper const& skipper)
|
||||
Iterator& first, Iterator const& last, Context& context, Skipper const& skipper)
|
||||
{
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
boost::ignore_unused(context);
|
||||
while (skipper.parse(first, last, unused, unused, unused))
|
||||
/***/;
|
||||
/* loop */;
|
||||
#else
|
||||
if constexpr (std::is_same_v<expectation_failure_t<Context>, unused_type>)
|
||||
{
|
||||
// The context given by parent was truly `unused_type`.
|
||||
// There exists only one such case in core; that is
|
||||
// `x3::phrase_parse(...)` which creates a fresh context
|
||||
// for the (post)skipper.
|
||||
//
|
||||
// In that case, it is perfectly fine to pass `unused`
|
||||
// because the skipper should have been wrapped
|
||||
// like `x3::with<x3::expectation_failure_tag>(failure)[skipper]`.
|
||||
// (Note that we have plenty of static_asserts in other
|
||||
// locations to detect the absence of the context.)
|
||||
//
|
||||
// If we encounter this branch in any other situations,
|
||||
// that should be a BUG of `expectation_failure` logic.
|
||||
|
||||
while (skipper.parse(first, last, unused, unused, unused))
|
||||
/* loop */;
|
||||
}
|
||||
else
|
||||
{
|
||||
// In order to cut the template instantiation chain,
|
||||
// we must *forget* the original context at least once
|
||||
// during the (recursive) invocation of skippers.
|
||||
//
|
||||
// Traditionally, implementation detail of `skip_over`
|
||||
// was disposing the context because we can clearly assume
|
||||
// that any 'context,' including those provided by users,
|
||||
// is semantically meaningless as long as we're just
|
||||
// *skipping* iterators. As you can see in the other branch,
|
||||
// `unused` was passed for that purpose.
|
||||
//
|
||||
// However, we need to do a quite different thing when the
|
||||
// non-throwing expectation_failure mode is enabled.
|
||||
//
|
||||
// Since the reference bound to `x3::expectation_failure_tag` is
|
||||
// provided by the user in the first place, if we do forget it
|
||||
// then it will be impossible to resurrect the value afterwards.
|
||||
// It will also be problematic for `skip_over` itself because the
|
||||
// underlying skipper may (or may not) raise an expectation failure.
|
||||
// In traditional mode, the error was thrown by a C++ exception.
|
||||
// But how can we propagate that error without throwing?
|
||||
//
|
||||
// For this reason we're going to cherry-pick the reference
|
||||
// and repack it into a brand new context.
|
||||
|
||||
auto const local_ctx = make_context<expectation_failure_tag>(
|
||||
x3::get<expectation_failure_tag>(context));
|
||||
|
||||
while (skipper.parse(first, last, local_ctx, unused, unused))
|
||||
/* loop */;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
inline void skip_over(Iterator&, Iterator const&, unused_type)
|
||||
template <typename Iterator, typename Context>
|
||||
inline void skip_over(Iterator&, Iterator const&, Context&, unused_type)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Iterator, typename Skipper>
|
||||
template <typename Iterator, typename Context, typename Skipper>
|
||||
inline void skip_over(
|
||||
Iterator&, Iterator const&, unused_skipper<Skipper> const&)
|
||||
Iterator&, Iterator const&, Context&, unused_skipper<Skipper> const&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// this tag is used to find the skipper from the context
|
||||
struct skipper_tag;
|
||||
|
||||
|
||||
template <typename Context>
|
||||
struct has_skipper
|
||||
: mpl::not_<detail::is_unused_skipper<
|
||||
@@ -91,9 +151,9 @@ namespace boost { namespace spirit { namespace x3
|
||||
|
||||
template <typename Iterator, typename Context>
|
||||
inline void skip_over(
|
||||
Iterator& first, Iterator const& last, Context const& context)
|
||||
Iterator& first, Iterator const& last, Context& context)
|
||||
{
|
||||
detail::skip_over(first, last, x3::get<skipper_tag>(context));
|
||||
detail::skip_over(first, last, context, x3::get<skipper_tag>(context));
|
||||
}
|
||||
}}}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2009 Chris Hoeppler
|
||||
Copyright (c) 2014 Lee Clagett
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -10,6 +12,7 @@
|
||||
#define BOOST_SPIRIT_X3_CONFIX_MAY_30_2014_1819PM
|
||||
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
@@ -43,6 +46,14 @@ namespace boost { namespace spirit { namespace x3
|
||||
this->subject.parse(first, last, context, rcontext, attr) &&
|
||||
postfix.parse(first, last, context, rcontext, unused)))
|
||||
{
|
||||
#if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
if (has_expectation_failure(context))
|
||||
{
|
||||
// don't rollback iterator (mimicking exception-like behavior)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
first = save;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -8,35 +10,12 @@
|
||||
#define BOOST_SPIRIT_X3_EXPECT_MARCH_16_2012_1024PM
|
||||
|
||||
#include <boost/spirit/home/x3/support/context.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>
|
||||
|
||||
#include <boost/config.hpp> // for BOOST_SYMBOL_VISIBLE
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
template <typename Iterator>
|
||||
struct BOOST_SYMBOL_VISIBLE expectation_failure : std::runtime_error
|
||||
{
|
||||
public:
|
||||
|
||||
expectation_failure(Iterator where, std::string const& which)
|
||||
: std::runtime_error("boost::spirit::x3::expectation_failure")
|
||||
, where_(where), which_(which)
|
||||
{}
|
||||
~expectation_failure() {}
|
||||
|
||||
std::string which() const { return which_; }
|
||||
Iterator const& where() const { return where_; }
|
||||
|
||||
private:
|
||||
|
||||
Iterator where_;
|
||||
std::string which_;
|
||||
};
|
||||
|
||||
template <typename Subject>
|
||||
struct expect_directive : unary_parser<Subject, expect_directive<Subject>>
|
||||
{
|
||||
@@ -51,13 +30,20 @@ namespace boost { namespace spirit { namespace x3
|
||||
bool parse(Iterator& first, Iterator const& last
|
||||
, Context const& context, RContext& rcontext, Attribute& attr) const
|
||||
{
|
||||
bool r = this->subject.parse(first, last, context, rcontext, attr);
|
||||
bool const r = this->subject.parse(first, last, context, rcontext, attr);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
boost::throw_exception(
|
||||
expectation_failure<Iterator>(
|
||||
first, what(this->subject)));
|
||||
#else
|
||||
if (!has_expectation_failure(context))
|
||||
{
|
||||
set_expectation_failure(first, this->subject, context);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -88,14 +74,21 @@ namespace boost { namespace spirit { namespace x3 { namespace detail
|
||||
, Iterator& first, Iterator const& last
|
||||
, Context const& context, RContext& rcontext, Attribute& attr)
|
||||
{
|
||||
bool r = parse_into_container(
|
||||
bool const r = parse_into_container(
|
||||
parser.subject, first, last, context, rcontext, attr);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
boost::throw_exception(
|
||||
expectation_failure<Iterator>(
|
||||
first, what(parser.subject)));
|
||||
#else
|
||||
if (!has_expectation_failure(context))
|
||||
{
|
||||
set_expectation_failure(first, parser.subject, context);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2015 Mario Lang
|
||||
Copyright (c) 2001-2011 Hartmut Kaiser
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -10,6 +12,7 @@
|
||||
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/move_to.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/support/unused.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
@@ -30,6 +33,11 @@ namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
bool const result = this->subject.parse(
|
||||
first, last, context, rcontext, unused);
|
||||
|
||||
#if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
if (has_expectation_failure(context)) return false;
|
||||
#endif
|
||||
|
||||
traits::move_to(result, attr);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
Copyright (c) 2001-2011 Joel de Guzman
|
||||
Copyright (c) 2001-2011 Hartmut Kaiser
|
||||
Copyright (c) 2014 Thomas Bernard
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -11,6 +13,7 @@
|
||||
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/operator/kleene.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3 { namespace detail
|
||||
{
|
||||
@@ -83,7 +86,8 @@ namespace boost { namespace spirit { namespace x3
|
||||
this->subject, first, last, context, rcontext, attr))
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
|
||||
return !has_expectation_failure(context);
|
||||
}
|
||||
|
||||
RepeatCountLimit repeat_limit;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2011 Jamboree
|
||||
Copyright (c) 2014 Lee Clagett
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -9,6 +11,7 @@
|
||||
#define BOOST_SPIRIT_X3_SEEK_APRIL_13_2014_1920PM
|
||||
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
@@ -36,6 +39,13 @@ namespace boost { namespace spirit { namespace x3
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
if (has_expectation_failure(context))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// fail only after subject fails & no input
|
||||
if (current == last)
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2013 Agustin Berge
|
||||
Copyright (c) 2024 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)
|
||||
@@ -10,6 +11,7 @@
|
||||
|
||||
#include <boost/spirit/home/x3/support/context.hpp>
|
||||
#include <boost/spirit/home/x3/support/unused.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/core/skip_over.hpp>
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
@@ -30,16 +32,22 @@ namespace boost { namespace spirit { namespace x3
|
||||
, typename RContext, typename Attribute>
|
||||
typename disable_if<has_skipper<Context>, bool>::type
|
||||
parse(Iterator& first, Iterator const& last
|
||||
, Context const& context, RContext& rcontext, Attribute& attr) const
|
||||
, Context& context, RContext& rcontext, Attribute& attr) const
|
||||
{
|
||||
auto const& skipper =
|
||||
detail::get_unused_skipper(x3::get<skipper_tag>(context));
|
||||
|
||||
return this->subject.parse(
|
||||
first, last
|
||||
, make_context<skipper_tag>(skipper, context)
|
||||
, rcontext
|
||||
, attr);
|
||||
auto const local_ctx = make_context<skipper_tag>(skipper, context);
|
||||
bool const r = this->subject.parse(first, last, local_ctx, rcontext, attr);
|
||||
|
||||
#if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
if (has_expectation_failure(local_ctx))
|
||||
{
|
||||
set_expectation_failure(get_expectation_failure(local_ctx), context);
|
||||
}
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
template <typename Iterator, typename Context
|
||||
, typename RContext, typename Attribute>
|
||||
@@ -47,11 +55,7 @@ namespace boost { namespace spirit { namespace x3
|
||||
parse(Iterator& first, Iterator const& last
|
||||
, Context const& context, RContext& rcontext, Attribute& attr) const
|
||||
{
|
||||
return this->subject.parse(
|
||||
first, last
|
||||
, context
|
||||
, rcontext
|
||||
, attr);
|
||||
return this->subject.parse(first, last, context, rcontext, attr);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,16 +71,58 @@ namespace boost { namespace spirit { namespace x3
|
||||
, skipper(skipper)
|
||||
{}
|
||||
|
||||
template <typename Iterator, typename Context
|
||||
, typename RContext, typename Attribute>
|
||||
template <typename Iterator, typename RContext, typename Attribute>
|
||||
bool parse(Iterator& first, Iterator const& last
|
||||
, unused_type const&, RContext& rcontext, Attribute& attr) const
|
||||
{
|
||||
// It is perfectly fine to omit the expectation_failure context
|
||||
// even in non-throwing mode if and only if the skipper itself
|
||||
// is expectation-less.
|
||||
//
|
||||
// For example:
|
||||
// skip(a > b) [lit('foo')]
|
||||
// skip(c >> d)[lit('foo')]
|
||||
// `a > b` should require non-`unused_type` context, but
|
||||
// `c >> d` should NOT require non-`unused_type` context
|
||||
//
|
||||
// However, it's impossible right now to detect whether
|
||||
// `this->subject` actually is expectation-less, so we just
|
||||
// call the parse function to see what will happen. If the
|
||||
// subject turns out to lack the expectation context,
|
||||
// static_assert will be engaged in other locations.
|
||||
//
|
||||
// Anyways, we don't need to repack the expectation context
|
||||
// into our brand new skipper context, in contrast to the
|
||||
// repacking process done in `x3::skip_over`.
|
||||
return this->subject.parse(first, last,
|
||||
make_context<skipper_tag>(skipper), rcontext, attr);
|
||||
}
|
||||
|
||||
template <typename Iterator, typename Context, typename RContext, typename Attribute>
|
||||
bool parse(Iterator& first, Iterator const& last
|
||||
, Context const& context, RContext& rcontext, Attribute& attr) const
|
||||
{
|
||||
return this->subject.parse(
|
||||
first, last
|
||||
, make_context<skipper_tag>(skipper, context)
|
||||
, rcontext
|
||||
, attr);
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
return this->subject.parse(first, last, make_context<skipper_tag>(skipper, context), rcontext, attr);
|
||||
|
||||
#else
|
||||
static_assert(
|
||||
!std::is_same_v<expectation_failure_t<Context>, unused_type>,
|
||||
"Context type was not specified for x3::expectation_failure_tag. "
|
||||
"You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
|
||||
"Note that you must also bind the context to your skipper.");
|
||||
|
||||
// This logic is heavily related to the instantiation chain;
|
||||
// see `x3::skip_over` for details.
|
||||
auto const local_ctx = make_context<skipper_tag>(skipper, context);
|
||||
bool const r = this->subject.parse(first, last, local_ctx, rcontext, attr);
|
||||
|
||||
if (has_expectation_failure(local_ctx))
|
||||
{
|
||||
set_expectation_failure(get_expectation_failure(local_ctx), context);
|
||||
}
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
Skipper const skipper;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -11,6 +13,7 @@
|
||||
#include <boost/spirit/home/x3/auxiliary/guard.hpp>
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/core/skip_over.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/directive/expect.hpp>
|
||||
#include <boost/spirit/home/x3/nonterminal/detail/transform_attribute.hpp>
|
||||
#include <boost/utility/addressof.hpp>
|
||||
@@ -233,25 +236,45 @@ namespace boost { namespace spirit { namespace x3 { namespace detail
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
try
|
||||
#endif
|
||||
{
|
||||
return parse_rhs_main(
|
||||
rhs, first, last, context, rcontext, attr, mpl::false_());
|
||||
if (parse_rhs_main(
|
||||
rhs, first, last, context, rcontext, attr, mpl::false_()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (expectation_failure<Iterator> const& x)
|
||||
{
|
||||
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
catch (expectation_failure<Iterator> const& x) {
|
||||
#else
|
||||
if (has_expectation_failure(context)) {
|
||||
auto& x = get_expectation_failure(context);
|
||||
#endif
|
||||
// X3 developer note: don't forget to sync this implementation with x3::guard
|
||||
switch (ID().on_error(first, last, x, context))
|
||||
{
|
||||
case error_handler_result::fail:
|
||||
clear_expectation_failure(context);
|
||||
return false;
|
||||
|
||||
case error_handler_result::retry:
|
||||
continue;
|
||||
|
||||
case error_handler_result::accept:
|
||||
return true;
|
||||
|
||||
case error_handler_result::rethrow:
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
throw;
|
||||
#else
|
||||
return false; // TODO: design decision required
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -8,6 +10,7 @@
|
||||
#define BOOST_SPIRIT_X3_ALTERNATIVE_JAN_07_2013_1131AM
|
||||
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_of_binary.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/operator/detail/alternative.hpp>
|
||||
|
||||
@@ -29,7 +32,8 @@ namespace boost { namespace spirit { namespace x3
|
||||
, Context const& context, RContext& rcontext, unused_type) const
|
||||
{
|
||||
return this->left.parse(first, last, context, rcontext, unused)
|
||||
|| this->right.parse(first, last, context, rcontext, unused);
|
||||
|| (!has_expectation_failure(context)
|
||||
&& this->right.parse(first, last, context, rcontext, unused));
|
||||
}
|
||||
|
||||
template <typename Iterator, typename Context
|
||||
@@ -39,7 +43,8 @@ namespace boost { namespace spirit { namespace x3
|
||||
, Context const& context, RContext& rcontext, Attribute& attr) const
|
||||
{
|
||||
return detail::parse_alternative(this->left, first, last, context, rcontext, attr)
|
||||
|| detail::parse_alternative(this->right, first, last, context, rcontext, attr);
|
||||
|| (!has_expectation_failure(context)
|
||||
&& detail::parse_alternative(this->right, first, last, context, rcontext, attr));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -9,6 +11,7 @@
|
||||
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_of.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/has_attribute.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
@@ -35,6 +38,20 @@ namespace boost { namespace spirit { namespace x3
|
||||
first = start;
|
||||
return false;
|
||||
}
|
||||
|
||||
// In case of `Left - expect[r]`,
|
||||
// if Right yielded expectation error,
|
||||
// the whole difference expression (*this) should also yield error.
|
||||
// In other words, when the THROW macro was 1 (i.e. traditional behavior),
|
||||
// Right should already have thrown an exception.
|
||||
#if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
if (has_expectation_failure(context))
|
||||
{
|
||||
// don't rollback iterator (mimicking exception-like behavior)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Right fails, now try Left
|
||||
return this->left.parse(first, last, context, rcontext, attr);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2001-2011 Hartmut Kaiser
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -11,6 +13,7 @@
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/container_traits.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_of.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
@@ -32,7 +35,7 @@ namespace boost { namespace spirit { namespace x3
|
||||
while (detail::parse_into_container(
|
||||
this->subject, first, last, context, rcontext, attr))
|
||||
;
|
||||
return true;
|
||||
return !has_expectation_failure(context);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2001-2011 Hartmut Kaiser
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -11,6 +13,7 @@
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/container_traits.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_of.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
@@ -42,7 +45,7 @@ namespace boost { namespace spirit { namespace x3
|
||||
first = iter;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !has_expectation_failure(context);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -8,6 +10,7 @@
|
||||
#define BOOST_SPIRIT_X3_NOT_PREDICATE_MARCH_23_2007_0618PM
|
||||
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
@@ -28,7 +31,8 @@ namespace boost { namespace spirit { namespace x3
|
||||
, Context const& context, RContext& rcontext, Attribute& /*attr*/) const
|
||||
{
|
||||
Iterator i = first;
|
||||
return !this->subject.parse(i, last, context, rcontext, unused);
|
||||
return !this->subject.parse(i, last, context, rcontext, unused)
|
||||
&& !has_expectation_failure(context);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2001-2011 Hartmut Kaiser
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -10,6 +12,7 @@
|
||||
|
||||
#include <boost/spirit/home/x3/core/proxy.hpp>
|
||||
#include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_of.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/move_to.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/optional_traits.hpp>
|
||||
@@ -38,7 +41,7 @@ namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
detail::parse_into_container(
|
||||
this->subject, first, last, context, rcontext, attr);
|
||||
return true;
|
||||
return !has_expectation_failure(context);
|
||||
}
|
||||
|
||||
// Attribute is an optional
|
||||
@@ -59,6 +62,9 @@ namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
// assign the parsed value into our attribute
|
||||
x3::traits::move_to(val, attr);
|
||||
|
||||
} else {
|
||||
return !has_expectation_failure(context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2001-2011 Hartmut Kaiser
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -9,6 +11,7 @@
|
||||
#define BOOST_SPIRIT_X3_PLUS_MARCH_13_2007_0127PM
|
||||
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/container_traits.hpp>
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_of.hpp>
|
||||
#include <boost/spirit/home/x3/core/detail/parse_into_container.hpp>
|
||||
@@ -36,7 +39,7 @@ namespace boost { namespace spirit { namespace x3
|
||||
while (detail::parse_into_container(
|
||||
this->subject, first, last, context, rcontext, attr))
|
||||
;
|
||||
return true;
|
||||
return !has_expectation_failure(context);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2014 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
@@ -8,6 +10,7 @@
|
||||
#define BOOST_SPIRIT_X3_SEQUENCE_JAN_06_2013_1015AM
|
||||
|
||||
#include <boost/spirit/home/x3/support/traits/attribute_of_binary.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/spirit/home/x3/core/parser.hpp>
|
||||
#include <boost/spirit/home/x3/operator/detail/sequence.hpp>
|
||||
#include <boost/spirit/home/x3/directive/expect.hpp>
|
||||
@@ -29,10 +32,20 @@ namespace boost { namespace spirit { namespace x3
|
||||
Iterator& first, Iterator const& last
|
||||
, Context const& context, RContext& rcontext, unused_type) const
|
||||
{
|
||||
Iterator save = first;
|
||||
Iterator const save = first;
|
||||
|
||||
if (this->left.parse(first, last, context, rcontext, unused)
|
||||
&& this->right.parse(first, last, context, rcontext, unused))
|
||||
return true;
|
||||
|
||||
#if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
if (has_expectation_failure(context))
|
||||
{
|
||||
// don't rollback iterator (mimicking exception-like behavior)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
first = save;
|
||||
return false;
|
||||
}
|
||||
|
||||
392
include/boost/spirit/home/x3/support/expectation.hpp
Normal file
392
include/boost/spirit/home/x3/support/expectation.hpp
Normal file
@@ -0,0 +1,392 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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)
|
||||
=============================================================================*/
|
||||
#if !defined(BOOST_SPIRIT_X3_SUPPORT_EXPECTATION_HPP)
|
||||
#define BOOST_SPIRIT_X3_SUPPORT_EXPECTATION_HPP
|
||||
|
||||
#if !defined(BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE)
|
||||
# define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 1
|
||||
#endif
|
||||
|
||||
#include <boost/config.hpp> // for BOOST_SYMBOL_VISIBLE, BOOST_ATTRIBUTE_NODISCARD
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/spirit/home/x3/support/unused.hpp>
|
||||
#include <boost/spirit/home/x3/support/context.hpp>
|
||||
|
||||
// We utilize `x3::traits::build_optional<...>` for customization point
|
||||
// instead of directly wrapping `expectation_failure` with `boost::optional`.
|
||||
// This would make it possible for the user to eliminate the usages of
|
||||
// `boost::optional<T>`, and use `std::optional<T>` everywhere.
|
||||
//
|
||||
// Note that we are intentionally including this header regardless of
|
||||
// the value of BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE, since the
|
||||
// helper types defined in non-throwing version might still be required
|
||||
// when the users benchmark their application just by switching the
|
||||
// macro while keeping their implementation unmodified.
|
||||
//
|
||||
// This will make it possible for the users to unconditionally
|
||||
// inject `x3::expectation_failure_optional<Iterator>` into their parser,
|
||||
// safely assuming that the value is no-op in throwing mode.
|
||||
#include <boost/spirit/home/x3/support/traits/optional_traits.hpp>
|
||||
|
||||
// This is required for partial specialization of relevant helpers.
|
||||
// TODO: Add a macro to discard all #includes of <boost/optional.hpp>.
|
||||
// (this is TODO because it requires changes in `optional_traits.hpp`.)
|
||||
#include <boost/optional.hpp>
|
||||
#include <optional>
|
||||
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE // throwing mode
|
||||
# define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_API BOOST_SYMBOL_VISIBLE
|
||||
# define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_BASE : std::runtime_error
|
||||
# define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS throwing
|
||||
# include <boost/throw_exception.hpp>
|
||||
# include <stdexcept>
|
||||
|
||||
#else // non-throwing mode
|
||||
# define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_API
|
||||
# define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_BASE
|
||||
# define BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS non_throwing
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
struct expectation_failure_tag;
|
||||
|
||||
inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
|
||||
{
|
||||
template <typename Iterator>
|
||||
struct BOOST_SPIRIT_X3_EXPECTATION_FAILURE_API
|
||||
expectation_failure BOOST_SPIRIT_X3_EXPECTATION_FAILURE_BASE
|
||||
{
|
||||
public:
|
||||
expectation_failure(Iterator where, std::string const& which)
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
: std::runtime_error("boost::spirit::x3::expectation_failure"),
|
||||
#else
|
||||
:
|
||||
#endif
|
||||
where_(where), which_(which)
|
||||
{}
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr Iterator const& where() const noexcept { return where_; }
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr std::string const& which() const noexcept { return which_; }
|
||||
|
||||
private:
|
||||
Iterator where_;
|
||||
std::string which_;
|
||||
};
|
||||
|
||||
|
||||
template <typename Context>
|
||||
using expectation_failure_t = std::remove_cv_t<std::remove_reference_t<
|
||||
decltype(x3::get<expectation_failure_tag>(std::declval<Context>()))>>;
|
||||
|
||||
template <typename Iterator>
|
||||
using expectation_failure_optional =
|
||||
typename traits::build_optional<expectation_failure<Iterator>>::type;
|
||||
|
||||
|
||||
// x3::where(x), x3::which(x)
|
||||
// Convenient accessors for absorbing the variation of
|
||||
// optional/reference_wrapper interfaces.
|
||||
|
||||
// beware ADL - we should avoid overgeneralization here.
|
||||
|
||||
namespace expectation_failure_helpers
|
||||
{
|
||||
// bare type
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) where(expectation_failure<Iterator> const& failure) noexcept { return failure.where(); }
|
||||
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) which(expectation_failure<Iterator> const& failure) noexcept { return failure.which(); }
|
||||
|
||||
// std::optional
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) where(std::optional<expectation_failure<Iterator>> const& failure) noexcept { return failure->where(); }
|
||||
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) which(std::optional<expectation_failure<Iterator>> const& failure) noexcept { return failure->which(); }
|
||||
|
||||
// boost::optional
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) where(boost::optional<expectation_failure<Iterator>> const& failure) noexcept { return failure->where(); }
|
||||
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) which(boost::optional<expectation_failure<Iterator>> const& failure) noexcept { return failure->which(); }
|
||||
|
||||
// std::optional + std::reference_wrapper
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) where(std::reference_wrapper<std::optional<expectation_failure<Iterator>>> const& failure) noexcept { return failure.get()->where(); }
|
||||
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) which(std::reference_wrapper<std::optional<expectation_failure<Iterator>>> const& failure) noexcept { return failure.get()->which(); }
|
||||
|
||||
// boost::optional + std::reference_wrapper
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) where(std::reference_wrapper<boost::optional<expectation_failure<Iterator>>> const& failure) noexcept { return failure.get()->where(); }
|
||||
|
||||
template <typename Iterator>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) which(std::reference_wrapper<boost::optional<expectation_failure<Iterator>>> const& failure) noexcept { return failure.get()->which(); }
|
||||
} // expectation_failure_helpers
|
||||
|
||||
using expectation_failure_helpers::where;
|
||||
using expectation_failure_helpers::which;
|
||||
|
||||
} // inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
|
||||
|
||||
#if !BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
namespace detail {
|
||||
inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
|
||||
{
|
||||
inline constexpr bool has_expectation_failure_impl(unused_type const&) noexcept = delete;
|
||||
|
||||
inline constexpr bool has_expectation_failure_impl(bool& failure) noexcept {
|
||||
return failure;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
constexpr bool has_expectation_failure_impl(std::optional<expectation_failure<Iterator>> const& failure) noexcept
|
||||
{
|
||||
return failure.has_value();
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
constexpr bool has_expectation_failure_impl(boost::optional<expectation_failure<Iterator>> const& failure) noexcept
|
||||
{
|
||||
return failure.has_value();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_expectation_failure_impl(std::reference_wrapper<T> const& ref) noexcept
|
||||
{
|
||||
return has_expectation_failure_impl(ref.get());
|
||||
}
|
||||
|
||||
|
||||
template <typename Iterator, typename T>
|
||||
constexpr void set_expectation_failure_impl(bool& failure, T&& value)
|
||||
{
|
||||
failure = std::forward<T>(value);
|
||||
}
|
||||
|
||||
template <typename Iterator, typename T>
|
||||
constexpr void set_expectation_failure_impl(std::optional<expectation_failure<Iterator>>& failure, T&& value)
|
||||
{
|
||||
failure = std::forward<T>(value);
|
||||
}
|
||||
|
||||
template <typename Iterator, typename T>
|
||||
constexpr void set_expectation_failure_impl(boost::optional<expectation_failure<Iterator>>& failure, T&& value)
|
||||
{
|
||||
failure = std::forward<T>(value);
|
||||
}
|
||||
|
||||
template <typename AnyExpectationFailure, typename T>
|
||||
constexpr void set_expectation_failure_impl(std::reference_wrapper<AnyExpectationFailure>& failure, T&& value)
|
||||
{
|
||||
set_expectation_failure_impl(failure.get(), std::forward<T>(value));
|
||||
}
|
||||
|
||||
|
||||
template <typename Iterator>
|
||||
constexpr void clear_expectation_failure_impl(unused_type const&) noexcept = delete;
|
||||
|
||||
template <typename Iterator>
|
||||
constexpr void clear_expectation_failure_impl(bool& failure) noexcept
|
||||
{
|
||||
failure = false;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
constexpr void clear_expectation_failure_impl(std::optional<expectation_failure<Iterator>>& failure) noexcept
|
||||
{
|
||||
failure.reset();
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
constexpr void clear_expectation_failure_impl(boost::optional<expectation_failure<Iterator>>& failure) noexcept
|
||||
{
|
||||
failure.reset();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr void clear_expectation_failure_impl(std::reference_wrapper<T>& ref) noexcept
|
||||
{
|
||||
return clear_expectation_failure_impl(ref.get());
|
||||
}
|
||||
} // inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
|
||||
} // detail
|
||||
#endif
|
||||
|
||||
inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
|
||||
{
|
||||
template <typename Context>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr bool has_expectation_failure(Context const& context) noexcept {
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
boost::ignore_unused(context);
|
||||
return false;
|
||||
#else
|
||||
using T = expectation_failure_t<Context>;
|
||||
static_assert(
|
||||
!std::is_same_v<unused_type, T>,
|
||||
"Context type was not specified for x3::expectation_failure_tag. "
|
||||
"You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
|
||||
"Note that you must also bind the context to your skipper."
|
||||
);
|
||||
return detail::has_expectation_failure_impl(
|
||||
x3::get<expectation_failure_tag>(context));
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Creation of a brand new expectation_failure instance.
|
||||
// This is the primary overload.
|
||||
//
|
||||
template <typename Iterator, typename Subject, typename Context>
|
||||
constexpr void set_expectation_failure(
|
||||
Iterator const& where,
|
||||
Subject const& subject,
|
||||
Context const& context
|
||||
) {
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
boost::ignore_unused(where, subject, context);
|
||||
|
||||
#else
|
||||
using T = expectation_failure_t<Context>;
|
||||
static_assert(
|
||||
!std::is_same_v<unused_type, T>,
|
||||
"Context type was not specified for x3::expectation_failure_tag. "
|
||||
"You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
|
||||
"Note that you must also bind the context to your skipper."
|
||||
);
|
||||
|
||||
if constexpr (std::is_same_v<T, bool>)
|
||||
{
|
||||
boost::ignore_unused(where, subject);
|
||||
detail::set_expectation_failure_impl(
|
||||
x3::get<expectation_failure_tag>(context),
|
||||
true);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail::set_expectation_failure_impl(
|
||||
x3::get<expectation_failure_tag>(context),
|
||||
expectation_failure<Iterator>(where, what(subject)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Copy-assignment of existing expectation_failure instance.
|
||||
//
|
||||
// When you're in the situation where this functionality is
|
||||
// *really* needed, it essentially means that you have
|
||||
// multiple valid exceptions at the same time.
|
||||
//
|
||||
// There are only two decent situations that I can think of:
|
||||
//
|
||||
// (a) When you are writing a custom parser procedure with very specific characteristics:
|
||||
// 1. You're forking a context.
|
||||
// 2. Your parser class has delegated some process to child parser(s).
|
||||
// 3. The child parser(s) have raised an exceptation_failure.
|
||||
// 4. You need to propagate the failure back to the parent context.
|
||||
//
|
||||
// (b) When you truly need a nested exception.
|
||||
// That is, you're trying to preserve a nested exception structure
|
||||
// raised by nested directive: e.g. `x3::expect[x3::expect[p]]`.
|
||||
// Note that all builtin primitives just save the first error,
|
||||
// so this structure does not exist in core (as of now).
|
||||
//
|
||||
template <typename AnyExpectationFailure, typename Context>
|
||||
constexpr void set_expectation_failure(
|
||||
AnyExpectationFailure const& existing_failure,
|
||||
Context const& context
|
||||
) {
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
boost::ignore_unused(existing_failure, context);
|
||||
|
||||
#else
|
||||
using T = expectation_failure_t<Context>;
|
||||
static_assert(
|
||||
!std::is_same_v<T, unused_type>,
|
||||
"Context type was not specified for x3::expectation_failure_tag. "
|
||||
"You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
|
||||
"Note that you must also bind the context to your skipper."
|
||||
);
|
||||
|
||||
static_assert(
|
||||
std::is_assignable_v<T, AnyExpectationFailure const&>,
|
||||
"previous/current expectation failure types should be compatible"
|
||||
);
|
||||
|
||||
detail::set_expectation_failure_impl(
|
||||
x3::get<expectation_failure_tag>(context), existing_failure);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
template <typename Context>
|
||||
constexpr decltype(auto) get_expectation_failure(Context const&) = delete;
|
||||
|
||||
#else
|
||||
template <typename Context>
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
constexpr decltype(auto) get_expectation_failure(Context const& context)
|
||||
{
|
||||
using T = expectation_failure_t<Context>;
|
||||
static_assert(
|
||||
!std::is_same_v<T, unused_type>,
|
||||
"Context type was not specified for x3::expectation_failure_tag. "
|
||||
"You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
|
||||
"Note that you must also bind the context to your skipper."
|
||||
);
|
||||
|
||||
return x3::get<expectation_failure_tag>(context);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename Context>
|
||||
constexpr void clear_expectation_failure(Context const& context) noexcept
|
||||
{
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
boost::ignore_unused(context);
|
||||
#else
|
||||
using T = expectation_failure_t<Context>;
|
||||
static_assert(
|
||||
!std::is_same_v<T, unused_type>,
|
||||
"Context type was not specified for x3::expectation_failure_tag. "
|
||||
"You probably forgot: `x3::with<x3::expectation_failure_tag>(failure)[p]`. "
|
||||
"Note that you must also bind the context to your skipper."
|
||||
);
|
||||
detail::clear_expectation_failure_impl(
|
||||
x3::get<expectation_failure_tag>(context));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // inline namespace BOOST_SPIRIT_X3_EXPECTATION_FAILURE_NS
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -79,7 +79,14 @@ run difference.cpp ;
|
||||
run eoi.cpp ;
|
||||
run eol.cpp ;
|
||||
run eps.cpp ;
|
||||
run expect.cpp ;
|
||||
|
||||
run expect_throw.cpp ;
|
||||
run expect_nothrow.cpp ;
|
||||
|
||||
obj expect_odr_throw : expect_odr_throw.cpp ;
|
||||
obj expect_odr_nothrow : expect_odr_nothrow.cpp ;
|
||||
run expect_odr.cpp expect_odr_throw expect_odr_nothrow ;
|
||||
|
||||
run extract_int.cpp ;
|
||||
run int1.cpp ;
|
||||
run kleene.cpp ;
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2013 Joel de Guzman
|
||||
|
||||
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/fusion/include/vector.hpp>
|
||||
#include <boost/fusion/include/at.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "test.hpp"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
using namespace boost::spirit;
|
||||
using namespace boost::spirit::x3::ascii;
|
||||
using boost::spirit::x3::lit;
|
||||
using boost::spirit::x3::expect;
|
||||
using spirit_test::test;
|
||||
using spirit_test::test_attr;
|
||||
using boost::spirit::x3::expectation_failure;
|
||||
|
||||
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(expect['x']);
|
||||
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(char_ > char_);
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((test("aa", char_ >> expect[char_])));
|
||||
BOOST_TEST((test("aaa", char_ >> expect[char_ >> char_('a')])));
|
||||
BOOST_TEST((test("xi", char_('x') >> expect[char_('i')])));
|
||||
BOOST_TEST((!test("xi", char_('y') >> expect[char_('o')]))); // should not throw!
|
||||
BOOST_TEST((test("xin", char_('x') >> expect[char_('i') >> char_('n')])));
|
||||
BOOST_TEST((!test("xi", char_('x') >> expect[char_('o')])));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "'o'");
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "i");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((test("aa", char_ > char_)));
|
||||
BOOST_TEST((test("aaa", char_ > char_ > char_('a'))));
|
||||
BOOST_TEST((test("xi", char_('x') > char_('i'))));
|
||||
BOOST_TEST((!test("xi", char_('y') > char_('o')))); // should not throw!
|
||||
BOOST_TEST((test("xin", char_('x') > char_('i') > char_('n'))));
|
||||
BOOST_TEST((!test("xi", char_('x') > char_('o'))));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "'o'");
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "i");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((!test("ay:a", char_ > char_('x') >> ':' > 'a')));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
#ifndef BOOST_SPIRIT_X3_NO_RTTI
|
||||
BOOST_TEST(x.which().find("sequence") != std::string::npos);
|
||||
#else
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "undefined");
|
||||
#endif
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "y:a");
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(BOOST_CLANG)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses"
|
||||
#endif
|
||||
{ // Test that attributes with > (sequences) work just like >> (sequences)
|
||||
|
||||
using boost::fusion::vector;
|
||||
using boost::fusion::at_c;
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
BOOST_TEST((test_attr(" a\n b\n c",
|
||||
char_ > char_ > char_, attr, space)));
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
BOOST_TEST((test_attr(" a\n b\n c",
|
||||
char_ > char_ >> char_, attr, space)));
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
BOOST_TEST((test_attr(" a, b, c",
|
||||
char_ >> ',' > char_ >> ',' > char_, attr, space)));
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
std::string attr;
|
||||
BOOST_TEST((test_attr("'azaaz'",
|
||||
"'" > *(char_("a") | char_("z")) > "'", attr, space)));
|
||||
BOOST_TEST(attr == "azaaz");
|
||||
}
|
||||
}
|
||||
#if defined(BOOST_CLANG)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((test(" a a", char_ > char_, space)));
|
||||
BOOST_TEST((test(" x i", char_('x') > char_('i'), space)));
|
||||
BOOST_TEST((!test(" x i", char_('x') > char_('o'), space)));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "'o'");
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "i");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
BOOST_TEST((test("bar", expect[lit("foo")])));
|
||||
}
|
||||
catch (expectation_failure<char const*> const& x)
|
||||
{
|
||||
BOOST_TEST_CSTR_EQ(x.which().c_str(), "\"foo\"");
|
||||
BOOST_TEST_CSTR_EQ(x.where(), "bar");
|
||||
}
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
807
test/x3/expect.ipp
Normal file
807
test/x3/expect.ipp
Normal file
@@ -0,0 +1,807 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-2013 Joel de Guzman
|
||||
Copyright (c) 2017 wanghan02
|
||||
Copyright (c) 2024 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/binary.hpp>
|
||||
#include <boost/spirit/home/x3/directive/with.hpp>
|
||||
#include <boost/spirit/home/x3/support/expectation.hpp>
|
||||
#include <boost/fusion/include/vector.hpp>
|
||||
#include <boost/fusion/include/at.hpp>
|
||||
#include <boost/preprocessor/facilities/overload.hpp>
|
||||
#include <boost/preprocessor/facilities/expand.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test.hpp"
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
|
||||
#if BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE
|
||||
# define TEST_SUCCESS_IMPL(tester, ...) \
|
||||
BOOST_TEST_NO_THROW({ \
|
||||
BOOST_TEST(tester(__VA_ARGS__)); \
|
||||
})
|
||||
|
||||
# define TEST_FAILURE_IMPL_4(tester, input, parser, catch_stmt) \
|
||||
BOOST_TEST_THROWS( \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
BOOST_TEST(tester(input, parser)); \
|
||||
} \
|
||||
catch (expectation_failure<char const*> const& x) \
|
||||
{ \
|
||||
catch_stmt \
|
||||
throw; \
|
||||
} \
|
||||
}, \
|
||||
expectation_failure<char const*> \
|
||||
)
|
||||
|
||||
# define TEST_FAILURE_IMPL_5(tester, input, parser, arg0, catch_stmt) \
|
||||
BOOST_TEST_THROWS( \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
BOOST_TEST(tester(input, parser, arg0)); \
|
||||
} \
|
||||
catch (expectation_failure<char const*> const& x) \
|
||||
{ \
|
||||
catch_stmt \
|
||||
throw; \
|
||||
} \
|
||||
}, \
|
||||
expectation_failure<char const*> \
|
||||
)
|
||||
|
||||
# define TEST_FAILURE_IMPL_6(tester, input, parser, arg0, arg1, catch_stmt) \
|
||||
BOOST_TEST_THROWS( \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
BOOST_TEST(tester(input, parser, arg0, arg1)); \
|
||||
} \
|
||||
catch (expectation_failure<char const*> const& x) \
|
||||
{ \
|
||||
catch_stmt \
|
||||
throw; \
|
||||
} \
|
||||
}, \
|
||||
expectation_failure<char const*> \
|
||||
)
|
||||
|
||||
#else
|
||||
|
||||
using expectation_failure_optional_t = x3::expectation_failure_optional<char const*>;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename T, typename Enabled = std::enable_if_t<!std::is_const_v<T>>>
|
||||
decltype(auto) wrap_with_expectation_failure(expectation_failure_optional_t&, T& attr)
|
||||
{
|
||||
return attr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto wrap_with_expectation_failure(expectation_failure_optional_t& xopt, T const& skipper)
|
||||
{
|
||||
return x3::with<x3::expectation_failure_tag>(xopt)[skipper];
|
||||
}
|
||||
} // detail
|
||||
|
||||
# define TEST_SUCCESS_IMPL_3(tester, input, parser) \
|
||||
BOOST_TEST_NO_THROW({ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
BOOST_TEST(tester(input, with<expectation_failure_tag>(xopt)[parser])); \
|
||||
BOOST_TEST(!xopt.has_value()); \
|
||||
})
|
||||
|
||||
# define TEST_SUCCESS_IMPL_4(tester, input, parser, arg0) \
|
||||
BOOST_TEST_NO_THROW({ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
BOOST_TEST(tester( \
|
||||
input, \
|
||||
with<expectation_failure_tag>(xopt)[parser], \
|
||||
detail::wrap_with_expectation_failure(xopt, arg0) \
|
||||
)); \
|
||||
BOOST_TEST(!xopt.has_value()); \
|
||||
})
|
||||
|
||||
# define TEST_SUCCESS_IMPL_5(tester, input, parser, arg0, arg1) \
|
||||
BOOST_TEST_NO_THROW({ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
BOOST_TEST(tester( \
|
||||
input, \
|
||||
with<expectation_failure_tag>(xopt)[parser], \
|
||||
detail::wrap_with_expectation_failure(xopt, arg0), \
|
||||
detail::wrap_with_expectation_failure(xopt, arg1) \
|
||||
)); \
|
||||
BOOST_TEST(!xopt.has_value()); \
|
||||
})
|
||||
|
||||
# define TEST_SUCCESS_IMPL(...) BOOST_PP_EXPAND(BOOST_PP_OVERLOAD(TEST_SUCCESS_IMPL_, __VA_ARGS__) (__VA_ARGS__))
|
||||
|
||||
|
||||
# define TEST_FAILURE_IMPL_4(tester, input, parser, catch_stmt) \
|
||||
BOOST_TEST_NO_THROW( \
|
||||
{ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
do \
|
||||
{ \
|
||||
if (!BOOST_TEST(tester(input, with<expectation_failure_tag>(xopt)[parser]))) break; \
|
||||
if (!BOOST_TEST(xopt.has_value())) break; \
|
||||
auto const& x = *xopt; \
|
||||
catch_stmt \
|
||||
} while (false); \
|
||||
} \
|
||||
)
|
||||
|
||||
# define TEST_FAILURE_IMPL_5(tester, input, parser, arg0, catch_stmt) \
|
||||
BOOST_TEST_NO_THROW( \
|
||||
{ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
do \
|
||||
{ \
|
||||
if (!BOOST_TEST(tester( \
|
||||
input, \
|
||||
with<expectation_failure_tag>(xopt)[parser], \
|
||||
detail::wrap_with_expectation_failure(xopt, arg0))) \
|
||||
) break; \
|
||||
\
|
||||
if (!BOOST_TEST(xopt.has_value())) break; \
|
||||
auto const& x = *xopt; \
|
||||
catch_stmt \
|
||||
} while (false); \
|
||||
} \
|
||||
)
|
||||
|
||||
# define TEST_FAILURE_IMPL_6(tester, input, parser, arg0, arg1, catch_stmt) \
|
||||
BOOST_TEST_NO_THROW( \
|
||||
{ \
|
||||
expectation_failure_optional_t xopt; \
|
||||
do \
|
||||
{ \
|
||||
if (!BOOST_TEST(tester( \
|
||||
input, \
|
||||
with<expectation_failure_tag>(xopt)[parser], \
|
||||
detail::wrap_with_expectation_failure(xopt, arg0), \
|
||||
detail::wrap_with_expectation_failure(xopt, arg1))) \
|
||||
) break; \
|
||||
\
|
||||
if (!BOOST_TEST(xopt.has_value())) break; \
|
||||
auto const& x = *xopt; \
|
||||
catch_stmt \
|
||||
} while (false); \
|
||||
} \
|
||||
)
|
||||
#endif
|
||||
|
||||
#define TEST_FAILURE_IMPL(...) BOOST_PP_EXPAND(BOOST_PP_OVERLOAD(TEST_FAILURE_IMPL_, __VA_ARGS__) (__VA_ARGS__))
|
||||
|
||||
// Comments below are intentionally written verbosely
|
||||
// to provide human-friendly intellisense tooltip for testers
|
||||
|
||||
// expect_throw: parser = ok, exception = none
|
||||
// expect_nothrow: parser = ok, exception = none, expectation_failure = none
|
||||
#define TEST_SUCCESS_PASS(...) TEST_SUCCESS_IMPL(test, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = ok, exception = none
|
||||
// expect_nothrow: parser = ok, exception = none, expectation_failure = none
|
||||
#define TEST_ATTR_SUCCESS_PASS(...) TEST_SUCCESS_IMPL(test_attr, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = fail, exception = none
|
||||
// expect_nothrow: parser = fail, exception = none, expectation_failure = none
|
||||
#define TEST_SUCCESS_FAIL(...) TEST_SUCCESS_IMPL(!test, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = fail, exception = none
|
||||
// expect_nothrow: parser = fail, exception = none, expectation_failure = none
|
||||
#define TEST_ATTR_SUCCESS_FAIL(...) TEST_SUCCESS_IMPL(!test_attr, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = fail, exception = thrown
|
||||
// expect_nothrow: parser = fail, exception = none, expectation_failure = yes
|
||||
#define TEST_FAILURE(...) TEST_FAILURE_IMPL(!test, __VA_ARGS__)
|
||||
|
||||
// expect_throw: parser = fail, exception = thrown
|
||||
// expect_nothrow: parser = fail, exception = none, expectation_failure = yes
|
||||
#define TEST_ATTR_FAILURE(...) TEST_FAILURE_IMPL(!test_attr, __VA_ARGS__)
|
||||
|
||||
|
||||
// For testers; development QOL purpose only.
|
||||
|
||||
#define DEBUG_PRINT(x) \
|
||||
do { \
|
||||
std::cout << "----------------------------------\n"; \
|
||||
std::cout << "which: " << x.which() << "\n"; \
|
||||
std::cout << "where: " << x.where() << "\n"; \
|
||||
std::cout << "----------------------------------\n"; \
|
||||
BOOST_TEST(!"remove DEBUG_PRINT before commit!"); \
|
||||
} while (false)
|
||||
|
||||
|
||||
#if defined(TEST_MAIN_FUNC)
|
||||
# define TEST_MAIN_FUNC_IS_MAIN 0
|
||||
#else
|
||||
# define TEST_MAIN_FUNC_IS_MAIN 1
|
||||
# define TEST_MAIN_FUNC main
|
||||
#endif
|
||||
|
||||
int TEST_MAIN_FUNC()
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
using x3::standard::alpha;
|
||||
using x3::standard::digit;
|
||||
using x3::standard::space;
|
||||
using x3::standard::blank;
|
||||
using x3::standard::char_;
|
||||
using x3::standard::string;
|
||||
using x3::standard::lit;
|
||||
|
||||
// using x3::lit;
|
||||
using x3::expect;
|
||||
using x3::lexeme;
|
||||
using x3::no_case;
|
||||
using x3::no_skip;
|
||||
using x3::omit;
|
||||
using x3::raw;
|
||||
using x3::skip;
|
||||
using x3::seek;
|
||||
using x3::repeat;
|
||||
using x3::matches;
|
||||
using x3::eps;
|
||||
using x3::eoi;
|
||||
using x3::eol;
|
||||
using x3::attr;
|
||||
using x3::dword;
|
||||
using x3::int_;
|
||||
using x3::symbols;
|
||||
using x3::confix;
|
||||
using x3::with;
|
||||
using x3::expectation_failure;
|
||||
using x3::expectation_failure_tag;
|
||||
|
||||
using boost::fusion::vector;
|
||||
using boost::fusion::at_c;
|
||||
|
||||
using spirit_test::test;
|
||||
using spirit_test::test_attr;
|
||||
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(expect['x']);
|
||||
BOOST_SPIRIT_ASSERT_CONSTEXPR_CTORS(char_ > char_);
|
||||
|
||||
{
|
||||
TEST_SUCCESS_PASS("aa", char_ >> expect[char_]);
|
||||
TEST_SUCCESS_PASS("aaa", char_ >> expect[char_ >> char_('a')]);
|
||||
TEST_SUCCESS_PASS("xi", char_('x') >> expect[char_('i')]);
|
||||
TEST_SUCCESS_FAIL("xi", char_('y') >> expect[char_('o')]); // should not throw!
|
||||
TEST_SUCCESS_PASS("xin", char_('x') >> expect[char_('i') >> char_('n')]);
|
||||
|
||||
TEST_FAILURE("xi", char_('x') >> expect[char_('o')], {
|
||||
BOOST_TEST(x.which() == "'o'"sv);
|
||||
BOOST_TEST(x.where() == "i"sv);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
TEST_SUCCESS_PASS("aa", char_ > char_);
|
||||
TEST_SUCCESS_PASS("aaa", char_ > char_ > char_('a'));
|
||||
TEST_SUCCESS_PASS("xi", char_('x') > char_('i'));
|
||||
TEST_SUCCESS_FAIL("xi", char_('y') > char_('o')); // should not throw!
|
||||
TEST_SUCCESS_PASS("xin", char_('x') > char_('i') > char_('n'));
|
||||
|
||||
TEST_FAILURE("xi", char_('x') > char_('o'),
|
||||
{
|
||||
BOOST_TEST(x.which() == "'o'"sv);
|
||||
BOOST_TEST(x.where() == "i"sv);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
#ifndef BOOST_SPIRIT_X3_NO_RTTI
|
||||
TEST_FAILURE("ay:a", char_ > char_('x') >> ':' > 'a',
|
||||
{
|
||||
BOOST_TEST(x.which().find("sequence") != std::string::npos);
|
||||
BOOST_TEST(x.where() == "y:a"sv);
|
||||
});
|
||||
#else
|
||||
TEST_FAILURE("ay:a", char_ > char_('x') >> ':' > 'a',
|
||||
{
|
||||
BOOST_TEST(x.which() == "undefined"sv);
|
||||
BOOST_TEST(x.where() == "y:a"sv);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(BOOST_CLANG)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses"
|
||||
#endif
|
||||
|
||||
// Test that attributes with > (sequences) work just like >> (sequences)
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
TEST_ATTR_SUCCESS_PASS(" a\n b\n c", char_ > char_ > char_, attr, space);
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
TEST_ATTR_SUCCESS_PASS(" a\n b\n c", char_ > char_ >> char_, attr, space);
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
vector<char, char, char> attr;
|
||||
TEST_ATTR_SUCCESS_PASS(" a, b, c", char_ >> ',' > char_ >> ',' > char_, attr, space);
|
||||
BOOST_TEST((at_c<0>(attr) == 'a'));
|
||||
BOOST_TEST((at_c<1>(attr) == 'b'));
|
||||
BOOST_TEST((at_c<2>(attr) == 'c'));
|
||||
}
|
||||
|
||||
{
|
||||
std::string attr;
|
||||
TEST_ATTR_SUCCESS_PASS("'azaaz'", "'" > *(char_("a") | char_("z")) > "'", attr, space);
|
||||
BOOST_TEST(attr == "azaaz");
|
||||
}
|
||||
|
||||
#if defined(BOOST_CLANG)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
{
|
||||
TEST_SUCCESS_PASS(" a a", char_ > char_, space);
|
||||
TEST_SUCCESS_PASS(" x i", char_('x') > char_('i'), space);
|
||||
|
||||
TEST_FAILURE(" x i", char_('x') > char_('o'), space, {
|
||||
BOOST_TEST(x.which() == "'o'"sv);
|
||||
BOOST_TEST(x.where() == "i"sv);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
TEST_FAILURE("bar", expect[lit("foo")],
|
||||
{
|
||||
BOOST_TEST(x.which() == "\"foo\""sv);
|
||||
BOOST_TEST(x.where() == "bar"sv);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// skipper
|
||||
{
|
||||
TEST_FAILURE("accb", repeat(7)[alpha], (lit('a') > 'b') | (lit('c') > 'd'), {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "ccb"sv);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// ********* Developers note **********
|
||||
//
|
||||
// As of now (see `git blame`), get_info<T> is still not
|
||||
// specialized for many of the X3 parsers so that the
|
||||
// value of `expectation_failure<...>::which()` will be
|
||||
// implementation-defined demangled string.
|
||||
// Therefore it's essentially impossible to test them
|
||||
// right now; further work must be done.
|
||||
//
|
||||
// Some specific situations are already been reported
|
||||
// (e.g. https://github.com/boostorg/spirit/issues/777)
|
||||
// but we really need to implement all specializations for
|
||||
// X3's predefined parsers, not just the one reported above.
|
||||
//
|
||||
|
||||
|
||||
// sanity check: test expectation_failure propagation
|
||||
// on custom skippers
|
||||
{
|
||||
TEST_SUCCESS_PASS("a..b", lit('a') >> 'b', lit('.') >> '.');
|
||||
TEST_SUCCESS_FAIL("a..b", lit('a') >> 'b', lit('.') >> 'z');
|
||||
|
||||
TEST_SUCCESS_PASS("a b", lit('a') >> 'b', blank);
|
||||
TEST_SUCCESS_PASS("a..b", lit('a') >> 'b', +lit('.'));
|
||||
TEST_SUCCESS_PASS("a..b", lit('a') >> 'b', lit('.') >> '.');
|
||||
|
||||
// if this test fails, there might be a problem in x3::skip_over
|
||||
TEST_FAILURE ("a..b", lit('a') >> 'b', lit('.') >> expect[lit('z')], {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == ".b"sv);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// skip(...) version of the code above
|
||||
// we must test against semantically identical cases!
|
||||
|
||||
TEST_SUCCESS_PASS("a..b", skip(lit('.') >> '.')[lit('a') >> 'b']);
|
||||
TEST_SUCCESS_FAIL("a..b", skip(lit('.') >> 'z')[lit('a') >> 'b']);
|
||||
|
||||
TEST_SUCCESS_PASS("a b", skip(blank)[lit('a') >> 'b']);
|
||||
TEST_SUCCESS_PASS("a..b", skip(+lit('.'))[lit('a') >> 'b']);
|
||||
TEST_SUCCESS_PASS("a..b", skip(lit('.') >> '.')[lit('a') >> 'b']);
|
||||
|
||||
// if this test fails, there might be a problem in x3::skip_over and/or x3::skip_directive
|
||||
TEST_FAILURE ("a..b", skip(lit('.') >> expect[lit('z')])[lit('a') >> 'b'], {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == ".b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// sanity check; test `post_skip` in `x3::phrase_parse(...)`
|
||||
{
|
||||
TEST_SUCCESS_PASS("a..b..", lit('a') >> 'b', lit('.') >> '.');
|
||||
TEST_SUCCESS_FAIL("a..b..", lit('a') >> 'z', lit('.') >> '.');
|
||||
TEST_SUCCESS_FAIL("a..b..", lit('a') >> 'b', lit('.') >> 'z');
|
||||
|
||||
// should fail in `post_skip`
|
||||
TEST_SUCCESS_FAIL("a..b.z", lit('a') >> 'b', lit('.') >> '.');
|
||||
|
||||
// if this test fails, x3::skip_over is BUGGED when `post_skip` is run
|
||||
TEST_FAILURE("a..b.z", lit('a') >> 'b', lit('.') > '.', {
|
||||
BOOST_TEST(x.which() == "'.'"sv);
|
||||
BOOST_TEST(x.where() == "z"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// sequence
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab", lit('a') >> 'b');
|
||||
TEST_SUCCESS_FAIL("ac", lit('a') >> 'b');
|
||||
|
||||
TEST_FAILURE("ac", lit('a') >> expect[lit('b')], {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
TEST_FAILURE("ac", lit('a') > lit('b'), {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// auxilary parsers
|
||||
{
|
||||
TEST_SUCCESS_PASS("a12", lit('a') > eps > +digit);
|
||||
TEST_SUCCESS_PASS("a12", lit('a') > +digit > eoi);
|
||||
TEST_SUCCESS_PASS("a12\n", lit('a') > +digit > eol);
|
||||
|
||||
TEST_FAILURE("a12", lit('a') > eps(false) > +digit, {
|
||||
BOOST_TEST(x.where() == "12"sv);
|
||||
});
|
||||
TEST_FAILURE("a12", lit('a') > eoi > +digit, {
|
||||
BOOST_TEST(x.where() == "12"sv);
|
||||
});
|
||||
TEST_FAILURE("a12\n", lit('a') > eol > +digit, {
|
||||
BOOST_TEST(x.where() == "12\n"sv);
|
||||
});
|
||||
|
||||
int n = 0;
|
||||
TEST_ATTR_SUCCESS_PASS("abc", lit("abc") > attr(12) > eoi, n);
|
||||
BOOST_TEST(n == 12);
|
||||
}
|
||||
|
||||
// binary, numeric, char, string parsers
|
||||
{
|
||||
TEST_SUCCESS_PASS("12abcd", +digit > dword);
|
||||
TEST_SUCCESS_PASS("abc12", +alpha > int_);
|
||||
TEST_SUCCESS_PASS("12a", +digit > lit('a'));
|
||||
|
||||
TEST_FAILURE("12abc", +digit > dword, {
|
||||
BOOST_TEST(x.where() == "abc"sv);
|
||||
});
|
||||
TEST_FAILURE("abc", +alpha > int_, {
|
||||
BOOST_TEST(x.where() == ""sv);
|
||||
});
|
||||
TEST_FAILURE("12a", +digit > lit('b'), {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "a"sv);
|
||||
});
|
||||
|
||||
symbols<> s;
|
||||
s.add("cat");
|
||||
TEST_SUCCESS_PASS("12cat", +digit > s);
|
||||
TEST_FAILURE("12dog", +digit > s, {
|
||||
BOOST_TEST(x.where() == "dog"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// confix
|
||||
{
|
||||
TEST_SUCCESS_PASS("[12cat]", confix('[', ']')[+digit > lit("cat")]);
|
||||
TEST_FAILURE("[12dog]", confix('[', ']')[+digit > lit("cat")], {
|
||||
BOOST_TEST(x.which() == "\"cat\""sv);
|
||||
BOOST_TEST(x.where() == "dog]"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// expect
|
||||
{
|
||||
TEST_SUCCESS_PASS("abc", lit('a') >> expect[lit('b') >> 'c']);
|
||||
TEST_FAILURE("abc", lit('a') >> expect[lit('b') >> 'd'], {
|
||||
BOOST_TEST(x.where() == "bc"sv);
|
||||
});
|
||||
TEST_FAILURE("abc", lit('a') >> expect[lit('b') > 'd'], {
|
||||
BOOST_TEST(x.which() == "'d'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// lexeme
|
||||
{
|
||||
TEST_SUCCESS_PASS("12 ab", int_ >> lexeme[lit('a') > 'b'], space);
|
||||
TEST_FAILURE("12 a b", int_ >> lexeme[lit('a') > 'b'], space, {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == " b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// matches
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab", matches[lit('a') >> 'b']);
|
||||
TEST_SUCCESS_PASS("ac", matches[lit('a') >> 'b'] >> "ac");
|
||||
TEST_SUCCESS_PASS("ab", matches[lit('a') > 'b']);
|
||||
TEST_FAILURE("ac", matches[lit('a') > 'b'] >> "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
|
||||
bool attr = false;
|
||||
TEST_ATTR_SUCCESS_PASS("ab", matches[lit('a') > 'b'], attr);
|
||||
BOOST_TEST(attr == true);
|
||||
}
|
||||
|
||||
// no_case
|
||||
{
|
||||
TEST_SUCCESS_PASS("12 aB", int_ >> no_case[lit('a') > 'b'], space);
|
||||
TEST_FAILURE("12 aB", int_ >> no_case[lit('a') > 'c'], space, {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "B"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// no_skip
|
||||
{
|
||||
TEST_SUCCESS_PASS("12 3ab", int_ >> int_ >> no_skip[lit('a') > 'b'], space);
|
||||
TEST_FAILURE("12 3ab", int_ >> int_ >> no_skip[lit('a') > 'c'], space, {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// skip
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab[]c[]d", skip(lit('[') > ']')[+alpha]);
|
||||
TEST_FAILURE("ab[]c[5]d", skip(lit('[') > ']')[+alpha], {
|
||||
BOOST_TEST(x.which() == "']'"sv);
|
||||
BOOST_TEST(x.where() == "5]d"sv);
|
||||
});
|
||||
|
||||
TEST_SUCCESS_PASS("a1[]b2c3[]d4", skip(lit('[') > ']')[+(alpha > digit)]);
|
||||
TEST_FAILURE("a1[]b2c3[]d", skip(lit('[') > ']')[+(alpha > digit)], {
|
||||
BOOST_TEST(x.where() == ""sv);
|
||||
});
|
||||
|
||||
TEST_FAILURE("a b c", lit('a') > 'c', space, {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "b c"sv);
|
||||
});
|
||||
|
||||
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b c d", skip(space)[*char_ > lit('z')], s, {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == ""sv);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_SUCCESS_PASS("a b\n c\n d", char_('a') > char_('b') > skip(space)[char_('c') > char_('d')], s, blank);
|
||||
BOOST_TEST(s == "abcd");
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b\n c\n d", char_('a') > char_('z') > skip(space)[char_('c') > char_('d')], s, blank, {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == "b\n c\n d"sv);
|
||||
});
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b\n c\n d", char_('a') > char_('b') > skip(space)[char_('z') > char_('d')], s, blank, {
|
||||
BOOST_TEST(x.where() == "\n c\n d"sv);
|
||||
});
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b\n c\n d", char_('a') > char_('b') > skip(space)[char_('c') > char_('z')], s, blank, {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == "d"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// reskip
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_SUCCESS_PASS("a b c d", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('d')]], s, blank);
|
||||
BOOST_TEST(s == "abcd");
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b c d", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('z')]], s, blank, {
|
||||
BOOST_TEST(x.where() == "d"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// reskip with expectation failure context propagation
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_SUCCESS_PASS("a b c d e", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('d') > char_('e')]], s, blank);
|
||||
BOOST_TEST(s == "abcde");
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b c d e", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('z') > char_('e')]], s, blank, {
|
||||
BOOST_TEST(x.where() == " d e"sv);
|
||||
});
|
||||
}
|
||||
{
|
||||
std::string s;
|
||||
TEST_ATTR_FAILURE("a b c d e", char_('a') > char_('b') > no_skip[lit(' ') > char_('c') > skip[char_('d') > char_('z')]], s, blank, {
|
||||
BOOST_TEST(x.which() == "'z'"sv);
|
||||
BOOST_TEST(x.where() == "e"sv);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// omit
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab", omit[lit('a') > 'b']);
|
||||
TEST_FAILURE("ab", omit[lit('a') > 'c'], {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// raw
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab", raw[lit('a') > 'b']);
|
||||
TEST_FAILURE("ab", raw[lit('a') > 'c'], {
|
||||
BOOST_TEST(x.which() == "'c'"sv);
|
||||
BOOST_TEST(x.where() == "b"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// repeat
|
||||
{
|
||||
TEST_SUCCESS_PASS("ababac", repeat(1, 3)[lit('a') >> 'b'] >> "ac" | +alpha);
|
||||
|
||||
TEST_FAILURE("ababac", repeat(1, 3)[lit('a') > 'b'] | +alpha, {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
TEST_FAILURE("acab", repeat(2, 3)[lit('a') > 'b'] | +alpha, {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "cab"sv);
|
||||
});
|
||||
|
||||
TEST_SUCCESS_PASS("bcab", repeat(2, 3)[lit('a') > 'b'] | +alpha);
|
||||
}
|
||||
|
||||
// seek
|
||||
{
|
||||
TEST_SUCCESS_PASS("a1b1c1", seek[lit('c') > '1']);
|
||||
TEST_FAILURE("a1b1c2c1", seek[lit('c') > '1'], {
|
||||
BOOST_TEST(x.which() == "'1'"sv);
|
||||
BOOST_TEST(x.where() == "2c1"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// alternative
|
||||
{
|
||||
TEST_SUCCESS_PASS("ac", lit('a') >> 'b' | "ac");
|
||||
TEST_SUCCESS_PASS("ac", lit('a') >> 'b' | lit('a') >> 'd' | "ac");
|
||||
|
||||
TEST_FAILURE("ac", (lit('a') > 'b') | "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
TEST_FAILURE("ac", lit('a') >> 'b' | (lit('a') > 'd') | "ac", {
|
||||
BOOST_TEST(x.which() == "'d'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// predicate
|
||||
{
|
||||
TEST_SUCCESS_PASS("abc", lit('a') >> &(lit('b') > 'c') >> "bc");
|
||||
TEST_FAILURE("abc", lit('a') >> &(lit('b') > 'd') >> "bc", {
|
||||
BOOST_TEST(x.which() == "'d'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// difference
|
||||
{
|
||||
TEST_SUCCESS_PASS("bcac", *(char_ - (lit('a') >> 'b')));
|
||||
TEST_SUCCESS_PASS("bcab", *(char_ - (lit('a') > 'b')) >> "ab");
|
||||
TEST_FAILURE("bcac", *(char_ - (lit('a') > 'b')) >> "ab", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// kleene
|
||||
{
|
||||
TEST_SUCCESS_PASS("abac", *(lit('a') >> 'b') >> "ac");
|
||||
TEST_SUCCESS_PASS("abbc", *(lit('a') > 'b') >> "bc");
|
||||
TEST_FAILURE("abac", *(lit('a') > 'b') >> "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// list
|
||||
{
|
||||
TEST_SUCCESS_PASS("ab::ab::ac", (lit('a') >> 'b') % (lit(':') >> ':') >> "::ac");
|
||||
TEST_SUCCESS_PASS("ab::ab:ac", (lit('a') > 'b') % (lit(':') >> ':') >> ":ac");
|
||||
|
||||
TEST_FAILURE("ab::ab::ac", (lit('a') > 'b') % (lit(':') >> ':') >> "::ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
TEST_FAILURE("ab::ab:ab", (lit('a') >> 'b') % (lit(':') > ':') >> ":ab", {
|
||||
BOOST_TEST(x.which() == "':'"sv);
|
||||
BOOST_TEST(x.where() == "ab"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// not
|
||||
{
|
||||
TEST_SUCCESS_PASS("[ac]", lit('[') >> !(lit('a') >> 'b') >> +alpha >> ']');
|
||||
TEST_SUCCESS_PASS("[bc]", lit('[') >> !(lit('a') > 'b') >> +alpha >> ']');
|
||||
TEST_FAILURE("[ac]", lit('[') >> !(lit('a') > 'b') >> +alpha >> ']', {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c]"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// optional
|
||||
{
|
||||
TEST_SUCCESS_PASS("ac", -(lit('a') >> 'b') >> "ac");
|
||||
TEST_SUCCESS_PASS("ab", -(lit('a') > 'b'));
|
||||
TEST_FAILURE("ac", -(lit('a') > 'b') >> "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
// plus
|
||||
{
|
||||
TEST_SUCCESS_PASS("abac", +(lit('a') >> 'b') >> "ac");
|
||||
TEST_SUCCESS_PASS("abbc", +(lit('a') > 'b') >> "bc");
|
||||
TEST_FAILURE("abac", +(lit('a') > 'b') >> "ac", {
|
||||
BOOST_TEST(x.which() == "'b'"sv);
|
||||
BOOST_TEST(x.where() == "c"sv);
|
||||
});
|
||||
}
|
||||
|
||||
#if TEST_MAIN_FUNC_IS_MAIN
|
||||
return boost::report_errors();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
8
test/x3/expect_nothrow.cpp
Normal file
8
test/x3/expect_nothrow.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2024 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)
|
||||
=============================================================================*/
|
||||
#define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 0
|
||||
#include "expect.ipp"
|
||||
45
test/x3/expect_odr.cpp
Normal file
45
test/x3/expect_odr.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2024 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/core/lightweight_test.hpp>
|
||||
|
||||
int main_expect_throw();
|
||||
int main_expect_nothrow();
|
||||
|
||||
// Test case for mixing throwing/non-throwing modes in a
|
||||
// single executable.
|
||||
int main()
|
||||
{
|
||||
main_expect_throw();
|
||||
|
||||
// If you encounter a GS Security Check failure (on MSVC) or
|
||||
// buffer-related assertion failures (on other compilers),
|
||||
// that is not a bug in X3's buffer manipulation code.
|
||||
// Instead, it's likely due to an ODR violation, which must
|
||||
// be fixed in X3's implementation details.
|
||||
//
|
||||
// In common cases it should be detectable as a link error.
|
||||
//
|
||||
// The worst case is with Visual Studio 2022 (at least in version 17.10.5),
|
||||
// which does not even detect the error at link time. It will
|
||||
// compile and link successfully, but when you execute the
|
||||
// resulting binary, the assembly mistakenly assumes that
|
||||
// `x3::expectation_failure` is an instance of the *throwing*
|
||||
// version, causing it to access a nonexistent base class.
|
||||
//
|
||||
// This situation leads to a genuine stack overrun, which
|
||||
// will be caught by the GS Buffer Security Check:
|
||||
// https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check
|
||||
//
|
||||
// Surprisingly, in a Release build, the assertion may not
|
||||
// be triggered as the vulnerable parameters might be handled
|
||||
// differently by the compiler.
|
||||
main_expect_nothrow();
|
||||
|
||||
BOOST_TEST(true);
|
||||
return boost::report_errors();
|
||||
}
|
||||
9
test/x3/expect_odr_nothrow.cpp
Normal file
9
test/x3/expect_odr_nothrow.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2024 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)
|
||||
=============================================================================*/
|
||||
#define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 0
|
||||
#define TEST_MAIN_FUNC main_expect_nothrow
|
||||
#include "expect.ipp"
|
||||
9
test/x3/expect_odr_throw.cpp
Normal file
9
test/x3/expect_odr_throw.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2024 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)
|
||||
=============================================================================*/
|
||||
#define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 1
|
||||
#define TEST_MAIN_FUNC main_expect_throw
|
||||
#include "expect.ipp"
|
||||
8
test/x3/expect_throw.cpp
Normal file
8
test/x3/expect_throw.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2024 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)
|
||||
=============================================================================*/
|
||||
#define BOOST_SPIRIT_X3_THROW_EXPECTATION_FAILURE 1
|
||||
#include "expect.ipp"
|
||||
Reference in New Issue
Block a user