在 Java Cup 状态中发现 Shift/Reduce 冲突

问题描述

我正在尝试在 java cup 中编写解析,但我遇到了冲突

start with prog;

prog ::= header;
header ::= t1 t3 t2 | t3 t1 t2 | t2 t3 t1 | t1 t2 t3 | t2 t1 t3 | t3 t2 t1;

t1 ::= TOK1;
t2 ::= TOK2 | TOK2 TOK2 ;
t3 ::= | t3 TOK3;

我收到此错误

Warning : *** Shift/Reduce conflict found in state #3
  between t3 ::= (*) 
  and     t1 ::= (*) TOK1 
  under symbol TOK1
  Resolved in favor of shifting.

谁能解释一下我哪里出错了?

解决方法

t3 可以为空(它匹配零个或多个 TOK3 标记的序列)。这会造成歧义,因为没有 TOK3 的输入可以匹配多个 header 的备选方案。

尽管所有的可能性都归约到同一个非终结符,但这是模棱两可的,因为每个产生式都有不同的归约动作,而且语法无法知道要执行哪个动作。正如冲突报告中所指出的,解析器将移动而不是减少。例如,如果第一个输入是 TOK1,解析器可以假设输入将匹配 t1 t2 t3t1 t3 t2,在这种情况下,TOK3 的序列将稍后出现,或者它可以假设输入是 t3 t1 t2 和一个空的 t3,其中它必须在继续解析之前对空的 t3 执行操作。选择 shift 意味着拒绝输入开头出现空 t3 的可能性。

在这种特殊情况下,默认班次可能不会引起问题。这意味着仅由 TOK1TOK2 组成的输入将始终在最后以空 t3 进行解析,但如果没问题,那么您可以随意忽略警告。

所谓的“可空非终结符”通常正是这种形式的移位归约冲突的原因。避免它们通常很有用;与创建可能为空的列表不同,更好的解决方案通常是使列表非终结符匹配一个或多个元素,然后通过添加列表不存在的产生式使其可选。换句话说,而不是

header: t3 t1 t2
t3    :  
      | t3 TOK3

使用

header: t3 t1 t2
      |    t1 t2
t3    : TOK3
      | t3 TOK3

该解决方案可能会导致不受欢迎的代码重复,但它是一种解决由于可空非终结符引起的移位归约冲突的简单方法。