避免在期望解析器失败时抛出Expectation_failure [英] Avoid throwing expectation_failure when expectation parser fails
问题描述
当期望解析器失败时,如何避免引发异常?
我有一条规则"function" > (!x3::lexeme[keyword >> !(x3::alnum | '_')] >> symbol) > ('(' > -lvalue_list > ')') > statements > "end"
来解析如下代码:
function a() return one end
keyword
s是(zero
,one
,function
,return
,end
等).
如果我向解析器提供function one() return zero end
代码,则在函数expect_directive::parse
中从这里抛出异常:
if (!r)
{
boost::throw_exception(
expectation_failure<Iterator>(
first, what(this->subject)));
}
发生这种情况时,我得到了程序意外完成.或已中止(内核被丢弃)(取决于所使用的终端).
调试代码 gdb 时,自动关闭boost::throw_exception
函数中的大括号'}'并显示以下消息:
The inferior stopped because it received a signal from the Operating System.
Signal name :
SIGABRT
Signal meaning :
Aborted
逐步查看所提到的功能时,可以看到throw enable_current_exception(enable_error_info(e));
行是信号发射之前执行的最后一行.为什么没有堆栈展开来进行异常处理程序搜索?为什么中止立即引发(看起来像boost::throw_exception
具有noexcept
指示符)?
我已经接受了try { ... } catch (x3::expectation_failure< input_iterator_type > const & ef) { ... }
x3::phrase_parse
函数调用. x3::expectation_failure< input_iterator_type >
正是boost::throw_exception
引发的期望.没关系.
是否有一种方法可以完全避免 Boost.Spirit X3 中的x3::expectation_failure
异常,但是仍然中断整个代码的解析,并使x3::phrase_parse
在预期失败时返回false
? >
下一个是我的怀疑:
由于所有解析器的成员函数parse()
的常规返回值(如 X3 中的概念)为bool
,我怀疑只有两种方法报告失败:异常xor返回代码(只能是true
或false
,并且true
已被 Parse成功结果报告所占用).它是 C ++ 中递归降序解析器实现的固有特性.但是,如果将parse
的结果类型从bool
更改为更宽的内容,则可以通过更灵活的方式(通过不同的返回码值)区分报告硬错误或软错误(或其他错误)的报告.>
使用期望解析器时,您不可避免地会引发期望失败.这是这个运算符的目的.
将operator>>
用于可追溯的期望"(即替代方案).
当您使用期望点(operator>
)时,也要处理例外情况¹.
注意:这看起来像是错字
('(' > -lvalue_list > '>')
应该是
('(' > -lvalue_list > ')')
而且return one end
与"begin" >> statements >> "end"
不匹配,无论statements
定义为...
修复问题:
实时进行规则调试 (c ++ 14仅)
#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";
}
打印:
<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
没有调试
变得更加简单:
在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";
}
只需打印
Parse success
¹只是为了向您展示可以很好地处理预期失败:预期失败处理
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
Without Debug
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
这篇关于避免在期望解析器失败时抛出Expectation_failure的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!