如何在此递归规则中的顺序不给出相同的结果?

时间:2019-01-07 12:33:29

标签: grammar bison context-free-grammar

谁能告诉我以下两个规则(请注意顺序)有什么区别?

  1. 第一个无效的

    without => "[" "]"  without | "[" "]"
    with => "[" INDEX "]"  with | "[" INDEX "]"
    array => ID with | ID without | ID with without
    
  2. 第二个看似有效的

    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]这样的语法。

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必须是右递归的,因为列表的末尾具有扩展的语法。