Я хочу оставить лексический анализ на 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, но не могу найти документацию или пример о том, как сделать это вручную.
В традиционном парсере бизонов / флекс 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));}
Очевидно, есть много вариантов на эту тему.
К сожалению, сгенерированный заголовок включает в себя довольно много ненужного багажа, включая набор макроопределений для стандартных «глобальных переменных», которые не будут работать, потому что в реентерабельном сканере эти переменные могут использоваться только в гибком действии.
Сканер создан с bison-bridge
определяет yylval
как макрос, который ссылается на поле в структуре непрозрачного состояния и сохраняет yylval_param
в этой области. yyget_lval
а также yyset_lval
функции предоставляются для того, чтобы получить или установить это поле извне yylex
, Я не знаю почему; кажется, что-то среднее между ненужным и опасным, так как государство будет содержать указатель к значению, как указано в вызове yylex
, который вполне может быть висящим указателем после возврата вызова.