Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this simple grammar have a shift/reduce conflict?

%token <token> PLUS MINUS INT
%left PLUS MINUS

THIS WORKS:

exp : exp PLUS exp;
exp : exp MINUS exp;
exp : INT;

THIS HAS 2 SHIFT/REDUCE CONFLICTS:

exp : exp binaryop exp;
exp : INT;
binaryop: PLUS | MINUS ;

WHY?

like image 869
MustafaM Avatar asked Mar 15 '12 09:03

MustafaM


People also ask

What causes shift-reduce conflict?

The Shift-Reduce Conflict is the most common type of conflict found in grammars. It is caused when the grammar allows a rule to be reduced for particular token, but, at the same time, allowing another rule to be shifted for that same token.

How can reduce shift-reduce conflict?

Rule 1. If there is a shift-reduce conflict in situations where no precedence rules have been created to resolve the conflict, the default action is to shift. The conflict is also reported in the yacc output so you can check that shifting is actually what you want.

What is a reduce-reduce conflict?

A reduce/reduce conflict occurs if there are two or more rules that apply to the same sequence of input. This usually indicates a serious error in the grammar. For example, here is an erroneous attempt to define a sequence of zero or more word groupings.

Is shift shift a conflict?

Shift/reduce conflicts occur when the parser can't tell whether to shift (push the next input token atop the parsing stack) or reduce (pop a series of terminals and nonterminals from the parsing stack). A reduce/reduce conflict is when the parser knows to reduce, but can't tell which reduction to perform.


2 Answers

This is because the second is in fact ambiguous. So is the first grammar, but you resolved the ambiguity by adding %left.

This %left does not work in the second grammar, because associativity and precedence are not inherited from rule to rule. I.e. the binaryop nonterminal does not inherit any such thing even though it produces PLUS and MINUS. Associativity and predecence are localized to a rule, and revolve around terminal symbols.

We cannot do %left binaryop, but we can slightly refactor the grammar:

exp : exp binaryop term
exp : term;
term : INT;
binaryop: PLUS | MINUS ;

That has no conflicts now because it is implicitly left-associative. I.e. the production of a longer and longer expression can only happen on the left side of the binaryop, because the right side is a term which produces only an INT.

like image 189
Kaz Avatar answered Oct 06 '22 00:10

Kaz


You need to specify a precedence for the exp binop exp rule if you want the precedence rules to resolve the ambiguity:

exp : exp binaryop exp %prec PLUS;

With that change, all the conflicts are resolved.

Edit

The comments seem to indicate some confusion as to what the precedence rules in yacc/bison do.

The precedence rules are a way of semi-automatically resolving shift/reduce conflicts in the grammar. They're only semi-automatic in that you have to know what you are doing when you specify the precedences.

Bascially, whenever there is a shift/reduce conflict between a token to be shifted and a rule to be reduced, yacc compares the precedence of the token to be shifted and the rule to be reduced, and -- as long as both have assigned precedences -- does whichever is higher precedence. If either the token or the rule has no precedence assigned, then the conflict is reported to the user.

%left/%right/%nonassoc come into the picture when the token and rule have the SAME precedence. In that case %left means do the reduce, %right means do the shift, and %nonassoc means do neither, causing a syntax error at runtime if the parser runs into this case.

The precedence levels themselves are assigned to tokens with%left/%right/%nonassoc and to rules with %prec. The only oddness is that rules with no %prec and at least one terminal on the RHS get the precedence of the last terminal on the RHS. This can sometimes end up assigning precedences to rules that you really don't want to have precedence, which can sometimes result in hiding conflicts due to resolving them incorrectly. You can avoid these problems by adding an extra level of indirection in the rule in question -- change the problematic terminal on the RHS to to a new non-terminal that expands to just that terminal.

like image 29
Chris Dodd Avatar answered Oct 06 '22 01:10

Chris Dodd