Можно ли вызвать один синтаксический анализатор yacc из другого для анализа определенного подпотока токена?

Предположим, у меня уже есть полная грамматика YACC. Пусть это будет, например, грамматика C. Теперь я хочу создать отдельный синтаксический анализатор для предметно-ориентированного языка с простой грамматикой, за исключением того, что ему все еще нужно анализировать полные объявления типа C. Я не хотел бы дублировать длинные правила из исходной грамматики с соответствующим кодом обработки, но вместо этого хотел бы вызвать исходный синтаксический анализатор для обработки ровно одного правила (назовем его «декларатор»).

Если бы это был синтаксический анализатор с рекурсивным спуском, для каждого правила была бы функция, которую было бы легко вызвать. Но как насчет YACC с его автоматом неявного стека?


person pfalcon    schedule 09.05.2013    source источник


Ответы (1)


В основном нет. Составление грамматик LR непросто, и bison не предлагает особой помощи.

Но еще не все потеряно. Ничто не мешает вам включить грамматику целиком (кроме объявления %start) и просто использовать ее часть, за исключением одной маленькой детали: bison будет жаловаться на бесполезные произведения.

Если это вас не устраивает, то вы можете использовать трюк, позволяющий создать грамматику с несколькими стартовыми правилами. Фактически, вы можете создать грамматику, которая позволит вам указывать начальный символ каждый раз, когда вы вызываете синтаксический анализатор; его даже не нужно запекать. Затем вы можете поместить это в библиотеку и использовать любой синтаксический анализатор, который вам нужен.

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

Взлом описан в часто задаваемых вопросах о зубрах довольно кратко. много деталей, поэтому я просто сделаю здесь набросок: для каждого запуска производства, которое вы хотите поддерживать, вы создаете одну дополнительную продукцию, которая начинается с псевдотокена (то есть лексического кода, который никогда не будет сгенерирован лексером ). Например, вы можете сделать следующее:

%start meta_start
%token START_C START_DSL

meta_start: START_C c_start | START_DSL dsl_start;

Теперь вам просто нужно настроить лексер для создания соответствующего токена START при первом запуске. Есть разные способы сделать это; в FAQ предлагается использовать глобальную переменную, но если вы используете повторно входящий сканер гибкости, вы можете просто поместить желаемый стартовый токен в состояние сканера (вместе с флагом, который устанавливается при отправке стартового токена).

person rici    schedule 09.05.2013
comment
Спасибо за подробный ответ, и я пошел по этому пути, который пока работает хорошо. Меня не особо беспокоит скорость синтаксического анализа, я уверен, что бизон достаточно умен, чтобы сохранять правила в структурах, подобных множеству, и не учитывать правила, неприменимые к текущему нетерминальному устройству. Самая большая рутинная работа до сих пор дает мне сканер, который, таким образом, используется совместно со сканером C: у моего DSL есть свои особые ключевые слова, которые загрязняют пространство токенов C. Думаю, в конечном итоге мне понадобится 2 отдельных сканера гибкости. - person pfalcon; 11.05.2013
comment
@pfalcon, Если бы это были просто ключевые слова, то гибкое решение было бы просто; используйте такие правила: "dslkeyword" { if (in_dsl) return DSLKEYWORD; else return ID; }. Но это может стать некрасивым; еще одно простое решение - использовать условия запуска: flex.sourceforge.net/manual/Start-Conditions .html - person rici; 11.05.2013