How to avoid throwing an exception, when expectation parser fails?
I have a rule "function" > (!x3::lexeme[keyword >> !(x3::alnum | '_')] >> symbol) > ('(' > -lvalue_list > ')') > statements > "end"
to parse code like:
function a() return one end
keyword
s is (zero
, one
, function
, return
, end
etc).
If I feed the parser with function one() return zero end
code, then in function expect_directive::parse
exception thrown from here:
if (!r)
{
boost::throw_exception(
expectation_failure<Iterator>(
first, what(this->subject)));
}
When it happens, I got The program has unexpectedly finished. or Aborted (core dumped) (depending on terminal used).
When debugging the code gdb automatically breaks on closing brace '}' in boost::throw_exception
function with message:
The inferior stopped because it received a signal from the Operating System.
Signal name :
SIGABRT
Signal meaning :
Aborted
When stepping through mentioned function step by step it is seen, that throw enable_current_exception(enable_error_info(e));
line is the last line executed before signal emitting. Why there is no stack unwinding for exception handler searching? Why abort instantly raised (looks like boost::throw_exception
have noexcept
specifier)?
I have embraced into try { ... } catch (x3::expectation_failure< input_iterator_type > const & ef) { ... }
x3::phrase_parse
function call. x3::expectation_failure< input_iterator_type >
is exactly the expection thrown from boost::throw_exception
. All it does not matter.
Is there a way to completely avoid x3::expectation_failure
exception in Boost.Spirit X3, but still interrupt parsing of code overall and make x3::phrase_parse
to return false
on expectation failure?
My suspicions are next:
Due to conventional return value of parse()
member function of all parsers (as concept in X3) is bool
, I suspect there are only two ways to report about failure: exception xor return code (which can be only true
or false
, and true
already occupied for Parse successful result reporting). It is inherent for recursive descending parsers implementation in C++. But if we change result type of parse
from bool
to something more wide, we can distinct reporting hard or soft errors (or something else) during parse in more flexible way — by means of different values of return code.
You cannot avoid throwing the expectation failure when using the expectation parser. It's the purpose of this operator.
Use operator>>
for "back-trackable expectations" (i.e. alternatives).
When you do use expectation points (operator>
) just handle the exception too¹.
Note This looks like a typo
('(' > -lvalue_list > '>')
should probably be
('(' > -lvalue_list > ')')
Also return one end
doesn't match "begin" >> statements >> "end"
regardless of what statements
is 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";
}
Prints:
<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
It gets a lot simpler:
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 you can handle the expectation failure just fine: Expectation Failure Handling