我在指定和解析一个相当简单的语法时遇到了一些问题。
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。
答案 0 :(得分:2)
您似乎回答了自己的问题:是的,这是因为PEG语法本质上是贪婪的,从左到右匹配。
备注
从语义操作打印是不公平测试,因为即使解析器 回溯(它可以继续下一个替代分支失败)单个表达式)副作用已经被解雇了。这是使用语义操作的一个主要缺点 - 除非你小心(Boost Spirit: "Semantic actions are evil"?)
BOOST_SPIRIT_DEBUG和BOOST_SPIRIT_DEBUG_NODES宏有这个目的,并提供更多信息
这里显而易见的解决方案是
重新排序分支:
start = (edge[&print] | vertex[&print]) % qi::eol[&print_eol];
<强> Live On Coliru 强>
a
eol
b
eol
a -> b
matched all
左 - 分解:
start = (vertex[print] >> -(" -> " >> vertex)[print]) % qi::eol[&print_eol];
<强> Live On Coliru 强>
a
eol
b
eol
a
b
matched all
如果你想要一些现实生活中的想法,除了满足你对PEG语法的好奇心之外,你可以搜索SO的答案,以便在SO的大约十几个boost-spirit答案中解析如何解析单独的顶点/边集合。我记得。