问题
The grammar is as follows:
1. program -> declaration-list
2. declaration-list -> declaration-list declaration | declaration
3. declaration -> var-declaration | fun-declaration
4. var-declaration -> type-specifier ID ; | type-specifier ID [ NUM ] ;
5. type-specifier -> int | void
6. fun-declaration -> type-specifier ID ( params ) compound-stmt
7. params -> param-list | void
8. param-list -> param-list , param | param
9. param -> type-specifier ID | type-specifier ID [ ]
10. compound-stmt -> { local-declarations statement-list }
11. local-declarations -> local-declarations var-declarations | empty
12. statement-list -> statement-list statement | empty
13. statement -> expression-stmt | compound-stmt | selection-stmt |
iteration-stmt | return-stmt
14. expression-stmt -> expression ; | ;
15. selection-stmt -> if ( expression ) statement |
if ( expression ) statement else statement
16. iteration-stmt -> while ( expression ) statement
17. return-stmt -> return ; | return expression ;
18. expression -> var = expression | simple-expression
19. var -> ID | ID [ expression ]
20. simple-expression -> additive-expression relop additive-expression |
additive-expression
21. relop -> <= | < | > | >= | == | !=
22. additive-expression -> additive-expression addop term | term
23. addop -> + | -
24. term -> term mulop factor | factor
25. mulop -> * | /
26. factor -> ( expression ) | var | call | NUM
27. call -> ID ( args )
28. args -> arg-list | empty
29. arg-list -> arg-list , expression | expression
The shift reduce conflict that I get through bison -d -v xyz.l is in state 97
state 97
29 selection-stmt: IF LFT_BRKT expression RGT_BRKT statement .
30 | IF LFT_BRKT expression RGT_BRKT statement . ELSE statement
ELSE shift, and go to state 100
ELSE [reduce using rule 29 (selection-stmt)]
$default reduce using rule 29 (selection-stmt)
But I do not know how to resolve this conflict. Waiting for an answer.
回答1:
You would want to resolve the conflict in favour of shifting the 'else'. Fortunately, bison has done that for you automatically (but it still lets you know about it.)
Section 5.2 of the Bison manual is about precisely this shift/reduce conflict. As it says there, you can eliminate the warning message if you want to with a %expect
declaration.
This specific shift/reduce conflict was a large part of the motivation for the resolution strategy of the original yacc
parser-generator, as described in the historic paper on yacc, or in the Dragon book, because it is incredibly annoying to eliminate the conflict from a grammar. So the solution to this question is a nice brain-teaser, but should never be deployed in practice. Using Bison's built-in ambiguity elimination is much more readable and maintainable, and there is no imprecision or shame in doing so.
If I recall correctly, this problem is one of the exercises in the Dragon book. The basic outline of the solution goes like this:
There would not be an issue if the
statement
inif (expression) statement
could not be anif
statement.else
cannot begin a statement, soif ( 0 ) break;
cannot be reduced withelse
in the lookahead. The problem isif (0) if (0) break; else
Now, it's not obvious whether else should be shifted (and thereby attached to the second if) or if the secondif
should be reduced, leaving theelse
to be shifted onto the firstif
. Normal practice (and yacc's ambiguity resolution algorithm) dictate the first.So let's distinguish between complete if-statements and incomplete if-statements. Now we can say that an incomplete if-statement (one without an else clause) cannot be immediately followed by
else
. In other words, a complete if-statement cannot have an incomplete if-statement as its first enclosed statement.
So we can try something like:
conditional : complete_conditional
| incomplete_conditional
;
complete_conditional : IF ( expression ) statement_other_than_conditional ELSE statement
| IF ( expression ) complete_conditional ELSE statement
;
incomplete_conditional : IF ( expression ) statement
;
And now we need:
statement : statement_other_than_conditional
| incomplete_conditional
| complete_conditional
;
回答2:
See my answer here: Reforming the grammar to remove shift reduce conflict in if-then-else. In my experience, you should never leave "known conflicts", solve them. Using %expect N with N != 0 is not safe, IMHO (GLR aside, of course).
回答3:
I have tried @rici 's answer (the accepted answer), and it fails.
statement: conditional | statement_other_than_conditional;
conditional: complete_conditional | incomplete_conditional;
complete_conditional: L_IF '(' expression ')' statement_other_than_conditional L_ELSE statement
| L_IF '(' expression ')' complete_conditional L_ELSE statement;
incomplete_conditional: L_IF '(' expression ')' statement;
statement_other_than_conditional: ';';
expression: IDENTIFIER;
$ bison --report=all rrr.y
rrr.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
State 14 conflicts: 1 shift/reduce
State 15 conflicts: 1 shift/reduce
State 14
3 conditional: complete_conditional . [$end, L_ELSE]
6 complete_conditional: L_IF '(' expression ')' complete_conditional . L_ELSE statement
L_ELSE shift, and go to state 16
L_ELSE [reduce using rule 3 (conditional)]
$default reduce using rule 3 (conditional)
State 15
2 statement: statement_other_than_conditional . [$end, L_ELSE]
5 complete_conditional: L_IF '(' expression ')' statement_other_than_conditional . L_ELSE statement
L_ELSE shift, and go to state 17
L_ELSE [reduce using rule 2 (statement)]
$default reduce using rule 2 (statement)
Chris Dodd's answer is (at least seems to be) good. A bit adapted:
statement: if_statement | noif_statement;
if_statement:
IF '(' expression ')' statement
| IF '(' expression ')' noif_statement ELSE if_statement
;
noif_statement:
IF '(' expression ')' noif_statement ELSE noif_statement
| RETURN ';'
;
expression: IDENTIFIER;
If you have further statement rules: if it is not right recursive (does not end with statement
) then add just to noif_statement (like RETURN ';'
).
Else, for example
statement: blah '(' blah ')' statement ;
Duplicate it:
| blah '(' blah ')' if_statement
| blah '(' blah ')' noif_statement
And add the first variant to the if_statements and the second to the noif_statements.
回答4:
Another strategy that works (the accepted answer by @rici DOES NOT work, the answer @RaqaouPi credits to Chris Dodd does) is to treat code as a series of if-else/for/while prefixes followed, maybe, by a single dangling if or a simple statement at the end. This example is adapted from this Nearley grammar for a C-like language.
Statement -> StatementPrefix:* (StatementEnd | DanglingIf)
StatementNoDangle -> StatementPrefix:* StatementEnd
StatementPrefix -> "if" _ "(" _ Expression _ ")" _ StatementNoDangle _ "else" _
| "while" _ "(" _ Expression _ ")" _
DanglingIf -> "if" _ "(" _ Expression _ ")" _ Statement
StatementEnd -> Simple _ ";"
| "return" (_ Expression):? _ ";"
| BlockStatement
| "break" _ ";"
| "continue" _ ";"
来源:https://stackoverflow.com/questions/12720219/bison-shift-reduce-conflict-unable-to-resolve