【问题标题】:Avoid throwing expectation_failure when expectation parser fails期望解析器失败时避免抛出expectation_failure
【发布时间】:2023-03-22 22:36:01
【问题描述】:

当期望解析器失败时如何避免抛出异常?

我有一条规则 "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)));
}

当它发生时,我得到 程序意外完成。Aborted (core dumped)(取决于使用的终端)。

在调试代码时,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)); 行是信号发出之前执行的最后一行。为什么异常处理程序搜索没有堆栈展开?为什么 abort 立即提出(看起来像 boost::throw_exceptionnoexcept 说明符)?

我已经接受了 try { ... } catch (x3::expectation_failure&lt; input_iterator_type &gt; const &amp; ef) { ... } x3::phrase_parse 函数调用。 x3::expectation_failure&lt; input_iterator_type &gt; 正是从 boost::throw_exception 引发的期望。一切都无所谓。

有没有办法完全避免 Boost.Spirit X3 中的 x3::expectation_failure 异常,但仍会中断整体代码解析并使 x3::phrase_parse 在预期失败时返回 false? p>

接下来是我的怀疑:

由于所有解析器的成员函数parse()的常规返回值(作为X3中的概念)是bool,我怀疑只有两种方法可以报告失败:异常xor return代码(只能是truefalse,并且true 已被占用解析成功 结果报告)。它是 C++ 中递归降序解析器实现所固有的。但是,如果我们将 parse 的结果类型从 bool 更改为更广泛的类型,我们可以在解析期间以更灵活的方式区分报告硬错误或软错误(或其他错误)——通过返回代码的不同值。

【问题讨论】:

  • 哇。你终于让我尝试 X3。非常喜欢
  • 我知道将两个问题合二为一是错误的。对于问题的第二部分,我找到了答案:我使用 libunwind,正如它在最新的Eli Bendersky's article 中所描述的那样,并链接到它。系统范围内安装的 libunwind 和 gdb 之间似乎存在干扰,后者在内部使用 libunwind(可能版本不同)。
  • 好的 - 很有趣。现在把它放在我的脑海里以备将来参考
  • @sehe 不值得。我无法在另一台机器上重现该行为。
  • 好的,感谢您的推动。事情发生了。

标签: c++ boost-spirit boost-spirit-x3


【解决方案1】:

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

使用operator&gt;&gt; 表示“可追溯的期望”(即替代方案)。

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

注意这看起来像是一个错字

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

应该是

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

此外,return one end"begin" &gt;&gt; statements &gt;&gt; "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

【讨论】:

  • 只是为了向您展示可以很好地处理期望失败:coliru.stacked-crooked.com/a/c6461f997d4b1c8b
  • 是的,只是打错了。
  • 你能说改变解析器的parse成员函数的结果类型是否有任何障碍,而不仅仅是我上面提到的bool?在这种情况下,可以避免使用异常来处理硬错误。
  • 你能把它包装在一个返回optional&lt;T&gt;maybe&lt;T&gt;的函数中(假设你有自己的maybe实现)
  • @TemplateRex 已修复。我不知道原始语法的来源;这个答案源于我在学习 X3 之前(请参阅 OP 帖子中的评论 :))。我所知道的是BOOST_SPIRIT_* x3 宏假设了一个命名约定,如果你想偏离这个约定,我可能会手动编写一些管道(管道用于将解析器类型映射到它们的标签和定义)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-03
  • 1970-01-01
  • 1970-01-01
  • 2020-03-15
  • 2020-08-22
  • 2015-08-14
  • 2021-10-04
相关资源
最近更新 更多