TIE448 Kääntäjätekniikka, syksy 2009 Antti-Juhani Kaijanaho TIETOTEKNIIKAN LAITOS 22. syyskuuta 2009
Sisällys
Sisällys
Seuraava deadline Vaihe B tiistai 6.10. klo 10 selaaja ja jäsentäjä toimivat
Kääntäjän rakenne lähdeohjelma SELAAJA sanasjono JÄSENTÄJÄ VÄLIKOODIN GENEROIJA rakennepuu välikoodi KOHDEKOODIN GENEROIJA kohdeohjelma TARKASTAJA SOKERINPILKKOJA OPTIMOIJA OPTIMOIJA
Jäsentäjän tehtävät selaajan tuottaman sanasjonon muuttaminen ohjelman loogista rakennetta kuvaavaksi tietorakenteeksi ohjelmassa olevien kielioppivirheiden tunnistaminen ja diagnosointi tällä luennolla kieliopit, seuraavalla jäsennysalgoritmit
Sisällys
Johdantoa Tämä on toivottavasti enimmäkseen tuttua asiaa Automaatit ja kieliopit -kurssilta. AuKi käsittelee teorian kannalta, tällä kurssilla ollaan käytännönläheisiä.
Kontekstiton kielioppi Kontekstiton kielioppi koostuu joukosta päätesymboleja (engl. terminal symbols), joukosta välikesymboleja (engl. nonterminal symbols), joukosta produktioita, joista kukin koostuu yhdestä välikesymbolista, jota seuraa nuoli oikealle, jota seuraa (mahdollisesti tyhjä) jono pääte- ja välikesymboleja
Esimerkki kaksi päätesymbolia: 0 ja 1 yksi välikesymboli: binääriluku neljä produktiota: 1. binääriluku 0 2. binääriluku 1 3. binääriluku binääriluku 0 4. binääriluku binääriluku 1
Tulkinta Välikesymboli on kielen nimi. Jos välikesymboleja on useita, kukin niistä nimeää eri kielen. Toimi näin: 1. Kirjoita paperille jokin välikesymboli. 2. Etsi kieliopista produktio, jossa on jokin paperilla oleva välikesymboli nuolen vasemmalla puolella. 3. Korvaa paperilla ko. välikesymbolin nimi ko. produktion oikealla puolella. 4. Toista kohtia 2 3 kunnes paperilla ei ole enää välikesymboleja.
Esimerkki 1. binääriluku 0 2. binääriluku 1 3. binääriluku binääriluku 0 4. binääriluku binääriluku 1 binääriluku binääriluku 0 binääriluku 1 0 binääriluku 1 1 0 binääriluku 0 1 1 0 1 0 1 1 0
Aritmeettinen lauseke seitsemän päätesymbolia: +, -, *, /, (, ), VAKIO VAKIO on selaajan tuottama vakiosananen; sen lukuarvolla ei ole jäsennyksen kannalta merkitystä. Seuraavissa esimerkeissä VAKIO merkitään kuitenkin lukuna. yksi välikesymboli: lauseke kuusi produktiota: 1. lauseke VAKIO 2. lauseke lauseke + lauseke 3. lauseke lauseke - lauseke 4. lauseke lauseke * lauseke 5. lauseke lauseke / lauseke 6. lauseke ( lauseke )
Tulkintaesimerkki 1. lauseke VAKIO 2. lauseke lauseke + lauseke 3. lauseke lauseke - lauseke 4. lauseke lauseke * lauseke 5. lauseke lauseke / lauseke 6. lauseke ( lauseke ) lauseke lauseke + lauseke lauseke + lauseke * lauseke 1 + lauseke * lauseke 1 + 2 * lauseke 1 + 2 * 3
Yhteenveto Kielioppi määrittelee (yhden tai useamman) kielen. Se koostuu produktioista. Produktioilla on vasen ja oikea puoli. Vasemmalla puolella on yksi välikesymboli. Oikealla puolella on välike- ja päätesymboleja. Välikesymboli voidaan kieliopin avulla avata joksikin sen kieleen kuuluvaksi merkkijonoksi. Tämä prosessi on epädeterministinen.
Backus Naur Form (BNF) 1 kontekstittomien kielioppien esitystapa eli ns. syntaktinen metakieli välikesymbolien nimet kulmasulkeissa (esim. <lauseke>) joskus myös sellaiset päätesymbolit, jotka edustavat useita lekseemejä (esim. <vakio>) metasymboleja ::= ja, muut symbolit edustavat itseään produktiot, joilla on yhteinen vasen puoli, kootaan yhdeksi ilmaisuksi seuraavasti: ilmaisu alkaa yhteisellä vasemmalla puolella sitä seuraa metasymboli ::= sitä seuraavat produktioiden oikeat puolet toisistaan metasymbolilla erotettuna 1 Peter Naur (ed.): The revised report on the algorithmic language Algol 60. Communications of the ACM 6 (1), 1963. Ks. myös Donald E. Knuth: Backus Normal Form vs. Backus Naur Form. Communications of the ACM 7 (12), 1964.
Aritmeettiset lausekkeet BNF:llä <lauseke> ::= <vakio> <lauseke> + <lauseke> <lauseke> - <lauseke> <lauseke> * <lauseke> <lauseke> / <lauseke> ( <lauseke> ) tässä <vakio> on nimetty päätesymboli
C-standardin metasyntaktinen kieli 2 Itseään edustavat päätesymbolit esitetään lihavoidulla tasavälisellä fontilla. Välikesymbolit sekä lekseemijoukkoa edustavat päätesymbolit esitetään kursiivilla. Yhteisen vasemman puolen omaavat produktiot esitetään seuraavasti koostettuna: Yhteinen vasen puoli aloittaa. Sen perään kirjoitetaan kaksoispiste. Kukin oikea puoli kirjoitetaan omalle rivilleen hieman sisennettynä. Oikeita puolia ei saa rivittää 2 En tiedä, mistä tämä on peräisin, mutta sitä käyttävät ainakin C-, C++-, C#- ja Java-määrittelyt sekä jotkin kyseisistä kielistä kertovat kirjat.
C-standardin metasyntaktinen kieli II Jos yhteisen vasemman puolen omaavien produktioiden oikeat puolet koostuvat kaikki yhdestä päätesymbolista, voidaan oikeat puolet luetella samalla rivillä (tai useammalla rivillä). Tällöin kaksoispisteen perään kirjoitetaan one of. Produktion oikealla puolella voidaan osoittaa symbolin valinnaisuus liittämällä sen perään alaindeksi opt.
Esimerkki expression: constant sign opt expression expression + expression expression - expression expression * expression expression / expression ( expression ) sign: one of + -
Muita syntaktisia metakieliä Myös monia muita kuin alkuperäinen Backus Naur Form kutsutaan (virheellisesti) BNF:ksi. Vuosikymmenten kuluessa on laadittu useita laajennettuja BNF:ejä, esim. Information Technology Syntactic Metalanguage Extended BNF. International Standard ISO/IEC 14977:1996. D. Crocker (ed.): Augmented BNF for Syntax Specifications: ABNF. RFC 5234, 2008. Internet Standard (STD 68).
Esimerkki STD 68:n ABNF:stä 3 date-time = [ day-of-week "," ] date time [CFWS] day-of-week = ([FWS] day-name) / obs-day-of-week day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" date = day month year day = ([FWS] 1*2DIGIT FWS) / obs-day month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" year = (FWS 4*DIGIT FWS) / obs-year time = time-of-day zone time-of-day = hour ":" minute [ ":" second ] hour = 2DIGIT / obs-hour minute = 2DIGIT / obs-minute second = 2DIGIT / obs-second zone = (FWS ( "+" / "-" ) 4DIGIT) / obs-zone 3 P. Resnick (ed): Internet Message Format. RFC 5322, 2008. Internet Draft Standard.
Kielioppimalleja Valinnaisuus <optional-semicolon> ::= <empty> ; <empty> ::= Toisto (tyhjä sallittu) <stmts> ::= <empty> <stmts> <stmt> <empty> ::= Epätyhjä lista (pilkulla erotettu) <list> ::= <item> <list>, <item>
Sisällys
Jäsennyspuu lauseke lauseke lauseke lauseke lauseke lauseke 2 * ( 3 + 4 ) Lausekkeen 2 * ( 3 + 4 ) jäsennyspuu.
Jäsennyspuun määritelmä Jäsennyspuu on puu, jonka lehtisolmut ovat päätesymboleja, jonka sisäsolmut ovat välikesymboleja ja jossa jokaiselle sisäsolmulle pätee seuraavaa: kun ko. solmu katsotaan produktion vasemmaksi puoleksi ja sen lasten katsotaan muodostavan produktion oikean puolen, löytyy tuo produktio kieliopista.
Kumpi on lausekkeen 2+3*4 jäsenyyspuu? lauseke lauseke lauseke lauseke lauseke 2 + 3 * 4 lauseke lauseke lauseke lauseke lauseke 2 + 3 * 4
Moniselitteiset kieliopit Vastaus edelliseen kysymykseen on molemmat. Edellä esitetty lausekkeiden kielioppi ei kerro, missä järjestyksessä laskutoimitukset tulee jäsentää. Kielioppi on moniselitteinen, jos samalla merkkijonolla on sen mukaan ainakin kaksi erilaista jäsennyspuuta. Pääsääntöisesti moniselitteiset kieliopit ovat hyödyllisiä lähinnä luonnosteluvaiheessa. Varsinaisessa toteutusvaiheessa tarvitaan yksiselitteinen kielioppi.
Operaattorikieliopit Operaattorikieliopilla tarkoitetaan tässä sellaista kielioppia, jossa on vain yksi välikesymboli (merkitään sitä seuraavassa E:llä); (vain) seuraavanlaisia produktioita E E on prefiksioperattori E E on postfiksioperaattori E E E on infiksioperaattori E on primäärilauseke missä on (mahdollisesti tyhjä) symbolijono, joka ei ala eikä lopu E:hen; yleensä (mutta ei aina) yksi päätesymboli; sekä jokaiselle operaattorille määritelty presedenssi ja assosiatiivisuus.
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.
Operaaattoreiden presedenssi Jos operaattorilla on suurempi presedenssi kuin operaattorilla, niin lauseke a b c tulkitaan (a b) c. Presedenssin tulee olla operaattorien presedenssiekvivalenssiluokkien täydellinen järjestysrelaatio. Yleensä helppo esittää taulukkomuodossa, esim. aritmetiikassa: / +
Operaattoreiden assosiatiivisuus Määritellään operaattoreille assosiatiivisuus: Operaattori assosioi vasemmalle, jos a b c tulkitaan (a b) c. Operaattori assosioi oikealle, jos a b c tulkitaan a (b c). Operaattori ei assosioi, jos a b c on diagnosoitava kielioppivirheeksi. Useimmat operaattorit assosioivat vasemmalle. Prefiksioperaattorit assosioivat joko oikealle tai eivät ollenkaan. a on ( a) jos mitään Postfiksioperaattorit assosioivat joko vasemmalle tai eivät ollenkaan. a on (a ) jos mitään
C-kielen operaattoritaulukko operaattorit (E on lauseke, T on tyyppi) ass. [E] (E,...,E). -> ++ -- postf. (vas.) sizeof & * + - ~! ++ -- pref. (oik.) (T) pref. (oik.) * / % vasemmalle + - vasemmalle << >> vasemmalle < <= > >= vasemmalle ==!= vasemmalle & vasemmalle ^ vasemmalle vasemmalle && vasemmalle vasemmalle?e: vasemmalle = *= /= %= += -= <<= >>= &= ^= = oikealle, vasemmalle
Operaattorikieliopin yksiselitteistäminen 1 Numeroidaan presedenssitasot 1,..., n, missä pienempi luku tarkoittaa korkeampaa presedenssitasoa. Luodaan kullekin presedenssitasolle i uusia välikesymboleja: E i joka tapauksessa, E l i (sekä produktio E i E l i ), jos presedenssitasolla on vasemmalle assosioivia operaattoreita, E r i (sekä produktio E i E r i ), jos presedenssitasolla on oikealle assosioivia operaattoreita, ja E n i (sekä produktio E i E n i ), jos presedenssitasolla on ei-assosioivia operaattoreita.
Operaattorikieliopin yksiselitteistäminen 2 Kullekin välikesymbolille E x i (missä x = l, r, n) lisätään produktio E x i E i 1. Kullekin presedenssitason i operaattorille lisätään produktio taulukon mukaisesti: assosioi fiksi produktio vasemmalle in E l i E l i E i 1 oikealle in E r i E i 1 E r i ei in E n i E i 1 E i 1 kyllä post E l i E l i ei post E n i E i 1 kyllä pre E r i E r i ei pre E n i E i 1
Operaattorikieliopin yksiselitteistäminen 3 Luodaan lisäksi uusi välikesymboli E 0. Jokaiselle primäärilausekkeelle lisätään produktio E 0. Lisätään vielä produktio E E n. Poistetaan lopputuloksesta tarpeettomat välikesymbolit ja produktiot. Annetaan luoduille välikesymboleille kuvaavat nimet (jos mahdollista).
Esimerkki primary-expression: constant ( expression ) multiplicative-expression: primary-expression multiplicative-expression * primary-expression multiplicative-expression / primary-expression multiplicative-expression % primary-expression additive-expression: multiplicative-expression additive-expression + multiplicative-expression additive-expression - multiplicative-expression expression: additive-expression
Roikkuva else <statement> ::= print <expression> ; { <statement-list> } if ( <expression> ) <statement> if ( <expression> ) <statement> else <statement> <statement-list> ::= <empty> <statement-list> <statement> <empty> ::= Kysymys Mitä tulostaa if (a) if (b) print 1; else print 2; jos a on epätosi? (a) 2 (b) ei mitään
Ratkaisu Yleensä valitaan (b), jolloin else paritetaan sisimmän parittoman if:n kanssa. Tämän yksiselitteistäminen ei onnistu operaattorikieliopin tekniikalla Harjoitustehtävä: miksi ei? Yksiselitteistys tehdään pakottamalla then-lauseen pariutuminen: <statement> ::= <matched-statement> <open-statement> <matched-statement> ::= print <expression> ; { <statement-list> } if ( <expression> ) <matched-statement> else <matched-statement> <open-statement> ::= if ( <expression> ) <statement> if ( <expression> ) <matched-statement> else <open-statement>
Sisällys
Tietorakenne-esityksessä ei yleensä tarvita kaikkea tekstiesityksen yksiselitteistämiseen käytettyä tauhkaa. Esimerkkinä lausekkeiden sulkeet: niillä ei ole mitään semanttista sisältöä sen jälkeen, kun lausekkeen rakenne on jäsennetty. Siksi kääntäjän sisäinen esitysmuoto ei ole yleensä ohjelman (konkreetti) jäsennyspuu vaan ns. abstrakti jäsennyspuu eli rakennepuu.
Jäsennyspuu versus rakennepuu lauseke lauseke lauseke lauseke lauseke 2 + 3 * 4 + 2 * 3 4
Rakennepuu Lausekkeen rakennepuu on puu, jonka sisäsolmut ovat operaattoreita, ja operaattorin lapsisolmut ovat sen operandilausekkeiden rakennepuiden juuret Yleistyy totta kai myös lauseisiin, määrittelyihin ym.
Abstrakti kielioppi Otetaan lähtökohdaksi yksikäsitteistämistoimenpiteistä siistitty kielioppi (esim. vain E, ei E 0 :aa). Poistetaan produktiot, joilla ei ole semanttista sisältöä (esim. E (E)). Haluttaessa poistetaan päätesymboleita, joilla ei ole muuta tehtävää kuin yksikäsitteistäminen. Tuloksena kielen abstrakti kielioppi. yleensä erittäin moniselitteinen kuitenkin ok, jos kaksi saman välikesymbolin produktiota voidaan erottaa toisistaan pelkästään oikean puolen terminaalien jonon perusteella Esim. E E E ja E E eivät ole ok; tällöin annettava toiselle -operaattorille (väliaikainen) uusi nimi.
Composite-suunnittelumalli 4 Javan kaltaisessa oliokielessä hyvä tapa toteuttaa rakennepuu on Composite-suunnittelumalli. Ideana on abstrakti yläluokka, jonka aliluokkia ovat niin puun lehti- kuin sisäsolmutkin. Hyvä luoda abstrakti yläluokka jokaiselle abstraktin kieliopin välikesymbolille, ja aliluokka jokaiselle sen olennaiselle produktiolle. Yläluokkaan voi lisätä abstraktin metodin kullekin rakennepuuta syötteenään käyttävälle kääntäjän osalle (esim. tarkastaja, välikoodin generoija). hyvä ratkaisu, jos kieli muuttuu useammin kuin sitä käsittelevät algoritmit 4 Gamma, Helm, Johnson, Vlissides: Design Patterns. Addison-Wesley 1995. Sivut 163 173.
Visitor-suunnittelumalli 5 Rakennepuuhun kohdistuvien operaatioiden erottaminen ohjelmatekstissä rakennepuun määrittelystä on usein hyödyllistä. Tämän toteuttaa Visitor: rajapinta, jossa on visit-metodi jokaiselle rakennepuu-compositen lehtisolmuluokalle. Lisäksi Compositen yläluokassa on abstrakti metodi accept, joka toteutetaan jokaisessa lehtisolmuluokassa täsmälleen samalla koodilla. Tässä hyödynnetään metodin ylikuormitusta, joten saman koodin siirtäminen yläluokkaan rikkoo Visitorin. Jokainen rakennepuusta erilleen koodattava operaatio on yksi Visitor-rajapinnan toteuttava luokka. esimerkki: mallikääntäjän ExpressionPrinter 5 Gamma ym.: Design Patterns, emt. Sivut 331 344.
Rakennepuu Javassa public abstract class Expression { public final Pos pos; protected Expression(Pos pos) { this.pos = pos; } public abstract <T> T accept(expressionvisitor<t> v); } public final class AdditionExpression extends Expression { public final Expression lrand; public final Expression rrand; public AdditionExpression(Expression l, Expression r, Pos pos) { super(pos); lrand = l; rrand = r; } public <T> T accept(expressionvisitor<t> v) { return v.visit(this); } } public final class VariableExpression extends Expression { public final String name; public VariableExpression(String name, Pos pos) { super(pos); this.name = name; } public <T> T accept(expressionvisitor<t> v) { return v.visit(this); } } public interface ExpressionVisitor<T> { T visit(additionexpression e); T visit(variableexpression e); }
Sisällys
Seuraava deadline Vaihe B tiistai 6.10. klo 10 selaaja ja jäsentäjä toimivat