- •1. Введение
- •2. Регулярные выражения в Lex-правилах
- •2.1. Обозначения символов в выражениях
- •2.2. Операторы регулярных выражений
- •2.3. Оператор выделения классов символов
- •2.4. Повторители
- •2.5. Операторы выбора
- •2.6. Оператор {}
- •3. Структура Lex-программы
- •3.1. Раздел определений Lex-программы
- •3.2. Раздел правил
- •3.2.1. Действия в правилах Lex-программы
- •3.2.2. Порядок действия активных правил
- •3.3. Раздел программ пользователя
- •3.4. Комментарии Lex-программы
- •3.5. Примеры Lex-программ
- •4. Структура файла lex.Yy.C
- •5. Функция yywrap()
- •6. Функция reject
- •7. Функции yyless и yymore
- •8. Совместное использование lex и yacc
- •9. Использование Ратфора
- •10. Флаги Lex
4. Структура файла lex.Yy.C
lex строит программу - лексический анализатор на языке
Си, которая размещается в файле со стандартным именем
lex.yy.c. Эта программа содержит две основных функции и нес-
колько вспомогательных. Основные - это:
функция yylex()
Она содержит разделы действий всех правил, которые
определены пользователем;
функция yylook()
Она реализует детерминированный конечный автомат, кото-
рый осуществляет разбор входного потока символов в
соответствии с регулярными выражениями правил Lex-
программы.
Вспомогательные функции, которые являются подпрограм-
мами ввода-вывода. К ним относятся:
input()
читает и возвращает символ из входного потока символов;
unput(c)
возвращает символ обратно во входной поток для повтор-
ного чтения;
output(c)
выводит в выходной поток символ c.
Эти функции определены как макроподстановки следующим
образом:
input -
fprintf( fout, "%s%d%s0,
"#define input() (((yytchar=yysptr>yysbuf
ctable['0],
"?(yylineno++,yytchar):yytchar)==EOF?0:yyt
unput -
26
#define unput(c){
yytchar = (c);
if( yytchar == '\n' ) yylineno--;
*yysptr++ = yytchar;
}
output -
#define output(c) putc(c,yyout)
Эти функции можно изменить, указав им те же имена и
разместив в разделе программ пользователя.
При сборке программы лексического анализа редактор
связи ld по флагу -ll подключает головную функцию main, если
она не определена. Ниже приведен текст этой функции из биб-
лиотеки /usr/lib/libl.a
# include "stdio.h"
main(){
yylex();
exit(0);
}
5. Функция yywrap()
Функция yywrap используется для определения конца
файла, из которого лексический анализатор читает поток сим-
волов. Если yywrap возвращает 1, лексический анализатор
прекращает работу. Однако, иногда имеется необходимость
начать ввод данных из другого источника и продолжить работу.
В этом случае пользователь должен написать свою подпрограмму
yywrap, которая организует новый входной поток и возвращает
0, что служит сигналом к продолжению работы анализатора. По
умолчанию yywrap всегда возвращает 1 при завершении входного
потока символов.
В Lex-программе невозможно записать правило, которое
будет обнаруживать конец файла. Единственный способ это сде-
лать - использовать фунцию yywrap. Эта функция также удобна,
когда необходимо выполнить какие-либо действия по завершению
входного потока символов, определив в разделе программ поль-
зователя новый вариант функции yywrap. Пример:
27
%START AA BB CC
/*
* Строится лексический анализатор,
* который распознает наличие
* включений файлов в Си-программе,
* условных компиляций,
* макроопределений,
* меток и головной функции main.
* Анализатор ничего не выводит, пока
* осуществляется чтение входного
* потока, а по его завершении
* выводит статистику.
*/
БУКВА = [A-ZА-Яa-zа-я_]
ЦИФРА [0-9]
ИДЕНТИФИКАТОР {БУКВА}({БУКВА}|{ЦИФРА})*
int a1,a2,a3,b1,b2,c;
%%
{a1 = a2 = a3 = b1 = b2 = c = 0;}
^# BEGIN AA;
^[ \t]*main BEGIN BB;
^[ \t]*{ИДЕНТИФИКАТОР} BEGIN CC;
\t ;
\n BEGIN 0;
<AA>define { a1++; }
<AA>include { a2++; }
<AA>ifdef { a3++; }
<BB>[^\,]*","[^\,]*")" { b1++; }
<BB>[^\,]*")" { b2++; }
<CC>":"/[ \t] { c++; }
%%
yywrap(){
if( b1 == 0 && b2 == 0 )
printf("В программе\
отсутствует функция main.\n");
if( b1 >= 1 && b2 >= 1 ){
printf("Многократное\
определение функции main.\n");
} else {
if(b1 == 1 )
printf("Функция main\
28
с аргументами.\n");
if( b2 == 1 )
printf("Функция main\
без аргументов.\n");
}
printf("Включений файлов: %d.\n",a2);
printf("Условных компиляций: %d.\n",a3);
printf("Определений: %d.\n",a1);
printf("Меток: %d.\n",c);
return(1);
}
Оператор return(1) в функции yywrap указывает, что лек-
сический анализатор должен завершить работу. Если необходимо
продолжить работу анализатора для чтения данных из нового
файла, нужно указать return(0), предварительно осуществив
операции закрытия и открытия файлов и, в этом случае, анали-
затор продолжит чтение и обработку входного потока. Однако,
если yywrap не возвращает 1, то это приводит к бесконечному
циклу.