lex_doc (1119420), страница 4
Текст из файла (страница 4)
правила, которое записано первым в списке раздела
правил Lex-программы.
Рассмотрим пример:
.
.
.
[Мм][Аа][Йй] ECHO;
[А-Яа-я]+ ECHO;
.
.
.
Слово "Май" распознают оба правила, однако, выполнится пер-
вое из них, так как и первое, и второе правило распознали
лексему одинакового размера (3 символа). Если во входном
потоке будет, допустим, слово "майский", то первые 3 символа
удовлетворяют первому правилу, а все 7 символов удовлетво-
ряют второму правилу, следовательно, выполнится второе пра-
вило, так как ему удовлетворяет более длинная последователь-
ность символов.
3.3. Раздел программ пользователя
Все, что размещено за вторым набором %%, относится к
разделу программ пользователя. Содержимое этого раздела
копируется в выходной файл lex.yy.c без каких-либо измене-
ний. В файле lex.yy.c строки этого раздела рассматриваются
как функции в смысле Си. Эти функции могут вызываться в
действиях правил и, как обычно, передавать и возвращать зна-
чения аргументов.
3.4. Комментарии Lex-программы
Комментарии можно указывать во всех разделах Lex-
программы. Формат комментариев должен соответствовать фор-
мату комментариев host-языка. Однако, в каждом разделе Lex-
программы комментарии указываются по разному. В разделе
определений комментарии должны начинаться не с первой пози-
ции строки. В разделе правил комментарии можно указывать
только внутри блоков, принадлежащих действиям. В разделе
программ пользователя комментарии указываются как и в host-
языке.
3.5. Примеры Lex-программ
Пример1.
22
%Start KOMMENT
/*
* Программа записывает в
* стандартный файл вывода
* комментарии Си-программы.
* Обратите внимание на то, что
* здесь строки комментариев указаны
* не с первой позиции строки!
*/
КОММ_НАЧАЛО "/*"
КОММ_КОНЕЦ "*/"
%%
{КОММ_НАЧАЛО} { ECHO;
BEGIN KOMMENT;}
[0* ;
<KOMMENT>[^*]* ECHO;
<KOMMENT>[^/] ECHO;
<KOMMENT>{КОММ_КОНЕЦ} {
ECHO;
printf("0);
/*
* Здесь приведен пример
* использования комментариев в
* разделе правил Lex-программы.
* Обратите внимание на то, что
* комментрий указан внутри блока,
* определяющего действие правила.
*/
BEGIN 0;}
%%
/*
* Здесь приведен пример комментариев
* в разделе программ пользователя.
*/
Пример 2.
23
%Start IC1 IC2 Normal
/*
* Отладочный фрагмент
* Lex-программы, которая строит
* лексический анализатор для
* компилятора языка Паскаль.
* Действие return(...)
* возвращает тип лексемы в
* в вызывающую анализатор
* программу.
* Обратите внимание на то, что в
* этой Lex-программе отсутствуют
* активные правила. Это сделано
* в связи с тем, что нет
* необходимости иметь правила,
* которые всегда активны.
* Все цепочки символов входного
* потока, не распознанные в
* правилах, копируются в выходной
* поток символов.
*/
LETTER [A-ZА-Яa-zа-я_]
DIGIT [0-9]
IDENT {LETTER}({LETTER}|{DIGIT})*
INT {DIGIT}+
FIXED {INT}?.{INT}
WHISP [ 0*
%%
BEGIN Normal;
<Normal>"{" BEGIN IC1;
<IC1>[^}] ;
<IC1>"}" BEGIN Normal;
<Normal>"(*" BEGIN IC2;
<IC2>[^*]|[^)] ;
<IC2>>"*)" BEGIN Normal;
<Normal>'([^']|'')*' return( строка );
<Normal>"<>" return( не_равно );
<Normal>"=" return( равно );
<Normal>"<" return( меньше );
<Normal>">" return( больше );
<Normal>">=" return(больше_или_равно);
<Normal>"<=" return(меньше_или_равно);
<Normal>".." return( точка_точка );
<Normal>"+" return( плюс );
<Normal>"-" return( минус );
<Normal>":=" return( присвоить );
<Normal>"*" return( умножить );
<Normal>"/" return( разделить );
<Normal>mod return( t_mod );
24
<Normal>div return( t_div );
<Normal>and return( t_and );
<Normal>or return( t_or );
<Normal>not return( t_not );
<Normal>"(" return( lpar );
<Normal>")" return( rpar );
<Normal>"[" return( lbracket );
<Normal>"]" return( rbracket );
<Normal>"," return( comma );
<Normal>":" return( colon );
<Normal>"^" return( circumflex );
<Normal>";" return( semicolon );
<Normal>write return( Write );
<Normal>writeln return( Writeln );
<Normal>label return( t_label );
<Normal>program return( );
<Normal>const x( "константы" ) ;
<Normal>type x( "типы" ) ;
<Normal>var x( "перем" ) ;
<Normal>procedure x( "процедура" ) ;
<Normal>function x( "функция" ) ;
<Normal>begin x( "начало" ) ;
<Normal>end{WHISP}. x( "конец прогр" ) ;
<Normal>end x( "конец" ) ;
<Normal>array x( "массив" ) ;
<Normal>of x( "из" ) ;
<Normal>record x( "запись" ) ;
<Normal>case x( "выбор" ) ;
<Normal>in x( "в" ) ;
<Normal>file x( "файл" ) ;
<Normal>for x( "для" ) ;
<Normal>to x( "к" ) ;
<Normal>downto x( "вниз к" ) ;
<Normal>do x( "выполн" ) ;
<Normal>while x( "пока" ) ;
<Normal>repeat x( "повт" ) ;
<Normal>until x( "до" ) ;
<Normal>set x( "множество" ) ;
<Normal>with x( "с" );
<Normal>nil x( "nil" ) ;
<Normal>if x( "если" ) ;
<Normal>then x( "то" ) ;
<Normal>else x( "иначе" ) ;
<Normal>{FIXED} x( "float" ) ;
<Normal>{INT} x( "ц.б.з" ) ;
<Normal>{IDENT} x( "идент" ) ;
<Normal>[ 0] ;
%%
x( s )
char *s ;
{
25
printf("%-15.15s 177> %s <1770,
s, yytext ) ;
}
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++; }
%%














