Я пишу простой парсер выражений в Jison. Вот моя грамматика:
{
"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;"]
]
}
}
Когда я пытаюсь запустить его, он выдает следующую ошибку:
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
Однако в конце он по-прежнему дает правильный вывод. Например, 2 + 3 * 5 / 7 % 11
правильно переводится как 2 + 3 * 5 / 7 % 11;
.
На мой взгляд, моя грамматика однозначна, так почему же Джисон жалуется?
Обновление: как объяснил @icktoofay, это проблема ассоциативности операторов. При анализе оператора как нетерминального символа информация о приоритете оператора и ассоциативности теряется. Поэтому я решил проблему следующим образом:
{
"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;"]
]
}
}
При этом эта грамматика допускает только одну необязательную новую строку после бинарного оператора. Как мне переписать его, чтобы за бинарным оператором могло следовать произвольное количество новых строк? Также должен быть какой-то способ, которым мне не нужно писать 2 правила для каждого оператора.