prevodioci i interpretatori

Upload: sambatron

Post on 02-Mar-2016

145 views

Category:

Documents


3 download

DESCRIPTION

Prevodioci i interpretatori

TRANSCRIPT

  • 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