Похоже, что ваш язык конфигурации является по сути свободной формой. Я бы забыл о том, чтобы сделать перевод строки символом грамматики. Если вы хотите ограничения новой строки, вы можете взломать его как некоторые лексические правила встраивания, в результате чего анализатор вызывает небольшой API, добавленный к лексеру, чтобы проинформировать лексера о том, где он находится в грамматике, и лексер может решить, принимать ли его. переводить строки или отклонять их с ошибкой.
Попробуйте эту грамматику.
%token NAME NUMBER TEXT
%%
config_file : assignments
| /* empty */
;
assignments : assignment
| assignments assignment
;
assignment : NAME '=' values comma_opt
comma_opt : ',' | /* empty */;
values : value
| values ',' value
;
value : NUMBER | TEXT ;
Он строит для меня без конфликтов. Я не запускал его, но случайное чтение y.output
выглядит так, как будто переходы нормальны.
Эта грамматика, конечно, позволяет
foo = 1, 2, 3, bar = 4, 5, 6 xyzzy = 7 answer = 42
без дополнительной связи с лексером.
Ваши ограничения означают, что переводы строки разрешены только в значениях. Два токена NAME никогда не должны появляться в одной строке, а символ = должен появляться в той же строке, что и предыдущее NAME (и, вероятно, первое значение также должно быть).
Обычно, когда анализатор сканирует первое значение, он может сказать лексеру: «значения сейчас сканируются, включите допуск новых строк». И затем, когда comma_opt
уменьшится, это можно снова отключить. Когда значение comma_opt
уменьшается, лексер, возможно, уже прочитал токен NAME
следующего назначения, но он может проверить, что это происходит в строке, отличной от предыдущего NAME
. Вы хотите, чтобы ваш лексер все равно отслеживал точное количество строк.