我正在使用Java CUP为Java的子集实现解析器。
语法就像
vardecl ::= type ID
type ::= ID | INT | FLOAT | ...
exp ::= ID | exp LBRACKET exp RBRACKET | ...
stmt ::= ID ASSIGN exp SEMI
这很好,但是当我添加
stmt ::= ID ASSIGN exp SEMI
|ID LBRACKET exp RBRACKET ASSIGN exp SEMI
CUP无法使用,警告是:
Warning : *** Shift/Reduce conflict found in state #122
between exp ::= identifier (*)
and statement ::= identifier (*) LBRACKET exp RBRACKET ASSIGN exp SEMI
under symbol LBRACKET
Resolved in favor of shifting.
Warning : *** Reduce/Reduce conflict found in state #42
between type ::= identifier (*)
and exp ::= identifier (*)
under symbols: {}
Resolved in favor of the first production.
Warning : *** Shift/Reduce conflict found in state #42
between type ::= identifier (*)
and statement ::= identifier (*) LBRACKET exp RBRACKET ASSIGN exp SEMI
under symbol LBRACKET
Resolved in favor of shifting.
Warning : *** Shift/Reduce conflict found in state #42
between exp ::= identifier (*)
and statement ::= identifier (*) LBRACKET exp RBRACKET ASSIGN exp SEMI
under symbol LBRACKET
Resolved in favor of shifting.
我认为有两个问题:
1. type ::= ID
和exp ::= ID
,当解析器看到ID时,它想减少ID,但不知道减少type
或exp
。< / p>
stmt ::= ID LBRACKET exp RBRACKET ASSIGN exp SEMI
用于分配数组中的元素,例如arr[key] = value;
exp :: exp LBRACKET exp RBRACKET
用于从数组中获取元素的表达式,例如arr[key]
因此在arr[key]
的情况下,当解析器看到arr
时,它知道它是一个ID,但不知道它是否应该移动或减少到exp
。
但是,我不知道如何解决此问题,如果有的话,请给我一些建议,谢谢。
答案 0 :(得分:2)
您的分析是正确的。语法为LR(2),因为在看到]
标记之前无法识别声明,该标记将是ID中的第二个下一个标记,可能是一种类型。
一个简单的解决方案是,当括号显示为连续标记时,攻击词法分析器以将[]
作为单个标记返回。 (词法分析器也应该在方括号之间留有空格,因此它并不琐碎,但并不复杂。)如果[
之后没有紧跟着]
,则词法分析器会将其返回为普通[
。这样,解析器就可以轻松区分对数组的分配(将具有[
令牌)和对数组的声明(将具有[]
令牌)之间的区别。
也可以重写语法,但这确实很麻烦。
第二个问题-数组索引分配与数组索引表达式。通常,编程语言允许分配以下形式:
exp [ exp ] = exp
,而不仅仅是ID [ exp ]
。进行此更改将延迟减少的需求,直到解析器识别出正确的减少的时间足够晚为止。根据语言的不同,此语法在语义上可能没有意义,但是在类型检查(语义)领域中进行的检查不是语法。但是,如果存在某种形式的有意义的语法,则没有明显的理由禁止它。
某些解析器生成器实现GLR解析器。 GLR解析器对此语法没有问题,因为它没有歧义。但是银联不是这样的发电机。