Avoid throwing expectation_failure when the expectation parser fails
How to avoid throwing an exception when the wait parser fails?
I have a rule "function" > (!x3::lexeme[keyword >> !(x3::alnum | '_')] >> symbol) > ('(' > -lvalue_list > ')') > statements > "end"
for parsing code:
function a() return one end
keyword
( zero
, one
, function
, return
, end
Etc.).
If I pass the parser with the code function one() return zero end
, then in the function an expect_directive::parse
exception is thrown here:
if (!r)
{
boost::throw_exception(
expectation_failure<Iterator>(
first, what(this->subject)));
}
When that happens, I got it . The program ended unexpectedly. or Canceled (kernel dump) (depending on the terminal used).
When debugging gdb code is automatically terminated when the curly brace '}' is closed boost::throw_exception
with the message:
The inferior stopped because it received a signal from the Operating System.
Signal name :
SIGABRT
Signal meaning :
Aborted
When you go through the specified function step by step, you can see that the line throw enable_current_exception(enable_error_info(e));
is the last line executed before the signal. Why is there no stack search to find exception handlers? Why is the interrupt raised immediately (looks like it boost::throw_exception
has a specifier noexcept
)?
I have included a function call try { ... } catch (x3::expectation_failure< input_iterator_type > const & ef) { ... }
x3::phrase_parse
. x3::expectation_failure< input_iterator_type >
- this is exactly deduction from boost::throw_exception
. None of this matters.
Is there a way to completely throw the exception x3::expectation_failure
in Boost.Spirit X3, but still abort the parsing of the code altogether and make the wait fail x3::phrase_parse
to return false
?
My suspicions are as follows:
Due to the normal return value of parse()
the member function of all parsers (as a concept in X3) bool
, I suspect that there are only two ways to report a failure: the xor return exception code (which can only be true
or false
, but is true
already taken to report the results of a successful parsing Parse). This is inherent in the recursive implementation of top-down parsers in C ++. But if we change the type of the result parse
from bool
something wider, we can clearly or hard-communicate hard or soft errors (or whatever) during parsing โ with different return code values.
source to share
You cannot avoid throwing a wait when using a wait parser. This is the purpose of this operator.
Use operator>>
for "tracked expectations" (ie alternatives).
When you use waitpoints ( operator>
), just handle the exception too.
Note This looks like a typo
('(' > -lvalue_list > '>')
it should be
('(' > -lvalue_list > ')')
Also return one end
does not match "begin" >> statements >> "end"
whatever is statements
defined as ...
Fixing things:
Live With Rule Debugging (C ++ 14 only)
#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/spirit/home/x3.hpp>
namespace SO {
namespace x3 = boost::spirit::x3;
x3::symbols<char> const keyword = []{
x3::symbols<char> kw;
kw += "for","begin","end","function","while","break","switch";
return kw;
}();
x3::rule<struct symbol_tag> const symbol ("symbol");
x3::rule<struct identifier_tag> const identifier ("identifier");
x3::rule<struct lvalue_list_tag> const lvalue_list("lvalue_list");
x3::rule<struct statements_tag> const statements ("statements");
x3::rule<struct rule_tag> const rule ("rule");
auto symbol_def = x3::lexeme[x3::alnum >> *(x3::alnum | '_')];
auto identifier_def = (!(x3::lexeme[keyword >> !(x3::alnum | '_')]) >> symbol);
auto lvalue_list_def = identifier % ',';
auto statements_def = *identifier;
auto rule_def = "function"
>> identifier
>> ('(' > -lvalue_list > ')')
>> ("begin" > statements > "end")
;
BOOST_SPIRIT_DEFINE(symbol, identifier, lvalue_list, statements, rule)
}
int main() {
std::string const sample = "function a() begin return one end";
auto f = sample.begin(), l = sample.end();
bool ok = phrase_parse(f, l, SO::rule, SO::x3::space);
if (ok)
std::cout << "Parse success\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
Printing
<rule>
<try>function a() begin r</try>
<identifier>
<try> a() begin return on</try>
<symbol>
<try> a() begin return on</try>
<success>() begin return one </success>
</symbol>
<success>() begin return one </success>
</identifier>
<lvalue_list>
<try>) begin return one e</try>
<identifier>
<try>) begin return one e</try>
<symbol>
<try>) begin return one e</try>
<fail/>
</symbol>
<fail/>
</identifier>
<fail/>
</lvalue_list>
<statements>
<try> return one end</try>
<identifier>
<try> return one end</try>
<symbol>
<try> return one end</try>
<success> one end</success>
</symbol>
<success> one end</success>
</identifier>
<identifier>
<try> one end</try>
<symbol>
<try> one end</try>
<success> end</success>
</symbol>
<success> end</success>
</identifier>
<identifier>
<try> end</try>
<fail/>
</identifier>
<success> end</success>
</statements>
<success></success>
</rule>
Parse success
No debugging
It's much easier:
Live On Coliru (g ++ / clang ++)
#include <boost/spirit/home/x3.hpp>
#include <iostream>
int main() {
namespace x3 = boost::spirit::x3;
x3::symbols<char> keyword;
keyword += "for","begin","end","function","while","break","switch";
static auto symbol = x3::lexeme[x3::alnum >> *(x3::alnum | '_')];
static auto identifier = (!(x3::lexeme[keyword >> !(x3::alnum | '_')]) >> symbol);
static auto lvalue_list = identifier % ',';
static auto statements = *identifier;
static auto rule = "function"
>> identifier
>> ('(' > -lvalue_list > ')')
>> ("begin" > statements > "end")
;
std::string const sample = "function a() begin return one end";
auto f = sample.begin(), l = sample.end();
bool ok = phrase_parse(f, l, rule, x3::space);
if (ok)
std::cout << "Parse success\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
Just prints
Parse success
ยน And just to show that it can handle waiting failures just fine: Handling waiting failures
source to share