* 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>>"*)" 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