LR(1) Parser: почему добавление эпсилон-продукции приводит к конфликтам r/r или s/r

Я хотел сделать ридер, который читает файлы конфигурации, похожие на файлы INI для mswin. Это для тренировки, чтобы научиться использовать генератор лексера/парсера, который я сделал. Грамматика такова:

%lexer
HEADER ::= "\\[[0-9a-zA-Z]+\\]"
TRUE ::= "yes|true"
FALSE ::= "no|false"
ASSIGN ::= "="
OPTION_NAME ::= "[a-zA-Z][0-9a-zA-Z]*"
INT ::= "[0-9]+"
STRING ::= "\"(\\\"|[^\"])*\""
CODE ::= "<{(.*)}>"
BLANK ::= "[ \t\f]+" :ignore
COMMENT ::= "#[^\n\r]*(\r|\n)?" :ignore
NEWLINE ::= "\r|\n"
%parser
Options ::= OptionGroup Options | OptionGroup | @epsilon@
OptionGroup ::= HEADER NEWLINE OptionsList 
OptionsList ::= Option NEWLINE OptionsList | Option 
Option ::= OPTION_NAME ASSIGN OptionValue
OptionValue ::= TRUE | FALSE | INT | STRING | CODE 

Проблема заключается в производстве @epsilon@. Я добавил его, потому что хочу, чтобы мой ридер принимал и пустые файлы. Но у меня возникают конфликты, когда «OptionsList» или «OptionGroup» содержат эпсилон-продукцию. Я пробовал переставлять элементы в постановках, но у меня возникают только конфликты (r/r или s/r, в зависимости от того, что я сделал), если я полностью не уберу эпсилон из своей грамматики. Это устраняет проблему, но... в моей логике один из 'OptionsList' или 'OptionGroup' должен содержать эпсилон, иначе моя цель по принятию пустых файлов не будет достигнута.

Мой генератор синтаксического анализатора использует метод LR(1), поэтому я подумал, что могу использовать эпсилон-продукцию в своей грамматике. Кажется, я хорош в написании генераторов, но не в построении безошибочных грамматик :(.

Должен ли я забыть об эпсилонах? Или моя грамматика принимает пустые входные данные, даже если нет производства эпсилон?


person Felix.leg    schedule 20.04.2020    source источник


Ответы (1)


Ваше производство Options позволяет Options быть последовательностью OptionGroup, начинающейся либо с пустого списка, либо со списком, состоящим из одного элемента. Это явно двусмысленно, потому что список ровно из одного OptionGroup может быть:

  • Базовый вариант OptionGroup
  • Базовый вариант @epsilon@ с добавлением OptionGroup.

Короче говоря, вместо

Options ::= OptionGroup Options | OptionGroup | @epsilon@

тебе нужно

Options ::= OptionGroup Options | @epsilon@

который соответствует точно такому же набору предложений, но однозначно.

В общих чертах, обычно лучше писать лево-рекурсивные правила для восходящих синтаксических анализаторов. Так бы и написал

Options ::= Options OptionGroup | @epsilon@
person rici    schedule 20.04.2020
comment
Оно работает! Но к сведению: мой генератор еще не понимает инструкции Make X a list Ys, поэтому мне пришлось писать свою продукцию пешком, и в результате получилась эта строка. Итак, мой другой вопрос: как выразить, что NTerm должен содержать список других элементов? - person Felix.leg; 20.04.2020
comment
@Felix: Рекурсивное производство ListOfX ::= ListOfX X | X (для непустых списков) или ListOfX ::= ListOfX X | @epsilon@ - это именно то, как вы пишете список X в BNF. Но вы должны выбрать один или другой. Либо список может быть пуст, либо нет. - person rici; 20.04.2020
comment
Спасибо за ваш ответ и извините за редактирование, я сделал это до того, как прочитал ваш ответ. - person Felix.leg; 20.04.2020
comment
Краткое примечание о левой и правой рекурсии: типичный генератор синтаксических анализаторов, который создает управляемые таблицами синтаксические анализаторы для LR (1) грамматик (или LALR (1)), вы не хотите писать вещи, чтобы быть леворекурсивными, как указано выше. То, как вы написали это как праворекурсивное, с меньшей вероятностью вызовет конфликты сдвига/уменьшения. Левая рекурсия лучше подходит для синтаксических анализаторов LL(1), которые часто имеют рекурсивный спуск. Книга о драконах — хорошее место для поиска информации по этим темам, если вам нужна справочная информация. - person Thomas Kammeyer; 20.04.2020
comment
@thomas: синтаксические анализаторы ll (1) не могут обрабатывать левую рекурсию. Проверьте свои ссылки. - person rici; 20.04.2020
comment
Книга Дракона, стр. 47. Парсер с рекурсивным спуском может зацикливаться вечно. Возникает проблема с леворекурсивными постановками… - person rici; 20.04.2020
comment
Но синтаксический анализ LR см. в руководстве Bison (табличный анализатор LALR(1)), раздел 3.3.3, рекурсивные правила: любая последовательность может быть определена как с левой, так и с правой рекурсией, но вы должны всегда использовать левую рекурсию - person rici; 20.04.2020
comment
Спасибо, мой плохой. Я перепутал их. Я думаю, что моя первоначальная реакция была основана на моем опыте работы с другим набором алгоритмов (парсеры диаграмм, особенно парсеры CYK) и проблеме эффективности, которую я наблюдал при работе с леворекурсивными грамматиками по сравнению с праворекурсивными. Мне удалось запутать это вопросом LL против LR. Я должен был проверить, прежде чем говорить. - person Thomas Kammeyer; 20.04.2020
comment
Еще одно интересное обсуждение здесь: gallium.inria.fr/blog/lr-lists - person Thomas Kammeyer; 20.04.2020
comment
@Thomas: Да, я знаю об этом мнении. В отличие от утверждения в этом посте, bison также размещает стек парсера в куче. Однако, в отличие от Menhir, многие реализации yacc (включая версии bison) накладывают произвольное ограничение на размер стека парсера. Вот интересный пример того, как может проявляться эта проблема: unix.stackexchange.com/a/186446/24557 . Есть синтаксические контексты, в которых левая рекурсия разрешает конфликт, и есть контексты, где правая рекурсия разрешает конфликт. Есть даже среды, где два списка должны быть противоположны рекурсивности... - person rici; 20.04.2020
comment
Но, в целом, для новичков вообще лучше всего воспользоваться советом из мануала по бизону, и писать списки с левой рекурсией. Во-первых, это позволит избежать вопроса, который довольно часто возникает на SO: почему мои списки печатаются задом наперед? - person rici; 20.04.2020
comment
Спасибо, что заставили меня почувствовать себя лучше из-за моего неправильного комментария, используя его для предоставления полезной дополнительной информации. - person Thomas Kammeyer; 20.04.2020