Фон
Я работаю над компилятором для латексного языка. Я уже написал файл lex, и пока он работает так, как должен. Однако теперь, когда я работаю над грамматикой в файле .y, у меня возникли проблемы.
Проблема
Я воспроизвел часть грамматики, которая, как я считаю, отвечает за то, что поставила меня в тупик:
%start document
%%
document: BEGINDOCUMENT documentbody ENDDOCUMENT;
documentbody: contentseq | ws MAKETITLE contentseq | MAKETITLE contentseq;
contentseq: | contentseq content;
content: STRING | ws;
ws: WHITESPACE;
Пробелы в этом контексте — это, по сути, любое сочетание пробелов, табуляций и новых строк.
Насколько я понял из файла y.output, ошибка сдвига/уменьшения возникает из-за правила
documentbody: ... | ws MAKETITLE contentseq | ...
Учитывая токен WHITESPACE, Bison не знает, является ли он частью «контента» терминала или вместо него будет следовать токен MAKETITLE. Оба являются абсолютно допустимыми входными данными, и я не уверен, как решить эту проблему.
Для ясности парафраз оригинальной спецификации EBNF:
document: BEGINDOCUMENT [ws] [MAKETITLE] contentseq ENDDOCUMENT
Другими словами, терминал ws и MAKETITLE необязательны.
Пример ввода
BEGINDOCUMENT WHITESPACE MAKETITLE STRING ENDDOCUMENT
BEGINDOCUMENT WHITESPACE STRING ENDDOCUMENT
BEGINDOCUMENT MAKETITLE STRING ENDDOCUMENT
BEGINDOCUMENT STRING ENDDOCUMENT
Все вышеперечисленное должно быть принято грамматикой.
Что я пробовал
Я знаю, что многие конфликты можно сгладить с помощью приоритета, но ничто из того, что я пробовал в этом ключе, не сработало. Я пробовал назначать маркерам MAKETITLE и WHITESPACE любой приоритет, но это не решило проблему.
Я видел предложения по другим проблемам, связанным со сдвигом/уменьшением, чтобы переписать грамматику, чтобы она была менее двусмысленной, но я не уверен, как это сделать - по крайней мере, без изменения того, какие входные данные принимает и не принимает грамматика.
Одно из решений, о котором я думал, но не пробовал, - это возиться с файлом lex, но это кажется довольно странным решением, и я бы предпочел найти какой-нибудь способ сделать это в yacc.