提升精神2 - 符号扩展和回溯

时间:2015-03-09 22:11:07

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

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

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 个答案:

答案 0 :(得分:2)

您似乎回答了自己的问题:是的,这是因为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的答案,以便在SO的大约十几个答案中解析如何解析单独的顶点/边集合。我记得。