Reputation: 74234
I'm writing a simple expression parser in Jison. Here's my grammar:
{
"operators": [
["left", "+", "-"],
["left", "*", "/", "%"]
],
"bnf": {
"program": [
["statement EOF", "return $1;"]
],
"statement": [
["expression NEWLINE", "$$ = $1 + ';';"]
],
"expression": [
["NUMBER", "$$ = yytext;"],
["expression binary expression", "$$ = $1 + $2 + $3;"]
],
"binary": [
["+", "$$ = ' + ';"],
["-", "$$ = ' - ';"],
["*", "$$ = ' * ';"],
["/", "$$ = ' / ';"],
["%", "$$ = ' % ';"],
["binary NEWLINE", "$$ = $1;"]
]
}
}
When I try to run it it gives me the following error:
Conflict in grammar: multiple actions possible when lookahead token is + in state
13
- reduce by rule: expression -> expression binary expression
- shift token (then go to state 8)
Conflict in grammar: multiple actions possible when lookahead token is - in state
13
- reduce by rule: expression -> expression binary expression
- shift token (then go to state 9)
Conflict in grammar: multiple actions possible when lookahead token is * in state
13
- reduce by rule: expression -> expression binary expression
- shift token (then go to state 10)
Conflict in grammar: multiple actions possible when lookahead token is / in state
13
- reduce by rule: expression -> expression binary expression
- shift token (then go to state 11)
Conflict in grammar: multiple actions possible when lookahead token is % in state
13
- reduce by rule: expression -> expression binary expression
- shift token (then go to state 12)
States with conflicts:
State 13
expression -> expression binary expression . #lookaheads= NEWLINE + - * / %
expression -> expression .binary expression
binary -> .+
binary -> .-
binary -> .*
binary -> ./
binary -> .%
binary -> .binary NEWLINE
However it still produces the correct output in the end. For example 2 + 3 * 5 / 7 % 11
is correctly translated to 2 + 3 * 5 / 7 % 11;
.
The way I see it my grammar appears to be unambiguous, so why is Jison complaining?
Update: As @icktoofay explained it's an operator associativity problem. By parsing an operator as a non-terminal symbol operator precedence and associativity information is lost. Hence I solved the problem as follows:
{
"operators": [
["left", "+", "-"],
["left", "*", "/", "%"]
],
"bnf": {
"program": [
["statement EOF", "return $1;"]
],
"statement": [
["expression NEWLINE", "$$ = $1 + ';';"]
],
"expression": [
["NUMBER", "$$ = yytext;"],
["expression + expression", "$$ = $1 + ' + ' + $3;"],
["expression - expression", "$$ = $1 + ' - ' + $3;"],
["expression * expression", "$$ = $1 + ' * ' + $3;"],
["expression / expression", "$$ = $1 + ' / ' + $3;"],
["expression % expression", "$$ = $1 + ' % ' + $3;"],
["expression + NEWLINE expression", "$$ = $1 + ' + ' + $4;"],
["expression - NEWLINE expression", "$$ = $1 + ' - ' + $4;"],
["expression * NEWLINE expression", "$$ = $1 + ' * ' + $4;"],
["expression / NEWLINE expression", "$$ = $1 + ' / ' + $4;"],
["expression % NEWLINE expression", "$$ = $1 + ' % ' + $4;"]
]
}
}
That being said this grammar only allows one optional newline to follow a binary operator. How do I rewrite it so as to allow an arbitrary number of newlines to follow a binary operator? Also there must be some way in which I don't have to write 2 rules for each operator.
Upvotes: 4
Views: 785
Reputation: 129049
I'm not entirely familiar with Jison, but it looks like you're defining a rule that looks like this:
expression ::= number;
expression ::= expression binary expression;
Consider the expression 1 - 2 - 3
. That could be interpreted as (1 - 2) - 3
or 1 - (2 - 3)
. Which is it? Your grammar is ambiguous. Normal mathematical rules say it should be left-associative. You need to make your grammar reflect that:
expression ::= number;
expression ::= expression binary number;
Upvotes: 5