mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
Add documentation for non-throwing expectations
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]
|
||||
Reference in New Issue
Block a user