Неоднозначная грамматика, не LALR (1), по умолчанию не разбирается в режиме yacc
Короче говоря, вы можете "исправить" это с помощью объявления %glr-parser
следующим образом:
%glr-parser
%%
start: rule1_list
. . .
. . .
Чтобы сделать длинную историю средней длины ...
Конфликты сдвиг-уменьшение обычно не являются ошибками. Конфликт разрешается, всегда делая сдвиг, который, как правило, то, что вы хотите. Большинство или все грамматики реального мира имеют конфликты сдвига-уменьшения. И если вы хотели сокращения, вы можете договориться об этом с объявлениями предшествования.
Однако в действительно неоднозначной грамматике выполнение сдвига отправит синтаксический анализатор по одному из двух путей, только один из которых в конечном итоге найдет строку в грамматике. В этом случае конфликт S / R является фатальной ошибкой.
Анализируя первый, когда анализатор видит новую строку в случае | rule2 NEWLINE rule3_list
, он может либо перейти в новое состояние, где ожидается ожидаемый список правил3, либо уменьшить правило1, используя rule1: rule2
. Он всегда будет искать список rule3_list из-за выбора по умолчанию shift.
Второй конфликт возникает, когда он видит новую строку в rule3_list: rule3_list . NEWLINE rule3
. Теперь он может либо сдвинуться и начать искать правило 3, либо уменьшить правило 1, используя | rule2 NEWLINE rule3_list
.
В результате, как написано, и принимая «2» и «3» для терминалов, вы можете анализировать только 2 строки, а затем 3 строки. Если вы возитесь с приоритетом, вы можете анализировать только строки «2», но не строки «3».
Наконец, я должен добавить, что использование сгенерированного yacc синтаксического анализатора GLR является чем-то вроде клуджа. Я предполагаю, что это будет работать просто отлично, но это чистый BFI, парсер разбивает, хранит два стека, продолжается по обоим путям, пока один не найдет строку в грамматике. К сожалению, другие исправления также являются клуджами: 1. переформулируйте грамматику как LALR (1), 2. добавьте дополнительный взгляд в сканер и верните составной токен, 3. Поэкспериментируйте с правилами для вашей грамматики, возможно, yacc может обработать вариант.
Вот почему я на самом деле не люблю yacc и предпочитаю рукописный рекурсивный спуск или что-то более современное, такое как PEG. (см. Верхушку дерева)
Я пробовал что-то с (предпочтительными) леворекурсивными правилами, которые просто игнорировали символы новой строки (которые усложняют вашу грамматику, создавая пробелы для пробелов ...) ... и это "работает", хотя я не уверен, что это то, что ты хочешь ...
%%
start: stmtList
;
stmtList: /* nothing */
| stmtList '2' threeList;
;
threeList: /* nothing */
| threeList '3'
;
%%
int yylex() { int c; do { c = getchar (); } while (c == '\n'); return c; }