PEG 中的替代规则能否在其初始标记中重叠?
Can alternative rules overlap in their intial tokens in PEG?
我想我刚刚意识到我 运行 遇到过好几次并且我总能找到一些奇怪的解决方法的问题可能只是根本问题。我希望有人可以验证这种理解或告诉我我做错了什么。
我正在尝试创建一个解析器来处理多种语法变体。但我猜这其中存在歧义,因为在最简单的情况下,每个变体都允许相同的输入。因为我将它们映射到相同的语法树,所以我希望这无关紧要。当然可以。
我正在使用 Peg.js 并尝试构建一个语法,为了简单起见,它可以接受像 abc/cd/e
、x/yz
或 foo
这样的输入,但也接受 abc.cd.e
、x.yz
,当然还有 foo
。我猜问题是我的两个变体都接受 foo
。
这就是我想要做的:
Expression = SlashTerm / DotTerm
SlashTerm = Name ('/' SlashTerm)?
DotTerm = Name ('.' DotTerm)?
Name = [a-z0-9_\-]i+
这可以识别 foo/bar
,但不能识别 foo.bar
。如果我切换到 Expression = DotTerm / SlashTerm
,它当然会识别 foo.bar
而不是 foo/bar
。
所以问题是有没有比手动强制选择
更好的方法来处理这个问题
Expression = DotTerm / SlashTerm
SlashTerm = Name ('/' SlashTerm)?
DotTerm = Name '.' DotNode
DotNode = Name ('.' DotNode)?
Name = [a-z0-9_\-]i+
虽然添加附加规则没有问题,但我不喜欢这需要我从 Expression = SlashTerm / DotTerm
切换到 Expression = DotTerm / SlashTerm
。我认为任何一个都应该工作,因为没有歧义。也许我误解了选择操作,但我的印象是使用Expression = SlashTerm / DotTerm
,当它试图解析foo.bar
时,它会通过foo
,命中.
,没有匹配并回溯到 SlashTerm 的顶部,它不会匹配,因此它会移动到 DotTerm,在那里它会找到匹配项。由于那行不通,我的理解不正确,我希望有人能解释为什么。
我也很想听听更好的方法。
我真正的语法当然要复杂得多; SlashTerm
涉及更多。但 DotTerm 等价物真的就是这么简单。 DotTerm 等效语法(我显然可以在没有成熟的解析器的情况下轻松处理)是现在唯一受支持的语法,但我正在尝试将其扩展为更健壮的语言。我不想根据语法选择对代码进行分支,并希望以后能够轻松弃用它。在我的语法中加入一些规则似乎是最简单的方法。但是,如果有另一种简单的方法可以做到这一点,我很想听听。
但是 SlashTerm
匹配。它没有匹配整个输入,但它成功匹配了输入的一部分。因此,SlashTerm / DotTerm
也成功匹配。 PEG 不会回溯成功匹配的组件。要使 PEG 回溯,匹配的组件必须失败。
所以一个解决方案是坚持备选方案与第一个定界符匹配,除非没有:
Expression = SlashTerm / DotTerm / Name
SlashTerm = Name ('/' Name)+
DotTerm = Name ('.' Name)+
Name = [a-z0-9_\-]i+
在这种情况下,SlashTerm
和 DotTerm
都不会匹配单个单词,因此选择运算符必须落到第三个选项。同样,foo.bar
无法匹配 SlashTerm
,因此它将失败,选择运算符将按预期回退到第二个备选方案。
上面我把递归换成了迭代,我觉得这样更简单。如果这不适合你的语法模型,你可以用一个额外的非终端来调整你原来的解决方案:
Expression = SlashTerm / DotTerm / Name
SlashTerm = Name '/' SlashRest
DotTerm = Name '.' DotRest
SlashRest = Name ('/' SlashRest)?
DotRest = Name ('.' DotRest)?
Name = [a-z0-9_\-]i+
我想我刚刚意识到我 运行 遇到过好几次并且我总能找到一些奇怪的解决方法的问题可能只是根本问题。我希望有人可以验证这种理解或告诉我我做错了什么。
我正在尝试创建一个解析器来处理多种语法变体。但我猜这其中存在歧义,因为在最简单的情况下,每个变体都允许相同的输入。因为我将它们映射到相同的语法树,所以我希望这无关紧要。当然可以。
我正在使用 Peg.js 并尝试构建一个语法,为了简单起见,它可以接受像 abc/cd/e
、x/yz
或 foo
这样的输入,但也接受 abc.cd.e
、x.yz
,当然还有 foo
。我猜问题是我的两个变体都接受 foo
。
这就是我想要做的:
Expression = SlashTerm / DotTerm
SlashTerm = Name ('/' SlashTerm)?
DotTerm = Name ('.' DotTerm)?
Name = [a-z0-9_\-]i+
这可以识别 foo/bar
,但不能识别 foo.bar
。如果我切换到 Expression = DotTerm / SlashTerm
,它当然会识别 foo.bar
而不是 foo/bar
。
所以问题是有没有比手动强制选择
更好的方法来处理这个问题Expression = DotTerm / SlashTerm
SlashTerm = Name ('/' SlashTerm)?
DotTerm = Name '.' DotNode
DotNode = Name ('.' DotNode)?
Name = [a-z0-9_\-]i+
虽然添加附加规则没有问题,但我不喜欢这需要我从 Expression = SlashTerm / DotTerm
切换到 Expression = DotTerm / SlashTerm
。我认为任何一个都应该工作,因为没有歧义。也许我误解了选择操作,但我的印象是使用Expression = SlashTerm / DotTerm
,当它试图解析foo.bar
时,它会通过foo
,命中.
,没有匹配并回溯到 SlashTerm 的顶部,它不会匹配,因此它会移动到 DotTerm,在那里它会找到匹配项。由于那行不通,我的理解不正确,我希望有人能解释为什么。
我也很想听听更好的方法。
我真正的语法当然要复杂得多; SlashTerm
涉及更多。但 DotTerm 等价物真的就是这么简单。 DotTerm 等效语法(我显然可以在没有成熟的解析器的情况下轻松处理)是现在唯一受支持的语法,但我正在尝试将其扩展为更健壮的语言。我不想根据语法选择对代码进行分支,并希望以后能够轻松弃用它。在我的语法中加入一些规则似乎是最简单的方法。但是,如果有另一种简单的方法可以做到这一点,我很想听听。
但是 SlashTerm
匹配。它没有匹配整个输入,但它成功匹配了输入的一部分。因此,SlashTerm / DotTerm
也成功匹配。 PEG 不会回溯成功匹配的组件。要使 PEG 回溯,匹配的组件必须失败。
所以一个解决方案是坚持备选方案与第一个定界符匹配,除非没有:
Expression = SlashTerm / DotTerm / Name
SlashTerm = Name ('/' Name)+
DotTerm = Name ('.' Name)+
Name = [a-z0-9_\-]i+
在这种情况下,SlashTerm
和 DotTerm
都不会匹配单个单词,因此选择运算符必须落到第三个选项。同样,foo.bar
无法匹配 SlashTerm
,因此它将失败,选择运算符将按预期回退到第二个备选方案。
上面我把递归换成了迭代,我觉得这样更简单。如果这不适合你的语法模型,你可以用一个额外的非终端来调整你原来的解决方案:
Expression = SlashTerm / DotTerm / Name
SlashTerm = Name '/' SlashRest
DotTerm = Name '.' DotRest
SlashRest = Name ('/' SlashRest)?
DotRest = Name ('.' DotRest)?
Name = [a-z0-9_\-]i+