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

8.5.1.Кодировка лексем и интерфейс

Файл, порожденный YACC'ом в процессе работы, содержит таблицы разбора и функцию yyparse, содержащую интерпретатор таблиц и семантические действия. Для запуска парсера достаточно вызвать эту функцию. В случае успешного разбора она возвращает 0, в случае ошибки - 1.

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

В процедуре лексического анализа кроме выделения лексем можно предусмотреть некоторую обработку лексем определенных типов, в частности, запоминание конкретных значений лексем.

Примером значения лексемы могут служить числовое значение символа - цифры, вычисленное значение константы, адрес идентификатора в таблице имен (построение таблицы имен осуществляет lex). Кроме того, эти значения обычно требуется передать грамматическому анализатору. С этой целью нужное значение должно быть присвоено внешней переменной целого типа с именем yylval. Функция yylex должна возвратить код лексемы и поместить ее значение в переменную yylval, которая имеет тип YYSTYPE. Если функция yylex находится в отдельном файле, то эта переменная должна быть объявлена:

extern int yylval;

Уточним, что значением_лексемы будем называть значение, присвоенное при ее распознавании переменной yylval. Заметим, что в yylval всегда должно находится значение последней выделенной лексемы.

Код лексемы - положительное целое число.

Лексемам, заданных в виде символьных констант, номер лексемы для литералов равен численному значению соответствующего символа в принятой кодировке символов, лежащий в диапазоне 0..255.

Код 256 зарезервирован для специальной лексемы error, которая служит для обработки ошибок.

Лексемам, имеющим имена, присваиваются коды начиная с 257.

Если пользователь предпочитает сам назначать имена лексем, сразу за первым вхождением имени лексемы или литерала в секции определений должно следовать неотрицательное целое число. Это число становится номером лексемы для имени или литерала. Не определенные таким способом имена и литералы доопределяет по умолчанию yacc. Данный механизм оставляет потенциальную возможность многократного использования одного номера. Необходимо обеспечить различие всех номеров лексем.

Процедуры синтаксического разбора и лексического анализа должны быть согласованы относительно номеров лексем. Номера может выбрать yacc или пользователь. В обоих случаях, чтобы дать возможность лексическому анализатору использовать символические обозначения номеров лексем, применяется механизм #define языка C.

Например, предположим, что имя лексемы DIGIT определено в секции определений файла yacc-спецификаций. Чтобы возвратить требуемый номер лексемы, соответствующий фрагмент лексического анализатора может выглядеть так:

int yylex ()

{

extern int yylval;

int c;

. . .

c = getchar ();

. . .

switch (c) {

. . .

case '0':

case '1':

. . .

case '9':

yylval = c - '0';

return (DIGIT);

. . .

}

. . .

}

Требуется возвратить номер лексемы DIGIT и значение, равное численному значению цифры. При условии, что процедура лексического анализа помещена в секцию подпрограмм файла спецификаций, идентификатор DIGIT определяется как номер, соответствующий лексеме DIGIT.

Такой механизм дает понятные, легко модифицируемые лексические анализаторы. Единственная неприятность, которую следует избегать, - это использование в грамматических правилах в качестве имен лексем слов, зарезервированных в языке C или в yacc'е. Например, использование имен лексем if или while почти наверняка приведет к серьезным трудностям при компиляции лексического анализатора. Имя лексемы error зарезервировано для обработки ошибок, не следует использовать его без нужды.

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

Для каждого имени лексемы независимо от того, переопределен ли ее номер пользователем, yacc генерирует в выходном файле y.tab.c оператор препроцессора:

#define <имя_лексемы> <номер_типа>

Значение, возвращаемое функцией yylex, является номером типа лексемы. Таким образом, список лексем и номера их типов указываются в Yacc-программе, а определения этих лексем в Lex- программе. Возникает проблема соответствия номеров типов лексем в файлах y.tab.c и lex.yy.c, которая разрешается следующим образом:

при вызове yacc с флагом -d последовательность операторов #define помещается в файл y.tab.h.;

этот файл посредством оператора #include включается в Lex-программу.