在 yacc 程序中使用模糊规则

如果输入字符串可以用两种不同方式的结构,那么语法规则集合就是模糊的

例如,下面的语法规则说明了通过将两个表达式放在一起(两者之间有负号)形成算术表达式的规则。
expr : expr '-' expr
但是,此语法规则不能指定如何构造所有复杂输入。 例如,如果输入是:
expr - expr - expr
程序可以将此输入构造为左关联:
( expr - expr ) - expr
或者右关联:
expr - ( expr - expr )

并会产生不同的结果。

解析器冲突

当解析器尝试处理模糊规则时,会混淆在处理输入时应该执行四个操作中的哪一个操作。 下面是产生的冲突的主要类型:

冲突 描述
移位 (shift)/减 (reduce) 冲突 可以使用移位 (shift) 操作或者减 (reduce) 操作正确计算规则,但是结果不同。
减 (reduce)/减 (reduce) 冲突 可以使用两种不同的减 (reduce) 操作之一正确计算规则,但是产生不同的结果。
移位 (shift)/移位 (shift) 冲突不可能发生。 移位 (shift)/减 (reduce)减 (reduce)/减 (reduce) 冲突是因为规则没有阐述完整而产生的。 例如,使用前一节中阐述的模糊规则,如果解析器接收输入:
expr - expr - expr
在读取前三个部分之后,解析器有:
expr - expr
它与前面语法规则的右侧匹配。 解析器可以通过应用此规则减少输入。 应用规则之后,输入变为:
expr
这是规则的左侧。 解析器然后读取输入的最后部分:
- expr

并减少它。 这产生左关联解释。

但是,解析器也能在输入流中预测。 如果,当解析器接收到前三个部分时:
expr - expr
它读取输入流,直到它拥有下两个部分,然后它有下面的输入:
expr - expr - expr
将规则应用于最右边的三个部分会将它们减少到expr. 然后解析器具有表达式:
expr - expr

将表达式再减少一次会产生右关联解释。

因此,在解析器仅读前三个部分处,可以采取两个有效操作中的任何一个:移位 (shift) 或者减 (reduce)。 如果解析器没有规则可以决定执行两个操作中的哪一个,会产生移位 (shift)/减 (reduce) 冲突。

如果解析器能在两个有效减 (reduce) 操作中选择,会产生相似的情况,被称为减 (reduce)/减 (reduce) 冲突

解析器如何响应冲突

当产生移位 (shift)/减 (reduce) 或者 减 (reduce)/减 (reduce) 冲突时,yacc 命令通过在有选项处选择一个有效的步骤产生解析器。 如果您不提供进行选择的规则, yacc 程序则使用下面的规则:

  • 移位 (shift)/减 (reduce) 冲突中,选择移位 (shift)。
  • 减 (reduce)/减 (reduce)冲突中,通过能应用到输入流中的最早点中的语法规则来减少。

如果必须在解析器确定哪一条规则被识别之前执行操作,那么使用规则中的操作会引起冲突。 在这种情况下,前面的规则会产生不正确的解析器。 由于此原因,yacc 程序报告使用前面的规则解决的 移位 (shift)/减 (reduce)减 (reduce)/减 (reduce) 冲突号。