Bison 1.35 - 5. Интерфейс анализатора на C
[Содержание] [Назад] [Пред] [Вверх] [След] [Вперед]5. Интерфейс анализатора на C
Анализатор Bison -- это на самом деле функция на C, называющаяся
yyparse
. Здесь мы опишем соглашения по интерфейсу yyparse
и
других необходимых ей функций.
Имейте в виду, что для своих внутренних целей анализатор использует множество идентификаторов C, начинающихся с `yy' и `YY'. Если вы используете такой идентификатор (кроме тех, что описаны в настоящем руководстве) в действии или дополнительном коде на C в файле грамматики, вероятно, вы столкнётесь с неприятностями.
5.1 Функция анализатора yyparse
Вы вызываете функцию yyparse
для запуска анализа. Эта функция читает
лексемы, выполняет действия, и в конце концов завершает работу, когда встречает
конец входного текста или сталкивается с невосстановимой синтаксической
ошибкой. Вы можете также написать действие, которое укажет yyparse
завершить работу немедленно, без продолжения чтения.
Если разбор завершён успешно (возврат вызван концом входного текста),
yyparse
возвращает значение 0.
Значение 1 возвращается, если разбор не удался (возврат вызван синтаксической ошибкой).
В действии вы можете потребовать немедленного возврата из yyparse
,
используя следующие макросы:
YYACCEPT
- Немедленный возврат со значением 0 (сообщение об удачном разборе).
YYABORT
- Немедленный возврат со значением 1 (сообщение об ошибке).
5.2 Функция лексического анализатора yylex
Функция лексического анализатора yylex
распознаёт лексемы во
входном потоке и передаёт их анализатору. Bison не создаёт эту функцию
автоматически, вы должны написать её так, чтобы yyparse
могла
вызывать её. Эту функцию иногда называют лексическим сканером.
В простых программах yylex
часто определяется в конце файла грамматики
Bison. Если yylex
определена в отдельном исходном файле, вам нужно
сделать доступными там макроопределения типов лексем. Для этого используйте
параметр `-d' при запуске Bison, чтобы он записал эти макроопределения
в отдельный файл заголовка `имя.tab.h', который вы можете включить
в другие исходные файлы, которым он нужен. См. раздел 10. Вызов Bison.
5.2.1 Соглашения о вызове 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.
5.2.2 Семантические значения лексем
В обычном (не повторно входимом) анализаторе семантические значения лексем
должны помещаться в глобальную переменную 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; /* Вернуть тип лексемы. */ ...
5.2.3 Позиции лексем в тексте
Если вы используете в действиях `@n'-свойства (см. раздел 4.6 Отслеживание положений) для отслеживания положений лексем и групп в тексте,
ваша функция yylex
должна предоставить эту информацию. Функция
yyparse
ожидает, что положение только что разобранной лексемы в тексте
находится в глобальной переменной yylloc
. Таким образом, yylex
должна поместить в эту переменную правильные данные.
По умолчанию значение yylloc
-- это структура, и вам нужно только
проинициализировать её элементы, которые вы собираетесь использовать в
действиях. Эти четыре элемента называются, first_line
,
first_column
, last_line
и last_column
. Отметим, что
использование этих свойств делает анализатор заметно более медленным.
Тип данных yylloc
называется YYLTYPE
.
5.2.4 Соглашения о вызове для чистых анализаторов
Если вы используете объявление 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
без аргументов, как обычно.
5.3 Функция сообщения об ошибках 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 Чистый (повторно входимый) анализатор), это локальная переменная, к которой могут иметь доступ
только действия.
5.4 Специальные возможности, используемые в действиях
Ниже представлена таблица конструкций Bison, переменных и макросов, которые могут быть полезны в действиях.
- `$$'
- Играет роль переменной, содержащей семантическое значение группы, собираемой текущим правилом. См. раздел 4.5.3 Действия.
- `$n'
- Играет роль переменной, содержащей семантическое значение n-го компонента текущего правила. См. раздел 4.5.3 Действия.
- `$<тип_альт>$'
-
Аналогична
$$
, но задаёт альтернативу тип_альт в объединении, заданном объявлением%union
. См. раздел 4.5.4 Типы данных значений в действиях. - `$<тип_альт>n'
-
Аналогична
n
, но задаёт альтернативу тип_альт в объединении, заданном объявлением%union
. См. раздел 4.5.4 Типы данных значений в действиях. - `YYABORT;'
-
Немедленно завершает работу
yyparse
, сообщая об ошибке. См. раздел 5.1 Функция анализатораyyparse
. - `YYACCEPT;'
-
Немедленно завершает работу
yyparse
, сообщая об удачном разборе. См. раздел 5.1 Функция анализатораyyparse
. - `YYBACKUP (лексема, значение);'
- Отмена сдвига лексемы. Этот макрос допустим только в правилах, которые выполняют свёртку единственного значения, и только когда нет предпросмотренной лексемы. Он устанавливает для предпросмотренной лексемы тип лексема и семантическое значение значение. Затем он отбрасывает значение, которое должно быть свёрнуто по этому правилу. Если макрос используется, когда его применение недопустимо, как например, когда уже есть предпросмотренная лексема, он сообщает о синтаксической ошибке сообщением `cannot back up' и производит обычное восстановление после ошибки. В любом случае оставшаяся часть правила не выполняется.
- `YYEMPTY'
-
Значение, помещаемое в
yychar
, когда там нет предпросмотренной лексемы. - `YYERROR;'
-
Немедленно вызывает синтаксическую ошибку. Этот оператор запускает
восстановление после ошибки, как если бы ошибку обнаружил сам анализатор,
и не выводит никакого сообщения. Если вы хотите вывести сообщение об ошибке,
перед оператором `YYERROR;' вызовите явно
yyerror
. См. раздел 7. Восстановление после ошибок. - `YYRECOVERING'
- Этот макрос заменяет выражение, имеющее значение 1 когда анализатор выполняет восстановление после синтаксической ошибки, и 0 всё остальное время. См. раздел 7. Восстановление после ошибок.
- `yychar'
-
Переменная, содержащая текущую предпросмотренную лексему (в чистом анализаторе
это на самом деле локальная для
yyparse
переменная). Когда предпросмотренной лексемы нет, в неё помещается значениеYYEMPTY
. См. раздел 6.1 Предпросмотренные лексемы. - `yyclearin;'
- Отбросить текущую предпросмотренную лексему. Это полезно, прежде всего, в правилах обработки ошибок. См. раздел 7. Восстановление после ошибок.
- `yyerrok;'
- Немедленно взобновляет создание сообщений об ошибках для последующих синтаксических ошибок. Это полезно, прежде всего, в правилах обработки ошибок. См. раздел 7. Восстановление после ошибок.
- `@$'
- Играет роль структурной переменной, содержащей информацию о позиции в тексте группы, создаваемой текущим правилом. См. раздел 4.6 Отслеживание положений.
- `@n'
- Играет роль структурной переменной, содержащей информацию о позиции в тексте n-го компонента текущего правила. См. раздел 4.6 Отслеживание положений.
[Содержание] [Назад] [Пред] [Вверх] [След] [Вперед]