谁能告诉我以下两个规则(请注意顺序)有什么区别?
第一个无效的
without => "[" "]" without | "[" "]"
with => "[" INDEX "]" with | "[" INDEX "]"
array => ID with | ID without | ID with without
第二个看似有效的
without => without "[" "]"| "[" "]"
with => with "[" INDEX "]" | "[" INDEX "]"
array => ID with | ID without | ID with without
我正在尝试实现具有一定大小的n-dims数组的语法,例如C#数组。因此,以下语法应该有效,arr[]
,arr[1]
,arr[1][]
,arr[1][1]
,arr[][]
,而不是像arr[][1]
这样的语法。
答案 0 :(得分:2)
我假设“不起作用”是指野牛报告了移动/减少冲突。如果继续使用生成的解析器,则在很多情况下它将无法正确解析,因为冲突是真实的,无法通过任何静态规则解决。
问题很简单。请记住,像bison生成的那样,LALR(1)自下而上的解析器仅在考虑到下一个标记(“超前标记”)的情况下,才在右侧的末尾执行所有归约操作。因此,它必须知道在完全读取产品时要使用哪个产品。 (这给它提供了比自上而下的解析器更大的自由度,后者需要知道在生产开始时将使用哪种生产方式。但这仍然并不总是足够的。)
有问题的情况是生产ID with without
。在此,在继续with
之前,任何与with
匹配的输入都需要减少为单个非终结符without
。为此,解析器必须经过一定数量的'[' INDEX ']'
维,并且超前标记必须为[
,无论下一维是否具有确定的大小。
如果with
规则是右递归:
with: '[' INDEX ']' with
| '[' INDEX ']'
然后解析器确实卡住了。如果随后的尺寸确定,则需要继续尝试第一次生产,这意味着移动[
。如果后面没有INDEX
,则需要减少第二次生产,这将触发一连串的减少,直到尺寸表的开头。
另一方面,使用左递归规则:
with: with '[' INDEX ']'
| '[' INDEX ']'
解析器根本没有问题,因为一旦看到with
,每个]
都会减少。这意味着解析器不必知道接下来要决定的缩减量。它根据过去而不是将来在两个规则之间做出决定:array
中的第一个维度使用第二个生产,而其余的(遵循with
的则使用第一个生产。< / p>
这并不是说左递归始终是答案,尽管通常是这样。在这种情况下可以看出,列表的右递归意味着各个列表元素会堆积在解析器堆栈上,直到最终终止列表为止,而左递归则允许立即进行归约,因此解析器堆栈不会不需要成长。因此,如果您有选择的话,通常应该选择左递归。
但是有时候右递归会很方便,特别是在像这样的语法中,列表的结尾与开头不同。编写语法的另一种方式可能是:
array : ID dims
dims : without
| '[' INDEX ']'
| '[' INDEX ']' dims
without: '[' ']'
| '[' ']' without
在这里,由于dims
的结构,语法仅在列表的末尾接受空白。但是要达到这种效果,dims
必须是右递归的,因为列表的末尾具有扩展的语法。