Как написать парсер, который обрабатывает операторы импорта?

Я использую lex & yacc для написания синтаксического анализатора VHDL. VHDL имеет некоторые языковые функции, которые делают его контекстно-зависимым, подобно C. Например, конструкции, подобные typedef, которые влияют на то, должен ли синтаксический анализатор размечать что-либо как IDENTIFIER или TYPEDEF_NAME.

Сложность возникает, когда вам нужно построить таблицу символов на основе другого файла, на который ссылаются операторы «использования» (аналогично «импорту» в Java или Python).

library ieee;
use ieee.std_logic_1164.all;

-- code which uses something defined in ieee.std_logic_1164 package

В C это довольно просто, потому что препроцессор уже объединил все заголовочные файлы в единую единицу перевода, которую можно сканировать сверху вниз. Но операторы «use» в VHDL не являются командами препроцессора.

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

Есть ли элегантный способ сделать это с помощью lex/yacc? Я знаю, что есть yyrestart, но я не уверен, что он идет по правильному пути.


person master_latch    schedule 23.09.2015    source источник


Ответы (1)


Если вы используете flex, то это довольно просто.

Базовый механизм (включая два функционирующих примера кода) описан в "Multiple Вводные буферы» руководства по flex. Вы также можете взглянуть на этот вопрос на SO.

Сокращение синтаксического анализатора (yacc/bison), которое распознает конструкцию use, может включать код, вызывающий yy_push_buffer. В примере кода конец включаемого файла распознается сканером (lex/flex), который просто выталкивает стек буфера.

В зависимости от формальных правил включения файлов вы можете захотеть, чтобы синтаксический анализатор знал, что включаемый файл завершен, чтобы избежать синтаксических конструкций, которые начинаются во включаемом файле и продолжаются во включателе. (C позволяет это, хотя это почти всегда ошибка; я ничего не знаю о VHDL, но определенно есть языки, которые этого не позволяют.) Одна из возможностей — рекурсивно вызвать синтаксический анализатор, чтобы проанализировать включенный файл. , для чего потребуется реентерабельный ("чистый") синтаксический анализатор. В этом случае сканер должен возвращать токен конца включаемого файла, когда он достигает конца включаемого файла, потому что создание грамматики включаемого файла должно быть завершено с таким токеном.

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

В этом случае вам придется сохранить упреждающий токен, чтобы вы могли перечитать его после того, как включенный файл будет проанализирован. Это было бы наиболее удобно сделать, спрятав упреждающий токен там, где сканер может его увидеть, и заставив сканер вернуть этот токен (если он установлен), когда он увидит конец включенного файла. В действии bison вы можете найти токен предпросмотра в yychar, а его семантическое значение и местоположение (если местоположения включены) находятся в yylval и yylloc. Если bison не читал упреждающий токен, значение yychar будет равно YYEMPTY, а простейшая возможная реализация bison будет равна assert(yychar == YYEMPTY), когда он собирается передать входной буфер. Если утверждение завершится ошибкой, вам потребуется реализовать более сложную стратегию.

person rici    schedule 24.09.2015