TIEA241 Automaatit ja kieliopit, kesä 2013 etenevä Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 10. kesäkuuta 2013
Sisällys etenevä etenevä
Chomskyn hierarkia (ja muutakin) kieli säännöllinen LL(k) LR(1) kontekstiton kontekstinen rekursiivisesti lueteltava automaatti äärellinen (ennustava jäsennin) deterministinen pino pino lineaarirajoitettu Turingin kone etenevä
Tunnistamis- ja ongelma Olkoon G = (N, Σ, P, S) kontekstiton kielioppi ja w Σ sen merkkijono. Tunnistamisongelmassa tehtävänä on selvittää, päteekö w L(G). Jäsennysongelma on tunnistamisongelman yleistys: tehtävänä on selvittää kaikki w:n puut. Jäsennysongelmalle on runsaasti laajassa käytössä olevia algoritmisia ratkaisuja. Useimmat eivät toimi kaikilla kieliopeilla. Tällä luennolla tarkastellaan ennustavaa tä. Lisämateriaaleissa lisäksi Earleyn algoritmi ja LR-. etenevä
1 Määritelmä Olkoon G = (V, Σ, P, S) CFG. Se on operaattorikielioppi välikesymbolien E V (operaattorivälikesymbolit) suhteen, jos kaikki operaattorivälikesymbolien produktiot ovat jotain seuraavista muodoista: etenevä A B ( on prefiksioperattori) A B ( on postfiksioperaattori) A B C A BC ( on infiksioperaattori) (ε on infiksioperaattori) A ( on primäärilauseke) A B missä A, B, C E ja sekä alkaa että päättyy päätesymboliin. 1 Tämä määritelmä ei ole yleisesti käytössä.
Esimerkkejä operaattoreista Tavanomaisten operaattoreiden (+, jne) lisäksi: C:n typecast (tyyppi) on prefiksioperaattori. Funktiokutsu (arg1,...,argn) on postfiksioperaattori. Mixfix-operaattorit kuten C:n?lauseke: ovat tämän analyysin kannalta infiksioperaattoreita. etenevä
Presedenssi ja assosiatiivisuus Miten on lauseke a b c tulkittava? 2 Operaattorien välille on tapana määritellä osittaisjärjestys nimeltä presedenssi: Jos :llä on korkeampi presedenssi kuin :lla, niin tuo lauseke tulkitaan (a b) c. Jos :llä on korkeampi presedenssi kuin :lla, niin tuo lauseke tulkitaan a (b c). Lisäksi on kullekin operaattorille tapana määritellä ominaisuus nimeltä assosiatiivisuus: Jos kummallakaan ei ole toista korkeampi presedenssi mutta molemmat assosioivat vasemmalle, tuo lauseke tulkitaan (a b) c. Jos kummallakaan ei ole toista korkeampi presedenssi mutta molemmat assosioivat oikealle, tuo lauseke tulkitaan a (b c). Muissa tapauksissa tuo lauseke on kielioppivirhe. etenevä 2 Tässä tai tai molemmat voivat toki olla prefiksi- tai postfiksioperaattoreita, jolloin a, b ja c voivat olla tyhjiä.
Yksiselitteisen operaattorikieliopin laatiminen I Lajitellaan operaattorit eri luokkiin siten, että samassa luokassa olevista operaattoreista millään ei ole suurempi presedenssi kuin muilla. 3 Järjestetään luokat jonoon siten, että luokka A tulee luokan B:n jälkeen, jos A:n operaattoreilla on suurempi presedenssi kuin B:n operaattoreilla. Lisätään jonon loppuun vielä yksi luokka primäärilausekkeita varten. Valitaan kullekin luokalle oma uniikki välikesymboli. etenevä 3 Oletetaan yksinkertaisuuden vuoksi, että kaikilla samaan luokkaan kuuluvilla operaattoreilla on sama assosiatiivisuus.
Yksiselitteisen operaattorikieliopin laatiminen II Kullekin operaattoreiden luokalle, jonka välikesymboli on A ja jota seuraavan luokan välikesymboli on B, tehdään seuraavat produktiot: A B A A B jokaiselle A-luokan vasemmalle assosioivalle infiksioperaattorille. A B A jokaiselle A-luokan oikealle assosioivalle infiksioperaattorille. A A jokaiselle A-luokan prefiksioperaattorille. A A jokaiselle A-luokan postfiksioperaattorille. Primäärilausekkeiden luokalle tehdään tarvittavat produktiot. Kaikki alkuperäisen kieliopin operaattorivälikesymbolien produktiot poistetaan ja kaikki viittaukset näihin symboleihin muutetaan viittamaan ensimmäisen luokan välikesymboliin. etenevä
etenevä engl. recursive descent parsing Tehdään kustakin välikesymbolista aliohjelma, joka kokeilee kutakin produktiota vuorollaan. Päätesymbolin kohdalla katsotaan onko se seuraavana merkkijonossa. Välikesymbolin kohdalla kutsutaan sitä vastaavaa aliohjelmaa. Jos ei onnistu, peruutetaan (backtrack) lähimpään tehtyyn valintaan, jossa ei ole vielä kaikki vaihtoehdot käyty läpi. etenevä
Välittömän vasemman rekursion poisto etenevä ei selviä vasenrekursiivisista produktioista: Esim. A Ab a kääntyisi aliohjelmaksi, joka ensi töikseen kutsuu itseään. Tuollainen produktio voidaan korvata kahdella uudella produktiolla A aa ja A ε ba, missä A on uusi välikesymboli. Yleisesti mikä tahansa produktiojoukko etenevä A Aα 1 Aα n β 1 β m voidaan kirjoittaa muotoon A β 1 A β m A A ε α 1 A α n A missä α i ja β i ovat välike- ja päätesymbolien jonoja, jotka eivät ala A:lla, ja A on uusi välikesymboli. Epäsuora vasen rekursio pitää poistaa toisella menetelmällä, joka sivuutetaan tässä.
Usein on mahdollista päättää heti, onko jokin produktio mahdollinen. Esim. E + TE ei tule kyseeseen, jos seuraava merkki ei ole +. Tällöin kokeile-ja-peruuta on täysin älytön idea. Ideaalitilanne on, jossa peruutusta ei tarvita lainkaan. Tällöin kyse on ennustavasta jäsennyksestä (engl. predictive parsing). Onnistuu vain osalle kieliopeista. Seuraavilla kalvoilla oletetaan annetuksi jokin kielioppi G = (V, Σ, P, S) ja rakennetaan sille (jos mahdollista) ennustava jäsennin. etenevä
NULLABLE NULLABLE on taulukko, joka kertoo kullekin välikesymbolille, voidaanko siitä johtaa tyhjä merkkijono. kaikki välikemerkit ovat taulukossa merkitsemättömiä. Toista kunnes taulukko ei enää muutu: Toista kaikille produktioille A ω, jolle pätee, että A:ta ei ole merkitty: Jos ω ei sisällä yhtään päätemerkkiä ja sen kaikki välikemerkit on merkitty, merkitse A. etenevä
Yleistetty NULLABLE NULLABLE-taulukko voidaan yleistää pääte- ja välikemerkkien jonojen funktioksi seuraavalla rekursiivisella määritelmällä: etenevä NULLABLE(ε) = tosi NULLABLE(cω) = epätosi { NULLABLE(ω) NULLABLE(Aω) = epätosi jos NULLABLE[A] pätee muuten
FIRST FIRST on taulukko, joka kertoo kullekin välikesymbolille niiden päätemerkkien joukon, joilla kyseisestä välikesymbolista johdettavissa olevat merkkijonot voivat alkaa. Alusta kaikki muut taulukon paikat tyhjiksi. Toista kunnes taulukko ei enää muutu: Toista kaikille produktioille A s 1 s n, missä n 1 ja i {1,..., n} : s i Σ V 1. Olkoon k suurin joukossa {0,..., n}, jolle pätee, NULLABLE(s 1 s k ). 2. Lisää A:n kohdalle taulukkoon kaikki merkit FIRST[s 1 ] FIRST[s k ]. 3. Jos k < n ja s k+1 on päätemerkki, lisää A:n kohdalle taulukkoon lisäksi s k+1. 4. Jos k < n ja s k+1 on välikemerkki, lisää A:n kohdalle taulukkoon lisäksi FIRST[s k+1 ]. etenevä
Yleistetty FIRST FIRST-taulukko voidaan yleistää pääte- ja välikemerkkien jonojen funktioksi seuraavalla rekursiivisella määritelmällä: etenevä FIRST(ε) = FIRST(cω) = {c} { FIRST[A] FIRST(ω) FIRST(Aω) = FIRST[A] jos NULLABLE[A] pätee muuten
FOLLOW Myös FOLLOW on välikemerkeillä indeksoitu taulukko: FOLLOW[A] = { c Σ ω 1, ω 2 (V Σ) : S G ω 1 Acω 2 } { ω (V Σ) : S G ωa } etenevä missä V Σ merkitsee merkkijonon loppua. Alusta kaikki taulukon paikat tyhjiksi paitsi aloitussymbolin, jonka paikalle laita. Toista kunnes taulukko ei enää muutu: Toista kaikilla produktioilla A ω 1 Bω 2, missä A, B V ja ω 1, ω 2 (V Σ) : Lisää B:n kohdalle taulukkoon merkit FIRST(ω 2 ). Jos NULLABLE(ω 2 ) pätee, lisää B:n kohdalle taulukkoon myös kaikki A:n kohdalla olevat merkit.
taulukko rivi jokaiselle välikesymbolille sarake jokaiselle päätesymbolille (ynnä syötteen loppu) Merkitse produktio X ω riville X ja sarakkeeseen t etenevä jokaiselle t FIRST(ω), ja jos NULLABLE(ω), myös jokaiselle t FOLLOW(X).
Taulukon tulkinta Tee jokaiselle välikesymbolille aliohjelma. Aliohjelman alussa tee switch case kaikille päätesymboleille (ynnä syötteen päättymiselle). Jos välikesymbolin X ja päätesymbolin t risteyskohta sisältää yhden produktion, niin koodaa ko. produktio X:n aliohjelmaan t:n caseen. sisältää useamman kuin yhden produktion 4, koodaa produktiot X:n aliohjelmaan t:n caseen ja käytä peruutusta valinnan tekemiseen produktioiden välissä. on tyhjä, niin koodaa X:n aliohjelmaan t:n caseen kielioppivirheen diagnosointi. Jos taulukossa ei ole yhtään konfliktia, jäsennin on ennustava. etenevä 4 Tällöin taulukossa on konflikti.
LL(1) left-to-right parse, leftmost derivation, 1-token lookahead. Jos kieliopista johdettu ennustava taulukko on konfliktiton, ko. kielioppi on LL(1). Jos kielioppi ei ole LL(1), kannattaa kokeilla vasemman rekursion poistoa ja vasenta tekijöintiä (engl. left factoring). Monet hyödylliset kieliopit eivät ole LL(1). Moniselitteinen kielioppi ei ole koskaan LL(1). On mahdollista yleistää LL(n):ään, jolloin FIRST ja FOLLOW sisältävät n:n mittaisia sanasjonoja. Tämä laajentaa jäsennettävien kielten joukkoa. etenevä
: pro ja contra + Helppo koodata kieliopin perusteella käsin. + Tehokas ei peruutusta. Vaadittu LL(1)-kielioppi on usein varsin vaikeaselkoinen. Kieliopin muuttaminen voi johtaa vaikeaselkoisiin virheisiin, kun FIRST- ja FOLLOW-joukkojen muuttumista ei muisteta ottaa kaikkialla huomioon. Tämän poistaa LL-generaattorin käyttäminen (esim. ANTLR). Käsin kirjoitetun prediktiivisen jäsentimen muokkaaminen voi johtaa epäselvyyteen siitä, mitä kieltä se oikeasti jäsentää. Tämänkin poistaa LL-generaattorin käyttäminen (esim. ANTLR). etenevä