【问题标题】:Boost Spirit optional parser and backtrackingBoost Spirit 可选解析器和回溯
【发布时间】:2026-01-30 00:05:02
【问题描述】:

为什么这个解析器在属性中保留'b',即使选项不匹配?

using namespace boost::spirit::qi;

std::string str = "abc";

auto a = char_("a");
auto b = char_("b");
qi::rule<std::string::iterator, std::string()> expr;
expr = +a >> -(b >> +a);

std::string res;

bool r = qi::parse(
        str.begin(),
        str.end(),
        expr >> lit("bc"),
        res
);

解析成功,但是res是"ab"

如果仅使用 expr 解析 "abac",则匹配选项且属性为 "aba"

"aac" 相同,选项不开始匹配且属性为"aa"

但是对于"ab",属性是"ab",即使b 被回溯,并且如示例一样与下一个解析器匹配。

UPD

expr.name("expr");debug(expr); 我得到了

<expr>
  <try>abc</try>
  <success>bc</success>
  <attributes>[[a, b]]</attributes>
</expr>

【问题讨论】:

  • X3 中的行为发生了变化,其中可选解析器总是创建一个临时解析器。

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


【解决方案1】:

首先,UB 使用 auto 变量来保留表达式模板,因为它们包含对临时变量 "a""b" 的引用 [1]

改为写

expr = +qi::char_("a") >> -(qi::char_("b") >> +qi::char_("a"));

或者,如果你坚持:

auto a = boost::proto::deep_copy(qi::char_("a"));
auto b = boost::proto::deep_copy(qi::char_("b"));
expr = +a >> -(b >> +a);

现在注意到 &gt;&gt; lit("bc") 部分隐藏在 parse 调用中,这表明当解析失败发生时,您可能会期望回溯到成功匹配的标记。

这不会发生:Spirit 生成 PEG 语法,并且总是从左到右贪婪地匹配。


对于给定的示例,ab 结果,即使发生回溯确实,如果没有qi::hold,对属性的影响也不会回滚:Live On Coliru

容器属性由 ref 传递,并且先前(成功)表达式的效果回滚,除非您也告诉 Spirit。这样一来,您就可以“按使用量付费”(因为一直复制临时文件会很昂贵)。

参见例如

<a>
  <try>abc</try>
  <success>bc</success>
  <attributes>[a]</attributes>
</a>
<a>
  <try>bc</try>
  <fail/>
</a>
<b>
  <try>bc</try>
  <success>c</success>
  <attributes>[b]</attributes>
</b>
<a>
  <try>c</try>
  <fail/>
</a>
<bc>
  <try>bc</try>
  <success></success>
  <attributes>[]</attributes>
</bc>
Success: 'ab'

[1]见此处:

【讨论】:

  • 但是,我不明白,你使用二进制减号?那是不同的语言,不是吗?
  • @MikhailCheshkov 我刚刚注意到这个错字。 更新答案。请原谅我的错误:/
  • 我希望在不匹配的字符上回溯 - 如果在 b 之后没有找到 a,则选项将失败,因此 b 不会被消耗,它将与 lit("bc") 匹配。
  • 我已经在选项内容上使用hold 完成了它,感谢您的链接!
  • @MikhailCheshkov 我仍在编辑答案。抱歉耽搁了,我不得不在这里分散注意力。我会尽快完成的。 tester here
【解决方案2】:

this SO 问题中引用@sehe

字符串属性是容器属性,许多元素可以是 由不同的解析器子表达式分配给它。现在为 效率原因,Spirit 不会回滚发出的值 回溯的属性。

所以,我已经暂停了可选解析器,并且完成了。

expr = +qi::char_("a") >> -(qi::hold[qi::char_("b") >> +qi::char_("a")]);

有关更多信息,请参阅提到的问题和hold docs

【讨论】:

  • 该信息在我的回答中是正确的。嗯。也许是因为我没有在那里显示hold 的用法。嗯,+1(感谢您忍受我的心不在焉)
最近更新 更多