Как использовать flex с моим собственным парсером?

Я хочу оставить лексический анализ на lex, но разрабатываю синтаксический анализатор самостоятельно.

Я сделал заголовок token.h, который имеет перечисления для типов токенов и простую иерархию классов,

Для правила lex:

[0-9]+ {yylval = new NumToken(std::stoi(yytext));return NUM;}

Как получить указатель NumToken из кода парсера?
Предположим, я просто хочу распечатать токены ..

while(true)
{
auto t = yylex();
//std::cout <<yylval.data<<std::endl; // What goes here ?
}

Я могу сделать это с помощью yacc / bison, но не могу найти документацию или пример о том, как сделать это вручную.

1

Решение

В традиционном парсере бизонов / флекс yylval это глобальная переменная, определенная в синтаксическом анализаторе, сгенерированная bison, и объявленная в заголовочном файле, сгенерированном bison (который должен быть # include’d в сгенерированном сканере). Так что простым решением было бы просто повторить это: объявить yylval (как глобальный) в token.h и определите это где-нибудь в вашем парсере.

Но современный стиль программирования отошел от использования глобальных (не зря) и даже flex по запросу генерирует сканеры, которые не зависят от глобального состояния. Чтобы запросить такой сканер, укажите

%option reentrant

в вашем определении сканера. По умолчанию это меняет прототип yylex чтобы:

int yylex(yyscan_t yyscanner);

где yyscan_t непрозрачный указатель (Это C, так что это означает, что это void*.) Вы можете прочитать о деталях в Flex manual; самое важное, что вы можете попросить Flex сгенерировать файл заголовка (с %option header-file), так что другие единицы перевода могут ссылаться на различные функции для создания, уничтожения и манипулирования yyscan_tи что вам нужно минимально создать так, чтобы yylex есть где хранить свое состояние. (В идеале вы также уничтожили бы его.) [Примечание 1].

Ожидаемый способ использования сканера с повторным входом из bison должен включить %option bison-bridge (а также %option bison-location если ваш лексер генерирует информацию о местоположении источника для каждого токена). Это добавит дополнительный параметр к yylex прототип:

int yylex(YYSTYPE *yylval_param, yyscan_t scanner);

С `% option bison-location ‘добавляются два параметра:

int yylex(YYSTYPE *yylval_param,
YYLTYPE *yylloc_param,
yyscan_t scanner);

Семантический тип YYSTYPE и тип местоположения YYLTYPE являются не объявлено сгенерированным во флексе кодом. Они должны появиться в token.h Заголовок вы #include в ваш сканер.

Цель параметров бизон-бриджа — предоставить механизм для возврата семантического значения yylval вызывающей стороне (то есть парсеру). поскольку yylval фактически совпадает с параметром yylval_param [Примечание 2], это будет указатель к фактическому семантическому значению, поэтому вам нужно написать (например) yylval->data = ... в ваших гибких действиях.

Так что это один из способов сделать это.

Возможно, более простая альтернатива bison-bridge просто чтобы обеспечить свой собственный yylex прототип, который вы можете сделать с помощью макроса YY_DECL, Например, вы могли бы сделать что-то вроде этого (если бы YYSTYPE был чем-то простым):

#define YY_DECL std::pair<int, YYSTYPE> yylex(yyscan_t yyscanner)

Тогда правило может просто вернуть пару:

[0-9]+ {return std::make_pair(NUM, new NumToken(std::stoi(yytext));}

Очевидно, есть много вариантов на эту тему.


Заметки

  1. К сожалению, сгенерированный заголовок включает в себя довольно много ненужного багажа, включая набор макроопределений для стандартных «глобальных переменных», которые не будут работать, потому что в реентерабельном сканере эти переменные могут использоваться только в гибком действии.

  2. Сканер создан с bison-bridge определяет yylval как макрос, который ссылается на поле в структуре непрозрачного состояния и сохраняет yylval_param в этой области. yyget_lval а также yyset_lval функции предоставляются для того, чтобы получить или установить это поле извне yylex, Я не знаю почему; кажется, что-то среднее между ненужным и опасным, так как государство будет содержать указатель к значению, как указано в вызове yylex, который вполне может быть висящим указателем после возврата вызова.

1

Другие решения


По вопросам рекламы [email protected]