避免在期望解析器失败时抛出 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

keywords 是(zeroonefunctionreturnend 等)。

如果我向解析器提供 function one() return zero end 代码,那么函数 expect_directive::parse 从这里抛出异常:

if (!r)
{
    boost::throw_exception(
        expectation_failure<Iterator>(
        first, what(this->subject)));
}

当它发生时,我得到 程序意外完成。中止(核心转储)(取决于使用的终端)。

调试代码时 gdbboost::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_exceptionnoexcept 说明符)?

我已经拥抱成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到 return false 预期失败?

接下来我的怀疑是:

由于所有解析器的 parse() 成员函数的常规 return 值(如 X3 中的概念)是 bool,我怀疑报错只有两种方式:exception xor return code(只能是truefalsetrue already occupied for Parse成功 结果报告)。它是 C++ 中递归递减解析器实现所固有的。但是,如果我们将 parse 的结果类型从 bool 更改为更广泛的类型,我们可以在解析期间以更灵活的方式区分报告硬错误或软错误(或其他)——通过不同的 return代码。

使用期望解析器时无法避免抛出期望失败。这就是这个运算符的目的。

对"back-trackable expectations"使用operator>>(即备选方案)。

当您使用期望点 (operator>) 时,也只需处理异常¹。

Note This looks like a typo

('(' > -lvalue_list > '>')

should probably be

('(' > -lvalue_list > ')')

此外 return one end"begin" >> statements >> "end" 不匹配,无论 statements 定义为...

修复问题:

Live With Rule Debugging(仅限 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

没有调试

它变得简单多了:

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";
}

只打印

Parse success

¹ 只是为了向您展示 可以 很好地处理期望失败:Expectation Failure Handling