* programmu. * Obratite vnimanie na to, chto v * etoj Lex-programme otsutstvuyut * aktivnye pravila. |to sdelano * v svyazi s tem, chto net * neobhodimosti imet' pravila, * kotorye vsegda aktivny. * Vse cepochki simvolov vhodnogo * potoka, ne raspoznannye v * pravilah, kopiruyutsya v vyhodnoj * potok simvolov. */ LETTER [A-ZA-YAa-za-ya_] 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>&gt;"*)" BEGIN Normal; <Normal>'([^']|'')*' return( stroka ); <Normal>"<>" return( ne_ravno ); <Normal>"=" return( ravno ); <Normal>"<" return( men'she ); <Normal>">" return( bol'she ); <Normal>">=" return(bol'she_ili_ravno); <Normal>"<=" return(men'she_ili_ravno); <Normal>".." return( tochka_tochka ); <Normal>"+" return( plyus ); <Normal>"-" return( minus ); <Normal>":=" return( prisvoit' ); <Normal>"*" return( umnozhit' ); <Normal>"/" return( razdelit' ); <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( "konstanty" ) ; <Normal>type x( "tipy" ) ; <Normal>var x( "perem" ) ; <Normal>procedure x( "procedura" ) ; <Normal>function x( "funkciya" ) ; <Normal>begin x( "nachalo" ) ; <Normal>end{WHISP}. x( "konec progr" ) ; <Normal>end x( "konec" ) ; <Normal>array x( "massiv" ) ; <Normal>of x( "iz" ) ; <Normal>record x( "zapis'" ) ; <Normal>case x( "vybor" ) ; <Normal>in x( "v" ) ; <Normal>file x( "fajl" ) ; <Normal>for x( "dlya" ) ; <Normal>to x( "k" ) ; <Normal>downto x( "vniz k" ) ; <Normal>do x( "vypoln" ) ; <Normal>while x( "poka" ) ; <Normal>repeat x( "povt" ) ; <Normal>until x( "do" ) ; <Normal>set x( "mnozhestvo" ) ; <Normal>with x( "s" ); <Normal>nil x( "nil" ) ; <Normal>if x( "esli" ) ; <Normal>then x( "to" ) ; <Normal>else x( "inache" ) ; <Normal>{FIXED} x( "float" ) ; <Normal>{INT} x( "c.b.z" ) ; <Normal>{IDENT} x( "ident" ) ; <Normal>[ 0] ; %% x( s ) char *s ; { 25 printf("%-15.15s 177> %s <1770, s, yytext ) ; } 4. Struktura fajla lex.yy.c lex stroit programmu - leksicheskij analizator na yazyke Si, kotoraya razmeshchaetsya v fajle so standartnym imenem lex.yy.c. |ta programma soderzhit dve osnovnyh funkcii i nes- kol'ko vspomogatel'nyh. Osnovnye - eto: funkciya yylex() Ona soderzhit razdely dejstvij vseh pravil, kotorye opredeleny pol'zovatelem; funkciya yylook() Ona realizuet determinirovannyj konechnyj avtomat, koto- ryj osushchestvlyaet razbor vhodnogo potoka simvolov v sootvetstvii s regulyarnymi vyrazheniyami pravil Lex- programmy. Vspomogatel'nye funkcii, kotorye yavlyayutsya podprogram- mami vvoda-vyvoda. K nim otnosyatsya: input() chitaet i vozvrashchaet simvol iz vhodnogo potoka simvolov; unput(c) vozvrashchaet simvol obratno vo vhodnoj potok dlya povtor- nogo chteniya; output(c) vyvodit v vyhodnoj potok simvol c. |ti funkcii opredeleny kak makropodstanovki sleduyushchim obrazom: 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) |ti funkcii mozhno izmenit', ukazav im te zhe imena i razmestiv v razdele programm pol'zovatelya. Pri sborke programmy leksicheskogo analiza redaktor svyazi ld po flagu -ll podklyuchaet golovnuyu funkciyu main, esli ona ne opredelena. Nizhe priveden tekst etoj funkcii iz bib- lioteki /usr/lib/libl.a # include "stdio.h" main(){ yylex(); exit(0); } 5. Funkciya yywrap() Funkciya yywrap ispol'zuetsya dlya opredeleniya konca fajla, iz kotorogo leksicheskij analizator chitaet potok sim- volov. Esli yywrap vozvrashchaet 1, leksicheskij analizator prekrashchaet rabotu. Odnako, inogda imeetsya neobhodimost' nachat' vvod dannyh iz drugogo istochnika i prodolzhit' rabotu. V etom sluchae pol'zovatel' dolzhen napisat' svoyu podprogrammu yywrap, kotoraya organizuet novyj vhodnoj potok i vozvrashchaet 0, chto sluzhit signalom k prodolzheniyu raboty analizatora. Po umolchaniyu yywrap vsegda vozvrashchaet 1 pri zavershenii vhodnogo potoka simvolov. V Lex-programme nevozmozhno zapisat' pravilo, kotoroe budet obnaruzhivat' konec fajla. Edinstvennyj sposob eto sde- lat' - ispol'zovat' funciyu yywrap. |ta funkciya takzhe udobna, kogda neobhodimo vypolnit' kakie-libo dejstviya po zaversheniyu vhodnogo potoka simvolov, opredeliv v razdele programm pol'- zovatelya novyj variant funkcii yywrap. Primer: 27 %START AA BB CC /* * Stroitsya leksicheskij analizator, * kotoryj raspoznaet nalichie * vklyuchenij fajlov v Si-programme, * uslovnyh kompilyacij, * makroopredelenij, * metok i golovnoj funkcii main. * Analizator nichego ne vyvodit, poka * osushchestvlyaetsya chtenie vhodnogo * potoka, a po ego zavershenii * vyvodit statistiku. */ BUKVA = [A-ZA-YAa-za-ya_] CIFRA [0-9] IDENTIFIKATOR {BUKVA}({BUKVA}|{CIFRA})* int a1,a2,a3,b1,b2,c; %% {a1 = a2 = a3 = b1 = b2 = c = 0;} ^# BEGIN AA; ^[ \t]*main BEGIN BB; ^[ \t]*{IDENTIFIKATOR} 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("V programme\ otsutstvuet funkciya main.\n"); if( b1 >= 1 && b2 >= 1 ){ printf("Mnogokratnoe\ opredelenie funkcii main.\n"); } else { if(b1 == 1 ) printf("Funkciya main\ 28 s argumentami.\n"); if( b2 == 1 ) printf("Funkciya main\ bez argumentov.\n"); } printf("Vklyuchenij fajlov: %d.\n",a2); printf("Uslovnyh kompilyacij: %d.\n",a3); printf("Opredelenij: %d.\n",a1); printf("Metok: %d.\n",c); return(1); } Operator return(1) v funkcii yywrap ukazyvaet, chto lek- sicheskij analizator dolzhen zavershit' rabotu. Esli neobhodimo prodolzhit' rabotu analizatora dlya chteniya dannyh iz novogo fajla, nuzhno ukazat' return(0), predvaritel'no osushchestviv operacii zakrytiya i otkrytiya fajlov i, v etom sluchae, anali- zator prodolzhit chtenie i obrabotku vhodnogo potoka. Odnako, esli yywrap ne vozvrashchaet 1, to eto privodit k beskonechnomu ciklu. 6. Funkciya REJECT Obychno lex razdelyaet vhodnoj potok, ne osushchestvlyaya poisk vseh vozmozhnyh sootvetstvij kazhdomu vyrazheniyu. |to oznachaet, chto kazhdyj simvol rassmatrivaetsya odin i tol'ko odin raz. Predpolozhim, chto my hotim podschitat' vse vhozhdeniya cepochek she i he vo vhodnom tekste. Dlya etogo my mogli by zapisat' sleduyushchie pravila: she s++; he h++; . | \n ; Tak kak she vklyuchaet v sebya he, analizator ne raspoznaet te vhozhdeniya he, kotorye vklyucheny v she, tak kak, prochitav odin raz she, eti simvoly on ne vernet vo vhodnoj potok. Inogda zhelatel'no pereopredelit' etot vybor. Dejstvie funkcii REJECT oznachaet "vybrat' sleduyushchuyu al'ternativu". |to privodit k tomu, chto kakim by ni bylo pravilo, posle nego neobhodimo vypolnit' vtoroj vybor. Sootvetstvenno izme- nitsya i polozhenie ukazatelya vo vhodnom potoke: 29 she { s++; REJECT; } he { h++; REJECT; } . | \n ; Zdes' posle vypolneniya odnogo pravila simvoly vozvrashchayutsya nazad vo vhodnoj potok, i vypolnyaetsya drugoe pravilo. Funkciya REJECT polezna v tom sluchae, kogda ona primenya- etsya dlya opredeleniya vseh vhozhdenij kakogo-libo ob®ekta, prichem vhozhdeniya mogut perekryvat'sya ili vklyuchat' drug druga. Predpolozhim, neobhodimo poluchit' iz odnogo potoka tablicu vseh dvuhbukvennyh sochetanij, kotorye obychno perek- ryvayutsya, naprimer, slovo the soderzhit kak th, tak i he. Dopustim, imeetsya dvumernyj massiv digram, togda: %% [a-z][a-z] { digram[yytext[0]][yytext[1]]++; REJECT; } \n ; Zdes' REJECT ispol'zuetsya dlya vydeleniya bukvennyh par, nachi- nayushchihsya na kazhdoj bukve, a ne na kazhdoj sleduyushchej. 7. Funkcii yyless i yymore V obychnoj situacii soderzhimoe yytext obnovlyaetsya vsyakij raz, kogda na vhode poyavlyaetsya sleduyushchaya stroka. Napomnim, chto v yytext vsegda nahodyatsya simvoly raspoznannoj posledo- vatel'nosti. Inogda voznikaet neobhodimost' dobavit' k teku- shchemu soderzhimomu yytext sleduyushchuyu raspoznannuyu cepochku sim- volov. Dlya etoj celi ispol'zuetsya funkciya yymore. Format vyzova etoj funkcii: yymore() V nekotoryh sluchayah voznikaet neobhodimost' ispol'zovat' ne vse simvoly raspoznannoj posledovatel'nosti v yytext, a tol'ko neobhodimoe ih chislo. Dlya etoj celi ispol'zuetsya funkciya yyless. Format ee vyzova: yyless(n) gde n ukazyvaet, chto v dannyj moment neobhodimy tol'ko n simvolov stroki v yytext. Ostal'nye najdennye simvoly budut vozvrashcheny vo vhodnoj potok. Primer ispol'zovaniya funckii yymore: 30 . . . \"[^"]* { if( yytext[yyleng - 1] == '\\'){ yymore(); }else{ /* * zdes' dolzhna byt' chast' * programmy, obrabatyvayushchaya * zakryvayushchuyu kavychku. */ } } . . . V etom primere raspoznayutsya stroki simvolov, vzyatye v dvoj- nye kavychki, prichem simvol dvojnaya kavychka vnutri etoj stroki mozhet izobrazhat'sya s predshestvuyushchej kosoj chertoj. Analizator dolzhen raspoznavat' kavychku, ogranichivayushchuyu stroku, i kavychku, yavlyayushchuyusya chast'yu stroki, kogda ona izob- razhena kak \". Dopustim, na vhod postupaet stroka "abv\"gde". Snachala budet raspoznana cepochka "abv\ i, tak kak poslednim simvolom v etoj cepochke budet simvol "\", vypolnitsya vyzov yymore(). V rezul'tate k cepochke "abv\ budet dobavleno "gde, i v yytext my poluchim: "abv\"gde, chto i trebovalos'. Primer ispol'zovaniya funcii yyless: . . . =-[A-ZA-YAa-za-ya] { printf("Operator (=-) dvusmyslennyj.\n"); yyless(yyleng - 2); /* * zdes' neobhodimo ukazat' * dejstviya dlya sluchaya "=-" */ } . . . V etom primere razreshaetsya dvusmyslennost' vyrazheniya "=- 31 bukva" v yazyke Si. |to vyrazhenie mozhno rassmatrivat' kak "=- bukva" (ravnosil'no "-=" ) ili "= -bukva" Predpolozhim, chto zhelatel'no etu situaciyu rassmatrivat' kak "= -bukva" i vyvodit' perduprezhdenie. Ukazannoe v primere pravilo raspoznaet etu situaciyu i vyvodit preduprezhdenie. Zatem, v rezul'tate vyzova "yyless(yyleng - 2);" dva sim- vola "-bukva" budut vozvrashcheny vo vhodnoj potok, a znak "=" ostanetsya v yytext dlya obrabotki, kak v normal'noj situacii. Takim obrazom, pri prodolzhenii chteniya vhodnogo potoka uzhe budet obrabatyvat'sya cepochka "-bukva", chto i trebovalos'. 8. Sovmestnoe ispol'zovanie lex i yacc yacc trebuet ukazanie leksicheskomu analizatoru imeni yylex(). Imenno poetomu eta funkciya tak nazyvaetsya v lex. Izvestno, chto yacc stroit vyhodnoj fajl y.tab.c . Osnovnoj v fajle y.tab.c yavlyaetsya funkciya yyparse, realizuyu- shchaya algoritm grammaticheskogo razbora. Funkciya yyparse soder- zhit mnogokratnoe obrashchenie k funkcii leksicheskogo analiza yylex. Dlya obespecheniya korrektnoj raboty grammaticheskogo ana- lizatora funkciya yylex dolzhna byt' soglasovana s konkretnoj specifikaciej grammatiki i udovletvoryat' opredelennym trebo- vaniyam. Pol'zovatel' pri opisanii grammatiki reshaet, kakie konstrukcii celesoobraznee neposredstvenno vydelyat' iz vhod- nogo teksta na etape leksicheskogo analiza. Slozhnost' leksicheskogo analizatora zavisit ot togo, kakie strukturnye edinicy vzyaty za osnovu pri opisanii gram- maticheskih pravil. Detalizovav grammatiku do otdel'nyh sim- volov, mozhno obojtis' prostejshim leksicheskim analizatorom. Odnako, v etom sluchae chislo pravil rastet, a grammaticheskij razbor okazyvaetsya menee effektivnym. Poetomu pol'zovatel' obychno dolzhen najti nekotoryj kompromiss pri vybore nabora leksem. Zametim, chto klyuchevye slova opisyvaemogo vhodnogo yazyka chasto byvaet udobno schitat' leksemami. Imena leksem mogut sovpadat' s etimi klyuchevymi slovami, nedopustimym yavlyaetsya lish' sovpadenie imen leksem s zarezervirovannymi slovami yazyka Si. Osnovnaya zadacha funkcii yylex sostoit vo vvode iz vhod- nogo potoka ryada ocherednyh simvolov do vyyavleniya konstruk- cii, sootvetstvuyushchej odnoj iz leksem, i vozvrashchenii nomera 32 tipa etoj leksemy i, kogda eto neobhodimo, znacheniya etoj leksemy. Vse vidy leksem, krome literalov, oboznachayutsya nekoto- rymi imenami i pod etimi imenami figuriruyut v Yacc- programme, gde ob®yavlenie imen leksem osushchestvlyaetsya direk- tivoj token: %token <spisok imen leksem> Blagodarya ob®yavleniyu imen leksem v direktive token, yacc otlichaet imena leksem ot imen neterminal'nyh simvolov. Primer ob®yavleniya imen leksem v Yacc-programme: %token IDENT CONST ZNAK IF THEN GOTO Pri pervom poyavlenii leksemy ili literala v sekcii ob®yavle- nij Yacc-programmy za kazhdym iz nih mozhet sledovat' neotri- catel'noe celoe chislo, rassmatrivaemoe kak nomer_tipa lek- semy. Po umolchaniyu nomera tipov vseh leksem opredelyayutsya yacc sleduyushchim obrazom: - dlya literala nomerom tipa leksemy schitaetsya chislovoe znachenie dannogo literal'nogo simvola, rassmatrivae- mogo kak odnobajtovoe celoe chislo; - leksemy, oboznachennye imenami, v sootvetstvii s oche- rednost'yu ih ob®yavleniya poluchayut posledovatel'nye nomera, nachinaya s 257. Dlya kazhdogo imeni leksemy nezavisimo ot togo, pereopre- delen li ee nomer pol'zovatelem, yacc generiruet v vyhodnom fajle y.tab.c operator preprocessora: #define <imya_leksemy> <nomer_tipa> Znachenie, vozvrashchaemoe funkciej yylex, yavlyaetsya nomerom tipa leksemy. Takim obrazom, spisok leksem i nomera ih tipov uka- zyvayutsya v Yacc-programme, a opredeleniya etih leksem v Lex- programme. Voznikaet problema sootvetstviya nomerov tipov leksem v fajlah y.tab.c i lex.yy.c, kotoroya razreshaetsya sle- duyushchim obrazom: - pri vyzove yacc s flagom -d posledovatel'nost' opera- torov #define pomeshchaetsya v fajl y.tab.h.; - etot fajl posredstvom operatora #include vklyuchaetsya v Lex-programmu. 33 V procedure leksicheskogo analiza krome vydeleniya leksem mozhno predusmotret' nekotoruyu obrabotku leksem opredelennyh tipov, v chastnosti, zapominanie konkretnyh znachenij leksem. Primerom znacheniya leksemy mogut sluzhit' chislovoe znache- nie simvola - cifry, vychislennoe znachenie konstanty, adres identifikatora v tablice imen (postroenie tablicy imen osu- shchestvlyaet lex). Krome togo, eti znacheniya obychno trebuetsya peredat' grammaticheskomu analizatoru. S etoj cel'yu nuzhnoe znachenie dolzhno byt' prisvoeno vneshnej peremennoj celogo tipa s imenem yylval. Esli funkciya yylex nahoditsya v otdel'- nom fajle, to eta peremennaya dolzhna byt' ob®yavlena: extern int yylval; Utochnim, chto znacheniem_leksemy budem nazyvat' znachenie, prisvoennoe pri ee raspoznavanii peremennoj yylval. Zametim, chto v yylval vsegda dolzhno nahoditsya znachenie poslednej vydelennoj leksemy. Dopustim, my raspolagaem Yacc-programmoj v fajle source.y i Lex-programmoj v fajle source.l, kotorye neobho- dimo sobrat' v rabotayushchuyu programmu. Sushchestvuet dva sposoba sborki: - sborka Lex- i Yacc-programmy s sozdaniem fajla y.tab.h; - sborka Lex- i Yacc-programmy bez sozdaniya fajla y.tab.h. Rassmotrim pervyj sposob sborki. Nizhe priveden primer makefile dlya programmy make, koto- raya osushchestvlyaet posledovatel'nuyu obrabotku i sborku eih programm i razmeshchaet rezul'tat v fajle program: 34 program: y.tab.o lex.yy.o cc y.tab.o lex.yy.o -ly -ll -o program y.tab.o: y.tab.c cc -c -O y.tab.c lex.yy.o: lex.yy.c y.tab.h cc -c -O lex.yy.c y.tab.h: y.tab.c: source.y yacc -d source.y lex.yy.c: source.l lex -v source.l clear: rm -f yy.tab.? lex.yy.? program V fajle source.l razmeshchena Yacc-programma, realizuyushchaya nebol'shoj nastol'nyj kal'kulyator. Kal'kulyator imeet 52 registra, pomechennyh bukvami ot A do z, i razreshaet ispol'- zovat' arifmeticheskie vyrazheniya, soderzhashchie operacii +, -, *, /, % (ostatok ot deleniya), & (pobitovoe i), | (pobitovoe ili) i prisvaivanie. Kak i v Si, celye chisla, nachinayushchiesya s 0, schitayutsya vos'merichnymi, vse ostal'nye - desyatichnymi. Rezul'tat vsegda vyvoditsya desyatichnymi chislami. Kal'kulyator rabotaet v interaktivnom rezhime s postroch- nym formirovaniem vyhoda, mozhet chitat' zadanie iz fajla i vyvodit' rezul'tat v fajl. Znak "=" ispol'zuetsya dlya prisvaivaniya, a dlya vyvedeniya rezul'tata dostatochno nazhat' klavishu <VK>. Raspoznayutsya sko- bochnye struktury, izmenyayushchie poryadok prioritetov pri vychis- leniyah. Kal'kulyator rabotaet tol'ko s celymi tipa integer. 35 %token DIGIT LETTER %left '|' %left '&' %left '+' '-' %left '*' '/' '%' %left UMINUS %{ int base, regs[26]; %} %% list: | list stat '\n' |list stat error '\n' { yyerrok; } stat: expr { printf( "%d\n",$1 ); } |LETTER '=' expr { regs[$1]=$3; } expr: '(' expr ')' { $$=$2; } |expr '+' expr { $$=$1+$3; } |expr '-' expr { $$=$1-$3; } |expr '*' expr { $$=$1*$3; } |expr '/' expr { $$=$1/$3; } |expr '%' expr { $$=$1%$3; } |expr '&' expr { $$=$1&$3; } |expr '|' expr { $$=$1|$3; } |'-' expr %prec UMINUS { $$= -$2; } | LETTER { $$=regs[$1]; } | number; number: DIGIT { $$=$1; base=10; if($1==0) base=8; } |number DIGIT { $$=base*$1+$2; } V fajle source.l razmeshchena Lex-programma leksichskogo analizatora dlya etogo kal'kulyatora: 36 %{ #include "y.tab.h" extern int yylval; %} %% ^\n ; [ \t]* ; [A-Za-z] { yylval = yytext[yyleng-1] - 'a'; return(LETTER);} [0-9] { yylval = yytext[yyleng-1] - '0'; return(DIGIT);} Rassmotrim vtoroj sposob sborki. Makefile teper' sushchestvenno proshche: program: y.tab.c lex.yy.c cc -O y.tab.c -ly -ll -o program y.tab.c: source.y yacc source.y lex.yy.c: source.l lex -v source.l clear: rm -f y.tab.? lex.yy.? program No v fajlah source.y i source.l proizojdut sleduyushchie izmene- niya. V razdele vhodnoj informacii dlya Yacc-programmy neobho- dimo ukazat' stroku #include lex.yy.c, a iz Lex-programmy neobhodimo ubrat' stroku #include "y.tab.h". Teper' fajl source.y vyglyadit sleduyushchim obrazom: 37 %token DIGIT LETTER %left '|' %left '&' %left '+' '-' %left '*' '/' '%' %left UMINUS %{ #include "lex.yy.c" int base, regs[26]; %} %% list: | list stat '\n' |list stat error '\n' { yyerrok; } stat: expr { printf( "%d\n",$1 ); } |LETTER '=' expr { regs[$1]=$3; } expr: '(' expr ')' { $$=$2; } |expr '+' expr { $$=$1+$3; } |expr '-' expr { $$=$1-$3; } |expr '*' expr { $$=$1*$3; } |expr '/' expr { $$=$1/$3; } |expr '%' expr { $$=$1%$3; } |expr '&' expr { $$=$1&$3; } |expr '|' expr { $$=$1|$3; } |'-' expr %prec UMINUS { $$= -$2; } | LETTER { $$=regs[$1]; } | number; number: DIGIT { $$=$1; base=10; if($1==0) base=8; } |number DIGIT { $$=base*$1+$2; } A fajl source.l vyglyadit sleduyushchim obrazom: 38 %{ extern int yylval; %} %% ^\n ; [ \t]* ; [A-Za-z] { yylval = yytext[yyleng-1] - 'a'; return(LETTER);} [0-9] { yylval = yytext[yyleng-1] - '0'; return(DIGIT);} 9. Ispol'zovanie Ratfora lex mozhno ispol'zovat' dlya generacii programm leksiches- kogo analiza na Ratfore. Dlya etogo v pervoj stroke razdela opredelenij neobhodimo ukazat' %R. Vse skazannoe vyshe ob ispol'zovanii Si v kachestve host-yazyka otnositsya i k Rat- foru. Neobhodimo uchest', chto Ratfor imeet svoyu biblioteku vvoda-vyvoda. Odnako, sostav funkcij lex dlya Ratfora tot zhe, chto i dlya Si. Est' i funkciya, vydelennaya tol'ko dlya Ratfora - lexshf. Funkciya lexshf perevodit vnutrennee predstavlenie simvola (mladshij bajt) iz Si vo vnutrennee predstavlenie simvola v Fortrane (starshij bajt). Destviya pravil Lex-programmy dlya Ratfora oformlyayutsya v vide vychislyaemyh goto v vyhodnom fajle, kotoryj nazyvaetsya lex.yy.r. Dopustim, imeetsya ishodnyj fajl source.l s Ratforom v kachestve host-yazyka, togda dlya polucheniya leksicheskogo anali- zatora neobhodimy sleduyushchie dejstviya: % lex source.l % rc lex.yy.r -llr Napomnim, chto v Ratfore indeksy massivov nachinayutsya s 1, poetomu, naprimer, yytex[yyleng] - eto poslednij poluchennyj iz vhodnogo potoka simvol. 10. Flagi Lex -t pomestit' rezul'tat v standartnyj fajl vyvoda, a ne v fajl lex.yy.c; -v vyvesti razmery vnutrennih tablic; -f uskorit' rabotu, ne upakovyvaya tablicy (tol'ko dlya nebol'shih programm); 39 -n ne vyvodit' razmery tablic (ustanavlivaetsya po umolcha- niyu); -d ispol'zuetsya pri otladke lex. Imeetsya vozmozhnost' sobrat' analizator dlya diagnostiki. Dlya etogo neobhodimo kompilyaciyu fajla lex.yy.c osushchestvlyat' s podklyucheniem razdelov diagnostiki: cc -d -DLEXDEBUG lex.yy.c Pri rabote poluchennogo takim obrazom analizatora budet vyvo- dit'sya diagnostika dejstvij. Flag -d, krome togo, pozvolyaet proverit' tekst programmy lex.yy.c s pomoshch'yu tekstovogo otladchika cdeb. 40 SODERZHANIE . Annotaciya ......................................... 2 1. Vvedenie .......................................... 3 2. Regulyarnye vyrazheniya v Lex-pravilah ............... 8 2.1. Oboznacheniya simvolov v vyrazheniyah ............... 8 2.2. Operatory regulyarnyh vyrazhenij .................. 9 2.3. Operator vydeleniya klassov simvolov ............. 9 2.4. Povtoriteli ..................................... 9 2.5. Operatory vybora ................................ 10 2.6. Operator {} ..................................... 11 2.7. Operator <>. Sluzhebnye slova START i BEGIN ...... 12 3. Struktura Lex-programmy ........................... 15 3.1. Razdel opredelenij Lex-programmy ................ 15 3.2. Razdel pravil ................................... 18 3.2.1. Dejstviya v pravilah Lex-programmy ............. 19 3.2.2. Poryadok dejstviya aktivnyh pravil .............. 21 3.3. Razdel programm pol'zovatelya .................... 22 3.4. Kommentarii Lex-programmy ....................... 22 3.5. Primery Lex-programm ............................ 22 4. Struktura fajla lex.yy.c .......................... 26 5. Funkciya yywrap() .................................. 27 6. Funkciya REJECT .................................... 29 7. Funkcii yyless i yymore ........................... 30 8. Sovmestnoe ispol'zovanie lex i yacc ............... 32 9. Ispol'zovanie Ratfora ............................. 39 10. Flagi Lex ......................................... 39 41