TIEA241 Automaatit ja kieliopit, kevät 2012 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 12. kesäkuuta 2013
Sisällys t
Chomskyn hierarkia (ja vähän muutakin) kieli säännöllinen LL(k) LR(1) kontekstiton kontekstinen rekursiivisesti lueteltava automaatti äärellinen (ennustava jäsennin) deterministinen pino pino lineaarirajoitettu Turingin kone
Viimeksi päästiin tähän asti E TE E ε + TE TE T FT T ε FT / FT F P F P c (E) NULLABLE FIRST FOLLOW E, c, ( ), E +, ), T, c, ( +,, ), T, / +,, ), F, c, (, /, +,, ), P c, (, /, +,, ),
Ennustava jäsennystaulukko rivi jokaiselle välikesymbolille sarake jokaiselle päätesymbolille (ynnä syötteen loppu) Merkitse produktio X ω riville X ja sarakkeeseen t 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 1, 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. 1 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.
Ennustava jäsennys: 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).
Kieliopilla yksin ei pitkälle pötki Kontekstiton kielioppi tuottaa merkkijonoja ja sen avulla voidaan selvittää, kuuluuko merkkijono sen kieleen. Entä jos merkkijonoon pitäisi liittää jotain tulkintaa? Voidaan toki rakentaa jäsennyspuu ja sitten määritellä tulkinta sen mukaan. Vähän tähän tapaan käytännössä toimitaankin! Yksi samankaltainen idea on attribuuttikieliopit. Donald E. Knuth: Semantics of Context-Free Languages. Mathematical systems theory, 2 (2), 1968.
Lähtökohtana kontekstiton kielioppi. Kullekin välike- ja päätesymboleille voidaan määritellä attribuutteja kuin olioluokille. Kukin attribuutti on joko synteettinen taikka periytyvä. Jokaiseen produktioon liitetään laskentasääntöjä: Laskentasääntö voi lukea produktion vasemman puolen symbolin periytyviä attribuutteja oikean puolen symbolien synteettisiä attribuutteja Laskentasääntöjen tulee kirjoittaa kuhunkin produktion vasemman puolen symbolin synteettiseen attribuuttiin oikean puolen symbolin periytyvään attribuuttiihin Laskentasäännöllä ei saa olla sivuvaikutuksia. Laskentasäännön käyttäytyminen saa riippua vain produktion symboleiden kenttien arvoista.
Huomioita Laskentasäännöt kirjoitetaan produktion oikealle puolelle. Jos sama symboli esiintyy useamman kerran produktiossa, tulee esiintymät erottaa toisistaan alaindeksein. Tarkka syntaksi vaihtelee lähteen mukaan. Nämä pätevät tällä kurssilla.
Laskentaperiaate 1. Selvitä ensiksi syötemerkkijonon jäsennyspuu. Jokainen puun solmu on oma olionsa attribuuttien näkökulmasta. 2. Alusta puun kaikkien solmujen kaikki attribuutit. Välikesymbolien attribuutit alustetaan määrittelemättömiksi. Päätesymbolien attribuutit johdetaan syötteestä. 3. Kunnes kaikkien solmujen kaikki attribuutit on määritelty, toista: 3.1 Valitse jokin solmu X, jolla on lapset α 1,..., α n, sekä produktion X α 1 α n laskentasääntö r, joille pätee solmun X ja sen lapsien kontekstissa: Attribuutti, jonka r laskee, on määrittelemätön Attribuutit, jotka r lukee, on määritelty. 3.2 Suorita laskentasääntö r solmun X ja sen lapsien kontekstissa.
Attribuutit ennustavassa jäsennyksessä Synteettiset attribuutit on luonnollisinta käsitellä jäsennysaliohjelman paluuarvona. Periytyvät attribuutit ovat luonnollisimmin jäsennysaliohjelman parametreja. Tämä ei tokikaan tue kaikkia mahdollisia attribuuttikielioppeja!
Rajoitettuja versioita S-attributoitu kielioppi on sellainen, jossa ei lainkaan esiinny periytyviä attribuutteja. L-attributoitu kielioppi on sellainen, jossa attribuutit voidaan laskea yhdellä puun läpikäynnillä vasemmalta oikealle.
t 2 engl. parser generators ohjelmia, jotka tuottavat (attribuutti)kieliopin perusteella jäsenninohjelman yksi vanhimmista edelleen käytössä olevista on Yacc, joka tuottaa C-kieltä moderneja versioita ovat Byacc ja GNU Bison Javaa tuottavat mm. Antlr, CUP, SableCC useimmille kielille on omansa, jotkin (kuten Antlr) tukevat useita 2 Meinasin otsikoida tämän jäsenninloimet mutta tulin järkiini.
Yacc Yacc-tiedosto jakaantuu kolmeen osaan, joiden välissä on symboli %%. Ensimmäisessä osassa on erilaisia määrittelyjä. Toisessa osassa on varsinainen kielioppi. Kolmas osa on vapaata C-koodia. Kielioppi on kontekstiton. jäsennysalgoritmina LALR jäsennys etenee jäsennyspuun lehdistä kohti juurta Yacc ei tosin konstruoi puuta tietorakenteeksi asti Yacc tykkää vasenrekursiosta, joten sitä ei ole syytä poistaa Produktion perään voidaan kirjoittaa aaltosulkeisiin C-koodia, joka suoritetaan, kun produktiota vastaava solmu lisätään puuhun. muistuttaa S-attributoitua kielioppia $n tarkoittaa oikean puolen n:nnen symbolin arvoa $$ tarkoittaa vasemman puolen symbolin arvoa
Yacc vs attribuuttikieliopit Yaccissa ei ole attribuutteja, mutta symboleilla on kokonaislukuarvo. Symbolien yhteisen tyypin vaihtaminen onnistuu määrittelemällä YYSTYPE Kullekin symbolille voidaan myös (hankalammin) määritellä oma tyyppinsä. Symbolin tyyppi voi olla tietue, jolloin symbolilla voi ajatella olevan attribuutteja. Yacc ei tue periytyviä attribuutteja. On tosin olemassa yksi überhaxxor-keino, jota ei oikein uskalla suositella... Yacc ei rajoita produktioon liitetyn koodinpätkän tekemisiä. Sivuvaikutukset ovat sallittuja ja yleisesti käytettyjä.
Yacc ja syötteen luku Yaccin mielestä symbolit ovat kokonaislukuja. Päätesymboleita ovat kaikki syötemerkistön merkit (kokonaisluvuksi muutettuna), joihin viitataan C-tyylisellä syntaksilla, esim. + erikseen %token-määrittelyllä luetellut symboliset päätesymbolit (joille Yacc määrittelee automaattisesti kokonaislukutunnuksen) Kun Yacc haluaa tietää syötteen seuraavan merkin, se kutsuu (ohjelmoijan laatimaa) aliohjelmaa yylex ilman parametreja. 3 Sen tulee palauttaa seuraava päätemerkki taikka 0 (syöte päättyi). 3 Sen voi laatia käsin tai käyttämällä esimerkiksi Flex-ohjelmaa.
Konfliktit Yaccissa Yacc käyttää LALR-jäsennystä Laajempi kuin LL(1): vasenrekursio on sallittua, jopa toivottavaa. LALR on useimpiin tietokonekielten käsittelytilanteisiin riittävä. LALR ei kuitenkaan tue kaikkia kontekstittomia kielioppeja. Jos kielioppi ei ole LALR-kielioppi, Yacc antaa virheilmoituksen. Virheitä on kahta tyyppiä: shift/reduce ja reduce/reduce.
shift/reduce Yaccilla on päätettävänään, kumpaa seuraavista produktioista sovelletaan (pistettä seuraava merkki on tarkastelun kohteena): A ω 1 A ω 1 ω 2 Reduce tarkoittaa ensimmäisen valintaa, shift jälkimmäisen. Yacc valitsee oletuksena shiftin mutta valittaa siitä huolimatta. Tämä on usein se, mitä haluttiin, mutta parempi olisi kirjoittaa kielioppi sellaiseksi, että konflikti katoaa.
reduce/reduce Yaccilla on päätettävänään, kumpaa seuraavista produktioista sovelletaan: A ω 1 A ω 2 Tämä tarkoittaa useimmiten, että kielioppi ei tee sitä, mitä tekijä haluaa.