Помните, что flex и bison - это два совершенно разных процессора, которые принимают два совершенно разных входных файла и генерируют две совершенно отдельные программы, которые затем компилируются отдельно. Bison и flex не читают ни входные файлы (или выходные файлы) друг друга, ни мысли программиста. Таким образом, любая совместимость между сгенерированными программами существует, потому что вы, как программист, организовали это. * Убедитесь, что заголовок, сгенерированный bison, равен #include
d в сканере, сгенерированном flex, и
Объявление прототипа
yylex
в анализаторе, сгенерированном bison.
Прототип yylex
обычно
int yylex(void);
Вы можете заставить flex сгенерировать yylex
с другим прототипом или даже с другим именем, но если вы это сделаете, вам нужно указать зубру, как вызовите yylex
(так как он не может узнать, что вы изменили прототип в своем сканере).
Если вы найдете эту строку в своем файле parser.tab.c
, это почти наверняка потому, что вы вставили ее в блоке кода в вашем файле parser.y
, потому что это было необходимо для компиляции синтаксического анализатора без повторного входа, и вы забыли удалить его, когда вы изменили файл на создать реентерабельный синтаксический анализатор.
По умолчанию синтаксический анализатор и лексический анализатор взаимодействуют с помощью глобальной переменной yylval
для хранения значения semanti c лексического токена. (И глобальная переменная yylloc
, если местоположения также передаются.) Bison определяет эти глобальные переменные в сгенерированном синтаксическом анализаторе и объявляет их в сгенерированном заголовке, пока сгенерированный заголовок имеет размер #include
d в сгенерированном сканере, эти две программы могут взаимодействовать.
Но это действительно не лучший способ обмена данными, и современные стили кодирования осуждают такое использование глобальных переменных. Создав повторно входящий синтаксический анализатор, вы можете включить альтернативный механизм, в котором синтаксический анализатор передает указатели сканера на свой собственный локальный yylval
(и yylloc
, если используется). Это решает проблему повторного входа, но теперь вам нужно сообщить о своем желании сгибаться, чтобы сгенерировать yylex
, который ожидает эти аргументы. Для этого нужно вставить %bison-bridge
(и, возможно, %bison-locations
) во входной файл flex.
Это приведет к корректировке соглашения о вызовах для yylex
, но на самом деле это не приведет к повторному входу сканер. Он просто создает сканер, который не полагается на глобальные переменные для связи с парсером. Сканер полагается на множество других глобальных переменных для поддержания своего состояния. Если вам нужен реентерабельный сканер, вам также необходимо вставить объявление %reentrant
в файл Flex, что заставит его сгенерировать сканер, который сохраняет свое «глобальное» состояние в объекте контекста непрозрачного типа yyscan_t
. Этот объект контекста должен быть передан в yylex
в качестве аргумента (который находится в конце списка аргументов yylex
). Объявление %reentrant
flex создает yylex
, который ожидает этого аргумента, но теперь bison вышел из цикла; еще раз, вы обязаны сообщить об этом факте bison.
И вы также несете ответственность за выделение объекта yyscan_t
, который может быть передан лексеру. Но вы не вызываете сканер напрямую. Вы вызываете синтаксический анализатор (yyparse
), и он при необходимости вызывает сканер.
Flex позволяет добавлять произвольный код в сканер (помещая его с отступом перед первым правилом). Но, к сожалению, у зубров нет такой возможности. Единственный способ ввести новую переменную в синтаксический анализатор - это добавить ее в список аргументов yyparse
(используя объявление %parse-param
). Итак, вам нужно создать объект yyscan_t
самостоятельно и передать его yyparse
. Затем вам нужно указать bison использовать этот объект, когда он вызывает yylex
, что вы делаете с объявлением %lex-param
.
Должно быть достаточно ясно, что почти всегда будет так, что * 1060 Указанные выше объявления * и %lex-param
будут идентичны. Единственный способ передать параметр через yyparse
в yylex
- это если параметр, добавленный с помощью %parse-param
, имеет то же имя, что и аргумент, добавленный с помощью %lex-param
. В этом случае bison очень разумно позволяет объединить %parse-param
и %lex-param
в одно объявление %param
.
Теперь у вас есть только одна небольшая проблема. Параметр, который необходимо передать через yyparse
в yylex
, имеет непрозрачный тип yyscan_t
. Поскольку этот параметр является параметром yyparse
, тип yyscan_t
должен быть виден в сгенерированном заголовке bison. Однако yylex
вызывается (и, следовательно, должен быть объявлен) с другими параметрами типа YYSTYPE*
и YYLTYPE*
, и эти типы объявляются в сгенерированном bison заголовке. Flex также может генерировать заголовок, но это вам не поможет, поскольку между сгенерированными файлами существует круговая зависимость заголовка. (Это естественное следствие связи типов между предположительно независимыми сканерами и синтаксическими анализаторами. Но я не буду развивать эту критику дальше, потому что это в основном данность.)
Есть два обходных пути. Лучше всего, IMHO, избежать циклической зависимости заголовка, используя синтаксический анализатор pu sh вместо синтаксического анализатора pull. В модели синтаксического анализатора pu sh синтаксический анализатор вызывается сканером (или вызывается драйвером, который передает маркер, созданный сканером) вместо вызова сканера. Я всегда рекомендую эту модель.
Другой - разрешить циклическую зависимость путем объявления вручную yyscan_t
:
typedef void* yyscan_t;
Полностью проработанный (и задокументированный) пример см. этот ответ