Updated ...
i know, my grammar is ambiguous but how can I rewrite this grammar to eliminate this ambiguity? ..................
I have a grammar just like this:
Bison file
%left equal nEqual
%left gre less greOrEqual lessOrEqual
%left plus sub
%left DIV mult exp mod
%left not
%left leftB rightB
%%
S :
var "=" A ";"
;
A:
Aexp
|Rexp
;
Aexp :
Num
| leftB Aexp rightB
| Aexp plus Aexp
| Aexp sub Aexp
| Aexp DIV Aexp
| Aexp mult Aexp
;
Rexp :
Aexp
| Rexp gre Rexp
| Rexp less Rexp
| Rexp greOrEqual Rexp
| Rexp lessOrEqual Rexp
| Rexp equal Rexp
| Rexp nEqual Rexp
;
I get 1 shift/reduce and 1 reduce/reduce conflicts doing this, how can I change the grammar to eliminate Conflicts ?
Your grammar is ambiguous. An A can be an Aexp or an Rexp. But an Rexp can also be an Aexp. This leads to a reduce/reduce conflict.
Let's say you give your parser this token sequence as input:
var = Num ;
The start symbol S expands to var = A ;
The non-terminal A has to match the token Num. But should that be an A that expands to and Aexp which then expands to Num, or should it be an A that expands to and Rexp, which then expands to Aexp, which then expands to Num?
Related
I've written a simple grammar for a language meant to be used in graph-based dialogue systems (primarily for video games).
Here are the grammars:
parser grammar DialogueScriptParser;
options {
tokenVocab = DialogueScriptLexer;
}
// Entry Point
script: scheduled_block* EOF;
// Scheduled Blocks
scheduled_block:
scheduled_block_open block scheduled_block_close;
scheduled_block_open: LT flag_list? LT;
scheduled_block_close: GT flag_list? GT;
block: statement*;
// Statements
statement:
if_statement
| switch_statement
| compound_statement
| expression_statement
| declaration_statement;
// Compound Statement
compound_statement: LBRACE statement_list? RBRACE;
statement_list: statement+;
// Expression Statement
expression_statement: expression SEMI;
// If Statement
if_statement:
IF LPAREN expression RPAREN statement (ELSE statement)?;
// Switch Statement
switch_statement: SWITCH LPAREN expression LPAREN switch_block;
switch_block: LBRACE switch_label* RBRACE;
switch_label: CASE expression COLON | DEFAULT COLON;
// Declaration Statement
declaration_statement: type declarator_init SEMI;
declarator_init: declarator (ASSIGN expression)?;
declarator: IDENTIFIER;
// Expression
expression_list: expression (COMMA expression);
expression:
name
| literal
| LPAREN expression RPAREN
| expression (INC | DEC)
| expression LBRACK expression RBRACK
| expression LPAREN expression_list? RPAREN
| expression LBRACE expression_list? RBRACE
| (SUB | ADD | INC | DEC | NOT | BIT_NOT) expression
| expression TURNARY expression COLON expression
| expression mul_div_mod_operator expression
| expression add_sub_operator expression
| <assoc = right> expression concat_operator expression
| expression relational_operator expression
| expression and_operator expression
| expression or_operator expression
| expression bitwise_operator expression;
// Operators
concat_operator: CONCAT;
and_operator: AND;
or_operator: OR;
add_sub_operator: ADD | SUB;
mul_div_mod_operator: MUL | DIV | MOD;
relational_operator: GT | LT | LE | GE;
equality_operator: EQUAL | NOTEQUAL;
bitwise_operator:
| '<' '<'
| '>' '>'
| BIT_AND
| BIT_OR
| BIT_XOR;
assignment_operator:
ASSIGN
| ADD_ASSIGN
| SUB_ASSIGN
| MUL_ASSIGN
| DIV_ASSIGN
| AND_ASSIGN
| OR_ASSIGN
| XOR_ASSIGN
| MOD_ASSIGN
| LSHIFT_ASSIGN
| RSHIFT_ASSIGN;
// Types
type:
primitive_type
| type LBRACK RBRACK
| name ('<' type (COMMA type)* '>')?;
primitive_type:
TYPE_BOOLEAN
| TYPE_CHAR
| TYPE_FLOAT_DEFAULT
| TYPE_FLOAT32
| TYPE_FLOAT64
| TYPE_INT_DEFAULT
| TYPE_INT8
| TYPE_INT16
| TYPE_INT32
| TYPE_INT64
| TYPE_UINT_DEFAULT
| TYPE_UINT8
| TYPE_UINT16
| TYPE_UINT32
| TYPE_UINT64
| TYPE_STRING;
// Name
name: namespace? IDENTIFIER (DOT IDENTIFIER)*;
// Namespace
namespace: IDENTIFIER COLONCOLON (namespace)*;
// Flags
flag_list: IDENTIFIER (COMMA IDENTIFIER)*;
// Literals
literal:
INTEGER_LITERAL
| FLOATING_POINT_LITERAL
| BOOLEAN_LITERAL
| CHARACTER_LITERAL
| STRING_LITERAL
| NULL_LITERAL;
and:
lexer grammar DialogueScriptLexer;
// Keywords
TYPE_BOOLEAN: 'bool';
TYPE_CHAR: 'char'; // 16 bits
TYPE_FLOAT_DEFAULT: 'float'; // 32 bits
TYPE_FLOAT32: 'float32';
TYPE_FLOAT64: 'float64';
TYPE_INT_DEFAULT: 'int'; // 32 bits
TYPE_INT8: 'int8';
TYPE_INT16: 'int16';
TYPE_INT32: 'int32';
TYPE_INT64: 'int64';
TYPE_UINT_DEFAULT: 'uint'; // 32 bits
TYPE_UINT8: 'uint8';
TYPE_UINT16: 'uint16';
TYPE_UINT32: 'uint32';
TYPE_UINT64: 'uint64';
TYPE_STRING: 'string'; // 16 bits per character
BREAK: 'break'; // used for switch
CASE: 'case'; // used for switch
DEFAULT: 'default'; // used for switch
IF: 'if';
ELSE: 'else';
SWITCH: 'switch';
// Integer Literals
INTEGER_LITERAL:
DecIntegerLiteral
| HexIntegerLiteral
| OctalIntegerLiteral
| BinaryIntegerLiteral;
fragment DecIntegerLiteral: '0' | NonZeroDigit Digit*;
fragment HexIntegerLiteral: '0' [xX] HexDigit+;
fragment OctalIntegerLiteral: '0' Digit+;
fragment BinaryIntegerLiteral: '0' [bB] BinaryDigit+;
// Floating-Point Literals
FLOATING_POINT_LITERAL: DecFloatingPointLiteral;
fragment DecFloatingPointLiteral:
DecIntegerLiteral? ('.' Digits) FloatTypeSuffix?
| DecIntegerLiteral FloatTypeSuffix?;
// Boolean Literals
BOOLEAN_LITERAL: 'true' | 'false';
// Character Literals
CHARACTER_LITERAL:
'\'' SingleCharacter '\''
| '\'' EscapeSequence '\'';
fragment SingleCharacter: ~['\\\r\n];
fragment EscapeSequence:
'\\\''
| '\\"'
| '\\\\'
| '\\0'
| '\\a'
| '\\b'
| '\\f'
| '\\n'
| '\\r'
| '\\t'
| '\\v';
// String Literals
STRING_LITERAL: '"' StringCharacters? '"';
fragment StringCharacters: StringCharacter+;
fragment StringCharacter: ~["\\\r\n] | EscapeSequence;
// Null Literal
NULL_LITERAL: 'null';
// Separators
LPAREN: '(';
RPAREN: ')';
LBRACE: '{';
RBRACE: '}';
LBRACK: '[';
RBRACK: ']';
SEMI: ';';
COMMA: ',';
DOT: '.';
COLON: ':';
COLONCOLON: '::';
// Operators
ASSIGN: '=';
ADD_ASSIGN: '+=';
SUB_ASSIGN: '-=';
MUL_ASSIGN: '*=';
DIV_ASSIGN: '/=';
AND_ASSIGN: '&=';
OR_ASSIGN: '|=';
XOR_ASSIGN: '^=';
MOD_ASSIGN: '%=';
LSHIFT_ASSIGN: '<<=';
RSHIFT_ASSIGN: '>>=';
GT: '>';
LT: '<';
EQUAL: '==';
LE: '<=';
GE: '>=';
NOTEQUAL: '!=';
NOT: '!';
BIT_NOT: '~';
BIT_AND: '&';
BIT_OR: '|';
BIT_XOR: '^';
/* Defining these here make recognizing scheduled blocks difficult BIT_SHIFT_L: '<<'; BIT_SHIFT_R:
'>>';
*/
AND: '&&';
OR: '||';
INC: '++';
DEC: '--';
ADD: '+';
SUB: '-';
MUL: '*';
DIV: '/';
MOD: '%';
CONCAT: '..';
TURNARY: '?';
// Identifiers
/* Order affects precedence IDENTFIER must come last. */
IDENTIFIER: Letter LetterOrDigit*;
fragment LetterOrDigit: Letter | Digit;
fragment Digits: Digit+;
fragment Digit: '0' | NonZeroDigit;
fragment NonZeroDigit: [1-9];
fragment HexDigit: [0-9a-fA-F];
fragment BinaryDigit: [01];
fragment Letter: [a-zA-Z_];
fragment FloatTypeSuffix: [fFdD];
// Whitespace and Comments
WHITESPACE: [ \t\r\n\u000C]+ -> skip;
COMMENT_BLOCK: '/*' .*? '*/' -> channel(HIDDEN);
COMMENT_LINE: '//' ~[\r\n]* -> channel(HIDDEN);
Project: https://github.com/Sahasrara/DialogueScript
The grammar is working for the most part, but it's struggling to parse certain types of expressions.
Example:
<<
if (intVar == 10 && globalFunc() || "string lit" .. "concat string" == stringVar)
{
anotherFunc();
}
>>
Here's the output tree:
I know there's a precedence issue here, but I'm not entirely sure how to resolve it. Would someone mind pointing me in the right direction?
You are not using the equality_operator rule that contains the == operator. Place it somewhere in your expression rule:
expression
: ...
| expression add_sub_operator expression
| expression equality_operator expression
| <assoc = right> expression concat_operator expression
| ...
;
When placed there, it will have a lower precedence than + and -, and a higher precedence than ..:
Also note that the assignment_operator is not used currently.
I am trying to add a special type of function to my antlr grammar called Window function. My Grammar looks something like this :
stat: expression;
equation: expression relop expression;
expression:
multiplyingExpression ((PLUS | MINUS) multiplyingExpression)*;
multiplyingExpression:
powExpression ((TIMES | DIV) powExpression)*;
powExpression: signedAtom (POW signedAtom)?;
signedAtom:
PLUS signedAtom
| MINUS signedAtom
| winfunc
| func
| iffunc
| atom;
atom:
scientific
| string_literal
| id
| constant
| LPAREN expression RPAREN;
string_literal: STRING;
scientific: SCIENTIFIC_NUMBER;
constant: PI | EULER | I;
variable: VARIABLE;
func: funcname LPAREN expression (COMMA expression)* RPAREN;
iffunc:
'if' LPAREN equation COMMA expression COMMA expression RPAREN;
funcname: variable;
relop: EQ | GT | LT;
LPAREN: '(';
RPAREN: ')';
PLUS: '+';
MINUS: '-';
TIMES: '*';
DIV: '/';
GT: '>';
LT: '<';
EQ: '==';
COMMA: ',';
POINT: '.';
POW: '^';
id: '[' idx ']' {console.log($idx.text);};
idx:
{(this.antlrHelper.isMetric(this.getCurrentToken().text))}? metricid
| {(this.antlrHelper.isDimension(this.getCurrentToken().text))}? entityid
| unknownid;
// metricid | entityid | unknownid;
metricid: VARIABLE;
entityid: VARIABLE;
unknownid: VARIABLE;
VARIABLE: VALID_ID_START VALID_ID_CHAR*;
fragment VALID_ID_START: ('a' .. 'z') | ('A' .. 'Z') | '_';
fragment VALID_ID_CHAR: VALID_ID_START | ('0' .. '9') | '.';
SCIENTIFIC_NUMBER: NUMBER ((E1 | E2) SIGN? NUMBER)?;
fragment NUMBER: ('0' .. '9')+ ('.' ('0' .. '9')+)?;
fragment E1: 'E';
fragment E2: 'e';
fragment SIGN: ('+' | '-');
STRING: '"' StringCharacters? '"';
fragment StringCharacters: StringCharacter+;
fragment StringCharacter: ~["\\] | EscapeSequence;
// ยง3.10.6 Escape Sequences for Character and String Literals
fragment EscapeSequence: '\\' [btnfr"'\\] | OctalEscape;
fragment OctalEscape:
'\\' OctalDigit
| '\\' OctalDigit OctalDigit
| '\\' ZeroToThree OctalDigit OctalDigit;
fragment ZeroToThree: [0-3];
fragment OctalDigit: [0-7];
WS: [ \r\n\t]+ -> skip;
It is taking normal functions. For windows function, I've added following rule :
winfunc:
WINDOW winfuncname LPAREN winMetricId COMMA scientific COMMA scientific RPAREN;
WINDOW: 'Window_';
winfuncname:
variable;
winMetricId: '[' winMetricIdx ']';
winMetricIdx:
{(this.antlrHelper.isMetric(this.getCurrentToken().text))}? metricid
| otherid;
otherid: VARIABLE;
On parsing
Window_ADD
it is parsing it into func rule but I want my grammar to parse it to winfunc rule.
Window_ ADD
it is parsing it into winfunc but I don't want that extra space to be there. How can I make Window_ADD parse to winfunc rule instead of Window_ ADD?
You have two options:
1. If you know exactly which function names (terminals) will be used, you may simply change your rule:
WINDOW: 'Window_';
to
WINDOW: 'Window_ADD';
If you want to add more functions, say, Window_DEL, just add one more terminal to this rule:
WINDOW: 'Window_' ('ADD' | 'DEL');
or
WINDOW: 'Window_';
WINDOW_ADD: WINDOW 'ADD';
WINDOW_DEL: WINDOW 'DEL';
2. In case function names are unknown, you may want to use wildcards to determine a terminal:
WINDOW: 'Window_' VALID_ID_CHAR+;
In this case the type of a function is determined during the phase of semantic analysis.
i have this Grammar
grammar Arith;
exp : LPAREN exp RPAREN
| fun
| num
| exp (OP exp)+
;
num : LPAREN num RPAREN
| LESS num
| INT
| INT 'b'
| '0x' INT
;
fun : LPAREN fun RPAREN
| LESS fun
| FUN_TXT LPAREN exp RPAREN
| 'pow' LPAREN exp ',' exp RPAREN
;
INT : ('0'..'9')+ ;
LPAREN : '(' ;
RPAREN : ')' ;
FUN_TXT : 'log' | 'acos' | 'asin' | 'atan' | 'cos' | 'abs' | 'sin' | 'sqrt' | 'tan' ;
OP : ADD | LESS | MUL | DIV | MOD ;
ADD : '+' ;
LESS : '-' ;
MUL : '*' ;
DIV: '/' ;
MOD: '%' ;
WS : [ \t\r\n] -> skip ;
I try to insert sin(-1) but the lexer said me "no viable alternative at input '-'".
I think that program translate it in "exp -> exp (OP exp)+" instead of "exp -> fun(num) -> fun(LESS num)"
Could someone help me to understand what i've forget and how change my rules in the right way?
Thanks
First I would simplify your rules for num and fun
num : INT
| INT 'b'
| '0x' INT
;
fun : FUN_TXT LPAREN exp RPAREN
| 'pow' LPAREN exp ',' exp RPAREN
;
Brackets and minuses are handled by the exp rule.
You also need to separate the ADD and SUB from the multiplicative operators to get precedence right. The calculator example for the Antlr grammars uses
expression
: multiplyingExpression ((PLUS|MINUS) multiplyingExpression)*
;
multiplyingExpression
: powExpression ((TIMES|DIV) powExpression)*
;
powExpression
: atom (POW expression)?
;
atom
: scientific
| variable
| LPAREN expression RPAREN
| func
;
scientific
: number (E number)?
;
func
: funcname LPAREN expression RPAREN
;
I would be inclined to start from that.
I am trying to write mincaml parser in antlr4. github(https://github.com/esumii/min-caml/blob/master/parser.mly).
Japanese site : http://esumii.github.io/min-caml/ .
here is antlr 4 code.
grammar MinCaml;
simple_exp: #simpleExp
| LPAREN exp RPAREN #parenExp
| LPAREN RPAREN #emptyParen
| BOOL #boolExpr
| INT #intExpr
| FLOAT #floatExpr
| IDENT #identExpr
| simple_exp DOT LPAREN exp RPAREN #arrayGetExpr
;
exp : #programExp
| simple_exp #simpleExpInExp
| NOT exp #notExp
| MINUS exp #minusExp
| MINUS_DOT exp #minusFloatExp
| left = exp op = (AST_DOT | SLASH_DOT) right = exp #astSlashExp
| left = exp op = (PLUS | MINUS | MINUS_DOT | PLUS_DOT) right = exp #addSubExp
| left = exp op = (EQUAL | LESS_GREATER | LESS | GREATER | LESS_EQUAL | GREATER_EQUAL) right = exp #logicExp
| IF condition = exp THEN thenExp = exp ELSE elseExp = exp #ifExp
| LET IDENT EQUAL exp IN exp #letExp
| LET REC fundef IN exp #letRecExp
| exp actual_args #appExp
| exp COMMA exp elems #tupleExp
| LET LPAREN pat RPAREN EQUAL exp IN exp #tupleReadExp
| simple_exp DOT LPAREN exp RPAREN LESS_MINUS exp #putExp
| exp SEMICOLON exp #expSeqExp
| ARRAY_CREATE simple_exp simple_exp #arrayCreateExp
;
fundef:
| IDENT formal_args EQUAL exp
;
formal_args:
| IDENT formal_args
| IDENT
;
actual_args:
| actual_args simple_exp
| simple_exp
;
elems:
| COMMA exp elems
|
;
pat:
| pat COMMA IDENT
| IDENT COMMA IDENT
;
LET : 'let';
REC : 'rec';
IF : 'if';
THEN : 'then';
ELSE : 'else';
IN : 'in';
IDENT : '_' | [a-z][a-zA-Z0-9_]+;
ARRAY_CREATE : 'Array.create';
LPAREN : '(';
RPAREN : ')';
BOOL : 'true' 'false';
NOT : 'not';
INT : ['1'-'9'] (['0'-'9'])*;
FLOAT : (['0'-'9'])+ ('.' (['0'-'9'])*)? (['e', 'E'] (['+', '-'])? (['0'-'9'])+)?;
MINUS : '-';
PLUS : '+';
MINUS_DOT : '-.';
PLUS_DOT : '+.';
AST_DOT : '*.';
SLASH_DOT : '/.';
EQUAL : '=';
LESS_GREATER : '';
LESS_EQUAL : '=';
LESS : '';
DOT : '.';
LESS_MINUS : ' skip ; // toss out whitespace
COMMENT : '(*' .*? '*)' -> skip;
but I get following errors on rules exp and actual args.
error(148): MinCaml.g4:13:0: left recursive rule exp contains a left recursive alternative which can be followed by the empty string
error(148): MinCaml.g4:41:0: left recursive rule actual_args contains a left recursive alternative which can be followed by the empty string
But I don't see a any possibility of empty string on both rules. Or am I wrong?
What is wrong with this code?
The first line of the exp rule (actually of every rule) is the likely problem:
exp : #programExp
The standard rule form is
r: alt1 | alt2 | .... | altN ;
The alt1s in the grammar are all empty. An empty alt "matches an empty string".
Given the elems rule appears to have an intentional empty alt, consider that, in general terms, rules with empty alts can be problematic. Rather than using an empty alt, make the corresponding element in the parent rule optional (either ? or *).
When I try to use yacc on the following file I get the error conflicts: 1 shift/reduce
How can I find and fix the conflict?
/* C-Minus BNF Grammar */
%token ELSE
%token IF
%token INT
%token RETURN
%token VOID
%token WHILE
%token ID
%token NUM
%token LTE
%token GTE
%token EQUAL
%token NOTEQUAL
%%
program : declaration_list ;
declaration_list : declaration_list declaration | declaration ;
declaration : var_declaration | fun_declaration ;
var_declaration : type_specifier ID ';'
| type_specifier ID '[' NUM ']' ';' ;
type_specifier : INT | VOID ;
fun_declaration : type_specifier ID '(' params ')' compound_stmt ;
params : param_list | VOID ;
param_list : param_list ',' param
| param ;
param : type_specifier ID | type_specifier ID '[' ']' ;
compound_stmt : '{' local_declarations statement_list '}' ;
local_declarations : local_declarations var_declaration
| /* empty */ ;
statement_list : statement_list statement
| /* empty */ ;
statement : expression_stmt
| compound_stmt
| selection_stmt
| iteration_stmt
| return_stmt ;
expression_stmt : expression ';'
| ';' ;
selection_stmt : IF '(' expression ')' statement
| IF '(' expression ')' statement ELSE statement ;
iteration_stmt : WHILE '(' expression ')' statement ;
return_stmt : RETURN ';' | RETURN expression ';' ;
expression : var '=' expression | simple_expression ;
var : ID | ID '[' expression ']' ;
simple_expression : additive_expression relop additive_expression
| additive_expression ;
relop : LTE | '<' | '>' | GTE | EQUAL | NOTEQUAL ;
additive_expression : additive_expression addop term | term ;
addop : '+' | '-' ;
term : term mulop factor | factor ;
mulop : '*' | '/' ;
factor : '(' expression ')' | var | call | NUM ;
call : ID '(' args ')' ;
args : arg_list | /* empty */ ;
arg_list : arg_list ',' expression | expression ;
As mientefuego pointed out you grammar has the classic "dangling else" problem.
You could beat the problem by assigning precedence to the rules that causes conflict.
The rule causing conflict is:
selection_stmt : IF '(' expression ')' statement
| IF '(' expression ')' statement ELSE statement ;
First start by making ELSE and LOWER_THAN_ELSE ( a pseudo-token ) non associative:
%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE
This gives ELSE more precedence over LOWER_THAN_ELSE simply because LOWER_THAN_ELSE is declared first.
Then in the conflicting rule you have to assign a precedence to either the shift or reduce action:
selection_stmt : IF '(' expression ')' statement %prec LOWER_THAN_ELSE ;
| IF '(' expression ')' statement ELSE statement ;
Here, higher precedence is given to shifting. I have incorporated the above mentioned corrections and listed the complete grammar below:
/* C-Minus BNF Grammar */
%token ELSE
%token IF
%token INT
%token RETURN
%token VOID
%token WHILE
%token ID
%token NUM
%token LTE
%token GTE
%token EQUAL
%token NOTEQUAL
%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE
%%
program : declaration_list ;
declaration_list : declaration_list declaration | declaration ;
declaration : var_declaration | fun_declaration ;
var_declaration : type_specifier ID ';'
| type_specifier ID '[' NUM ']' ';' ;
type_specifier : INT | VOID ;
fun_declaration : type_specifier ID '(' params ')' compound_stmt ;
params : param_list | VOID ;
param_list : param_list ',' param
| param ;
param : type_specifier ID | type_specifier ID '[' ']' ;
compound_stmt : '{' local_declarations statement_list '}' ;
local_declarations : local_declarations var_declaration
| /* empty */ ;
statement_list : statement_list statement
| /* empty */ ;
statement : expression_stmt
| compound_stmt
| selection_stmt
| iteration_stmt
| return_stmt ;
expression_stmt : expression ';'
| ';' ;
selection_stmt : IF '(' expression ')' statement %prec LOWER_THAN_ELSE ;
| IF '(' expression ')' statement ELSE statement ;
iteration_stmt : WHILE '(' expression ')' statement ;
return_stmt : RETURN ';' | RETURN expression ';' ;
expression : var '=' expression | simple_expression ;
var : ID | ID '[' expression ']' ;
simple_expression : additive_expression relop additive_expression
| additive_expression ;
relop : LTE | '<' | '>' | GTE | EQUAL | NOTEQUAL ;
additive_expression : additive_expression addop term | term ;
addop : '+' | '-' ;
term : term mulop factor | factor ;
mulop : '*' | '/' ;
factor : '(' expression ')' | var | call | NUM ;
call : ID '(' args ')' ;
args : arg_list | /* empty */ ;
arg_list : arg_list ',' expression | expression ;
maybe you should try a yacc -v <filename>, it generates an output of the details.
I tested here, and your grammar description fails in the classic "dangling else" problem.
Take a look at this Wikipedia article.
Ahem, the correct answer to this problem is usually: do nothing.
Shift/reduce conflicts are expected with ambiguous grammars. They are not errors, they are conflicts.
The conflict will be resolved by preferring shift over reduce, which just happens to solve the canonical dangling else problem.
And bison even has an %expect n statement so that you don't get a S/R conflict warning when there are exactly n conflicts.
First, get a state machine output from yacc. A state which can be either shifted or reduced represents a shift/reduce conflict. Find one, and then solve the conflict by rewriting the grammar.
This article gives an alternative solution to the one posted by ardsrk.