Parsing 减少/减少CUP中的冲突
我正在使用JavaCup为Java的一个子集实现一个解析器 语法就像Parsing 减少/减少CUP中的冲突,parsing,compiler-construction,grammar,shift-reduce-conflict,cup,Parsing,Compiler Construction,Grammar,Shift Reduce Conflict,Cup,我正在使用JavaCup为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
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
杯子不工作,警告如下:
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时,它想减少它,但不知道减少哪个,type
或exp
stmt::=ID LBRACKET exp RBRACKET ASSIGN exp SEMI
用于分配数组中的元素,例如arr[key]=value代码>
exp::exp LBRACKET exp RBRACKET
用于表示从数组中获取元素,例如arr[key]
arr[key]
的情况下,当解析器看到arr
时,它知道它是一个ID,但不知道它是否应该移动或减少到exp
但是,我不知道如何解决这个问题,如果有,请给我一些建议,非常感谢。您的分析是正确的。语法是LR(2),因为在看到
]
标记之前无法识别声明,该标记将是ID中的第二个下一个标记,可能是类型
一个简单的解决方案是,当括号显示为连续标记时,破解lexer将[]
作为单个标记返回。(lexer可能也应该允许括号之间有空格,所以这不是很简单,但并不复杂。)如果[
后面没有紧跟]
,lexer将返回普通的[
,这使得解析器很容易区分对数组的赋值(将具有[
标记)和数组声明(将具有[]
标记)
重写语法也是可能的,但那真是讨厌
第二个问题——数组索引赋值与数组索引表达式。通常编程语言允许赋值形式:
exp [ exp ] = exp
而且不仅仅是ID[exp]
。进行此更改将延迟还原的需要,直到解析器能够识别正确的还原为止。根据语言的不同,此语法可能在语义上没有意义,而是在类型检查(语义)领域进行检查不是语法。但是,如果有某种形式的语法是有意义的,就没有明显的理由禁止它
一些解析器生成器实现GLR解析器。GLR解析器对此语法没有问题,因为它没有歧义。但CUP不是这样的生成器。您的分析是正确的。语法是LR(2),因为只有在
]
将看到令牌,它将是ID中的下一个令牌,可能是一种类型
一个简单的解决方案是,当括号显示为连续的标记时,破解lexer将[]
作为单个标记返回。(lexer可能也应该允许括号之间有空格,所以这不是很简单,但并不复杂。)如果[
后面没有紧跟]
,lexer将返回它作为普通的[
。这使得解析器很容易区分对数组的赋值(将有[
标记)和数组的声明(将有[
标记)
重写语法也是可能的,但那真是讨厌
第二个问题——数组索引赋值与数组索引表达式。通常编程语言允许赋值形式:
exp [ exp ] = exp
而且不仅仅是ID[exp]
。进行此更改将延迟还原的需要,直到解析器能够识别正确的还原为止。根据语言的不同,此语法可能在语义上没有意义,而是在类型检查(语义)领域进行检查不是语法。但是,如果有某种形式的语法是有意义的,就没有明显的理由禁止它
一些语法分析器生成器实现GLR语法分析器。GLR语法分析器不会有问题,因为它没有歧义。但是CUP不是这样的生成器。谢谢你的回答。但我认为它不能在我的例子中作为单个标记返回
[]
,因为[
和]之间有一个表达式键
在案例arr[key]=value
@jiahao中:如果[
和]
arr[key]
之间只有空格,则只能将其作为单个标记返回。是的,但问题是解析器仍然无法区分arr[key]
是exp
或stmt
的一部分。如果arr[key]
是arr[key]=value
的一部分,那么它是stmt
,并且arr
应该作为ID
进行移位;如果arr[key]
是arr[key]的一部分,init()
,那么arr
应该减少为exp
。对不起,我想我忘了添加一个规则,它是stmt::=exp DOT ID LPAREN参数\u list rpare SEMI
,这个规则是用于函数调用的。@jiahao:这是一个不同的问题,它有不同的解决方案。最简单的一个是允许表单exp>的赋值[exp]=
,这在大多数语言中都是有效的。是的,我同意,非常感谢你的回答,你非常有帮助;)谢谢你的回答。但我认为它不能在我的情况下作为单个标记返回[]
,因为在[
和]
之间有一个表达式key
=value
@jiahao:如果[
和]
之间除了空格之外没有任何东西,那么您只能将其作为单个标记返回arr[key]
仍然是四个标记,但是arr[]
它只有两个。是的,但问题是解析器仍然不能