Skip to content

Commit

Permalink
Add documentation for non-throwing expectations
Browse files Browse the repository at this point in the history
  • Loading branch information
saki7 committed Aug 12, 2024
1 parent 88d9987 commit cf643c1
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 3 deletions.
2 changes: 2 additions & 0 deletions doc/x3/spirit_x3.qbk
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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]
Expand Down
12 changes: 9 additions & 3 deletions doc/x3/tutorial/error_handling.qbk
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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;
}
};
Expand All @@ -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:
Expand Down
97 changes: 97 additions & 0 deletions doc/x3/tutorial/non_throwing_expectations.qbk
Original file line number Diff line number Diff line change
@@ -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]

0 comments on commit cf643c1

Please sign in to comment.