LEKSIČKA ANALIZA

LEKSIČKA ANALIZA
Prvi korak rada jezičnog procesora jest leksička analiza. Leksička analiza je jedini korak rada
jezičnog procesora koji izravno pristupa znakovima teksta izvornog programa spremljenog u
memoriji računala. Leksički analizator slijedno čita tekst izvornog programa znak po znak,
stvara učinkovit zapis znakova izvornog programa znak po znak, stvara učinkovit zapis
znakova izvornog programa, odbacuje znakove koji se ne koriste u daljnjim koracima rada
jezičnog procesora, grupira znakove u leksičke jedinke, određuje klase leksičkim jedinkama,
provjerava leksička pravila, pronalazi pogreške, određuje mjesto pogreški u izvornom
programu, zapisuje parametre leksičkih jedinki u tablicu znakova i čuva tekstualnu strukturu
izvornog programa.
Ostvareni leksički analizator koristi dvije osnovne tablice. To su tablica uniformnih znakova i
tablica znakova. Tablica znakova razlaže se na tri tablice: tablicu identifikatora, tablicu
konstanti, te tablicu ključnih riječi, operatora i specijalnih znakova. Prvi korak prije
pokretanja leksičke analize je dohvat potrebnih podataka. Iz datoteke source.txt se dohvaća
izvorni kod koji će leksički analizator obraditi. Iz datoteke kljucne_rijeci.txt se dohvaća lista
ključnih riječi, operatora i specijalnih znakova.
break
char
const
continue
do
else
float
for
if
int
return
void
while
{
}
[
]
<
>
(
)
;
-
+
*
=
/
==
*=
+=
-=
/=
#
&
|
&&
||
!
<=
>=
%
++
-Tabela 1: Popis ključnih riječi , opera tora i speci jalnih zna kova
1
Navedene ključne riječi, operatore i specijalne znakove potrebno je poredati po veličini i
spremiti u tablicu u memoriji. Navedena operacija se izvršava pozivom funkcije
ucitajKROS(). Funkcija učitava ključne riječi, operatore i specijalne znakove iz datoteke.
Funkcija vraća tablicu koja sadrži leksičke jedinke poredane od najduže prema najkraćoj, a
poredak onih iste dužine nije bitan (duže jedinke imaju manji indeks u tablici).
Slika 1: Pri mjer sorti rane KROS ta blice
Nakon navedenih radnji može se početi sa leksičkom analizom. Klasa LeksickiAnalizator
predstavlja leksički analizator.
Koristi se na sljedeći način:
LeksičkiAnalizator leksAnalizator(&tablicaKROS,&tablicaKON, &tablicaIDN,
&tablicaUniformnih, datoteka);
leksAnalizator.pokreni();
2
Funkcija koja koristi leksički analizator treba imati tablice leksičkih jedinki čije se adrese
prosljeđuju leksičkom analizatoru. U tablicu KROS moraju biti prethodno učitani sve ključne
riječi, operatori i specijalni znakovi programskog jezika poredani silazno prema duljini
ključne riječi/operatora/specijalnog znaka. Ostale tablice moraju biti prazne. Leksički
analizator će popuniti tablice konstanti, identifikatora i uniformnih znakova iz pozivajuće
funkcije. Uz adrese tablica leksičkih jedinki leksičkom analizatoru se prosljeđuje i putanja do
datoteke iz koje će se učitati izvorni niz s kodom. Ako prilikom leksičke analize dođe do
greške na standardni izlaz ispisuje se poruka o grešci. Ovisno o uspješnosti leksičke analize
funkcija pokreni() vraća true ili false.
LeksickiAnalizator::LeksickiAnalizator(Tablica *tablicaKROS, Tablica *tablicaKON, Tablica
*tablicaIDN, TablicaUniformnihZnakova *tablicaUniformnih, string datoteka)
Konstruktor stvara novi leksički analizator. Prima pokazivače na tablicu KROS, tablicu
konstanti, identifikatora i uniformnih znakova. Niti jedan od pokazivača koje funkcija prima
kao argumente ne smije pokazivati na NULL. Funkcija prima i string objekt koji sadrži
putanju do datoteke u kojoj se nalazi niz koji sadrži izvorni kod programa. Ova metoda
učitava niz iz te datoteke.
LeksickiAnalizator::pokreni()
Funkcija radi leksičku analizu niza znakova iz datoteke koja je predana ovom leksičkom
analizatoru
preko
konstruktora.
Prije
same
leksičke
analize
pomoću
funkcije
procistiNiz(string) uklanja nepotrebne znakove i komentare iz ulaznog niza. Funkcija gradi
tablicu uniformnih znakova, te dodaje odgovarajuće leksičke jedinke u tablice identifikatora i
konstanti. U slučaju da dođe do greške prilikom leksičke analize, leksički analizator nastavlja
analizu i ispisuje poruku o grešci na standardni izlaz. U slučaju da leksička analiza ne uspije,
funkcija vraća false, u protivnom true.
Kao što je navedeno, prvi korak je pročišćavanje ulaznog niza. Leksički analizator izbacuje
sve znakove koji se ne koriste u daljnim fazama rada jezičnog procesora. Pročišćavanje
ulaznog niza izvršava se pozivom:
procistiNiz(ulazniNiz);
3
Funkcija miče suvišne bjeline, tabove i znakove koji služe za grafičku stukturu ulaznog
programa iz zadanog ulaznog niza. Funkcija Izbacuje sve jednolinijske i višelinijske
komentare. Svaki podniz koji sadrži više uzastopnih tabova ili bjelina (ili oboje) zamjenjuje se
jednom bjelinom. Funkcija mora vratiti string objekt koji sadrži niz preuređen na navedeni
način.
Grupiranje leksičkih jedinki
Nakon pročišćavanja ulaznog niza, prelazi se na analizu pročišćenog niza. Niz znakova
izvornog programa grupira se u leksičke jedinke. Tijekom grupiranja traži se najdulji prefiks
koji je definiran. Najdulji prefiks može biti ključna riječ, operator ili specijalan znak,
identifikator ili konstanta (float, int, char, string).
a
k
o
1
9
8
8
=
2
0
0
ako1988
=
2009
Identifikator
KROS
INT Konstanta
9
(a)
a
k
o
1
9
8
8
=
2
0
0
ako
1988
=
2009
KROS
INT Konstanta
KROS
INT Konstanta
9
(b)
Slika 2: Pri mjer nejednozna čnos ti , dobro (a ) i loše (b) grupi ranje znakova
Na slici se vidi zašto je potrebno grupirati po najvećem prefiksu. Kada bi se znakovi grupirali
po pravilu da se grupiraju čim se nađe prvi prefiks, dobila bi se kriva analiza ulaznog niza.
Niz ako1988 je najdulji prefiks niza ako1988=2009 te se može zaključiti da je on
identifikator.
4
Kako bi se izbjegla navedena nejednoznačnost, poziva se sljedećih šest funkcija koje će tražiti
najveću duljinu prefiksa zadanog niza:
Funkcije za traženje najvećeg prefiksa ulaznog niza:
1. int jeKROS(string niz);
Funkcija traži da li se u tablici KROS nalazi ključna riječ, operator ili specijalni znak
koji je prefiks zadanog niza, tj. funkcija traži najdulju leksičku jedinku iz tablice koja
se nalazi na početku niza. Funkcija Vraća indeks te leksičke jedinke u tablici ili -1 ako
niz ne započinje leksičkom jedinkom iz tablice.
2. int jeIntKON(string niz);
Funkcija provjerava da li se na početku niza nalazi cjelobrojna konstanta i vraća njenu
duljinu, tj. funkcija vraća duljinu najdulje moguće leksičke jedinke koja se nalazi na
početku zadanog niza i predstavlja cjelobrojnu konstantu. Ako se na početku niza ne
nalazi konstanta, funkcija vraća 0.
Za tu operaciju korišten je konačni automat s četiri stanja:
Slika 3: Kona čni automat za tra ženje dul jine prefiksa koji je cjelobrojna konstanta
5
Ako se automat na kraju izvođenja nalazi u prihvatljivom stanju, funkcija vraća
duljinu cjelobrojne konstante. Ako se automat nalazi u neprihvatljivom stanju,
funkcija vraća 0.
Primje r izvođenja
Funkcija vraća:
Ulazni niz:
+ 1265
6
+1265
5
+123Ba654GG
4
+GZT
0
Tabela 2: Pri mjer izvođenja funkcije jeINTKon(string niz);
3. int jeFloatKON(string niz);
Funkcija provjerava da li se na početku niza nalazi konstanta s posmačnim zarezom i
vraća njenu duljinu, tj. funkcija vraća duljinu najdulje moguće leksičke jedinke koja se
nalazi na početku zadanog niza i predstavlja konstantu s posmačnim zarezom. Ako se
na početku niza ne nalazi konstanta, funkcija vraća 0.
Kada automat dođe u prihvatljivo stanje, funkcija vraća duljinu konstante s
posmačnim zarezom.
Primje r izvođenja
Funkcija vraća:
Ulazni niz:
2.3
3
+ 2.3dfg12.3
5
2.3e10a
6
Tabela 3: Pri mjer izvođenja funkcije jeFloatKON(string niz);
6
Za tu operaciju korišten je automat s 10 stanja:
Slika 4: Kona čni automat za tra ženje dul jine prefiksa koji je kons tanta s pos ma čnim za rezom
4. int jeCharKON(string niz);
Funkcija provjerava da li se na početku niza nalazi znakovna konstanta i vraća njenu
duljinu, tj. funkcija vraća duljinu najdulje moguće leksičke jedinke koja se nalazi na
početku zadanog niza i predstavlja znakovnu konstantu. U ovom slučaju najdulja
moguća (i jedina moguća) duljina leksičke jedinke je 3.
Znakvone konstante su sljedećeg oblika:
<jednostrukiNavodnik><znak><jednostrukiNavodnik>
Jednostruki navodnik je znak: ', a znak može biti bilo koji ASCII znak osim newlinea i
taba. Escape sekvence (npr. \n, \\, \t, ...) nisu podržane i ne mogu se nalaziti unutar
jednostrukih navodnika. Ako se na početku niza ne nalazi znakovna konstanta,
funkcija vraća 0.
7
Za tu operaciju potreban je automat s 5 stanja:
Slika 5: Kona čni automat za tra ženje dul jine prefiksa koji je znakovna kons tanta
Ako se automat na kraju izvođenja nalazi u prihvatljivom stanju, funkcija vraća
duljinu 3. Ako se automat nalazi u neprihvatljivom stanju, funkcija vraća 0.
5. int jeStringKON(string niz);
Funkcija provjerava da li se na početku niza nalazi znakovni niz i vraća njegovu
duljinu, tj. funkcija vraća duljinu najdulje moguće leksičke jedinke koja se nalazi na
početku zadanog niza i predstavlja znakvoni niz.
Znakovni nizovi su sljedećeg oblika:
<dvostrukiNavodnici><nizZnakova><dvostrukiNavodnici>
Dvostruki navodnici su znak: ", a niz znakova je niz bilo kojih ASCII znakova osim
newlinea i taba. Escape sekvence (npr. \n, \\, \t, ...) nisu podržane i ne mogu se nalaziti
unutar jednostrukih navodnika. Ako se na početku ulaznog niza ne nalazi leksička
jedinka koja predstavlja znakovni niz, funkcija vraća 0.
8
Za tu operaciju potreban je automat s 4 stanja:
Slika 6: Kona čni automat za tra ženje prefiksa koji je s tring
Primje r izvođenja
Ulazni niz:
Funkcija vraća:
''abc''
5
''a''
3
Tabela 4: Pri mjer izvođenja funkcije jeStringKON(string niz);
Ako se automat na kraju izvođenja nalazi u prihvatljivom stanju, funkcija vraća
duljinu stringa. Ako se automat nalazi u neprihvatljivom stanju, funkcija vraća 0.
6. int jeIDN(string niz);
Funkcija provjerava da li se na početku niza nalazi identifikator i vraća njegovu
duljinu, tj. funkcija vraća duljinu najdulje moguće leksičke jedinke koja se nalazi na
početku zadanog niza i predstavlja identifikator. Ako se na početku niza ne nalazi
identifikator, funkcija vraća 0.
9
Za tu operaciju potreban je automat s 3 stanja:
Slika 7: Kona čni automat za tra ženje prefiksa koji je identi fika tor
Automat započinje u stanju 0, i radi dok ima prijelaza. U stanju 0 nalazi se samo na
početku, zatim prelazi u stanje 1 za prihatljivi znak, odnosno u 2 za neprihvatljivi.
Automat uvijek završava rad stanjem 2. Prelaskom u stanje 2 funkcija vraća duljinu
pronađenog prefiksa.
Oporavak od pogreške
Ako se ne može pronaći niti jedan mogući prefiks niza, potrebno je pokrenuti postupak
oporavka od pogreške. Postupak oporavka od pogreške zasniva se na odbacivanju krajnje
lijevog znaka niza koji nema niti jedan prefiks koji je definiran. Neka niz znakova w = a1 a2 -- an x nema nijedan prefiks koji je definiran. Postupak oporavka od pogreške odbaci znak a1
niza w, ispiše taj znak te nastavlja analizom ostatka niza a2 --- an x. Ako ni nakon odbačenog
znaka a1 nijedan prefiks niza nije definiran, onda se nastavlja s izbacivanjem sljedećeg znaka.
Odbacivanje se nastavlja sve dok ne ostane niz x koji ima definirani prefiks.
Nejednoznačnost
Glavni problem je tumačenje znakova + i - . Znakovi + i – mogu biti unarni ili binarni
operatori. Navedena nejednoznačnost se rješava tako da se definira da ako navedeni znakovi
dolaze poslije ' ( ' , ' = ' , ' = = ' ili ' != ' , onda se smatraju unarnim operatorima.
10
Popunjavanje tablica
Na početku rada postoji jedna popunjena tablica te tri prazne. Popunjena je jedino tablica
ključnih riječi, operatora i specijalnih znakova. Prazne tablice su tablica identifikatora, tablica
konstanti te tablica uniformnih znakova. Osnovna tablica je tablica uniformnih znakova. U toj
tablici zapisani su uniformni znakovi onim redoslijedom koim su leksičke jedinke zadane u
izvornom programu. Nakon što leksički analizator odredi klasu leksičke jedinke, njezin
uniformni znak zapisuje se u tablicu uniformnih znakova. Zapis u tablici sadrži dvije kazaljke
za svaku leksičku jedinku. Prva kazaljka je sam uniformni znak leksičke jedinke. Ta kazaljka
pokazuje na jednu od tri preostale tablice: tablicu identifikatora (uniformni znak IDN), tablicu
konstanti (uniformni znak KON) ili tablicu ključnih riječi, operatora i specijalnih znakova
(uniformni znak KROS). Druga kazaljka pokazuje na mjesto zapisa leksičke jedinke u tablici
odabranoj primjenom prve kazaljke.
Tablica uniformnih
znakova
Izvorni
Uniformni
program
znak
Kazaljka
ako
KROS
9 Leksička jedinka pripada KROS tablici, pozicija: 9. red
(
KROS
21 Leksička jedinka pripada KROS tablici, pozicija: 21. red
Kolicina
IDN
1 Leksička jedinka pripada IDN tablici, pozicija: 1. red
>
KROS
19 Leksička jedinka pripada KROS tablici, pozicija: 19. re
30
KON
1 Leksička jedinka pripada KON tablici, pozicija: 1. red
)
KROS
Cijena
IDN
22
...
2
…
=
KROS
17
…
1500
KON
2
…
;
KROS
26
…
Tabela 5: Pri mjer popunja vanja tablice uniformnih znakova
Tablice IDN i KON se popunjavaju u ovisnosti o duljini prefiksa ulaznog niza. Na primjer
ako je zadovoljen uvjet:
if(maxInt > maxFloat && maxInt > maxChar && maxInt > maxString) ,
11
onda je navedeni prefiks tipa IN T te se sprema u tablicu konstanti. Na sličan način se
određuju i ostale konstante. Tablica konstanti se sastoji od pročitane konstante te njezinog
tipa.
1
20
INT
2
13.5
FLOAT
3
12345
INT
Tabela 6: Pri mjer tablice kons tanti
Nakon zapisa u jednu od tri navedene tablice vraća se indeks retka pojedine tablice u koji je
leksička jedinka zapisana. Leksička jedinka se zapisuje na kraj tablice. Ako u tablici već
postoji navedena leksička jedinka, onda se samo vraća njezin indeks. U tablicu uniformnih
znakova se zapisuje samo uniformni znak pojedine leksičke jedinke, njezina pozicija u tablici
konstanti, tablici identifikatora ili KROS tablici te broj linije izvornog programa gdje se
nalazi. U slučaju kada funkcije za brojanje duljine prefiksa vraćaju da je duljina leksičke
jedinke jednaka ključnoj riječi i identifikatoru, uzima se da je leksička jedinka ključna riječ te
se u tablicu uniformnih znakova zapisuje uniformni znak KROS te njezina pozicija u KROS
tablici. Nakon završetka analize cijelog ulaznog koda i popunjavanja svih tablica, sve tablice
se ispisuju.
Slika 8: Pri mjer ispisa tabli ce identifi katora
Slika 9: Pri mjer ispisa tabli ce konstanti
12
Slika 10: Pri mjer ispisa tabli ce kl jučni h ri ječi , opera tora i specijalnih znakova
13
Slika 11: Pri mjer ispisa tabli ce uni formnih znakova
14