由于yacc报告了6个shift / reduce冲突,所以我一直迷糊了一段时间。我查看了y.output文件,并试图了解如何查看状态并找出解决歧义语法但无济于事的方法。我被合理地固定在应该如何解决这些问题上。我查看了很多关于堆栈溢出的问题,以了解其他人的解释是否可以帮助我解决我的问题,但这也没有太大帮助。作为记录,我不能使用任何优先级定义指令(例如%left
)来解决解析冲突。
有人可以通过指导我如何改变语法以解决班次/减少冲突的方式来帮助我吗?也许通过尝试解决其中一个问题并向我展示其背后的思维过程?我知道语法很长很重,为此我深表歉意。如果有人愿意为此腾出时间,将不胜感激,但我意识到我可能无法拥有。
无论如何,这是我所关注的语法(它是MiniJava语法的略微扩展):
Grammar
0 $accept: program $end
1 program: main_class class_decl_list
2 main_class: CLASS ID '{' PUBLIC STATIC VOID MAIN '(' STRING '[' ']' ID ')' '{' statement '}' '}'
3 class_decl_list: class_decl_list class_decl
4 | %empty
5 class_decl: CLASS ID '{' var_decl_list method_decl_list '}'
6 | CLASS ID EXTENDS ID '{' var_decl_list method_decl_list '}'
7 var_decl_list: var_decl_list var_decl
8 | %empty
9 method_decl_list: method_decl_list method_decl
10 | %empty
11 var_decl: type ID ';'
12 method_decl: PUBLIC type ID '(' formal_list ')' '{' var_decl_list statement_list RETURN exp ';' '}'
13 formal_list: type ID formal_rest_list
14 | %empty
15 formal_rest_list: formal_rest_list formal_rest
16 | %empty
17 formal_rest: ',' type ID
18 type: INT
19 | BOOLEAN
20 | ID
21 | type '[' ']'
22 statement: '{' statement_list '}'
23 | IF '(' exp ')' statement ELSE statement
24 | WHILE '(' exp ')' statement
25 | SOUT '(' exp ')' ';'
26 | SOUT '(' STRING_LITERAL ')' ';'
27 | ID '=' exp ';'
28 | ID index '=' exp ';'
29 statement_list: statement_list statement
30 | %empty
31 index: '[' exp ']'
32 | index '[' exp ']'
33 exp: exp OP exp
34 | '!' exp
35 | '+' exp
36 | '-' exp
37 | '(' exp ')'
38 | ID index
39 | ID '.' LENGTH
40 | ID index '.' LENGTH
41 | INTEGER_LITERAL
42 | TRUE
43 | FALSE
44 | object
45 | object '.' ID '(' exp_list ')'
46 object: ID
47 | THIS
48 | NEW ID '(' ')'
49 | NEW type index
50 exp_list: exp exp_rest_list
51 | %empty
52 exp_rest_list: exp_rest_list exp_rest
53 | %empty
54 exp_rest: ',' exp
这是y.output中具有移位/减少冲突的相关状态。
State 58
7 var_decl_list: var_decl_list . var_decl
12 method_decl: PUBLIC type ID '(' formal_list ')' '{' var_decl_list . statement_list RETURN exp ';' '}'
INT shift, and go to state 20
BOOLEAN shift, and go to state 21
ID shift, and go to state 22
ID [reduce using rule 30 (statement_list)]
$default reduce using rule 30 (statement_list)
var_decl go to state 24
type go to state 25
statement_list go to state 69
State 76
38 exp: ID . index
39 | ID . '.' LENGTH
40 | ID . index '.' LENGTH
46 object: ID .
'[' shift, and go to state 64
'.' shift, and go to state 97
'.' [reduce using rule 46 (object)]
$default reduce using rule 46 (object)
index go to state 98
State 100
33 exp: exp . OP exp
34 | '!' exp .
OP shift, and go to state 103
OP [reduce using rule 34 (exp)]
$default reduce using rule 34 (exp)
State 101
33 exp: exp . OP exp
35 | '+' exp .
OP shift, and go to state 103
OP [reduce using rule 35 (exp)]
$default reduce using rule 35 (exp)
State 102
33 exp: exp . OP exp
36 | '-' exp .
OP shift, and go to state 103
OP [reduce using rule 36 (exp)]
$default reduce using rule 36 (exp)
State 120
33 exp: exp . OP exp
33 | exp OP exp .
OP shift, and go to state 103
OP [reduce using rule 33 (exp)]
$default reduce using rule 33 (exp)
就在那里。对于这种语法的长度以及移位/减少冲突的次数,我再次表示歉意。我似乎无法理解如何通过更改相关语法来解决这些问题。我们将不胜感激任何帮助,尽管如果没有人有时间浏览如此庞大的职位,我将理解。如果有人需要更多信息,请随时询问。
答案 0 :(得分:2)
基本问题是,在解析method_decl
正文时,它无法分辨var_decl_list
的结尾和statement_list
的开始位置。这是因为当前瞻为ID
时,它不知道这是另一个var_decl
的开始还是第一个statement
的开始,因此需要减少一个空语句才能开始在statement_list
上工作。
您可以通过多种方式处理此问题:
让词法分析器为类型ID和其他ID返回不同的令牌-这样,差异将告诉解析器哪个是下一个。
在语句列表的开头不需要空语句。将语法更改为:
statement_list: statement | statement_list statement ;
opt_statement_list: statement_list | %empty ;
,并在opt_statement_list
规则中使用method_decl
。可以解决在开始分析语句之前必须减少空statement_list
的问题。当您用多个变体替换规则时,此过程称为“解因”语法。它使语法更加复杂,在这种情况下,它不能解决问题,而只是解决问题。然后您会在statement: ID . index
前瞻中看到type: ID
和[
之间的转移/减少冲突。此问题也可以通过分解来解决,但难度更大。
因此,这提出了通过分解来解决移位减少冲突的一般思想。基本思想是摆脱导致班次减少冲突减少的规则,而将其替换为上下文中更受限制的规则,因此不要触发冲突。上面的示例很容易通过“用1个或多个递归重复和一个可选规则替换0个或多个递归重复”来解决。如果以下情况意味着您可以轻松地确定何时0例应合法(在这种情况下仅当下一个标记为}
时),这对于重复操作的epsilon规则上的shift-reduce冲突效果很好。 / p>
第二场冲突更加艰难。当提前量为type: ID
时,冲突就在减少[
上。因此,我们需要重复类型规则,直到没有必要为止。像这样:
type: simpleType | arrayType ;
simpleType: INT | BOOLEAN | ID ;
arrayType: INT '[' ']' | BOOLEAN '[' ']' | ID '[' ']'
| arrayType '[' ']' ;
将“ '[' ']'
后缀的0个或多个重复”替换为“ 1个或多个”,并且由于类似的原因起作用(将减少量推迟到看到'[' ']'
之后再使用,而不是之前要求。)关键在于,{a {1}}的前瞻性永远不需要减少simpleType: ID
规则,因为它仅在其他情况下有效。