prevodioci i interpretatori
DESCRIPTION
Prevodioci i interpretatoriTRANSCRIPT
-
OSNOVE RAUNARSKIH SISTEMA 2
Prevodioci i interpretatori
Banjaluka, 2003.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
Sadraj:
A. UVOD 1
A1. Upotreba jezika u raunarstvu 1 A2. Sintaksa, Semantika, Pragmatika 2
A3. Tipovi jezikih procesora 3 A4. Struktura jezikih procesora 4 A5. Analogija s prevoenjem govornih jezika 6 A6. Regularni izrazi 6
B. GENERATOR LEKSIKIH ANALIZATORA FLEX 10 B1. Format ulazne datoteke 10
B2. Uzorci 11
B3. Kako se procesira input 13
B4. Akcije 14
C. GRAMATIKE 27
C1. Definicija gramatike 27
C2. Jezik definisan gramatikom 29
C3. Hijerarhija omskog 33 C4. Transformacije kontekst slobodnih gramatika 35
C4.1. Oslobaanje od suvinih slova (neterminala) 35
C4.2. Oslobaanje od pravila 37 C4.3. Oslobaanje od jednostrukih projekcija 37 C4.4. Oslobaanje od lijeve rekurzije 38
D. PARSIRANJE ODOZGO NADOLE (TOP DOWN) 42
D1. Algoritam za odreivanje da li je data gramatika LL(1) raunanje skupa izbora 44 D2. Rekurzivni spust 50
E. PARSIRANJE ODOZDO NAGORE (BOTTOM UP) 59
F. YACC GENERATOR PARSERA 61 F1. Osnovna specifikacija 61
F2. Akcije 63
F3. Leksiki analizator 64 F4. Dvosmislenost i konflikti 69
F5. Prioritet 71
F6. Sheme sintaksnog prevoenja 85
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
1
A. UVOD
A1. Upotreba jezika u raunarstvu
Jezici se kao tip komunikacije izmeu ovjeka i raunara primjenjuju od druge polovine pedesetih godina, kada je definisana prva verzija programskog jezika
FORTRAN i kada je napravljen prevodilac za njega. Osnovni motiv za uvoenje jezika je potreba da se premosti razlika u nivou razmiljanja ovjeka i raunara. Dok ovjek ima mogunost da razmilja na razliitim, a ponekad i visokim, nivoima apstrakcije, raunar je ogranien na mogunosti koje su definisane u trenutku njegove izgradnje i u toku izgradnje njegovog operativnog sistema. Jezici omoguavaju ovjeku da formulie poruku na nain koji je za njega relativno jednostavan i prirodan. S druge strane, perecizna definicija doputenih oblika i njihovog znaenja omoguava izgradnju jezikih procesora pomou kojih raunar tumai jezik.
U upotrebi su razliite vrste jezika.
Mainski jezici su jezici koje raunar (zajedno sa svojim operacionim sistemom) razumije bez druge programske opreme. Podaci i instrukcije mainskog jezika su binarni. Oblik podataka i instrukcija definisan je u trenutku izgradnje raunara i njegovog operacionog sistema. Programiranje na mainskom jeziku omoguavalo bi maksimalno iskoritenje mogunosti raunara, ali njihova binarna forma ini ove jezike praktino neupotrebljivim za ovjeka.
Simboliki jezici ili asembleri uvedeni su da bi se pojednostavilo programiranje za ovjeka. Simboliki jezik je uvijek vezan za neki mainski jezik. On je, po nivou instrukcija, veoma blizak mainskom jeziku za koji je vezan, ali je forma pogodnija za ovjeka operacije se predstavljaju mnemonikim skraenicama, a podaci na nain koji je blizak notacijama koje ljudi i obino koriste. U mnogim simbolikim jezicima postoji mogunost uvoenja makroinstrukcija koje dalje pojednostavljuju programiranje.
Jezici visokog nivoa oblikuju se na nain koji prije svega vodi rauna o problematici za koju su namijenjeni. Struktura ovih jezika u principu omoguava njihovo koritenje na svim raunarima. Vanu klasu tih jezika ine proceduralni ili algoritamski jezici, kao to su Pascal, FORTRAN, C itd. Standardizacija algoritamskih jezika omoguava prenosivost programske opreme s jednog raunara na drugi.
Problem orijentisani ili specijalizovani jezici namijenjeni su rjeavanju problema u specifinim oblastima primjene. Osnovne klase podataka i operacije u takvim jezicima obino su elementarne samo u okviru problematike za koju su namijenjeni, a njihova realizacija je obino relativno komplikovana. Postojanje problem orijentisanog jezika omoguava strunjacima iz te oblasti da pri koritenju raunara koncentriu napore na ono to je karakteristino za oblast primjene, bez ulaenja u detalje raunarske realizacije. Time se znatno poveava efikasnost rada, a od korisnika se trai samo elementarno poznavanje raunara koji koriste.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
2
A2. Sintaksa, Semantika, Pragmatika
Za komunikaciju izmeu ovjeka i raunara pomou nekog jezika koristi se jedan konaan skup simbola, koji se zove azbuka. Nizanjem slova dobijaju se rijei ili niske nad odgovarajuim azbukama.
Sintaksa jezika je skup pravila kojima se opisuju sve niske (rijei koje su dozvoljene u jeziku).
Azbuka mainskih jezika je binarna, tj. ima samo dva slova. Ta slova obino oznaavamo nulom i jedinicom. Dozvoljene niske su, dakle, binarne niske ija duina je vezana za duinu registra raunara obino je duina niske umnoak duine registra. Naravno, nisu sve binarne niske dio mainskog jezika dozvoljene niske zapravo su mainske instrukcije raunara. Ve kod simbolikih jezika koristi se bogatija azbuka azbuka koja ukljuuje simbole iz ASCII koda. Dozvoljene niske se dobijaju nizanjem pojedinanih instrukcija, koje se, po pravilu, sastoje iz tri dijela: obiljeja, mnemonikog koda operacije i adresnog dijela, kojim se predstavljaju adrese operanada. Za sva tri dijela
postoje pravila obiljeje je obino niska koja poinje slovom, a nastavlja se slovima i ciframa, mnemoniki kod je neka skraenica iz konanog skupa skraenica, a adresni dio ima neto sloeniju formu. Kod viih jezika, azbuka je opet skup simbola iz ASCII koda, ali su pravila za pisanje dozvoljenih niski kompleksnija (dozvoljene niske su programi,
koji se sastoje iz instrukcija, a za svaki tip instrukcije postoje posebna pravila).
Semantika jezika je skup pravila kojima se definie znaenje (ispravnih) konstrukcija jezika. Formalno, ako je L neki jezik i D skup moguih znaenja niski iz L, onda je semantika neko preslikavanje iz L u D.
Zadavanje semantike programskog jezika zasniva se na njegovoj sintaksi. Niska
nekog jezika moe se posmatrati kao konstrukcija sastavljena od jednostavnijih konstrukcija, koje se opet mogu posmatrati kao konstrukcije sastavljene od jo jednostavnijih itd. sve do konstrukcija koje se dalje ne mogu rastavljati. Semantika
pridruena nekoj niski izvodi se iz semantike konstrukcija od kojih je ona sastavljena. Semantika svojstva jednostavnijih komponenti takoe se izvode iz svojstava konstrukcija od kojih su sastavljene (to su tzv. izvedena ili sintetizovana svojstva ili
atributi), kao i od svojstava konstrukcije iji su dio (to su tzv. nasljeena svojstva). Svojstva najjednostavnijih konstrukcija izvode se iz njihove forme i svojstava
konstrukcije iji su dio. I opis sintakse i opis semantike u osnovi su rekurzivni.
U nekih programskim jezicima postoje konstrukcije koje nemaju uticaja na
semantiku programa, ve utiu na rad samog jezikog procesora. Efekti tih instrukcija ine pragmatiku jezika. Na primjer, u mnogim jezicima postoji instrukcija INCLUDE dat i sl., kojom se jeziki procesor upuuje da na mjesto instrukcije ukljui sadraj imenovane datoteke, kao da je sastavni dio programa. Analogiju za to moemo nai kod govornih jezika na interpretaciju neke reenice moe da utie kontekst u kom je reena.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
3
A3. Tipovi jezikih procesora
Precizne formulacije sintakse i semantike omoguavaju izradu programa za obradu jezika, odnosno jezikih procesora. Postoje dvije osnovne vrste jezikih prevodilaca, a to su interpretatori i prevodioci.
Kako u programu mogu postojati ciklusi, interpretator uva izvorni program u nekom unutranjem obliku. Unutranji oblik je veoma blizak izvornom obliku u tom smislu da se iz unutranjeg moe rekonstruisati izvorni oblik. Ulazni podaci D programa P su takoe ulazni podaci interpretatora. On ih uzima kada interpretira instrukcije koje to zahtijevaju. Rezultat rada interpretatora su izlazni podaci programa koji se interpretira.
S druge strane, prevodioci su jeziki procesori koji prevode program s jednog jezika na drugi. Jezik sa kojeg se prevodi je izvorni, a jezik na koji se prevodi je izlazni ili
ciljni jezik prevodioca. Izvravanje nekog programa P napisanog na izvornom programu L odvija se u dva koraka. U prvom koraku P se prevodi sa izvornog na izlazni jezik L'. U
drugom koraku dobijeni program se interpretira. Posebno znaajna vrsta prevodilaca su prevodioci iji izlazni jezik je mainski jezik raunara. U tom sluaju interpretaciju izlaznog programa (odnosno, izvravanje tog programa) obavlja sam raunar, bez dodatnog jezikog procesora.
Interpretator
Unutranji oblik
programa P
Program P na
jeziku L
Radni podaci
programa P
Izbor prve instrukcije
Analiza instrukcije
Izvravanje (obrada)
Izbor naredne instrukcije
Tebele
interpretatora
Po-
daci
D
Re-
zul-
tat
R
Kod interpretatora, jezika ana-liza (koja se, na kraju, svodi na
izvravanje programa) ostvaruje se na sljedei nain: uzima se instrukcija programa, vri se sin- taksna analiza te instrukcije, nakon
toga se vri semantika obrada te instrukcije, a zatim se prelazi na
sljedeu instrukciju, za koju se postupak ponavlja, sve dok se ne
naie na instrukciju koja trai zavretak rada. U ovom sluaju se semantika obrada izvrnih instrukcija svodi na izvravanje instrukcija. Izvorni kod (program) P
je jedan od ulaznih podataka
interpretatora.
Prevodilac
Program P na jeziku L
Program P' na jeziku L'
Podaci D
Rezultat R
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
4
Prevoenje i interpretacija su pristupi ije su prednosti i nedostaci komplementarni.
Kako interpretator analizira i izvrava jednu po jednu instrukciju, u sluaju da u programu postoje ciklusi, iste instrukcije e se analizirati vie puta, ime se gubi u brzini izvravanja. Meutim, kako su izvorni program i njegovi podaci prisutni u toku izvravanja, interpretatori po pravilu omoguavaju neposredan pristup korisnika procesu raunanja, izvornom programu i podacima tog programa. Koncept interpretatora posebno je pogodan za programe koji imaju samo jednu instrukciju, ili koji nemaju cikluse to je sluaj kod jezika u sastavu tekst editora ili jezika za kontolu paketnih obrada, kao i u sluaju da je vrijeme analize zanemarljivo u odnosu na vrijeme izvravanja, to se deava kod jezika za rad s bazama podataka i s problem orijentisanim jezicima.
Prevodilac (u sluaju da je izlazni jezik mainski) daje kao rezultat programe ije izvravanje se odvija vrlo brzo. Iako je proces prevoenja relativno spor, ako izlazni program treba da se izvrava vie puta, troak prevoenja e se isplatiti. S druge strane, prevedeni program gubi skoro svaku vezu s izvornim. Ako neto u programu treba da se izmjeni, cio postupak mora da se ponovi. Koncept prevoenja na mainski jezik nezamjenljiv je u sluaju programa s intenzivnom numerikom obradom, kada je vano da se operacije izvravaju maksimalnom brzinom koju raunar omoguava.
A4. Struktura jezikih procesora
Ovdje emo govoriti o prevodiocima iji izlazni jezik je mainski. Opta struktura takvih prevodilaca prikazana je na slici:
Izvorni program P
na jeziku L
Leksika analiza
Sintaksna analiza
Semantika obrada
Generisanje meukoda
Globalna optimizacija
Generisanje koda
Lokalna optimizacija
Izlazni program P'
na jeziku L'
Tabele
Leksiki analizator. Ve smo naveli da se program na nekom jeziku moe razloiti na jednostavnije komponente, a one na jo jednostavnije itd. Obrada jednostavnih
konstrukcija, koje se nazivaju leksikim klasama, moe se obaviti efikasnim algoritmima koji se zasnivaju na konceptu
konanih automata. Leksiki analizator ita izvorni program, izdvaja u njemu leksike klase i zamjenjuje ih leksemama. Svaka
leksema se sastoji iz dva dijela: jedan dio
predstavlja vrstu leksike klase, a drugi sadri dodatne podatke o konstrukciji na koju se odnosi. Obino se ti podaci uvaju u nekim tabelama i tada je druga
komponenta lekseme pokaziva na tabelu koja odgovara leksikoj klasi. Primjeri leksikih klasa u programskim jezicima su konstante, identifikatori, kljune rijei,
simboli operatora, nizovi blankova itd.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
5
Sintaksni analizator. Zadatak sintaksnog analizatora je da konstruie sintaksnu strukturu izvornog programa. Ulazni podatak sintaksnog analizatora je niz leksema koji
se dobija iz leksikog analizatora. U toku sintaksne analize odreuje se sintaksna struktura izvornog programa. Ako tu strukturu nije mogue odrediti, to znai da izvorni program nije korektan. Tada sintaksni analizator daje poruku o sintaksnoj greci. Izlaz iz sintaksnog analizatora obino je struktura izvornog programa, predstavljena pomou drveta ili na neki slian nain. Listovi drveta su lekseme, a unutranji vorovi odgovaraju sintaksnim klasama.
Semantika obrada. Na osnovu sintaksne strukture mogu se izvriti semantike obrade. Cilj semantikih obrada je da se uz svaki vor drveta sintakse vezuju odreeni podaci koje smo nazvali atributima. Za izraunavanje nekog izvedenog atributa koriste se atributi donjih vorova. Slino, za izraunavanje nasljeenih atributa potrebni su atributi gornjih vorova. Na prvi pogled moe izgledati dovoljno prei po drvetu dva puta jednom odozdo navie da bi se izraunali izvedeni atributi i drugi put odozgo nanie da bi se izraunali nasljeeni atributi. To je tano u mnogim sluajevima, ali u optem sluaju ne. Naime, neki izvedeni atributi mogu zavisiti od atributa donjih vorova koji su nasljeeni itd. U tom sluaju kretanje po drvetu navie i nanie moe da se obavlja vie puta.
Generisanje meukoda. Moe se rei da je rezultat semantike obrade drvo sintakse okieno atributima na drvetu se moe nai sve to je potrebno da se generie program na nekom meujeziku. Oblik meujezika uvijek zavisi od mainskog jezika i bira se tako da dvije faze prevoenja koje slijede mogu da se izvre na najefikasniji nain.
Globalna optimizacija. Cilj globalne optimizacije je da se program na
meujeziku, dobijen iz prethodne faze, transfornie u efukasniji program na istom jeziku. Za tu transformaciju potrebna je detaljna analiza toka izvravanja programa i meuzavisnosti podataka. Nakon takve analize moe se utvrditi da se neka izraunavanja mogu izvui ispred ciklusa ili da neki izrazi imaju zajednike podizraze.
Generisanje koda. U ovoj fazi, program na meujeziku transformie se u program na izlaznom jeziku.
Lokalna optimizacija. Mada je zavretkom prethodne faze dobijen program na izlaznom jeziku, on se obino moe podvrgnuti jo jednoj transformaciji. Ona zavisi od izlaznog jezika, tj. od mainskog jezika raunara. Sastoji se od zamjene nekih instrukcija efikasnijim, eventualnog izbacivanja suvinih instrukcija itd. Za svaku izmjenu analizira se jedna ili vie uzastopnih instrukcija, po emu je ova vrsta optimizacije dobila ime.
to se tie strukture interpretatora, njihova struktura se moe naslutiti iz slike kojom su prikazani. Analiza instrukcije zapravo sadri i leksiku i sintaksnu analizu. Semantika obrada kod interpretatora svodi se na izvravanje pojedinanih instrukcija. Jasno, potreba za optimizacijom ne postoji, jer bi uteda u brzini izvravanja bila zanemarljiva u odnosu na vrijeme koje je potrebno da se izvri optimizacija.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
6
A5. Analogija s prevoenjem govornih jezika
Faze rada jezikog analizatora, koje smo upravo razmatrali, mogu se usporediti sa fazama prevoenja jedne reenice s jednog govornog jezika na drugi, recimo sa srpskog na engleski. Naravno da veine ovih faza u toku prevoenja nismo svjesni one se odvijaju automatski.
Pretpostavimo da imamo neku reenicu na srpskom jeziku. Ona se sastoji iz rijei, pa moemo rei da su u tom sluaju rijei njeni sastavni dijelovi, njene jednostavnije komponente, koje mogu da pripadaju razliitim leksikim klasama (u govornom jeziku te klase bi bile imenice, glagoli, prijedlozi, veznici, zamjenice...).
Na poetku, u okviru leksike analize, analiziramo rije po rije, pa rijeima, dodjeljujemo lekseme, koje se sastoje iz dva dijela prvi nas obavjetava o leksikoj klasi kojoj rije pripada, a drugi dio prenosi samu rije. U ovoj fazi takoe kontroliemo da li svaka od tih rijei odgovara pravopisnim pravilima srpskog jezika tj. da li su to sve rijei koje pripadaju srpskom jeziku - ako neka nije, dajemo poruku o leksikoj greci i prekidamo analizu/prevoenje.
Sada je na redu semantika obrada. Analiziramo reenicu, koja je sada pretvorena u skup leksema. Prvi dio lekseme obavjetava nas o vrsti leksike klase, a drugi dio nas obavjetava o rijei. Najprije moramo izanalizirati da li ta reenica prati sva gramatika pravila srpskog jezika, odnosno, da li je raspored rijei u reenici odgovarajui. Ako nije, izdajemo poruku o sintaksnoj greci i prekidamo analizu/prevoenje. Nakon toga, ako je reenica korektna, njenim leksemama kaimo atribute, koji u osnovi predstavljaju misaone pojmove koji se vezuju s datim rijeima.
Generisanje meukoda svodilo bi se na formiranje misaone predstave o znaenju cijele reenice, odnosno niza pojmova koji odgovaraju rijeima iz polazne reenice.
Sada vrimo globalnu optimizaciju, odnosno prilagoavamo taj niz pojmova uobiajenoj strukturi reenice na izlaznom jeziku, odnosno, u ovom sluaju, engleskom.
Generisanje koda svodi se na nalaenje engleskih rijei koje odgovaraju, redom, svakom od pojmova u nizu.
Posljednja faza je globalna optimizacija reenicu na engleskom uljepavamo, traimo dijelove koji bi se mogli izraziti nekim idiomom, biramo ljepe sinonime, itd.
A6. Regularni izrazi
Def1. Azbuka je konaan neprazan skup. Elementi azbuke su slova.
Def2. Rije ili niska nad azbukom je konaan skup slova iz . Prazna rije ili prazna niska je rije bez slova, koja se obiljeava simbolom .
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
7
Duina rijei w je broj slova rijei w i oznaava se sa |w|. Definiemo ={ }.
Skup rijei nad azbukom je * = , gdje n=0....
Jezik nad azbukom je svaki skup L *.
Def3. Neka su u i v rijei nad azbukom i neka je |u| = n i |v| = m. Proizvod, konkatenacija ili dopisivanje rijei u i v, u oznaci uv ili uv, je rije w takva da |w| = n+m i i-to slovo rijei w jednako je i-tom slovu rijei rijei u, kad je in, odnosno (i-n)-tom slovu rijei v kad je i>n.
Iz gornje definicije slijedi da je za svaku rije u*, u = u =u.Specijalno, = .
Proizvod rijei nad azbukom je takoe rije nad azbukom . Dopisivanje je asocijativno.
Kako su jezici definisani kao skupovi, nad njima su definisane sve skupovne operacije i
relacije. Osim njih emo definisati operacije dopisivanja i iteracije.
Def4. Neka su L, L1 i L2 jezici nad azbukom . 1. Proizvod ili konkatenacija jezika L1 i L2 u oznaci L1L2 ili L1L2 je jezik
L1L2= {uv | uL1 i vL2}. 2. Iteracija jezika L je jezik
L* = L, gdje n=0.... 3. Pozitivna iteracija jezika L je jezik
L = L, gdje n=1....
Def5. Neka je azbuka i neka je N pomona azbuka (N=), ijim slovima su pridrueni neki jezici nad azbukom . Izraz nad azbukama i N i jezik predstavljen izrazom uvode se rekurzivno na sljedei nain:
(a) je izraz koji predstavlja jezik ; (b) je izraz koji predstavlja jezik {}.
(c) Za svako a, a je izraz koji predstavlja jezik {a};
(d) Za svako AN, A je izraz koji predstavlja odgovarajui jezik; (e) Ako su E1 i E2 izrazi koji predstavljaju redom jezike L1 i L2, onda je
(f1)(E1)(E2) izraz koji predstavlja jezik L1L2.
(f2)E1+E2 je izraz koji predstavlja jezik L1L2. (f) Ako je E izraz koji predstavlja jezik L, tada je
(f1)E* izraz koji predstavlja jezik L*, i
(f2)E izraz koji predstavlja jezik L.
Def6. Izraz nad azbukama i N koji sadri samo slova iz je regularni izraz nad azbukom .
Primjeri:
Posmatraemo azbuku {a,b,c} a + b je regularni izraz koji odgovara jeziku {a}{b}, tj. jeziku koji sadri rijei a i b
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
8
a b ili ab odgovara jeziku {ab}, to je jezik koji sadri samo rije ab. Regularni operator (koji se izostavlja u pisanju) naziva se konkatenacija (nadovezivanje). (a+b)b je regularni izraz koji odgovara jeziku koji sadri dvije rijei, ab i bb. (a+b)(a+b) je regularni izraz koji odgovara jeziku {aa,ab,ba,bb}
a* je regularni izraz koji odgovara jeziku ije rijei se sastoje od slova a, ponovljenog proizvoljan broj puta, ukljuujui i nulu, tj. jeziku {,a,aa,aaa,aaaa,aaaaa, ...} (a + b)* odgovara jeziku {,a,b,aa,ab,ba,bb,aaa, ...}- skup svih rijei nad azbukom {a,b} ako je regularni izraz, * = + + + +... tj. rije pripada * ako se moe sastaviti od rijei iz , tako
ako je ={aa,bb}, onda aabbaa*, a aabaa*.
= { + + +...} (* bez prazne rijei, odnosno ponavljanje proizvoljan broj
puta, ne ukljuujui nulu). - plusi
1. Napisati regularni izraz koji opisuje jezik ije sve rijei poinju sa a, a iza toga sadre proizvoljno mnogo slova bi c u proizvoljnom redoslijedu, npr. abbc, a, acb, ...
a (b + c)*
2. Napisati regularni izraz koji opisuje jezik ije sve rijei poinju proizvoljno dugim nizom slova a i b u proizvoljnom redoslijedu, a zavravaju se slovom c, npr. abbc, c, bc...
(a + b)* c
3. ta predstavlja regularni izraz (a* + b*) c? To je regularni izraz koji opisuje jezik ije rijei poinju nizom slova a ili nizom
slova b, a zavravaju se slovom c. Npr. aaaac (a* + b*) c, bbbbc (a* + b*) c, ali
abc (a* + b*) c.
4. Napisati regularni izraz koji opisuje jezik ije sve rijei sadre proizvoljne kombinacije slova a, b i c proizvoljne duine, ali uz uslov da slovo a uvijek dolazi u paru, npr bcaacaa pripada jeziku, ali abc ne pripada.
(aa + b + c)*
5. Neka je ={,a,b} azbuka, a jezik je jezik literala, odnosno niski slova iz azbuke ogranienih navodnicima, pri emu:
a) unutar rijei nema apostrofa, (a + b)* b) unutar rijei apostrofi moraju biti udvojeni. (a + b + )*
6. Uvodimo promjenljive, tj. Oznake koje mogu da predstavljaju vie razliitih slova azbuke, recimo neka X predstavlja sva slova engleskog alfabeta, onda moemo pisati samo X umjesto da piemo a+b+c+d+e+f+...+x+y+z.
Na primjer, ako u prethodnom primjeru kaemo da Y predstavlja sva slova azbuke osim navodnika, onda regularni izraz piemo kao (Y+)*.
Takoe, ako elimo opisati sve rijei oblika P......I, pisaemo PX*I.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
9
Osobine regularnih izraza
Neka su , i regularni izrazi. Tada vai: 1. + = + , + predstavlja uniju
2. * = , je prazna rije 3. + ( + ) = ( + ) + 4. ( )= ( ) 5. ( + )= + 6. ( + ) = + 7. = =
8. = =
9. * = * = 10. (*)*= * 11. + =
12. + =
13. + = , pa + = *
Primjeri
1. Dokazati ekvivalentnost sljedeih izraza: a + a + , a*.
a + a + = a + , prema pravilu 1, a + = a , prema pravilu 14 (jer je a a , pa
a + a= a)
2. Dokazati ekvivalentnost sljedeih izraza: a* (a + b) b* , a b* + a* b
a* (a + b) b*=(a* a + a* b) b*=(a + a* b) b*=a b* + a* b b* = a b* + a* b
3. Dokazati ekvivalentnost sljedeih izraza: (a + b)* a*, (a + b)* Ovdje ne moemo koristiti distributivnost, pa, da bismo dokazali da ta dva regularna izraza definiu isti jezik, pokazaemo najprije da je jezik koji definie izraz s lijeve strane podskup jezika koji definie izraz s desne strane, a zatim i suprotno, da je jezik koji definie izraz s desne strane podskup jezika koji definie izraz s lijeve strane.
Najprije primijetimo da ako vai , vai i , * *, i + +
(tj. relacija se slae sa svim regularnim operacijama).
() a* (a + b)* (a + b)* a* (a + b)* (a + b)* a*
() (a + b)* je skup svih rijei nad azbukom a,b, a (a + b)* a* je skup nekih rijei nad tom istom azbukom, pa mora biti podskup skupa svih rijei.
4. Dokazati ekvivalentnost sljedeih izraza: (a + b)*, (a* + b)*
() kao u prethodnom primjeru
() a a* a + b a* + b (a + b)* (a* + b)*
5. Dokazati ekvivalentnost sljedeih izraza: (a + b), (a + b)
() a a a + b a + b (a + b) (a + b)
() Skup svih rijei izuzev je nadskup skupa nekih rijei koji takoe ne ukljuuje .
6. Dokazati da je (abc)+=a(bca)*bc Dokazuje se indukcijom.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
10
B. GENERATOR LEKSIKIH ANALIZATORA FLEX
FLEX je alat za generisanje skenera (leksikih analizatora), tj. programa koji prepoznaju niske koje odgovaraju odreenim leksikim klasama u tekstu. FLEX oitava date input datoteke (ulazne datoteke, specifikacije, opise) ili standardni ulaz, ako se pri
pozivu ne navede ime datoteke, i odatle uzima opis skenera kojeg treba da generie. Opis je u obliku parova koje ine regularni izrazi i C kod, pri emu se ti parovi nazivaju pravila. FLEX kao output generie C datoteku LEXYY.C koja definie rutinu yylex(). Ta datoteka se kompajlira i linkuje, ime se dobija izvrni kod. Kad se izvrna datoteka pokrene ona analizira input koji joj se daje i trai pojave niski koje su primjerci leksikih klasa opisanih pomou regularnih izraza. Kad god se naie na nisku koja odgovara regularnom izrazu izvrava se odgovarajui C kod.
B1. Format ulazne datoteke
Ulazna datoteka za FLEX sastoji se od tri sekcije, koje su odvojene redom koji
sadrti samo %%.
definicije
%%
pravila
%%
korisnicki kod
Sekcija definicija sadri deklaracije jednostavnih definicija imena koja slue za pojednostavljivanje specifikacije skenera i deklaracije poetnih uslova (neemo ih obraivati na vjebama, pogledati Flex User Manual). Definicije imena imaju sljedei oblik:
ime definicija
Ime je rije koja poinje slovom ili podcrtom, to prati 0 ili vie slova, cifara, podcrta ili crtica. Definicija poinje na prvom karakteru koji nije blanko nakon imena i zavrava se krajem linije. Definiciju kasnije moemo pozvati piui samo ime, to se onda automatski proiruje na (definicija). Na primjer, cifra [0-9]
identifikator [a-z] [a-z0-9]*
definie leksiku klasu s imenom cifra kao regularni izraz kojem odgovara jedna cifra, koja moe biti u intervalu od 0 do 9, a identifikator je leksika klasa kojoj odgovara regularni izraz koji poinje slovom iza koga slijedi nula ili vie slova ili cifara. Vidjeti primjer L3.
Ako kasnije napiemo {cifra}+.{cifra}*
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
11
to je isto kao
[0-9]+.([0-9])*
i odgovara jednoj ili vie cifara iza koje slijedi taka, iza koje slijedi nula ili vie cifara tj. to predstavlja dfefiniciju realnih brojeva, napravljenu pomou jednostavnijih definicija.
Sekcija pravila inputa za FLEX sadri niz pravila oblika uzorak akcija
gdje uzorak ne smije biti uvuen, a akcija mora poeti na istoj liniji. U daljem tekstu emo detaljno opisati uzorke i pravila.
Sekcija korisnikog koda se jednostavno doslovno prekopira u LEXYY.C. Ona se koristi za pratee rutine koje pozivaju skener ili koje poziva skener. Ova sekcija je opciona ako nije ukljuena u opis ne mora se pisati ni drugi red %%.
U sekciji definicija, svaki uvueni tekst ili tekst okruen sa %{ i %} takoe se doslovno kopira u rezultat, odnosno u LEXYY.C, pri emu se izostave znakovi %{ i %}. Ti znakovi se moraju pojaviti u zasebnim linijama i ne smiju biti uvueni.
U sekciji pravila, svaki uvueni tekst ili tekst okruen sa %{ i %} koji se pojavljuje prije prvog pravila moe se koristiti za deklaraciju promjenljivih koje su lokalne za skenersku rutinu i, nakon toga, za unos koda koji e se izvravati svaki put kad se pokrene skenerska rutina.
U sekciji definicija, neuvueni komentari (ogranieni sa /* i */) takoe se doslovno kopiraju u rezultujuu C datoteku.
B2. Uzorci
Uzorci u inputu piu se uz koritenje proirenog skupa regularnih izraza. FLEX notacija se razlikuje od regularnih izraza iz dva osnovna razloga:
1. Omoguava efikasniju reprezentaciju nekih tipova simbola, u smislu broja karaktera koji se koriste;
2. Proiruje mo notacije regularnih izraza u odreenim, relativno ogranienim, sluajevima.
Na primjer, regularni izraz ne moe predstaviti pojam svaki karakter azbuke sem jednog, bez pisanja svih drugih karaktera azbuke. Kod FLEX notacije, meutim, napisaemo samo taj karakter, uz znak ^, koji oznaava komplement.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
12
U FLEX notaciji koriste se sljedei uzorci:
x odgovara karakteru/znaku x
. svaki karakter osim oznake za novi red
[xyz] klasa karaktera; u ovom sluaju odgovara jednom karakteru, koji moe biti x ili y ili z
[abj-oZ] klasa karaktera sa opsegom; u ovom sluaju odgovara jednom karakteru, koji moe biti a ili b ili bilo koje slovo izmeu j i o, ili Z
[^A-Z] negirana klasa karaktera tj. svaki karakter sem onih u klasi; u ovom sluaju svaki karakter sem velikog slova
[^A-Z\n] svaki karakter sem velikog slova ili oznake za novi red
r* nula ili vie r, pri emu je r neki regularni izraz u FLEX notaciji r+ jedno ili vie r r? nula ili jedno r (opciono r)
r{2,5} od 2 do 5 r
r{2,} dva ili vie r r{4} tano etiri r {ime} proirenje definicije imena [xyz]\foo doslovno, niska [xyz]\foo \X ako je X a, b, f, n, r, t, ili v, onda C interpretacija \X
\123 karakter s oktalnom vrijednou 123 \x2a karakter s heksadecimalnom vrijednou 2a (r) odgovara r; zagrade se koriste za eksplicitno odreenje prioriteta rs regularni izraz r praen regularnim izrazom s, naziva se konkatenacija r|s ili r ili s (ekskluzivno ili)
^r regularni izraz r, ali samo na poetku linije r$ regularni izraz r, ali samo na kraju linije.
Regularni izrazi koji su gore navedeni grupiu se prema prioritetu i navedeni su prema stepenu prioriteta oni na vrhu liste imaju najvii prioritet, oni na dnu imaju najnii. Tako, npr
foo|bar*
je isto to i (foo)|(ba(r*))
jer operator * ima vii prioritet od konkatenacije, a konkatenacija ima vii prioritet od alternacije |. Prema tome, taj uzorak odgovara ili niski foo ili niski ba koju prati nula
ili vie r. Ako elimo uzorak koji odgovara niski foo ili nula ili vie niski bar, koristiemo foo|(bar)*
a ako elimo uzorak koji odgovara nula ili vie niski foo ili niski bar, koristiemo
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
13
(foo|bar)*
B3. Kako se procesira input
Kad se pokrene generisani skener, on analizira svoj input traei niske koje odgovaraju nekom od uzoraka koji se nalaze u njegovom opisu. Ako nae vie od jednog uzorka koji odgovaraju nisci, odluuje se za onaj uzorak kojemu odgovara najdui tekst. Ako nae dva ili vie uzoraka koji odgovaraju tekstu iste duine, odluuje se za onaj uzorak koji se nalazi prvi na listi uzoraka input datoteke (opisa) za FLEX. Kada se odredi
koji uzorak odgovara ulaznoj nisci, tekst koji odgovara uzorku (koji nazivamo token ili
leksema) postaje dostupan u globalnoj promjenljivoj yytext, koja je po tipu niz karaktera,
odnosno pokaziva na karakter. Onda se izvrava akcija koja je dodijeljena uzorku koji je prepoznat (detaljnije o akcijama u nastavku teksta), a zatim se preostali dio inputa skenira
u potrazi za novim poklapanjem dijela inputa i nekog uzorka. Ako se ne nae nijedno takvo poklapanje, izvrava se default pravilo: naredni karakter inputa smatra se prepoznatim i kopira se na standardni izlaz. Prema tome, najjednostavniji input za FLEX
ima sljedei oblik: %%
a to generie skener koji jednostavno kopira svoj input na standardni izlaz, karakter po karakter.
B4. Akcije
Svaki uzorak u pravilu ima odgovarajuu akciju, koja moe biti bilo koji izraz u C-u. Uzorak se zavrava na prvoj bjelini a ostatak pravila predstavlja akciju. Ako je akcija prazna, onda kad se odgovarajui uzorak prepozna, ulazni token jednostavno odbacuje.
PrimjerL1. Na primjer, evo specifikacije za program koji brie sve pojave niske brisi me iz svog inputa, a sve ostale karaktere kopira sa inputa na izlaz poto e oni biti prepoznati po default pravilu.
%%
brisi me
Ova specifikacija nalazi se u datoteci PR1L.L. Sve specifikacije za FLEX imaju
ekstenziju .L. Da bismo je mogli izvriti, dodaemo joj poziv funkcije main() u skeciju korisnikog koda. U ovom, najjednostavnijem, sluaju funkcija main e samo pozivati skenersku rutinu yylex(). Dakle, u datoteci imamo sljedee %%
brisi me
%%
main()
{ yylex();
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
14
}
Skener (C-ovski program za skeniranje inputa koji se unosi sa standardnog ulaza)
emo dobiti tako to emo fleksovati ovu datoteku, odnosno ovaj opis za FLEX. U direktorijumu FLEX, u kojem se nalazi program FLEX, izvriemo sljedeu naredbu: C:\FLEX> flex pr1l.l
Ako je opis za FLEX u redu, ne bismo trebali dobiti nikakvu poruku. Time smo u
istom direktorijumu formirali C-ovsku datoteku LEXYY.C. Sada emo pokrenuti skener koji se u njoj nalazi. Najprije emo prei u direktorijum tc, i u njemu pokrenuti kompajler za turbo C.
C:\FLEX> cd\tc
C:\tc> tc
Kad smo otvorili kompajler za tc, sa Alt+F izaberemo opciju File, a u opadajuem meniju koji nam se otvori izabraemo Load, a zatim upisati C:\FLEX\LEXYY.C
Nakon to pritisnemo Enter, otvara se datoteka koja sadri na skener. Nju emo kompajlirati sa Alt+C. Pri kompajliranju se pojavljuje izvjetaj, koji bi, ako je sve do sada korektno uraeno, trebao sadravati samo upozorenja (warnings). Ona se uglavnom odnose na dijelove koda koji nee biti upotrebljeni jasno je da su nepotrebni dijelovi koda neto to se ne moe izbjei kad se kod generie automatski. Sada pokrenemo skener sa Alt+R. Unesemo nisku, recimo
treuuqetruztqerztbrisi mesfgjgjfhbrisi mez
i pritisnemo Enter. Ispie se naredni red treuuqetruztqerztsfgjgjfhz
dakle, sve pojave niske brisi me su eliminisane. Ako elimo ponovo isprobati skener, pritisnemo enter i opet ukucavamo nisku. Ako pritisnemo Ctrl+Z pa enter, izvravanje programa bie prekinuto.
PrimjerL2. Evo programa koji kompresuje viestruke blankove i tabulatore na jedan blanko, i odbacuje bjeline koje se nalaze na kraju linije:
%%
[ \t]+ putchar( );
[ \t]+$ /* ignorisi ovaj token*/
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
15
Program se izvrava analogno prethodnom.
Ako akcija sadri {, onda traje do se ne nae odgovarajue }. Akcija moe da se protegne preko vie redova. Akcija koja se sastoji samo od vertikalne crte | znai isto kao i akcija za sljedee pravilo. Akcije mogu da sadre razliite C izraze, ukljuujui return izraze za vraanje vrijednosti rutini koja pozove yylex(). Svaki put kad se pozove yylex(), skener nastavlja da procesira tokene na mjestu gdje je posljednji put prekinuo,
sve dok ne doe do kraja datoteke ili sve dok ne izvri return izraz. Akcijama nije dozvoljeno da modifikuju yytext.
PrimjerL3. Input za FLEX koji prepoznaje identifikatore nalazi se u datoteci PR3L.L:
slovo [a-z]
cifra [0-9]
ident {slovo}({slovo}|{cifra})*
%%
{ident} {printf("Identifikator je prepoznat\n");}
%%
main()
{yylex();
}
Ako ukucate tekst koji sadri neke identifikatore i neke niske koje nisu identifikatori (brojeve, interpunkcijske znake), a zatim enter, skener e umjesto identifikatora ispisati poruku identifikator je prepoznat, a niske koje nisu identifikatori
e prekopirati.
PrimjerL4. Neto interesantniji analizator bio bi proizveden iz sljedeeg opisa za FLEX, koji se nalazi u datoteci PR4L.L:
slovo [a-z]
cifra [0-9]
identifikator {slovo}({slovo}|{cifra})*
%%
{identifikator} {printf(" identifikator %s duzine %d\n",
yytext, yyleng);}
%%
main()
{yylex();
}
jer koristi FLEX promjenljive yytext, ija vrijednost je tekstualna reprezentacija posljednjeg simbola (tokena, lekseme) koji je prepoznat i yyleng, koja uva duinu tog posljedenjeg oitanog simbola. Ako ukucate tekst koji sadri neke identifikatore i neke niske koje nisu identifikatori (brojeve, interpunkcijske znake), a zatim Enter, skener e
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
16
umjesto identifikatora ispisati poruku identifikator (taj i taj) duzine (te i te), a niske
koje nisu identifikatori e prekopirati. Npr. za ulaz qrwzetr6726hjdhd..z
izlaz e biti identifikator qrwzetr duzine 7
6726 identifikator hjdhd duzine 5
.. identifikator z duzine 1
PrimjerL5. FLEX input za proizvodnju analizatora koji identifikuje i tampa pojave realnih brojeva definisanih regularnim izrazom (+|-| ) cifra* . cifra cifra* nalazi se u
datoteci PR5L.L:
cifra [0-9]
realni [+\-]?{cifra}*\.{cifra}+
%%
{realni} {printf("realni broj %s\n", yytext);}
%%
main()
{yylex();
}
Upitnik iza karakterske klase koja sadri + i znai da je znak opcion. U karakterskoj klasi se ispred minusa nalazi kosa crta, da bi se ukazalo da se misli na
minus, doslovno, a ne na znaenje koje minus inae ima u opisima za FLEX interval karaktera. Iz istog razloga imamo kosu crtu i ispred take dakle, misli se na taku, doslovno, a ne na svaki karakter osim oznake za novi red. Svi karakteri inputa koji se takoe koriste i kao dio notacije moraju se pisati sa \ karakterom ispred sebe (ili izmeu dvostrukih navodnika) svugdje gdje moe doi do dvosmislenosti. Npr, kod prve pojave + to ne mora da se radi, jer se plus ne moe protumaiti drugaije nema dvosmislenosti.
Realni brojevi se, dakle, definiu tako da na poetku imaju opcioni znak, iza kojeg slijedi nula ili vie cifara, pa taka, pa jedna ili vie cifara. Za vjebu, pokuajte da ubacite i eksponencijalni dio slovo e ili E, iza kojeg slijedi opcioni znak, pa jedna ili vie cifara.
PrimjerL6. Proizvodi analizator za prepoznavanje (pojednostavljenih) konstanti,
identifikatora, niski (stringova) i odreenih kljunih (rezervisanih) rijei jezika u nekom programu na programskom jeziku Pascal. Druge rijei bie prepoznate kao identifikatori. Primjer se nalazi u datoteci PR6L.L
cifra [0-9]
intconst [+\-]?{cifra}+
realconst [+\-]?{cifra}+\.{cifra}+(e[+\-]?{cifra}+)?
slovo [a-zA-Z]
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
17
identifikator {slovo}({slovo}|{cifra})*
bjelina [\t\n]
stringk [^']
string '{stringk}+'
ostalik [^0-9a-zA-Z+\-'\t\n]
ostalisimb {ostalik}+
%%
program printf ("prepoznat program \n");
var printf ("prepoznat var \n");
begin printf ("prepoznat begin \n");
for printf ("prepoznat for \n");
to printf ("prepoznat to \n");
do printf ("prepoznat do \n");
end printf ("prepoznat end \n");
{intconst} printf ("integer %s\n ", yytext);
{realconst} printf ("realni broj %s\n ", yytext);
{string} printf ("string %s\n ", yytext);
{identifikator} printf ("identifikator %s\n ", yytext);
{bjelina} ; /* bez akcije */
{ostalisimb} ; /* bez akcije */
%%
main()
{yylex();
}
Primijetite da bjelina moe biti bilo kakva sekvenca blankova, znakova za novi red i tabulatora i da se \n i \t koriste da otznae karaktere koji oznaavaju novi red i tabulator na slian nain na koji se koriste u printf izrazima u C-u. Ipak, simbol \ se koristi u suprotnom smislu od onog kojeg smo vidjeli u prathodnom primjeru da oznai notacijsko koritenje t i n, a ne da predstavlja same karaktere. U praksi, nekonzistentno koritenje karaktera \ ne dovodi do konfuzije.
String se definie kao svaka sekvenca karaktera koja ne ukljuuje navodnik, a ograniena je navodnicima. Ostalisimb je sekvenca sastavljena od karaktera koji nisu ve pomenuti. Primijetite da je prazna akcija povezana sa bjelinama i ostalim simbolima. To
je zato to, ako ti simboli ne budu prepoznati u ostatku opisa za FLEX, bie tampani na kanal standardnog izlaza, tj. meu ostalim outputom, koji nas obavjetava o prepoznatim leksemama, to bi dalo neuredan rezultat.
Pretpostavimo da analizatoru koji proizvede FLEX damo sljedei Pascal program kao input
program double (input, output);
var i: 1..10;
begin
writeln ('number':10, 'timestwo':10);
for i:= 1 to 10 do
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
18
writeln (i:10, i*i:10);
writeln
end.
Output bi bio sljedei prepoznat program
identifikator double
identifikator input
identifikator output
prepoznat var
identifikator i
integer 1
integer 10
prepoznat begin
identifikator writeln
string 'number'
integer 10
itd.
Primijetite da su kljune rijei prepoznate kao kljune rijei, a ne kao identifikatori. To je zato to FLEX prihvata prvo poklapanje u sekciji pravila i prema tome je vano da su kljune rijei definisane na poetku sekcije pravila. Takoe primijetite da je double pravilno prepoznat kao identifikator, a prva dva slova do nisu prepoznata kao kljuna rije do. To je zato to FLEX uvijek trai najdue poklapanje i samo ako su oba poklapanja iste duine uzima prvo.
PrimjerL7. Ovaj primjer ilustruje kako C kod moe biti integrisan u analizator koji proizvodi FLEX. Primjer broji linije i karaktere inputa i tampa izvjetaj o tome. Nalazi se u datoteci PR7L.L:
%{
int brojlinija=0, brojkaraktera=0;
%}
%%
\n ++brojlinija; ++brojkaraktera;
. ++brojkaraktera;
%%
main()
{yylex();
printf("broj linija je %d, broj karaktera je %d\n",
brojlinija, brojkaraktera);
}
Kada ga budete izvravali, ovaj primjer nee dati rezultat nakon to pritisnete enter, ve input moete unositi u vie redova, a zavravate ga kad unesete Ctrl+Z pa
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
19
Enter. Nakon toga, sa Alt+F i OS Shell vidite rezultat izvjetaj o tome koliko linija i koliko karaktera je bilo u inputu.
Kao i funkcije koje se pojavljuju u treem dijelu inputa, deklaracije ili drugi kod mogu se pojavljivati u prvom dijelu, sve dok su uvuene ili ograniene znacima %{ i %}. Svaki takav kod se samo prekopira u C program koji FLEX proizvede. Vjerovatno je
bolje koristiti % i % za ograniavanje takvog koda umjesto oslanjanja na uvlaenje, ija svrha moda nee biti u toj mjeri jasna. Ako se ti simboli koriste, oni se moraju pojaviti na poetku linije. Mora se zapamtiti da e FLEX ignorisati sve uvuene linije i da e one biti prekopirane u C program bez izmjena.
Primijetite regularni izraz koji prihvata sve (oznaen takom) koji e, u ovom sluaju, biti izjednaen sa svakim karakterom osim oznake za novi red. Uopte gledano, on e biti izjednaen sa svakim prethodno definisanim simbolom koji ve nije prepoznat. PrimjerL8. Opis za FLEX dat u datoteci PR8L.L slui za izraunavanje maksimalne i prosjene duine rijei u datom tekstu (inputu). Zasniva se na analizatoru iz prethodnog primjera:
%{
int slova=0, rijeci=0, duz=0, duzina;
double prosjek;
%}
rijec [a-zA-Z]+
blanko [ \n]
bjelina {blanko}+
%%
{rijec} {++rijeci; duzina=yyleng;
slova=slova+duzina;
if (duzina>duz) duz=duzina;}
{bjelina} ;
. ;
%%
main()
{yylex();
printf("maximalna duzina rijeci=%d", duz);
prosjek=slova/rijeci;
printf("prosjecna duzina rijeci je %f\n", prosjek);
}
Primijetite koritenje promjenljive yyleng koja daje duinu posljednjeg oitanog simbola.
PrimjerL9. Da se od analizatora iz prethodnog primjera trailo da zavri s radom na kraju prve reenice, pri emu se reenicom smatra niz karaktera koji se zavrava takom, uzvinikom ili upitnikom, input za FLEX mogao je biti sljedei, dat u datoteci PR9L.L: %{
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
20
int slova=0, rijeci=0, duz=0, duzina;
double prosjek;
%}
rijec [a-zA-Z]+
blanko [ \n]
bjelina {blanko}+
eor [!?.]
%%
{rijec} {++rijeci; duzina=yyleng;
slova=slova+duzina;
if (duzina>duz) duz=duzina;}
{eor} yyterminate();
bjelina ;
. ;
%%
main()
{yylex();
prosjek=slova/rijeci;
printf("maximalna duzina rijeci=%d, prosjecna duzina
rijeci= %f\n", duz, prosjek);
}
Efekt poziva rutine yyterminate() je zaustavljanje analize.
PrimjerL10. Jo jedan jednostavan primjer koritenja FLEX-a je proizvodnja alata za dodavanje broja linije izvrnom kodu. Rezmotrite sljedei opis za FLEX, dat u datoteci PR10L.L:
%{
int brojlinije=0;
%}
linija [^\n]*\n
%%
{linija} {printf("%d %s", brojlinije++, yytext);}
%%
main()
{yylex();
}
Output e biti izvorni kod oitamn sa svakom linijom, ukljuujui prazne linije, sa prefiksom i obliku broja linije.
Moda iznenaujue, prepoznavanje komentara u jeziku (to je uvijek zadatak leksike analize) nije, u optem sluaju, jednostavno. Problem proistie iz karaktera koji se koriste za ograniavanje koemntara i koji se, prema tome, ne smiju pojavljivati unutar komantara. Naravno, mogue je definisati regularni izraz za komentar (u C-u, npr.) ali je
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
21
to teko, kao to emo vidjeti i, prema tome, podlono grekama. Bolja solucija je jednostavno napisati dio koda za prepoznavanje poetka komentara i dalje, za skidanje svih karaktera unutar komentara, dok se ne doe do kraja komentara. Naizad, sadraj komentara nije uopte zanimljiv sa gledita kompajlera. Sljedei fragment opisa za FLEX prikazuje jedan metod obrade komentara u C-u.
%%
/* { char in;
for ( ; ; )
{
while ((in = getchar())!=*);
/*preskace sve karaktere koji nisu
zvjezdice*/
while ((in = getchar()) ==*);
/*preskace sve zvjezdice*/
if ((in == /) break; /*kraj komentara*/
}
}
Pazi se da se ignoriu zvjezdice koje nisu praene sa / i znakovi / ispred kojih se ne nalaze zvjezdice. Kod se moe poboljati tako da otkrije pojavu EOF (kraja datoteke) unutar komentara.
Komentar se takoe moe definisati kao regularni izraz, prema sljedeem "/*""/"*([^*/]|[^*]"/"|"*"[^/])*"*"*"*/"
"/*" na poetku i "*/" na kraju jednostavno oznaavaju parove karaktera koji su neophodni na poetku i na kraju komentara, to ostavlja "/"*([^*/]|[^*]"/"|"*"[^/])*"*"*
to predstavlja ono to se moe pojaviti unutar komentara. "/"* na lijevoj strani
predstavlja injenicu da unutar komentara moe postojati proizvoljan broj (ukljuujui i
nulu) kosih crta na poetku komentara, a "*"* na desnoj strani predstavlja injenicu da unutar komentara moe postojati proizvoljan broj (ukljuujui i nulu) zvjezdica na kraju komentara.
Srednji dio, ([^*/]|[^*]"/"|"*"[^/])*, predstavlja nula ili vie
(proizvoljan broj) segmenata, od kojih svaki
- ne sadri pojavu "/" ili "*"
- sadri samo"/", ispred kojeg nema "*"
- sadri samo "*", iza kojeg nema "/".
Kao to smo vidjeli iz prethodnih primjera, tipovi analize koji se odnose na leksiku strukturu inputa ukljuuju prepoznavanje simbola, brisanje komentara,
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
22
upisivanje broja linije, identifikaciju i evaluaciju konstanti, identifikaciju kljunih (rezervisanih) rijei, identifikaciju svih razliitih identifikatora u programu, brojanje linija komentara u programu, raunanje broja i prosjene duine literala itd. Kompleksniji tipovi analize, koji se odnose na sintaktiku strukturu programa, a ne na leksiku, u optem se sluaju rade uz pomo alata za parsiranje (sintaksnu analizu). Tu spadaju koritenje izraza, strukture s ugnjeavanjem, uporeivanje promjenljivih i provjera da li su promjenljive dobro definisane.
PrimjerL11. Izbrojati linije koda koje ne ukljuuju samo komentare brojanje linija koda, pri emu se ne raunaju blanko linije i linije koje ukljuuju samo komentare. Opis je dat u datoteci PR11L.L:
%{
int linbezkom=0, broj=0;
%}
komentar "/*""/"*([^*/]|[^*]"/"|"*"[^/])*"*"*"*/"
bjelina [ \t]
novalinija \n
%%
{komentar} ;
{bjelina} ;
{novalinija} {if (broj>0) linbezkom=linbezkom+1; broj=0;}
. broj=broj+1;
%%
main()
{yylex();
printf("broj linija koda koje ne sadrze samo komentare je
%d", linbezkom);
}
PrimjerL12. Prosjean broj karaktera po liniji je dobra metrika (alat za ocjenjivanje veliine ili efikasnosti nekog koda). To, zajedno s prethodnim, moe dati bolji uvid u veliinu programa. Brojaemo prosjean broj karaktera po liniji koda, pri emu uzimamo u obzir samo one linije koje ne sadre samo komentare. Prazne linije emo ignorisati. Opis se nalazi u datoteci PR12L.L:
%{
int brojkar=0, linbezkom=0, broj=0;
double prosjek;
%}
komentar "/*""/"*([^*/]|[^*]"/"|"*"[^/])*"*"*"*/"
bjelina [ \t]
novalinija \n
%%
{komentar} ;
{bjelina} ;
{novalinija} {if (broj>0) {linbezkom=linbezkom+1; broj=0;}}
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
23
. {broj=broj+1;brojkar=brojkar+1;}
%%
main()
{yylex();
printf("prosjecan broj karaktera po linijama koda koje\n");
prosjek=brojkar/linbezkom;
printf ("ne sadrze samo komentare je %f",prosjek);
}
Ostale mjere veliine programa mogu se odrediti na isti nain, npr. ukupan broj linija koda, broj linija koda s komentarima, ukupan broj karaktera itd i sve su odgovarajue za skupljanje kroz leksiku analizu. Druge mjere veliine, kao to je broj funkcija, broj izraza, broj izraza po liniji bolje odgovaraju skupljanju u toku sintaksne
analize i mogu se zasnivati na YACC-u.
Primjer 13. Mogue je traiti i odlike koda koje su iz nekog razloga nepoeljne kao to su predstavljaju defekte, npr. vrlo dugi ili vrlo kratki identifikatori. Skener koji se dobija
iz sljedeeg opisa za FLEX, koji se nalazi u datoteci PR13L.L izvjetava o konstantama koje su preduge ili prekratke.
slovo [a-zA-Z]
cifra [0-9]
identifikator {slovo}({slovo}|{cifra})*
%%
{identifikator} {if (yyleng==1)
printf("identifikator %s je dug samo jedan
karakter\n", yytext);
if (yyleng>8)
printf("identifikator %s je duzi od osam
karaktera\n", yytext);
}
%%
main()
{yylex();
}
Duina konstanti takoe moe biti provjeravana na slian nain, kako bi se vidjelo da one ne prelaze ogranienja implementacije. Meutim, to treba ostaviti za kasnije, jer prve faze analize trebaju biti to nezavisnije od maine.
Na izvornom kodu moe se uraditi veliki broj provjera, kao to su neodgovarajue koritenje goto naredbe, visoka kompleksnost kontrole toka, neodgovarajua dubina ugnjeavanja, neoznaene konstante u izrazima itd.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
24
PrimjerL14. Ovaj primjer u datoteci PR14L.L daje leksiki analizator za rimske brojeve. Najprije primijetimo da nadskup rimskih brojeva moe da bude definisan regularnim izrazom kao to je M*(CM|CD|DC*|C*)(XC|XL|LX*|X*)(IX|IV|VI*|I*)
kojeg je lako dobiti ako razmotrimo nain na koji se hiljade, stotine, desetice i jedinice mogu napisati. Taj izraz generie sve rimske brojeve, ali i neke niske koje nisu rimski brojevi, kao to je npr VIIIIIIIIIIII, koji ima previe I. Takve niske bismo mogli izbjei time to bismo posljednji dio regularnog izraza zamijenili sa
IX|V|VI|VII|VIII|IV|I|II|III|
Ostalim dijelovima izraza bi trebalo pruiti slian tretman, tako da bi bilo mogue napisati (prilino kompleksan) regularni izraz koji bi predstavljao tano sve rimske cifre. Meutim, neke od provjera koje su neophodne za eliminaciju neodgovarajuih niski mogu se kodirati u akcije. Sljedei input za FLEX, koji se nalazi u datoteci PR13L.L, dovoljan je za evaluaciju rimskih brojeva:
%{
int vr=0;
%}
hiljade M*
stotine CM|CD|DC*|C*
desetine XC|XL|LX*|X*
jedinice IX|IV|VI*|I*
%%
{hiljade} {vr=vr+yyleng*1000;}
{stotine} {if (!strcmp(yytext,"CM"))
vr=vr+900;
else if (!strcmp(yytext,"CD"))
vr=vr+400;
else if (yytext[0]=='D')
if (yyleng>4)
printf("previse C\n");
else vr=vr+500+(yyleng-1)*100;
else if(yyleng>4)printf("previse C\n");
else vr=vr+yyleng*100;}
{desetine} {if (!strcmp(yytext,"XC"))
vr=vr+90;
else if (!strcmp(yytext,"XL"))
vr=vr+40;
else if (yytext[0]=='L')
if (yyleng>4)
printf("too many X's\n");
else vr=vr+50+(yyleng-1)*10;
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
25
else if (yyleng>4)printf("previse X\n");
else vr=vr+yyleng*10;}
{jedinice} {if (!strcmp(yytext,"IX"))
vr=vr+9;
else if (!strcmp(yytext,"IV"))
vr=vr+4;
else if (yytext[0]=='V')
if (yyleng>4)
printf("previse I\n");
else vr=vr+5+(yyleng-1);
else if (yyleng>4)printf("previse
I\n");
else vr=vr+yyleng;}
%%
main()
{yylex();
printf("vrijednost rimskog broja je %d\n", vr);
}
Ovaj program se dalje moe doraivati, da bi ukljuio provjere za previe C, X ili L. Funkcija strcmp, koja uporeuje niske, definisana je tako da daje 0 ako su njena dva argumenta, koji su niske, identini. To bi u C-u bilo ekvivalentno s false, pa ta vrijednost mora biti invertovana koritenjem operatora !.
PrimjerL15. Napisati opis ya FLEX koji e dati leksiki analizator koji prepoznaje vaee automobilske tablice u BiH (tri cifre, crtica, slovo koje se jednako pie i latinicom i irilicom, crtica, tri cifre). Analizator takoe treba da prepoznaje automobilske tablice vozila politiara, ako politiari visokog ranga voze automobile s tablicama na kojima su sve cifre jednake, politiari srednjeg ranga voze automobile s tablicama na kojima su desna i lijeva strana jednake, a politiari niskog ranga voze automobile s tablicama na kojima su desna i lijeva strana simetrine. Opis se nalazi u datoteci PR15L.L:
cifra [0-9]
slovo [AEOJKMT]
tablica {cifra}{cifra}{cifra}"-"{slovo}"-
"{cifra}{cifra}{cifra}
%%
{tablica} {printf("vazeca registarska tablica
%s\n",yytext);
if (yytext[0]==yytext[1] && yytext[1]==yytext[2]
&& yytext[2]==yytext[6] && yytext[6]==yytext[7] &&
yytext[7]==yytext[8])
printf("visoko politicka. sve cifre jednake\n");
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
26
else if (yytext[0]==yytext[6] &&
yytext[1]==yytext[7] && yytext[2]==yytext[8])
printf("srednje politicka, lijeva i desna strana
jednake\n");
else if (yytext[0]==yytext[8] &&
yytext[1]==yytext[7] && yytext[2]==yytext[6])
printf("nize politicka. lijeva i desna strana
simetricne\n");
}
%%
main()
{yylex();}
PrimjerL16. Skener za paskaloliki jezik, datoteka PR16L.L:
%{
#include
%}
cifra [0-9]
ident [a-z][a-z0-9]*
%%
{cifra}* {printf("cijeli broj: %s (%d)\n", yytext,
atoi(yytext));}
{cifra}+"."{cifra}* {printf("realni broj: %s (%g)\n",
yytext, atof(yytext));}
if|then|begin|end|procedure|function {printf("kljucna
rijec:
%s\n",
yytext);}
{ident} {printf("identifikator: %s\n", yytext);}
"+"|"-"|"*"|"/" {printf("operator: %s\n", yytext);}
"{"[^}\n]*"}" ; /*eliminisemo komentare*/
[ \t\n]+ ; /*eliminisemo bjeline*/
. printf("neprepoznat karakter: %s\n",yytext);
%%
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
27
main(argc, argv)
int argc;
char **argv;
{
++argv, --argc; /*preskacemo ime programa*/
if (argc>0)
yyin = fopen (argv[0],"r");
else
yyin = stdin;
yylex();
}
Kod ovog primjera zanimljiv je mehanizam poziva programa sa komandne linije.
Ovdje moemo program izvriti tako to (nakon kompajliranja) iz komandne linije pozovemo izvrnu verziju programa s argumentom (imenom datoteke u kojoj se nalazi input, tako da ga ne moramo unositi s standardnog ulaza - tastature). Meutim, program moemo izvravati i na uobiajen nain.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
28
C. GRAMATIKE
C1. Definicija gramatike
Primijetimo da postoje jezici za koje se jednostavno ne moe napisati odgovarajui regularni izraz. Najjednostavniji primjer za to je jezik ije se rijei sastoje od jednakog broja x-eva i y-na, tj, jezik xy, n>0. To to x-ova i y-na ima proizvoljan broj moemo izraziti regularnim operatorima * i +, ali nam oni ne pruaju mogunost da zapamtimo koliko je x-ova generisano prije nego to ponemo generisati y-ne. Ako x izjednaimo s otvorenom zagradom (ili poetkom bloka u programu, recimo, kljunom rijeju BEGIN), a y sa zatvorenom zagradom (ili krajem bloka, tj. END), jasno je da je se taj problem vrlo esto javlja.
Prema tome, jasno je da e nam za definisanje takvih jezika trebati jai mehanizam od regularnih izraza, odnosno, gramatike. Da bismo uveli gramatike
razmotriemo jezik aritmetikih izraza s cjelobrojnim nenegativnim argumentima i operatorima sabiranja i mnoenja.
Azbuka nad kojom se formiraju izrazi je = {0,1,2,3,4,5,6,7,8,9,+,*, (,)}. Da bismo definisali skup izraza I, moramo se posluiti i nekim jednostavnijim konstrukcijama sintaksnim klasama. To su cifre, brojevi, faktori i sabirci. Uvodimo i oznake I za izraz, S za sabirak, F za faktor, B za broj i C za cifru. Smjestiemo te pomone simbole u azbuku N = {I,S,F,B,C}. Izraze i potrebne sintaksne klase uvodimo sljedeim pravilima:
1. Jednoslovne niske 0, 1, 2, 3, 4, 5, 6, 7, 8, i 9 su cifre. 2. Ako je C cifra onda je C i broj. 3. Ako je B broj i C cifra, onda je BC broj. 4. Ako je B broj, onda je B i faktor. 5. Ako je I izraz onda je (I) faktor 6. Ako je F faktor, onda je F i sabirak. 7. Ako je S sabirak i F faktor, onda je S*F sabirak. 8. Ako je S sabirak, onda je S i izraz. 9. Ako je I izraz i S sabirak onda je I+S izraz 10. Izrazi se dobijaju konanim brojem primjena pravila 1-9.
Koristei se slovima iz azbuke N mogu se pisati rijei kao to je npr. S + F * F. ako se u takvoj rijei S zamijeni nekim sabirkom, a svaki F nekim faktorom (ne obavezno istim) dobie se izraz koji pripada jeziku. Ideja proizvodnje izraza sastoji se upravo u zamjenama slova iz N.
U namjeri da proizvedemo izraz poi emo od jednoslovne rijei i zatim emo vriti zamjene slova iz n dok ne dobijemo rije nad azbukom . Za formulisanje pravila zamjene oslanjaemo se na pravila 1-9.
Tako, na primjer, moemo rei da u bilo kojoj rijei I moe da se zamijeni sa I+S. To, u stvari, znai da gdje moe da se pojavi I (izraz) umjesto njega moe da stoji I+S.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
29
Postupajui na taj nain sa svim pravilima 1-9 dobijamo sljedea pravila zamjene (ona odgovaraju redom pravilima od 9 do 1 iz prethodnog dijela teksta):
1. I se moe zamijeniti sa I + S 2. I se moe zamijeniti sa S 3. S se moe zamijeniti sa S * F 4. S se moe zamijeniti sa F 5. F se moe zamijeniti sa ( I ) 6. F se moe zamijeniti sa B 7. B se moe zamijeniti sa BC 8. B se moe zamijeniti sa C 9. C se moe zamijeniti bilo kojom cifrom.
Rijei se moe zamijeniti sa zamjenjujemo simbolom . Tako imamo:
I S * F F * F ( I ) * F ( I + S) * F (S + S) * F (F + S) * F (F + F) * F (B + F) * F (B + B) * F (B + B) * B (BC + B) * B (CC + B) * B (CC + C) * B (CC + C) * C (2C + C) * C (23 + C) * C (23 + 5) * 2
Polazei od I mogli smo vriti zamjene i na drugi nain, pri emu vjerovatno ne bismo dobili istu rije na kraju. Meutim, rije koju bismo dobili uvijek bi bila ispravan izraz u jeziku koji smo definisali u primjeru 1.1. I obratno, svaki izraz jezika iz primjera
1.1 moe se dobiti polazei od I primjenom pravila 1-9. Zakljuak moemo uoptiti na sljedei nain:
Da bismo generisali rijei nekog jezika nad azbukom uveli smo pomonu azbuku N, skup pravila zamjene i jedan polazni simbol iz N. Po pravilima zamjene u
naem sluaju uvijek se zamjenjuje po jedno slovo, a uopteno uzevi moe da se zamjenjuje neka podrije koja ima bar jedno slovo iz N, tj. podrije iz skupa (N U )* N (N U )*. Konano, pravila zamjene moemo formalizovati kao ureeni par: prvi element para je rije koja se zamjenjuje, a drugi rije kojom se ona zamjenjuje. Tako smo zapravo doli do definicije gramatike.
Def. Gramatika je ureena etvorna G=(N, , S, P), gdje je
N nezavrna azbuka. Slova te azbuke su nezavrna slova ili neterminali. zavrna azbuka. Slova te azbuke su zavrna slova ili terminali.
P konaan skup pravila zamjene P (N U )* N (N U )* x (N U )*.
S N je poetni simbol.
Umjesto (,) P piemo i itamo se zamjenjuje sa u gramatici G.
Nezavrna slova ponekad se nazivaju i sintaksne klase. Pravila koja na lijevoj strani imaju nezavrno slovo A zvaemo A pravila. Pravila koja na lijevoj strani imaju zvaemo pravila.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
30
Poslije ove definicije moemo rei da jeziku iz primjera 1.1 odgovara gramatika G=(N, , P, S), gdje je N={I,S,F,B.C},
={0,1,2,3,4,5,6,7,8,9,+,*,(,)}, P={I I + S, I S, S S * F, S F, F ( I ), F B, B B C, B C, C 0, C 1, C 2, C 3, C 4, C 5, C 6, C 7, C 8, C 9} S =I
Vie pravila s istom lijevom stranom mogu da se piu u obliku jednog produenog pravila uz upotrebu simbola |. Npr. I I + S | S.
Gramatika se esto zadaje tako to se samo ispiu njena pravila. Tada se podrazumijeva da su nezavrna slova ona koja se nalaze s lijeve strane pravila, da su zavrna slova sva ostala, a da je startni simbol na lijevoj strani prvog pravila.
C2. Jezik definisan gramatikom
Relacija igra glavnu ulogu u postupku generisanja rijei. Meutim, iako u prethodnoj gramatici vai pravilo I I + S, to ne povlai da vai i ptavilo ( I ) * F (I + S) * F, odnosno, relacija P nije saglasna s dopisivanjem slijeva i zdesna. Zbog toga
emo uvesti relaciju koja ukljuuje i koja je saglasna s dopisivanjem slijeva i zdesna. Pored te relacije, od interesa su i njen k-ti stepen, tranzitivno zatvorenje i refleksivno i
tranzitivno zatvorenje te relacije.
Def. Ako u gramatici G=(N, , P, S) postoji pravilo onda za bilo koje dvije rijei
, (N U )* vai
, i kae se da se rije neposredno izvodi iz rijei .
K-ti stepen relacije oznaava se sa k. Ako vai k onda se kae da se izvodi iz u k koraka.
Tranzitivno zatvorenje rekacije oznaava se sa +. Ako vai + onda se kae da se u pozitivnom broju koraka netrivijalno izvodi iz .
Tranzitivno i refleksivno zatvorenje relacije oznaava se sa *. Ako vai * onda se kae da se izvodi iz .
Def. Jezik definisan gramatikom G=(N, , P, S) je L(G)={w | w * i S * w}. Jezik definisan gramatikom ine sve rijei nad zavrnom azbukom koje se mogu izvesti iz poetnog simbola.
Def. Gramatike G1=(N1, 1, P1, S1) i G2=(N2, 2, P2, S2) su ekvivalentne ako je L(G1)=L(G2).
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
31
Za izvoenje neke rijei jezika zadatog nekom gramatikom obino je potrebno vie koraka neposrednog izvoenja. Rijei nad azbukom N U koje se dobijaju kao meurezultati takvog izvoenja su takoe od interesa.
Def. Jezik gramatikih formi gramatike G=(N, , P, S) je
GF(G)={ | (N U )* i S * }.
Gramatike forme nazivaju se jo i reeninim formama ili sentencijalnim formama.
Def. Izvoenje u gramatici G=(N, , P, S) je niz gramatikih formi 0, 1, ... n, gdje je
0 = S, i i-1 i za svako i, 0
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
32
S S
S + S S + S
S + S a a S + S
a a a a
Primjer3. Data je gramatika
S S p A q
S A S p A A A q S
A Iz tih pravila mogu se izvesti i pravila A p i A q pa emo i njih koristiti, radi skraivanja izvoenja.
Napisati izvoenja i drvo izvoenja za rije ppqpqq. Uvijek kreemo od startnog simbola: S
S p A q
Mogli bismo poi i suprotnim smjerom dok ne dobijemo S Lijevo izvoenje:
S SpAq pAq pSpAq ppAq ppAqS ppqS ppqSpAq ppqpAq
ppqpAqSq ppqpqSq ppqpqq. Drvo izvoenja: S
S p A q
S p A
A q S
S p A q
Primjer4. Napisati gramatiku za dati regularni izraz (a + b). S SE | E E a | b
Primjer5. Dat je jezik aritmetikih izraza kod kojih su operandi a i b a operatori + i -. Napisati gramatiku za taj jezik.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
33
Treba napraviti gramatiku za jezik izraza sljedeeg oblika a + b + a a b ... Pokuamo kod razliitih izraza koji pripadaju jeziku nai njihovu zajedniku strukturu:
Prvi nain: primijetiemo da svaki izraz poinje operandom (promjenljivom, a ili b), iza kojeg slijedi operator, koji moe biti + ili -, iza kojeg opet slijedi izraz, koji moe biti skoro toliko komplikovan kao i polazni izraz, ili se moe svoditi samo na promjenljivu, pa je gramatika
S P Z S | P Z + | - P a | b Pri emu P oznaava promjenljivu, Z znak, a S neki izraz.
Drugi nain: S P + S | P S | P
Trei nain: S PN
N + PN | - PN |
etvrti nain: S S + S | S S | P (nezgodno je jer ova gramatika nije jednoznana/nedvosmislena).
Primjer6. Dat je jezik koji je podskup prethodnog ukljuuje samo one izraze kod kojih znaci + i alterniraju. Napisati gramatiku za taj jezik.
Izrazi koji pripadaju tom jeziku su oblika a + b a + a b + b ..... Dakle, poinju promjenljivom, iza koje slijedi zank, pa neki nastavak. Ako je taj znak + imaemo nastavak u kojem je prvi znak -, i obratno, ako je taj znak -, imaemo nastavak u kojem je prvi znak +. Dakle, imamo dvije vrste nastavaka, Np kao plus nastavak i Nm
kao minus nastavak:
S P Np
Np + P Nm |
Nm - P Np | P a | b
Primjer7. Dat je jezik slian jeziku iz primjera 5, samo to osim navedenog sadri jo i zagrade.
U osnovi, na mjestu svakog izraza moe stajati izraz u zagradama. Gramatike se mogu predstavljati i Backus-Naurovom formom. U tom sluaju terminali se piu izmeu dvostrukih navodnika, a na kraju niza slijedi taka zarez. Ovu gramatiku emo predstaviti tako:
::= ''+'' | ''-'' | ;
::= | ''('' '')'' ;
::= ''a'' | ''b'' ;
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
34
Primjer8. Prethodnom primjeru dodati jo i mnoenje i dijeljenje. ::= + a + b * a | - | ; P P P
::= * | / F F F | ;
::= | ( ); S S ::= a | b ;
S
Jedino mogue (jedinstveno) drvo izvoenja I
I
Zadatak. Dodati operaciju stepenovanja i unarne operatore plus i minus. Unarni
operatori imaju najvii stepen prioriteta, nakon toga stepenovanje, pa mnoenje i dijeljenje itd.
Primjer8. Napisati gramatiku za rimske brojeve.
Razdvajamo sintaksne klase za jedinice, desetice, stotice i hiljade: skup neterminala N =
{P,Q,R,S,T}. Skup terminala je ={I,V,X,L,C,D,M}. Gramatika pravila su:
S T Q R P
P I | II | III | IV | V | VI | VII | VIII | IX |
Q X | XX | XXX | XL | L | LX | LXX | LXXX | XC |
R C | CC | CCC | CD | D | DC | DCC | DCCC | DM |
T M | MM | MMM |
C3. Hijerarhija omskog
Hijerarhija omskog predstavlja klasifikaciju jezika, odnosno gramatika koje ih generiu. Kreemo od najire klase, i svaka klasa obuhvata u sebi i sljedeu, uu klasu.
Najira klasa gramatika naziva se klasa Tip 0 gramatika, odnosno klasa rekurzivno nabrojivih gramatika. Kod tih gramatika ne postoje restrikcije vezane za tip
produkcija (pravila). One su ekvivaltntne Turingovim mainama.
Prvo ogranienje vezano za oblik pravila koje se moe pojaviti u gramatici je da se kae da za sva pravila oblika vai da je duina niske manja ili jednaka duini niske , tj. da je | |
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
35
Klasa gramatika koje zadovoljavaju naredno ogranienje, da se samo jedan neterminal moe pojaviti s lijeve strane pravila, nazivaju se Tip 2 ili kontekst slobodne gramatike.
Moe se dopustiti da pravilo S bude ukljueno ak i u kontekst sobodnoj gramatici (ne samo u kontekst osjetljivoj, iako kri pravilo za kontekst osjetljive gramatike), kako bi se prazna niska ukljuila u jezik, jer to ne poveava ekspresivnost. Tip 2 gramatike su ekvivalentne potisnim automatima.
Posljednju, najuu, klasu ine Tip 3 ili regularne gramatike. Prvo definiemo da je desno linearna gramatika ona kod koje je svako pravilo jednog od dva sljedea oblika
A a A b C
Za gramatike koje su desno linearne kae se da su regularne. Analogno i za gramatike koje su lijevo linearne, ali gramatika kod koje ima mijeanja desno linearnih i lijevo linearnih pravila nije regularna.
Jezik definisan regularnom gramatikom je regularan. Takav jezik se moe definisati i regularnim izrazom i takve gramatike su ekvivalentne konanim automatima.
Kao to je ve reeno, radi se o inkluzivnoj gramatici ako je gramatika tipa 3, ona e biti i tipa 2 i tipa 1 i tipa 0. Meutim, ako je jezik tipa 2, ne mora znaiti da nije i tipa 3, jer moda postoji gramatika tipa 3 koja definie isti jezik. Na primjer, jezik x*y* definiu obje doljenavedene gramatike, pri emu je lijeva tipa 2, a desna tipa 3.
S X Y S x S X x X S y B
X S x Y y Y S y
Y B y B B y
S
Regularne gramatike i jezici su podskup kontekst slobodnih gramatika i kontekst
slobodnih jezika. U analizi, gdje god je mogue, treba koristiti regularne gramatike i jezike. Postoji jednostavna odlika gramatike koja se moe koristiti da se odredi da li je jezik koji se generie regularan. Radi se o rekurziji. Rekurzije mogu biti lijeve (A A b), desne (B c B) i srednje (C d C e). Tkoe, mogu biti i neposredne (kao ove koje smo do sada naveli) i posredne (A B c, B C d, C A c). Meutim, postoji jednostavan algoritam za prevoenje posredne rekurzije u neposrednu (vidjeti naredno poglavlje).
Da bismo odredili da li neka gramatika generie regularan jezik, razmotrimo da li sadri rekurzije. Ako ih ne sadri, to je malo vjerovatno, onda je jezik regularan jer je konaan. Meutim, ako gramatika sadri rekurzije, ali ne sadri srednje rekurzije, onda je jezik koji generie opet regularan, a regularnu gramatiku emo dobiti metodom
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
36
oslobaanja od lijeve rekurzije (vidjeti naredno poglavlje). Dakle, jezik generisan gramatikom
S X Y X x X X x Y y Y Y y S y
S
je regularan, jer pravila ne sadre srednju rekurziju.
Jednostavan jezik koji nije regularan je, kao to smo ve pomenuli, jezik ije se rijei sastoje od jednakog broja x-eva i y-na, tj, jezik xy, n>0.
Leksiki aspekti veine jezika imena promjenljivih, brojevi, konstante isl. Mogu se skoro uvijek definisati regularnim izrazima. Meutim, kod aritmetikih izraza ili blokova (gdje imamo uparivanje zagrada), to nije mogue izraziti regularnim izrazima ni regularnim gramatikama, jer gramatika koja se sastoji od niski uparenih zagrada
ukljuuje pravila tipa S ( S ), odnosno pravila s unutranjom rekurzijom. Prema tome, za leksiku analizu koristiemo gramatike tipa 3, a za sintaksnu analizu gramatike tipa 2.
C4. Transformacije kontekst slobodnih gramatika
C4.5. Oslobaanje od suvinih slova (neterminala)
Def1. Slovo je neproduktivno ako se iz njega ne moe izvesti nijedna rije iz zavrne azbuke (azbuke terminala).
Def2. Slovo je nedostino ako se iz poetnog simbola ne moe izvesti ni jedna rije koja sadri to slovo.
Slovo moe biti i dostino i produktivno pa da ipak bude suvino (npr. dostino je preko nedostinog).
Primjer 1. Osloboditi se suvinih slova u gramatici S a | A A A B B b
Prvo se oslobaamo neproduktivnih pa nedostinih slova. Pitanje produktivnosti se ne postavlja za terminale.
Pravimo niz skupova P1, P2, ... pri emu je P1 skup svih neterminala iz kojih se izvode rijei sastavljene od terminala, P2 je skup svih neterminala iz kojih se izvode rijei
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
37
sastavljene od terminala i slova iz P1, P3 je skup svih neterminala iz kojih se izvode rijei sastavljene od terminala i slova iz P2... i tako dalje, a postupak se prekida kad doemo u situaciju da je Pi-1=Pi. Tada je Pi skup svih produktivnih terminala date gramatike.
P1={S,B}, P2={S,B} itd.
Dakle, A nije produktivno slovo jer njim dobijamo rije; koja poinje sa A.
to se tie dostinih slova, opet pravimo niz skupova, D1, D2, ... pri emu skup D1 sadri poetni simbol, skup D2 simbole koji se mogu nai u reeninim formama koje se mogu izvesti iz poetnog simbola, skup D3 simbole koji se mogu nai u reeninim formama koje koje se mogu izvesti iz simbola koji pripadaju skupu D2 itd... i tako dalje, a postupak
se prekida kad doemo u situaciju da je Di-1=Di. Tada je Di skup svih dostinih terminala date gramatike.
D1={S}, D2={S,A,a}, D3={S,A,a,B,b}, D4=D3.
Dakle, u poetnoj gramatici nema nedostinih slova. Meutim, kad smo otkrili da je slovo A neproduktivno, moramo ga se osloboditi, a rezultujua gramatika
S a B b
Sadri nedostino slovo B. prema tome, rezultujua gramatika, koja nema suvinih slova, je
S a
Prema tome, postupak tee tako da se najprije oslobodimo neproduktivnih, a onda nedostinih slova. Slovo moe biti dostino zahvaljujui neproduktivnom, ali ne moe biti neproduktivno zahvaljujui nedostinom.
Primjer 2. Osloboditi se od suvinih slova u sljedeoj gramatici: S A | B A a B | b S | b B A B | B a C A S | b
P1={A,C}, P2={A,C,S}, P3={A,C,S}=P2, pa slovo B nije produktivno
S A A b S | b C A S | b
D1={S}, D2={S,A}, D3={S,A,b}, D4={S,A,b}, dakle, C nije dostino S A A b S | b
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
38
C4.6. Oslobaanje od pravila
Def. Gramatika je slobodna ukoliko nema pravila ili (u sluaju da jezik sadri rije)
ima samo jedno pravilo, S (a inae nije nigdje s desne strane pravila).
Primjer 1. Osloboditi se pravila u datoj gramatici
S a S b S | b S a S |
Postupak je sljedei: U sluaju da jezik sadri rije (a u ovom sluaju je tako), najprije dodajemo novi neterminal, S koji e imati ulogu startnog simbola, i sljedea pravila za
taj neterminal S' | S, koja e dovesti do toga da ostatak bude bez .Zatim pravimo skup niz skupova Ni, pri emu je Ni, i
Ni = {A | A } ( se moe izvesti iz neterminala A u i koraka, i=0,1,...)
Postupak prekidamo kad dva uzastopna skupa N budu jednaka. U ovom sluaju, N ={S}. Nakon toga, gdje god se u pravilima pojavljuju neterminali iz skupa N, ta pravila
dupliramo, jednom s tim neterminalom, drugi put bez. Dakle, rezultujua gramatika je
S' | S S a S b S | a b S | a S b | a b | b S a S | b a S | b S a | b a
Primjer 2. Osloboditi se pravila u datoj gramatici S A B C
A B B | B C C | a C A A | b
N1={A}, N2={A,C}, N3={A,C,B}, N4={A,C,B,S}, N5={A,C,B,S}=N4
Pa je gramatika bez pravila:
S' | S S A B C | A B | A C | B C | A | B | C A B B | B B C C | C | a C A A | A | b
C4.7. Oslobaanje od jednostrukih projekcija
Primjer1. Osloboditi se jednostrukih projekcija (pravila) u gramatici
S A | B A a B | b S | b B A B | B a C A S | b
Jednostruka pravila su S A i S B. Pravilo S A zamjenjujemo pravilima
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
39
S a B | b S | b, a pravilo S B pravilima S A B | B a.
Uopte, za neterminale koji su na desnoj strani jednostrukih pravila prave se skupovi
Ns ={X | S * X} ( znai da i S pripada N). Zahtjeva se da je gramatika slobodna.
U ovom sluaju, NS ={S, A, B}. Zatim se desne strane pravila kojima su s lijeve strane terminali iz NS koji nisu S stave s desne strane pravila kojima je S s lijeve strane, kao
gore. Dakle, rezultujua gramatika je:
S a B | b S | b | A B | B a A a B | b S | b B A B | B a C A S | b
Primjer2. Osloboditi se jednostrukih projekcija (pravila) u gramatici
E E + T | T T T * F | F F ( E ) | a
NE = {E,T,F}, NT = {T,F}, NF = {F}
E E + T | T * F | ( E ) | a T T * F | ( E ) | a F ( E ) | a
Primjer3. Osloboditi se jednostrukih projekcija (pravila) u gramatici
S B + S | A | B A A + B | S | B B a | S A
Ovo je gramatika s ciklusima!
NS = {S,A,B}, NA = {A,S,B}, NB={B}
S B + S | A + B | a | S A A A + B | B + S | a | S A B a | S A
Sad su neterminali S i A ekvivalentni, pa se ova gramatika moe i dalje transformisati.
C4.8. Oslobaanje od lijeve rekurzije
Def. Gramatika je lijevo rekurzivna ako A + A i A N (ako se iz nekog neterminala moe u pozitivnom broju kortaka izvesti reenina forma koja poinje tim istim neterminalom. U sluaju da je taj broj koraka jednak jedan, radi se o neposrednoj lijevoj rekurziji, a ako je broj koraka vei od jedan, radi se o posrednoj lijevoj rekurziji).
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
40
Def. Pravilna gramatika je gramatika koja nema suvinih slova, nema pravila i nema cikluse. Moe imati jednostruka pravila, ali oslobaanje od jednostrukih pravila e eliminisati i ckluse, koji su nepoeljni.
U postupak oslobaanja od lijeve rekurzije ulazi pravilna gramatika.
Primjer lijeve rekurzije: A A a | b. To je primjer neposredne lijeve rekurzije. Jezik te
gramatike je ba* (A Aa Aaa baa isl.). Kako za taj jezik napisati gramatiku koja nije lijevo rekurzivna? Uveemo novi neterminal, A i njegova pravila
A b A'
A' a A' |
Sada nam smeta pravilo, N ={A'}, pa je gramatika
A B A' | b A' a A' | a
Ovaj postupak s uvoenjem novog neterminala uoptavamo, pa za sve gramatike koje ukljuuju pravila oblika: A A 1 | A 2 | ... | A n | 1 | 2 | ... | m Ta pravila zamjenjujemo pravilima
A 1 A' | 2 A' | ... | m A' | 1 | 2 | ... | m A' 1 A' | 2 A' | ... | n A' | 1 | 2 | ... | n
Ako lijeva rekurzija nije neposredna, pravimo takvu transformaciju da za svako pravilo
vai da ako ono ima neterminale s desne strane, to su neterminali koji su nie u redoslijedu (njima poinju pravila koja su ispod).
Primjer1. Osloboditi se lijeve rekurzije u sljedeoj gramatici
A B C | a B C A | A b C A B | C C | a
Najprije ispitamo da li je gramatika pravilna ima li suvinih slova, pravila ili jednostrukih projekcija. U ovom sluaju gramatika jeste pravilna. Nakon toga uvodimo red meu neterminalima, ali tako da se poredak to manje mijenja. Dalje, za svaki neterminal po redu:
- vidimo da li neterminal gleda unazad (da li mu se s desne strane pojavljuju neterminali koji su ispred njega u redoslijedu),
- ako gleda unazad, to eliminiemo, - vidimo da li sada u tom praviolu postoji neposredna lijeva rekurzija (da li taj
neterminal gleda u sebe),
- oslobodimo se neposrednih lijevih rekurzija prema gore navedenom pravilu.
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
41
A B C | a ovo pravilo je u redu B C A | A b ovo pravilo gleda unazad, pa ga zamjenjujemo sa B C A | B C b | a b ovdje imamo neposrednu lijevu rekurziju B C A B' | a b B' | C A | a b B' C b B' | C b B' ne pravi novu rekurziju, pa je u redu
C A B | C C | a ovo pravilo gleda unazad, pa ga zamjenjujemo sa C B C B | a B | C C | a ovo pravilo gleda unazad, pa ga zamjenjujemo sa C C A B' A B | a b B' C B | C A C B | a B C B | a B | C C | a, ovdje imamo neposrednu lijevu rekurziju
C a b C B C' | a b B' C b C' | a B C' | a C' | a b C B | a b B' C B | a B | a | C' A C B C' | A B' C B C' | C C' | A C B | A B' C B | C
Primjer2. Osloboditi se lijeve rekurzije u sljedeoj gramatici
S A | B | C | D | E A C | x | y | z
B A x | y | z | C B x | A y | z D C x | B y | A z E D x | C y | B z
1) Gramatika nije pravilna, ima pravilo. Oslobaamo se od pravila N = {B,S}
S S | S A | B | C | D | E A C | x | y | z B A x | y | z C B x | A y | z D C x | B y | A z E D x | C y | B z
2) Sada treba napraviti adekvatniji redoslijed
Vidimo da se D i E ne pojavljuju u pravilima za A, B i C i treba ih, dakle, premjestiti
naprijed. Redoslijed kod kog tek u estom pravilu mijenjamo je:
S S | S A | B | C | D | E E D x | C y | B z D C x | B y | A z A C | x | y | z B A x | y | z C B x | A y | z
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
42
3) Oslobaanje od lijevih rekurzija
S S | S A | B | C | D | E E D x | C y | B z D C x | B y | A z A C | x | y | z ova pravila su sva u redu B A x | y | z ovo pravilo gleda unazad, pa ga zamjenjujemo sa B C x | x x | y x | z x | y | z ovdje nema neposredne lijeve rekurzije C B x | A y | z ovo pravilo gleda unazad, pa ga zamjenjujemo sa C C x x | x x x | y x x | z x x | y x | z x | x | C y | x y | y y | z y | z ovdje imamo neposrednu lijevu rekurziju
C x x x | y x x | y x x | y x | z x | x | x y | y y | z y | z | x x x C' | y x x C' | y x x C' | y x C' | z x C' | x C' | x y C' | y y C' | z y C' | z C'
C' x x | y | x x C' | y C'
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
43
D. PARSIRANJE ODOZGO NADOLE (TOP DOWN)
Problem parsiranja se sastoji iz nalaenja izvoenja (ako postoji) odreene reenice uz koritenje date gramatike. U sluaju parsiranja odozgo nadole (top down), trai se lijevo izvoenje, dok se u sluaju parsiranja odozdo navie (bottom up) trai desno izvoenje. Kod parsiranja odozgo nadole poinjemo s reeninim simbolom i generiemo reenicu, dok kod parsiranja odozdo nagore polauzimo od reenice i redukujemo je do reeninog simbola. Pretpostaviemo da e reenice koje treba generisati, odnosno redukovati, biti oitane slijeva nadesno, iako su teorijski mogua vraanja koja reenice oitavaju zdesna nalijevo.
Razmatramo jezik x*y* koji je generisan sljedeim pravilima izvoenja S X Y X x X X x Y y Y Y y
kako bismo pokazali da reenica xxxyy moe biti generisana desnim izvoenjem, tj.
S XY xXY xxXY xxxY xxxyY xxxyy
Prvi korak izvoenja je direktan, jer se reenini simbol S pojavljuje na lijevoj
strani samo jednog pravila izvoenja, tako da je jednostavno napisati S XY. Drugi korak, meutim, nije tako jednostavan, jer se najljevlji nezavrni simbol reenine forme (X) pojavljuje na lijevoj strani vie od jednog pravila izvoenja (dva, u ovom sluaju). Ipak, treba imati na umu da je, kada se izvrava parsiranje, rezultat, odnosno generisana reenica, unaprijed poznata. U ovom sluaju, trai se izvoenje reenice xxxyy, a kako ona ima vie od jednog x, sljedee pravilo izvoenja koje treba koristiti je X x X. Slino, u treem koraku izvoenja treba primijeniti isto pravilo izvoenja, to daje xxXY.
etvrti korak je xxXY xxxY i prikazuje prvu upotrebu pravila X x, jer je to
posljednja x koje treba biti generisano. Peti korak, xxxY xxxyY, prikazuje primjenu pravila Y y Y, koje se primjenjuje zato to treba biti generisano jo y-na. Zavrni
korak u izvoenju, xxxyY xxxyy, prikazuje primjenu pravila Y y, poto nee vie biti generisan ni jedan y.
Drugi koristan nain prikazivanja koraka izvoenja je sljedei:
Input Pravilo zamjene Reenina forma xxxyy S X Y XY xxxyy X x X xXY xxxyy X x X xxXY xxxyy X x xxxY xxxyy Y y Y xxxyY xxxyy Y y xxxyy xxxyy
-
Osnove raunarskih sistema 2 Prevodioci i interpretatori
44
Karakteri reenice se razmatraju jedan po jedan i koriste se za voenje procesa parsiranja. Kada se karakter reenice generie, u input nisci se prikazuje kao prekrien. U svakoj fazi parsiranja prikazuju se tri stavke, input reenica s vodeim karakterinma precrtanim, pravilo zamjene koje se u tom trenutku primjenjuje u reenina forma u trenutnom obliku. Kada je parsiranje zavreno, svi karakteri u input reenici su prekrieni i reenina forma je jednaka poetnoj reenici koja je unijeta.
U svakoj fazi, prvi simbol input niske koji nije prekrien definie se kao nailazei simbol i koristi se za voenje parsiranja. Ide se simbol po simbol, sve dok se ne generie zavrni simbol u reeninoj formi. U toku parsiranja, nailazei simbol dobija vrijednost tekueg ulaznog simbola ili markera kraja. Marker kraja je zaseban simbol za koji se pretpostavlja da lei na kraju svake reenice, i koji se obino predstavlja simbolom .
Pri parsiranju odozgo nadole, odluke se obino zasnivaju na nailazeem simbolu, ili sekvenci nailazeih simbola, iako postoje i optiji metodi u kojima se takoe uzima u obzir i dosadanji tok parsiranja.
U primjeru se izvoenje lako nalazi iz toga to znamo koja reenica treba biti generisana, iako je u najveem dijelu izvoenja potrebno poznavanje jo dva simbola, nakon onih koji su do sada generisani. Za neke gramatike se i vie od dva simbola (ak, ponekad i proizvoljan broj simbola!) trai da bi se identifikovalo odgovarajue izvoenje.
Na vjebama emo se skoncentrisati na gramat