避免在期望解析器失败时抛出Expectation_failure [英] Avoid throwing expectation_failure when expectation parser fails

查看:136
本文介绍了避免在期望解析器失败时抛出Expectation_failure的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当期望解析器失败时,如何避免引发异常?

我有一条规则"function" > (!x3::lexeme[keyword >> !(x3::alnum | '_')] >> symbol) > ('(' > -lvalue_list > ')') > statements > "end"来解析如下代码:

function a() return one end

keyword s是(zeroonefunctionreturnend等).

如果我向解析器提供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返回代码(只能是truefalse,并且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

keywords 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆