【问题标题】:Boost Spirit 2 - Symbol expansion and backtrackingBoost Spirit 2 - 符号扩展和回溯
【发布时间】:2015-03-09 22:11:07
【问题描述】:

我在指定和解析一个相当简单的语法时遇到了一些问题。

vertex = char+
edge = vertex " -> " vertex
start = ((vertex | edge) eol)*

input = "a\nb\na -> b\n"

Spirit 正在做以下事情:

"a" -> vertex
"\n" -> eol
-> start

"b" -> vertex
"\n" -> eol
-> start

"a" -> vertex
terminate

而不是最终识别边缘并解析整个输入。 也就是说,它可以解析整个输入,但事实并非如此。 它不应该回溯并尝试使用备用规则进行解析吗?从而完成启动规则。

是因为 Spirit 使用 PEG 吗? (http://www.boost.org/doc/libs/1_57_0/libs/spirit/doc/html/spirit/abstracts/parsing_expression_grammar.html#spirit.abstracts.parsing_expression_grammar.alternatives)

最小的工作示例:

#include <cstdio>
#include <string>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

void print(const std::string & str) {
  printf("%s\n", str.c_str());
}

void print_eol() {
  printf("eol\n");
}

int main() {
  std::string str = "a\nb\na -> b\n";
  std::string::iterator it = str.begin(), begin = str.begin(), end = str.end();

  qi::rule<std::string::iterator, std::string()> vertex = +qi::alnum;
  qi::rule<std::string::iterator, std::string()> edge = vertex >> " -> " >> vertex;
  qi::rule<std::string::iterator> start = (vertex[&print] | edge[&print]) % qi::eol[&print_eol];

  bool matched = parse(it, end, start);

  if (matched) {
    printf("matched\n");
  }
  if (it == end) {
    printf("all\n");
  } else if (it != begin) {
    printf("some\n");
  } else {
    printf("none\n");
  }

  return 0;
}

输出:

$ ./a.out
a
eol
b
eol
a
matched
some

我在 MSYS2 上使用 Boost 1.57.0 和 Clang 3.5.1。

【问题讨论】:

  • 我假设你的意思是提升 1.57.0 而不是 1.57
  • @harmic 呵呵呵呵。我假设您的意思是“不是 1.5.7”,而不是“不是 1.57”o.O

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


【解决方案1】:

您似乎回答了自己的问题:是的,这是因为 PEG 语法本质上是贪婪的,从左到右的匹配。

注意事项

  • 从语义动作打印不是公平的测试,因为即使解析器确实回溯(它可以在一个失败的情况下继续下一个替代分支单个表达式)副作用已经触发。这是使用语义操作的一个主要缺点 - 除非你很小心 (Boost Spirit: "Semantic actions are evil"?)

  • BOOST_SPIRIT_DEBUG 和 BOOST_SPIRIT_DEBUG_NODES 宏用于此目的并提供更多信息

  • 这里明显的解决方案是

    1. 重新排序分支:

      start = (edge[&print] | vertex[&print]) % qi::eol[&print_eol];
      

      Live On Coliru

      a
      eol
      b
      eol
      a -> b
      matched all
      
    2. 左-分解:

      start = (vertex[print] >> -(" -> " >> vertex)[print]) % qi::eol[&print_eol];
      

      Live On Coliru

      a
      eol
      b
      eol
      a
      b
      matched all
      

如果除了满足您对 PEG 语法的好奇心之外,您还想要一些现实生活中的想法,您可以在 SO 上搜索大约十几个 答案中关于如何解析为单独的顶点/边集合的想法,我记得.

【讨论】:

  • 感谢您的回答。我确实发现了一些关于图形解析的问题,但没有人详细回答我的问题。我不明白为什么解析器没有回溯。贪婪使它首先解析顶点规则,但随后它无法解析输入的其余部分,因此它无法完成开始规则,这是它试图完成的规则​​。如果他回溯,它可以解析输入。解析器将开始规则扩展 2 次,而它可以扩展 3 次。(我不习惯 PEG。)我认为当规则扩展失败时它会查看备用规则。
  • @joca.bt 确实如此。但是在您的示例中 vertex[&amp;print] 匹配,所以第一个分支是成功的。第二个分支从未尝试过。您需要重新排序分支开始,因此第一个分支可能会失败,而第二个 - 它是它的一个子集 - 一个可以用作后备。是一个子集
  • 第一个分支成功,但启动规则没有完全展开,但它可以(它缺少要完全展开的 eol)。
  • 这对你来说确实是 PEG 语法。这就是我所说的贪婪。替代解析器已经成功,因此将不再回溯(同样,Kleen Star 或 Plus 在消耗匹配后也不会回溯)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多