Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
УМК по СПО.doc
Скачиваний:
12
Добавлен:
01.07.2025
Размер:
2 Мб
Скачать
☆

8.5.Совместное использование lex и yacc

В результате обработки пользовательской спецификации утилитой yacc создается содержащий C-подпрограммы файл y.tab.c. Основной в файле y.tab.c является функция yyparse, реализующая алгоритм грамматического разбора; это функция с целочисленным результатом. Когда она выполняется, то периодически запрашивает у yylex(), функции лексического анализа, заданной пользователем, входные лексемы. В конечном итоге либо обнаруживается ошибка, yyparse() возвращает значение 1 и восстановление после ошибки невозможно, либо лексический анализатор возвращает маркер конца и разбор успешно завершается. В этом случае yyparse() возвращает значение 0.

Чтобы получить работающую программу, пользователь должен сформировать для процедуры разбора определенное окружение. Например, как в любой программе на языке C, должна быть определена процедура с именем main(), вызывающая yyparse(). Кроме того, чтобы печатать сообщение об обнаруженной синтаксической ошибке, нужна процедура, называемая yyerror().

Эти две процедуры в той или иной форме должны быть заданы пользователем. Чтобы упростить на начальном этапе работу с yacc'ом, предоставляется библиотека с подразумеваемыми версиями функций main() и yyerror(). Библиотека задается при помощи аргумента ly команды cc(1) или редактора связей. Исходный текст

main ()

{

return (yyparse ());

}

и

#include

yyerror (s)

char *s;

{

(void) fprintf (stderr, "%s\n", s);

}

демонстрирует банальность этих предопределенных программ. Аргумент у функции yyerror() - текст, содержащий сообщение об ошибке, обычно syntax error. В реальных приложениях чаще выполняются более сложные действия. Обычно программа подсчитывает номер входной строки и печатает его вместе с сообщением об обнаружении синтаксической ошибки. Внешняя переменная целого типа yyc-har содержит номер предварительно просмотренной лексемы на момент обнаружения ошибки. Это может представлять некоторый интерес для формирования лучшей диагностики. Так как функция main() вероятнее всего задается пользователем (необходимо прочесть аргументы и т.п.), yacc-библиотека полезна только в маленьких проектах или на ранних этапах разработки больших.

Внешняя переменная целого типа yydebug обычно устанавливается равной 0. Если она имеет ненулевое значение, процедура разбора будет выдавать подробное описание действий, включающее сообщение о прочитанных входных символах и выполняемых операциях разбора. Значение этой переменной можно переустановить, используя отладчик sdb(1).

Для обеспечения корректной работы грамматического анализатора функция yylex должна быть согласована с конкретной спецификацией грамматики и удовлетворять определенным требованиям.

Пользователь при описании грамматики решает, какие конструкции целесообразнее непосредственно выделять из входного текста на этапе лексического анализа.

Сложность лексического анализатора зависит от того, какие структурные единицы взяты за основу при описании грамматических правил. Детализировав грамматику до отдельных символов, можно обойтись простейшим лексическим анализатором. Однако, в этом случае число правил растет, а грамматический разбор оказывается менее эффективным. Поэтому пользователь обычно должен найти некоторый компромисс при выборе набора лексем.

Заметим, что ключевые слова описываемого входного языка часто бывает удобно считать лексемами. Имена лексем могут совпадать с этими ключевыми словами, недопустимым является лишь совпадение имен лексем с зарезервированными словами языка Си.

Основная задача функции yylex состоит во вводе из входного потока ряда очередных символов до выявления конструкции, соответствующей одной из лексем, и возвращении номера типа этой лексемы и, когда это необходимо, значения этой лексемы.

Все виды лексем, кроме литералов, обозначаются некоторыми именами и под этими именами фигурируют в Yacc-программе, где объявление имен лексем осуществляется директивой token:

%token <список имен лексем>

Благодаря объявлению имен лексем в директиве token, yacc отличает имена лексем от имен нетерминальных символов.

Пример объявления имен лексем в Yacc-программе:

%token IDENT CONST ЗНАК IF THEN GOTO

При первом появлении лексемы или литерала в секции объявлений Yacc-программы за каждым из них может следовать неотрицательное целое число, рассматриваемое как номер_типа лексемы.