Node.js 在peg.js中回溯是如何工作的(附示例)?

Node.js 在peg.js中回溯是如何工作的(附示例)?,node.js,grammar,peg,pegjs,Node.js,Grammar,Peg,Pegjs,我定义了以下最小Peg.js语法: start = "A1" / "A123" 你可以试试 我本来希望匹配“A1”和“A123”(根据我对回溯工作原理的理解)。但事实并非如此:语法识别“A1”,而不是“A123” 注意:我不是在寻找相关问题中提出的“颠倒条款顺序”的建议。相反,我希望了解我看到的行为,以及为什么Peg.js的回溯不适用于这种情况。有关为什么颠倒术语顺序没有帮助的解释,请参见下面更现实的示例 对于一个更实际的例子,考虑单元解析。语法应该识别带有可选前缀的公制单位(如“m”、

我定义了以下最小Peg.js语法:

start  =  "A1" / "A123"
你可以试试

我本来希望匹配“A1”和“A123”(根据我对回溯工作原理的理解)。但事实并非如此:语法识别“A1”,而不是“A123”

注意:我不是在寻找相关问题中提出的“颠倒条款顺序”的建议。相反,我希望了解我看到的行为,以及为什么Peg.js的回溯不适用于这种情况。有关为什么颠倒术语顺序没有帮助的解释,请参见下面更现实的示例


对于一个更实际的例子,考虑单元解析。语法应该识别带有可选前缀的公制单位(如“m”、“mol”),如“mm”、“mmol”,以及非公制单位,如“yr”、“week”或“mo”

下面的Peg.js语法无法识别“mol”,因为它在使用“mo”时会出错,并且不会回溯。(更改术语顺序没有帮助;或者更确切地说,将导致以“mol”或“mmol”为代价识别“mo”。)

我可以在Antlr中成功地完成一件有趣的事情:

grammar units;
start  :  nonmetric | metric | prefix metric;
metric : 'mol' | 'l' | 'm' | 'g';
nonmetric : 'yr' | 'mo' | 'week' | 'day' | 'hour';
prefix : 'm' | 'k' | 'c';

问题在于回溯的概念。PEG解析器不像其他递归下降解析器那样进行回溯。相反,当面临选择时,PEG解析器将尝试每个选项,直到其中一个成功。一旦成功,无论规则是如何调用的,它都将提交给它

从:

但是,与上下文无关语法和正则表达式不同, 这些操作符总是表现得贪婪,消耗尽可能多的输入 可能而且永远不会回溯

在复杂的情况下,你所要求的与在中所要求的是一样的。到目前为止,答案是肯定的:您必须调整PEG语法中的规则,以确保始终首先匹配最长的选项,即使结果是更难看的语法

调整PEG语法的一种方法是使用lookaheads(这是为什么在PEG中使用lookaheads的主要原因之一):


这是故意的。由您指定用于匹配的正确顺序或规则

引用原文:

当然,这些工具并不能使语言语法设计变得简单。在里面 必须确定一个项目中是否有两个可能的备选方案的位置 CFG是模棱两可的,PEGs为语言设计者提供了类似的 确定“/”表达式中是否有两个备选方案的挑战 可以在不影响语言的情况下重新排序。这个问题是 通常是显而易见的,但有时不是,一般来说是无法确定的。 然而,正如在CFGs中发现歧义一样,我们希望 寻找自动算法来识别订单敏感度或 在一般情况下保守地不敏感


在这个简单的例子中,PEG.js可能会更聪明一些,并认识到您指定的规则是不明确的。可能对作者来说很有价值。

感谢您的背景、清晰的解释和对lookaheads w/示例的描述!谢谢你的解释。对于一个在解析器方面没有什么背景的人,你有没有建议提供回溯的替代方案?Antlr似乎是下一个选项Antlr是预测性LL(*)。它并不完全做回溯,但它可以处理各种各样的解析情况@Apalala我已经添加了一个链接到这个评论@ChristofferBubach,当你试图学习来自Antlr的Peg.js时,请参阅关于这个问题的好例子。它真的帮助我理解了我的语法到底出了什么问题。
grammar units;
start  :  nonmetric | metric | prefix metric;
metric : 'mol' | 'l' | 'm' | 'g';
nonmetric : 'yr' | 'mo' | 'week' | 'day' | 'hour';
prefix : 'm' | 'k' | 'c';
start  =  nonmetric / metric / prefix metric
metric = "mol" / "l" / !"mo" "m" / "g"
nonmetric = "yr" / !"mol" "mo" / "week" / "day" / "hour"
prefix = !("mol"/"mo") "m" / "k" / "c"