Professional Documents
Culture Documents
Lex Dobro
Lex Dobro
U prethodnom poglavlju je pokazano kako se moe napisati funkcija getToken() koja obavlja
leksiku analizu. Sada e biti pokazano kako se, na temelju specifikacije leksema pomou regularne
gramatike ili regularnih izraza, moe, generirati programski kd leksikog analizatora.
I
F
T - relacija prijelaza S x S,
(stanje S i znak odreuju prijelaz u stanja S)
Konani automat se najee prikazuje kao tranzitivni dijagram. To je usmjereni graf koji se
sastoji od vorova i usmjerenih veza. vorovi oznaavaju stanje automata, a usmjerene veze, koje se
oznaavaju znakovima ulaznog alfabeta, oznaavaju prijelaz u stanje. vorovi su oznaeni krunicom.
vorovi koji predstavljaju konana stanja oznaeni su dvostrukom krunicom.
Primjerice, slijedei automat sa 4 stanja A, B, C i D, prepoznaje literalni zapis realnog broj u
jeziku Fortran, primjerice oblika: 7.89, .98., .8 i 8. Ulazni alfabet se sastoji od znamenki i decimalne
toke .
.
S2
S3
S1
S4
d
= { . , d } - d je znamenka
T opisan dijagram prijelaza
I = S1
F = { S3, S4 }
Rad automata poinje u stanju S1. Zatim se dobavlja ulazni znak. Ako je taj znak toka, prelazi se u
stanje S2, a ako je znamenka, prelazi sa u stanje S3. Ako je to neki drugi znak, onda je greka. Iz stanja
3. Leksika analiza
S2 prelazi se u stanje S3 ukoliko je ulazni znak znamenka, ili nastupa greka. Stanje S3 je konano
stanje u kojem se automat zadrava sve dok je u ulazni znak znamenka. Kada ulazni znak prestane biti
znamenka znai da je dosegnut kraj zapisa numerike konstante. Stanje S4, takoer, moe biti
konano stanje, ali ako je ulazni znak toka, prelazi se u stanje S3.
Ovakvi automat se moe jednostavno programski realizirati tako da se funkcija prijelaza T, kojom se
odreuje prijelaz iz jednog stanja u drugo stanje, a na temelju trenutnog ulaznog znaka, opie
dvodimenzionalnom tablicom prijelaza:
Klasa
znakova
vrijednost
znamenka
(digit)
S4
S3
S3
S4
toka
(dot')
S2
error
error
S3
Zavrno
stanje
ne
ne
da
da
[0-9]
[.]
ostali
digit
dot
ostali
Rad konanog automata realizira funkcija getToken(), koja vraa Token ili greku. Najprije se
automat postavlja u poetno stanje, a zatim se vri petlja koja se ponavlja dok se automat ne postavi u
zavrno (konano) stanja ili stanje greke.
int Transition[MAXSTATE][MAXSIMBOL]; /* stanje klasa-znaka
char currentChar
/*globalna varijabla - sadri ulazni znak
char *lexeme;
/* leksem koji dobavljamo
void Concat(char *s, char ch);
/* dopunja string znakom ch
*/
*/
*/
*/
int getToken()
{
int state = INITIALSTATE; lexeme[0] = '\0'
while (currentChar != EOF)
{
state = Transition[state][CharClass(currentChar)];
if (state == ERROR) // or final state
break;
Concat(lexeme, currentChar);
currentChar = nextChar();
}
if (isFinalState(state))
return(token(state));
else
return(ERROR_TOKEN);
}
3. Leksika analiza
Prethodno opisani automat naziva se deterministiki konani automat (DKA) jer je kod tog
automata uvijek jednoznano odreen prijelaz u neko stanje. Ukoliko bi imali automat u kojem
sljedei ulazni znak vodi u vie razliitih stanja takovi automat se naziva nedeterministiki konani
automat (NKA). On se ne moe realizirati pomou tablice prijelaza.
Kasnije emo pokazati kako se nedeterministiki konani automat moe transformirati u
deterministiki konani automat.
Deterministiki konani automat se moe realizirati i bez definiranja tablice prijelaza. Prijelazi se tada
mogu realizirati pomou goto naredbe i odreditem skoka (labelom) koja odgovaraju stanjima
automata, kao u slijedeem primjeru:
char currentChar
char *lexeme;
*/
*/
int getToken()
{
lexeme[0] = '\0';
/* prazni string */
int klas;
S1:
klas = AdvanceInput();
if(klas == dot) goto S2;
if(klas == digit) goto S3;
return ERROR_TOKEN;
S2:
klas = AdvanceInput();
if(klas == digit) goto S3;
return ERROR_TOKEN;
S3:
klas = AdvanceInput();
if(klas == digit) goto S3;
return NUM_TOKEN;
S4:
klas = AdvanceInput();
if(klas == dot) goto S3;
if(klas == digit) goto S4;
return NUM_TOKEN;
Verzija s goto naredbom se esto koristi u "runo kodiranim" automatima, a verzije s tablicom
prijelaza se koriste programski generiranim leksikim analizatorima.
3. Leksika analiza
->
->
->
->
digit
digit
digit
digit
D | dot B
C
| digit D | dot C
C | digit
ab
a|b
a*
a+
(izraz)
Primjer: Regularni izraz za literalni zapis realnog broja u oblicima: 78.8, .98, 78 i 78., je
(d*.d+) | (d+(|.))
Prva alternativa je specifikacija za zapis koji ima decimalni dio, a druga specifikacija je za zapis koji
nama decimalni dio, ali moe sadravati decimalnu toku.
3. Leksika analiza
Bitno je napomenuti da su s teorijskog stajalita sva tri oblika specifikacije leksema (konani
automat, regularna gramatika i regularni izrazi) ekvivalentna i mogu se transformirati jedan u drugog.
U praksi se za specifikaciju najee koriste regularni izrazi, a za programsku implementaciju
leksikih analizatora se koriste konani automati.
Svako pravilo moe imati samo jedan neterminal na lijevoj strani i samo jedan ili nijedan neterminal
na desnoj strani. Neterminale se moe tretirati i kao stanja u procesu prepoznavanja leksema ili
reenice jezika.
Proces pretvorbe u konani automat vri se u dva koraka. U prvom koraku se odreuju stanja
konanog automata, prema pravilu:
Svaki neterminalni simbol postaje oznaka stanja konanog automata.
Automatu se dodaje jo jedno, konano stanje, u kojem e biti automat kada se prepozna
sekvenca koja odgovara leksikoj specifikaciji.
3. Leksika analiza
Dakle, regularna gramatika sa n neterminalnih simbola se moe pretvoriti u konani automat sa n+1
stanjem. Skup terminalnih simbola koje prepoznaje automat isti je kao i skup terminalnih simbola
gramatike.
U drugom koraku se oznaavaju prijelazi, prema pravilu:
Primjer: Formirat emo konani automat prema regularnoj gramatici za zapis realnog numerike
konstante:
R
R
A
A
B
C
dA
.B
dA
.C
dC
dC
| d
|
|
|
|
d
.
d
d
.
R
d
d
d
d
d
A
S = { R, A, B, C, Z }
= { . , d } d je znamenka
T odreen dijagramom
I=R
F={Z}
3. Leksika analiza
regularni izraz
a+
a*
ab
a|b
Najjednostavniji regularni izraz sadri jedan terminalni simbol a. Odgovarajui automat ima dva
stanja izmeu kojih je prijelaz oznaen simbolom a. Operatori ponavljanja (+ ili *) se oznaavaju
prijelazom koji poinje i zavrava u istom stanju.
AB
A|B
A*
3. Leksika analiza
Najprije se definiraju dva automata koji predstavljaju alternativne regularne izraze (d+(|.)) i
(d*.d+):
(d*.d+)
(d+(|.))
d
W
d
d
A
d
d
.
d
F
Tablica prijelaza, koja jednoznano opisuje konani automat, moe se definirati samo za
deterministiki konani automat, stoga je nuno provesti pretvorbu NKA u DKA, ako se eli
programski realizirati konani automat pomou tablice prijelaza.
Kod DKA svakom prijelazu odgovara samo jedno stanje. Tu injenicu u tablici prijelaza
opisujemo s parom (simbol, stanje). Kod NKA nekom prijelazu pripada vie stanja, to znai da bi
tablica prijelaza za NKA trebala sadravati parove (simbol, skup stanja).
3. Leksika analiza
operacija
-closure(s)
opis
je operacija koja daje skup NKA stanja u koja se moe doi iz stanja s
koristei -prijelaze, tj.
je operacija koja daje skup NKA stanja u koja se moe doi iz svih
stanja sT koristei -prijelaze (ukljuuje T), tj.
move(T, a)
Koristei ove operacije (skupove) definira se sljedei algoritam (Aho, Sethi, Ullman):
3. Leksika analiza
Koristei ovaj algoritam pokazat emo kako se vri pretvorba u DKA automata iz prethodnog
primjera.
Akcija
poetno stanje
simbol
move(T,a)
A start
-prijelaz
W,E
Y,C
C
C
kompletiraj S
WxdX
ExdE
Ex. F
XxdX
ExdE
Xx. Y
Ex. F
FxdG
kompletiraj V
kompletiraj U
d
d
GxdG
FxdG
kompletiraj R
.
d
kompletiraj T
.
Dstate
dodaj R =
{A,W,E}
dodaj T =
{X,E,Y,C}
Dtable
dodaj S = {F}
T
Dtable[R,.]=S
Dtable[T,d]=T
dodaj U =
{F,Y,C}
Dtable[T,.]=U
konano stanje
dodaj V =
{G,C}
V
V
Dtable[S,d]=V
konano stanje
Dtable[V,d]=V
Dtable[U,d]=V
Y,C
Dtable[R,d]=T
konano stanje
{F}
{A,W,E}
R
{X,E,Y,C}
d
V {G,C}
{F,Y,C}
U
d
d
Ulazni simboli
ostali
.
S
error
stanje
R
d
T
error
error
prihvati
error
prihvati
error
prihvati
Oito je da rezultirajui automat sadri prijelaze izmeu skupova stanja (ili podskupova NKA).
3. Leksika analiza
10
Partiticije
[R,S] [T,U,V]
Provjera
konzistentnosti
Rxd->T
Sxd->V
Rx.->S
Sx.->error
Txd->T
Uxd->V
Vxd->V
Tx.->U
Ux.->error
Vx.->error
Uxd->V
Vxd->V
Ux.->error
Vx.->error
Zakljuak
Fragmentacija
[R,S]
nekonzistentan
[T,U,V]
nekonzistentan
[U,V]
konzistentan
nije potrebna
V
d
T
d
3. Leksika analiza
11
Mogue je daljnje komprimiranje tablice tako da se ona ne implementira kao prosti dvodimenzionalni
niz, ve kao rijetka matrica (sparse array).
^r
r$
\c
r?
"..."
r1 / r2
3. Leksika analiza
\a
ab|cd
ab?c
-?[0-9]+
[0-9]*\.[0-9]+
[^a-zA-Z]
[A-Za-z][A-Za-z0-9]*
a
ab ili cd
ac ili abc
cijeli broj s opcionim predznakom
decimalni broj koji mora imati bar jednu decimalu
bilo koji znak osim slova
identifikator koji zapoima slovom
Ako dva regularna izraza poinju s istim podnizom znakova tada se uvijek odabire dulji niz znakova,
ako postoji. Ako su oba niza iste duljine tada se uvijek daje prednost prvom izrazu.
definicije
%%
pravila
%%
korisnike funkcije
Ne moraju biti zastupljene sekcije definicija i korisnikih funkcija. Pravila se zapisuju nakon oznake
%%. Ako nema definiranih korisnikih funkcija ne mora se zadati druga oznaka %%.
Evo kako izgleda specifikacija za leksiki analizator Mikro jezika, koji je opisan u prethodnom
poglavlju:
%{
#include <stdlib.h>
#include <string.h>
#include "global.h"
int linenum = 1;
tValue tokenval;
int lineno=1;
3. Leksika analiza
13
%}
%%
[0-9]+
[0-9]+\.[0-9]*
print
int
float
return
return
return
T_PRINT;
T_INT ;
T_FLOAT;
[a-zA-Z_][a-zA-Z0-9_]* return
[ \t\r]+
[\n]
.
%%
T_ID;
{;}
linenum++;
return tokenval.op = yytext[0];
int yywrap(void)
{
return 1;
}
void syn_error(char *str)
/* error handling */
{
printf("Line %d,: %s\n",lineno,str);
exit(1);
}
Prije upoznavanja s pravilima za definiranje ovih sekcija potrebno je upoznati regularne izraze koji se
koriste u programu Lex.
3.8.2 Upoznavanje programa Lex kroz primjere
Primjer 1. Trivijalni sluaj primjene Lex-a je da prepozna ulazni niz znakova i da ga proslijedi na
izlaz. Specifikacijska datoteka za takvu "analizu" izgleda ovako:
/* File : ex1.lex */
%%
. | \n
ECHO;
%%
int yywrap(void)
{
return 1;
}
int main(void)
{
yylex();
return 0;
}
3. Leksika analiza
14
U prethodnoj specifikaciji, u sekciji "pravila", koja je omeena s %%...%%, napisano je samo jedno
pravilo
. | \n
ECHO;
Ovo pravilo znai da ako se prepozna bilo koji znak osim nove linije (.) ili znak nove linije (\n) izvrit
e se akcija ECHO.
ECHO je makro naredba koja je definirana u Lex-u, tipino s
#define ECHO fwrite(yytext, yyleng, 1, yyout)
to znai da se iz stringa yytext, proslijedi yyleng znakova u izlazni tok yyout. Ove tri varijable
su globalne varijable koje Lex generira u lex.yy.c. Znaenje ime je dano u tablici 3.8.1.
Varijabla yytext sadri prepoznati leksem, a yyleng sadri duljinu prepoznatog leksema.
Isti uinak bi bio postignut i s pravilom:
. | \n
printf("yytext");
jer je yytext pokaziva na znakovni niz koji je uvijek zakljuen s '\0', dakle predstavlja string.
Uoite: Pravilo se sastoji od regularnog izraza i "akcije", koja sadri naredbu C jezika.
Ako ima vie naredbi, mora ih se pisati unutar vitiastih zagrada.
Nakon sekcije "pravila", definirane su dvije korisnike funkcije yywrap() i main(). U ovoj sekciji
se moe definirati proizvoljan programski kod u C jeziku, ali obvezatno se mora definirati funkcija
yywrap(), koja mora vratiti cjelobrojnu vrijednost 0 ili 1. Ako ova funkcija vrati vrijednost 1, to
znai da s leksika analiza prekida kada Lex dostigne kraj datoteke. Ako vrati vrijednost 0, analiza se
nastavlja datotekom, koju mora otvoriti korisnik (obino unutar ove funkcije).
Neke verzije Lex-a sadre biblioteke s prethodno definiranim "default" oblikom yywrap() funkcije.
Ime
Funkcija
poziv leksikog analizatora (obino se realizira da
int yylex(void)
vraa vrijednost tokena)
char *yytext
pokaziva na prepoznati leksem
int yyleng
duljina orepoznatog leksema
yylval
vrijednost tokena (korisnik definira tip od yylval)
wrapup, vraa 1 ili 0, ovvisno o tome da li se eli
int yywrap(void)
zavriti procesiranje ulaza
FILE *yyout
izlazna datoteka (default stdout)
FILE *yyin
ulazna datoteka (default stdin)
INITIAL
inicijaliziraj startne uvjete
BEGIN condition zaponi od startnih uvjeta
ECHO
ispii prepoznati leksem na yyout
Tablica 3.8.1 Lex - predefinirane varijable, funkcije i makroi
Primjer 2. U sljedeem programu se ispisuje da li je neki leksem: kljuna rije, operator, separator ili
broj.
/* File ex2.lex:
3. Leksika analiza
15
Ako otkucamo:
c:>ex2
do a=3+c; while(x>7);
3. Leksika analiza
16
Pravilo 2 sadri listu kljunih rijei (odvojeno vertikalnom crtom to oznaava alternativne
regularne izraze) . Ako je prepoznata kljuna rije ispisuje se poruka. Ovu listu se moglo zapisati u
jednom retku:
(while)|(if)|(do)|(for)|(...)|(else)| {printf("%s: kljucna rijec\n", yytext);}
Zapamti: ako vie sekvenci zapoima istim znakovima Lex izvrava akciju za najdulju
sekvencu.
Pravilo 6 (.) se gotovu uvijek zadaje, je bez njega Lex ne bi mogao razlikovati podsekvence,
primjerice, bez ovog "default" pravila, Lex bi prepoznao leksem "do" i u sekvencama "undo" i
"done".
17
^(.*)\n
%%
int yywrap(void) {
return 1; }
[0-9]
[A-Za-z]
%{
int count;
%}
%%
/* match identifier */
(-|{letter})({letter}|{digit})*
%%
int yywrap(void) {
count++;
return 1; }
3. Leksika analiza
18
\n
{ nline++; nchar++; }
[^ \t\n]+ { nword++, nchar += yyleng; }
.
{ nchar++; }
%%
int main(void) {
yylex();
printf("%d\t%d\t%d\n", nchar, nword, nline);
return 0;
}
Primjer 7. Ovom primjeru, (a) velika slova se pretvaraju u mala slova, (b) odstranjuju se znakovi
razmaka na kraju linije, (c) viestruki znak razmaka se zamjenjuje jednim znakom.
%%
[A-Z]
[ ]+$
[ ]+
putchar(yytext[0]-A+a);
;
putchar( );
Primjer 8: U ovom primjeru je specifikacija leksikog analizatora za komandni jezik koji prepoznaje
brojeve, stringove, znak nove linije, identifikator komande, te ignorira bijele znakove i komentar koji
zapoima znakovima // i traje do kraja linije.
%{
#define NUMBER 400
#define COMMENT 401
#define STRING 402
#define COMMAND 403
%}
%%
[ \t]+;
[0-9]+|[0-9]+\.[0-9]+| \.[0-9]{ return NUMBER; }
\"[^\"\n]*\"
{ return STRING; }
[a-zA-Z][a-zA-Z0-9]+
{ return COMMAND; }
//.*
\n
;
{ return \n; }
%%
#include <stdio.h>
3. Leksika analiza
19
Primjer 10: Demostrirat emo upotrebu makro naredbi input() i unput(), u detekciji komentara u C
jeziku, koji je napisan unutar znakova /* .. */.
Predefinirane makro naredbe su:
(i)
(ii)
(iii)
D [0-9]
%%
if
[a-z]+
0{D}+
{D}+
"++"
"+"
"/*"
printf("IF statement\n");
printf("ID %s\n",yytext);
printf("octal number %s\n",yytext);
printf("decimal number %s\n",yytext);
printf("unary op\n");
printf("binary op\n");
{ loop:
while (input() != *)
continue;
switch (input())
{
case /: break;
case *: unput(*);
default: go to loop;
}
}
3. Leksika analiza
20