TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 3. toukokuuta 2011
Sisällys t
Chomskyn hierarkia kieli säännöllinen kontekstiton kontekstinen rekursiivisesti lueteltava automaatti äärellinen pino lineaarirajoitettu Turingin kone
Pieni koukkaus alkuun Mikä ihme on ε?
Sisällys t
Kieliopilla yksin ei pitkälle pötki Kontekstiton kielioppi vastaa kysymykseen, kuuluuko jokin merkkijono kieleen. Entä jos merkkijonoon pitäisi liittää jotain tulkintaa?
Kieliopilla yksin ei pitkälle pötki Kontekstiton kielioppi vastaa kysymykseen, kuuluuko jokin merkkijono 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.
Yksinkertainen laskin E T E 0.v := T 1.v E T + E E 0.v := T 1.v + E 1.v E T E E 0.v := T 1.v + E 1.v T F T 0.v := F 1.v T F T T 0.v := F 1.v T 1.v F c F 0.v := c 1.v F (E) F 0.v := E 1
Yksinkertainen laskin LL(1) E TE E 0.v := E 1.v E 1.l := T 1.v E ε E 0.v := E 0.l E +TE E 0.v := E 1.v E 1.l := E 0.l + T 1.v E TE E 0.v := E 1.v E 1.l := E 0.l T 1.v T FT T 0.v := T 1.v T 1.l := F 1.v T ε T 0.v := T 0.l T FT T 0.v := T 1.v T 1.l := T 0.l F 1.v T /FT T 0.v := T 1.v T 1.l := T 0.l/F 1.v F c F 0.v := c 1.v F (E) F 0.v := E 1.v
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. Produktion vasemman puolen symboliin viitataan alaindeksillä 0. Kunkin symbolin n:nteen esiintymiseen produktion oikealla puolella viitataan alaindeksillä n. 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.
Esimerkki taululla E TE E 0.v := E 1.v E 1.l := T 1.v E ε E 0.v := E 0.l E +TE E 0.v := E 1.v E 1.l := E 0.l + T 1.v E TE E 0.v := E 1.v E 1.l := E 0.l T 1.v T FT T 0.v := T 1.v T 1.l := F 1.v T ε T 0.v := T 0.l T FT T 0.v := T 1.v T 1.l := T 0.l F 1.v T /FT T 0.v := T 1.v T 1.l := T 0.l/F 1.v F c F 0.v := c 1.v F (E) F 0.v := E 1.v w = c + c c c 1.v = 1 c 2.v = 2 c 3.v = 3
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.
Sisällys t
t 1 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 1 Meinasin otsikoida tämän jäsenninloimet mutta tulin järkiini.
Laskin Yaccilla, osa 1 %{ #include <stdio.h> #define YYSTYPE double %} %token CONST EOL ERR %%
Laskin Yaccilla, osa 2 input : /**/ { } input EOL { } input error EOL { } input expression EOL { printf("%lg\n", $2); } expression : term { $$ = $1; } expression + term { $$ = $1 + $3; } expression - term { $$ = $1 - $3; } term : primary { $$ = $1; } term * primary { $$ = $1 * $3; } term / primary { if ($3 == 0) { fputs("division by zero.\n", stderr); YYERROR; } else { $$ = $1 / $3; } } primary : CONST { $$ = $1; } ( expression ) { $$ = $2; } %%
Laskin Yaccilla, osa 3 double getnum(void) {... } void yyerror(const char *s) {... } int yylex(void) {... } int main(void) { yyparse(); return 0; }
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 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, kuten esimerkissä. 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ä syntaksillä, 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. Sen tulee palauttaa seuraava päätemerkki taikka 0 (syöte päättyi).
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 (pistenotaatio kuten Earleyn algoritmissa): 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.
ajk@kukkaseppele:~/opetus/auki/koodit$ cat shiftred.y %token CONST %% expression : expression + expression expression - expression expression * expression expression / expression ( expression ) CONST ajk@kukkaseppele:~/opetus/auki/koodit$ byacc shiftred.y byacc: 16 shift/reduce conflicts.
reduce/reduce Yaccilla on päätettävänään, kumpaa seuraavista produktioista sovelletaan (pistenotaatio kuten Earleyn algoritmissa): A ω 1 A ω 2 Tämä tarkoittaa useimmiten, että kielioppi ei tee sitä, mitä tekijä haluaa.
ajk@kukkaseppele:~/opetus/auki/koodit$ cat redred.y %token ID %% prog : type expr type : ID expr : ID ajk@kukkaseppele:~/opetus/auki/koodit$ byacc redred.y byacc: 1 rule never reduced byacc: 1 reduce/reduce conflict.