Как да напиша анализатор, който обработва изрази за импортиране?

Използвам 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 това е доста лесно, тъй като препроцесорът вече е комбинирал всички заглавни файлове в една единица за превод, която може да бъде сканирана отгоре надолу. Но изразите „използване“ във VHDL не са команди на предпроцесора.

И така, някак си, докато анализирам файла, трябва да разпозная, когато видя израз за употреба и след това да изляза и да анализирам съответния файл и след това да продължа да анализирам оригиналния файл с тази символна таблица.

Има ли елегантен начин да направите това с lex/yacc? Знам, че има yyrestart, но не съм сигурен дали това върви по правилния път.


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


Отговори (1)


Ако използвате flex, тогава е доста лесно.

Основният механизъм (включително две примерни функциониращи кодове) е описан в "Множество Входни буфери" глава от ръководството за flex. Можете също да хвърлите един поглед на този въпрос на SO.

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

В зависимост от формалните правила за включване на файлове, може да искате анализаторът да знае, че включеният файл е приключил, за да избегнете синтактични конструкции, които започват във включения файл и продължават във включващия файл. (C позволява това, въпреки че почти винаги е грешка; не знам нищо за VHDL, но определено има езици, които не го позволяват.) Една от възможностите е рекурсивно извикване на анализатора, за да се анализира включеният файл , което ще изисква повторно влизане („чист“) анализатор. В този случай скенерът трябва да върне токен за край на включения файл, когато достигне края на включения файл, тъй като производството на граматиката на вашия включен файл ще трябва да бъде прекратено с такъв токен.

Може да се наложи да се тревожите за възможността анализаторът вече да е поискал следващия входен токен. Повечето граматики на LALR(1) не зависят от токена за предварителен преглед за изрази, завършващи с точка и запетая, а bison обикновено не изисква токен за предварителен преглед в контекст, в който не се нуждае от него. Но това поведение не е гарантирано от всички Posix-съвместими реализации на yacc и може да използвате такова, което не го прави.

В този случай ще трябва да запазите предварителния токен, за да можете да го прочетете отново, след като включеният файл бъде анализиран. Това би било най-удобно да се направи, като скриете токена за предварителен преглед някъде, където скенерът може да го види, и скенерът върне този токен (ако е зададен), когато види края на включения файл. В действие bison можете да намерите token lookahead в yychar и неговата семантична стойност и местоположение (ако местоположенията са разрешени) са в yylval и yylloc. Ако bison не е прочел предварителния токен, стойността на yychar ще бъде YYEMPTY, а най-простата възможна реализация на bison би assert(yychar == YYEMPTY), когато е на път да натисне входния буфер. Ако твърдението е неуспешно, ще трябва да приложите по-сложна стратегия.

person rici    schedule 24.09.2015