Передача аргумента в yylex - PullRequest
0 голосов
/ 09 июля 2020

Я пытаюсь изменить мои файлы .l и .y, чтобы сделать мой сканер и синтаксический анализатор реентерабельными. Следуя документации GNU и др., Я поместил это в свой файл .y:

%define api.pure
%lex-param { YYSTYPE *yylval }
%parse-param { astNodePtr *programTree }

Кроме того, я добавил параметры reentrant и bison-bridge в свой файл .l.

Однако после того, как я построю parser.tab.h и parser.tab. c с bison -dv parser.y, я заметил, что parser.tab. c содержит объявление

int yylex(void);

и еще позже включает вызовы, такие как

yychar = yylex (&yylval, yylval);

Кроме того, попытка скомпилировать файл. c, который создает flex, генерирует всевозможные ошибки, сосредоточенные на некоторой переменной с именем yyg.

Есть ли дополнительные флаги / все, что мне нужно добавить в мой файл .l или .y?

1 Ответ

2 голосов
/ 10 июля 2020

Помните, что 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 в сгенерированном сканере, эти две программы могут взаимодействовать.

Но это действительно не лучший способ обмена данными, и современные стили кодирования осуждают такое использование глобальных переменных. Создав повторно входящий синтаксический анализатор, вы можете включить альтернативный механизм, в котором синтаксический анализатор передает указатели сканера на свой собственный локальный yylvalyylloc, если используется). Это решает проблему повторного входа, но теперь вам нужно сообщить о своем желании сгибаться, чтобы сгенерировать 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;

Полностью проработанный (и задокументированный) пример см. этот ответ

...