Анализатор Bison -- это на самом деле функция на C, называющаяся
yyparse
. Здесь мы опишем соглашения по интерфейсу yyparse
и
других необходимых ей функций.
Имейте в виду, что для своих внутренних целей анализатор использует множество идентификаторов C, начинающихся с `yy' и `YY'. Если вы используете такой идентификатор (кроме тех, что описаны в настоящем руководстве) в действии или дополнительном коде на C в файле грамматики, вероятно, вы столкнётесь с неприятностями.
yyparse
Вы вызываете функцию yyparse
для запуска анализа. Эта функция читает
лексемы, выполняет действия, и в конце концов завершает работу, когда встречает
конец входного текста или сталкивается с невосстановимой синтаксической
ошибкой. Вы можете также написать действие, которое укажет yyparse
завершить работу немедленно, без продолжения чтения.
Если разбор завершён успешно (возврат вызван концом входного текста),
yyparse
возвращает значение 0.
Значение 1 возвращается, если разбор не удался (возврат вызван синтаксической ошибкой).
В действии вы можете потребовать немедленного возврата из yyparse
,
используя следующие макросы:
YYACCEPT
YYABORT
yylex
Функция лексического анализатора yylex
распознаёт лексемы во
входном потоке и передаёт их анализатору. Bison не создаёт эту функцию
автоматически, вы должны написать её так, чтобы yyparse
могла
вызывать её. Эту функцию иногда называют лексическим сканером.
В простых программах yylex
часто определяется в конце файла грамматики
Bison. Если yylex
определена в отдельном исходном файле, вам нужно
сделать доступными там макроопределения типов лексем. Для этого используйте
параметр `-d' при запуске Bison, чтобы он записал эти макроопределения
в отдельный файл заголовка `имя.tab.h', который вы можете включить
в другие исходные файлы, которым он нужен. См. раздел 10. Вызов Bison.
yylex
Значение, возвращаемое yylex
должно быть числовым кодом типа только что
встреченной лексемы, или 0 для обозначени конца входного текста.
Если в правилах грамматики на лексему ссылаются по имени, это имя становится
в файле анализатора макросом C, определением которого будет числовой код,
соответствующий этому типу лексемы. Таким образом, yylex
может
использовать для обозначения типа это имя. См. раздел 4.2 Символы, терминальные и нетерминальные.
Если в правилах грамматики на лексему ссылаются с помощью однолитерной
константы, числовой код этой литеры также является кодом типа лексемы. Таким
образом yylex
может просто вернуть код этой литеры. Нулевая литера
не должна использоваться таким образом, потому что её код -- ноль, означающий
конец входного текста.
Приведём пример, иллюстрирующий это:
int
yylex (void)
{
...
if (c == EOF) /* Проверка конца файла. */
return 0;
...
if (c == '+' || c == '-')
return c; /* Полагаем, что тип лексемы `+' -- '+'. */
...
return INT; /* Вернуть тип лексемы. */
...
}
Этот интерфейс разрабатывался так, чтобы выход утилиты lex
мог быть
без изменений использован как определение yylex
.
Если грамматика использует строковые лексемы, есть два способа, которыми
yylex
может определить коды их типов лексем:
yylex
может использовать эти символические имена как и все
остальные. В этом случае использование строковых лексем в файле грамматики
не окажет влияния на yylex
.
yylex
может найти многолитерную лексему в таблице yytname
.
Индекс лексемы в этой таблице -- это код типа лексемы. Имя многолитерной
лексемы записывается в yytname
в виде: двойная кавычка, литеры лексемы,
вторая двойная кавычка. Литеры лексемы никаким образом не экранируются, они
дословно переносятся в содержимое строки в таблице.
Приведём код поиска лексемы в yytname
, полагая, что литеры лексемы
находятся в массиве token_buffer
.
for (i = 0; i < YYNTOKENS; i++)
{
if (yytname[i] != 0
&& yytname[i][0] == '"'
&& strncmp (yytname[i] + 1, token_buffer,
strlen (token_buffer))
&& yytname[i][strlen (token_buffer) + 1] == '"'
&& yytname[i][strlen (token_buffer) + 2] == 0)
break;
}
Таблица yytname
создаётся только если вы используете объявление
%token_table
. См. раздел 4.7.8 Обзор объявлений Bison.
В обычном (не повторно входимом) анализаторе семантические значения лексем
должны помещаться в глобальную переменную yylval
. Если вы используете
единственный тип данных для семантических значений, yylval
имеет этот
тип. Так, если этот тип int
(по умолчанию), вы можете написать в
yylex
:
...
yylval = value; /* Поместить значение на вершину стека Bison. */
return INT; /* Вернуть тип лексемы. */
...
Если вы используете множественные типы данных, тип yylval
-- объединение
типов, полученное из объявления %union
(см. раздел 4.7.3 Набор типов значений). Так, если вы сохраняете значение лексемы, вы должны
использовать правильный элемент объединения. Если объявление %union
выглядит так:
%union {
int intval;
double val;
symrec *tptr;
}
то код в yylex
может выглядеть так:
...
yylval.intval = value; /* Поместить значение на вершину */
/* стека Bison. */
return INT; /* Вернуть тип лексемы. */
...
Если вы используете в действиях `@n'-свойства (см. раздел 4.6 Отслеживание положений) для отслеживания положений лексем и групп в тексте,
ваша функция yylex
должна предоставить эту информацию. Функция
yyparse
ожидает, что положение только что разобранной лексемы в тексте
находится в глобальной переменной yylloc
. Таким образом, yylex
должна поместить в эту переменную правильные данные.
По умолчанию значение yylloc
-- это структура, и вам нужно только
проинициализировать её элементы, которые вы собираетесь использовать в
действиях. Эти четыре элемента называются, first_line
,
first_column
, last_line
и last_column
. Отметим, что
использование этих свойств делает анализатор заметно более медленным.
Тип данных yylloc
называется YYLTYPE
.
Если вы используете объявление Bison %pure_parser
, требующее создания
чистого, повторно входимого анализатора, глобальные переменные взаимодействия
yylval
и yylloc
использовать нельзя (см. раздел 4.7.7 Чистый (повторно входимый) анализатор). В таких
анализаторах эти две глобальные переменные замещаются указателями,
передаваемыми в качестве аргументов функции yylex
. Вы должны объявить
их, как здесь показано, и передавать информацию назад, помещая её по этим
указателям.
int
yylex (YYSTYPE *lvalp, YYLTYPE *llocp)
{
...
*lvalp = value; /* Поместить значение на вершину стека Bison. */
return INT; /* Вернуть тип лексемы. */
...
}
Если файл грамматики не использует конструкции `@' для ссылок на позиции
в тексте, тип YYLTYPE
не будет определён. В этом случае опустите второй
аргумент, yylex
будет вызываться только с одним аргументом.
Если вы используете повторно входимый анализатор, вы можете (необязательно)
передавать ему информацию о дополнительных параметрах повторно входимым
способом. Для этого определите макрос YYPARSE_PARAM
как имя переменной.
Это изменит функцию yyparse
чтобы она принимала один аргумент с этим
именем типа void *
.
При вызове yyparse
передайте адрес объекта, приведя его к типу
void *
. Действия грамматики могут ссылаться на сожержимое объекта,
приводя значение указателя обратно к его правильному типу, и затем
разыменовывая его. Приведём пример. Напишите в анализаторе:
%{
struct parser_control
{
int nastiness;
int randomness;
};
#define YYPARSE_PARAM parm
%}
Затем вызовите анализатор следующим образом:
struct parser_control
{
int nastiness;
int randomness;
};
...
{
struct parser_control foo;
... /* Поместить правильные данные в
foo
. */
value = yyparse ((void *) &foo);
...
}
В действиях грамматики используйте для обращения к данным выражения наподобие
следующего:
((struct parser_control *) parm)->randomness
Если вы хотите передать данные дополнительных параметров функции yylex
,
определите макрос YYLEX_PARAM
тем же способом, что и для
YYPARSE_PARAM
, как показано ниже:
%{
struct parser_control
{
int nastiness;
int randomness;
};
#define YYPARSE_PARAM parm
#define YYLEX_PARAM parm
%}
Затем вам следует определить yylex
, чтобы она принимала дополнительный
аргумент -- значение parm
(всего будет два или три аргумента, в
зависимости от того, передаётся ли аргумент типа YYLTYPE
). Вы можете
объявить аргумент как указатель на правильный тип объекта, или же объявить его
как void *
и получать доступ к содержимому как показано выше.
Вы можете использовать `%pure_parser' и потребовать создания повторно
входимого анализатора, не используя при этом YYPARSE_PARAM
. Тогда
вам следует вызывать yyparse
без аргументов, как обычно.
yyerror
Анализатор Bison обнаруживает ошибку разбора или синтаксическую
ошибку каждый раз, когда читает лексему, которая не может удовлетворять
никакому синтаксическому правилу. Действие в грамматике может также явно
сообщить об ошибке, используя макрос YYERROR
(см. раздел 5.4 Специальные возможности, используемые в действиях).
Анализатор Bison рассчитывает сообщить об ошибке, вызывая функцию сообщения
об ошибке yyerror
, которую должны предоставить вы. Она вызывается
функцией yyparse
каждый раз при обнаружении синтаксической ошибки,
и принимает один аргумент. В случае ошибки разбора это обычно строка
"parse error"
.
Если вы определите макрос YYERROR_VERBOSE
в секции объявлений Bison
(см. раздел 4.1.2 Секция объявлений Bison), Bison будет давать
более подробные и обстоятельные строки сообщений об ошибках, вместо обычного
"parse error"
. Не имеет значения, какое определение вы используете
для YYERROR_VERBOSE
, только то, определили ли вы его.
Анализатор может обнаружить ещё один тип ошибки -- переполнение стека. Это
происходит, когда входной текст содержит конструкции слишком большой глубины
вложенности. Маловероятно, что вы столкнётесь с этим, поскольку анализатор
Bison расширяет свой стек автоматически до очень больших пределов. Но если
переполнение всё же происходит, yyparse
вызывает обычным образом
yyerror
, за исключением того, что аргументом будет строка
"parser stack overflow"
.
Следующего определения достаточно для простых программ:
void
yyerror (char *s)
{
fprintf (stderr, "%s\n", s);
}
После возвращения из yyerror
в yyparse
последняя попытается
произвести восстановление после ошибки, если в грамматике вы написали
подходящие правила восстановления после ошибок (см. раздел 7. Восстановление после ошибок). Если восстановление невозможно, yyparse
немедленно завершит
работу, вернув 1.
Переменная yynerrs
содержит число обнаруженных до сих пор синтаксических
ошибок. Обычно эта переменная глобальная, но если вы требуете создания
чистого анализатора (см. раздел 4.7.7 Чистый (повторно входимый) анализатор), это локальная переменная, к которой могут иметь доступ
только действия.
Ниже представлена таблица конструкций Bison, переменных и макросов, которые могут быть полезны в действиях.
$$
, но задаёт альтернативу тип_альт в объединении,
заданном объявлением %union
. См. раздел 4.5.4 Типы данных значений в действиях.
n
, но задаёт альтернативу тип_альт в объединении,
заданном объявлением %union
. См. раздел 4.5.4 Типы данных значений в действиях.
yyparse
, сообщая об ошибке.
См. раздел 5.1 Функция анализатора yyparse
.
yyparse
, сообщая об удачном разборе.
См. раздел 5.1 Функция анализатора yyparse
.
yychar
, когда там нет предпросмотренной лексемы.
yyerror
.
См. раздел 7. Восстановление после ошибок.
yyparse
переменная). Когда
предпросмотренной лексемы нет, в неё помещается значение YYEMPTY
.
См. раздел 6.1 Предпросмотренные лексемы.