Ваша грамматика не использует INDENT_DEC
; без этого вы не можете знать, где заканчивается отступ block
.
По сути, это то, что говорят вам эти конфликты сдвига / уменьшения. Так как грамматика не видит INDENT_DEC
, она не может различить EOL
, который разделяет два expr
s в одном блоке, и EOL
, который завершает block
. Таким образом, EOL
является неоднозначным (при условии, что хотя бы один INDENT_INC
был замечен).
Вот простая демонстрация двусмысленности. Выражение для разбора:
a EOL INDENT_INC b EOL INDENT_INC c EOL d
Вот два крайних левых вывода, которые отличаются тем, где вложено d
(я упростил путь subexpr ⇒ var ⇒ TOK
для простоты):
# Here, d belongs to the outer subexpr (effectively, a single indent)
expr ⇒ subexpr EOL INDENT_INC block
⇒ TOK (a) EOL INDENT_INC block
⇒ TOK (a) EOL INDENT_INC block EOL expr
⇒ TOK (a) EOL INDENT_INC expr EOL expr
⇒ TOK (a) EOL INDENT_INC subexpr EOL INDENT_INC block EOL expr
⇒ TOK (a) EOL INDENT_INC subexpr EOL INDENT_INC expr EOL expr
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC subexpr EOL expr
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC TOK (c) EOL expr
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC TOK (c) EOL subexpr
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC TOK (c) EOL TOK (d)
# Here, d belongs to the inner subexpr (effectively two indents)
expr ⇒ subexpr EOL INDENT_INC block
⇒ TOK (a) EOL INDENT_INC block
⇒ TOK (a) EOL INDENT_INC expr
⇒ TOK (a) EOL INDENT_INC subexpr EOL INDENT_INC block
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC block
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC block EOL expr
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC expr EOL expr
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC subexpr EOL expr
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC TOK (c) EOL expr
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC TOK (c) EOL subexpr
⇒ TOK (a) EOL INDENT_INC TOK (b) EOL INDENT_INC TOK (c) EOL TOK (d)
Так что грамматика действительно неоднозначна. Но конфликты сдвига / уменьшения не указывают прямо на неопределенность. Они указывают на проблему принятия решения о том, уменьшать или нет конструкцию до EOL
, не видя символа, следующего за EOL
. В этом суть ограничения LR (1): каждое сокращение должно быть выполнено до сдвига следующего символа, поэтому даже если грамматика будет однозначной, если вы сможете увидеть достаточно далеко в будущем, у нее все еще будут конфликты сдвига / уменьшения, если Решение о сокращении может пойти в любую сторону.