Kääntäjätekniikka

Luento 6 (30.3.2017)

Rivinumerotiedon ylläpito

  • lekserin tehtävä pitää yllä rivi- ja sarakenumerotietoa
  • kannattaa tallettaa sanaseen lekseemin alku- ja loppupaikka (rivi ja sarake)
  • parserin kannattaa ottaa joka produktiossa ensimmäisen ja viimeisen symbolin paikkatieto ja rakentaa siitä produktion alku- ja loppupaikka (rivi ja sarake)
    • paikkatieto kannattaa tallentaa rakennepuun jokaiseen solmuun
  • ks

Valintalauseet

Perusrakenne kaikille tuttu if...then...else, josta else-osuus voidaan jättää pois:

<statement> ::= ...
              | if ( <expression> ) <statement>
              | if ( <expression> ) <statement> else <statement>

Tämä on moniselitteinen! Ns. dangling else. Ratkaistaan tavallisesti niin, että else paritetaan aina lähimpään iffiin, jolta else vielä puuttuu

Toinen vaihtoehto on pakottaa sulutus. Eri tapoja:

Perl-tyyli

<statement> ::= <if statement>
<if statement> ::= if ( <expression> ) <block> <else-part>
<else-part> ::= <empty>
              | else <block>
              | else <if statement>

Algol 68 -tyyli

<statement> ::= <if statement>
<if statement> ::= if <expression> then <statements> <else-part> fi
<else-part> ::= <empty>
              | else <statements>
              | elif <expression> then <statements> <else-part>

Digressio: Pascal, puolipiste ja dangling else

Pascalissa puolipiste on erotin, ei lopetin. Lisäksi Pascalissa on dangling else. Ks. FreePascalin wiki.

Digressio: LALR-jäsentimien konfliktit

  • LALR osaa käsitellä useita samalla tavalla alkavia ja samaan tilanteeseen sopivia produktioita yhtä aikaa.
    • mutta vain niin kauan, kun se ei joudu valitsemaan niiden välillä
  • shift/reduce-konflikti tarkoittaa, että jäsentimellä on tarkasteltavana yhtä aikaa produktio, joka päättyy nyt, ja produktio, joka jatkuu, eikä jäsennin tiedä, mitä pitäisi tehdä.
    • Useimmat työkalut tuottavat konfliktitilanteessa jäsentimen, joka valitsee shiftin (eli sen pidemmän produktion). Tämä ratkaisee esim. dangling else -tilanteen oikein.
  • reduce/reduce-konflikti tarkoittaa, että jäsentimellä on tarkasteltavana yhtä aikaa kaksi nyt päättyvää produktiota. LALR-jäsennin ei tiedä, kumpi pitäisi valita.
    • Älä koskaan jätä kielioppiisi yhtään reduce/reduce-konfliktia!
  • ks myös AuKi 29.9.2016 (luentovideo)

Tapausvalinta

<statement> ::= switch ( <expression> ) { <cases> }
<cases> ::= <case>
          | <cases> <case>
<case> ::= <case header> <statements>
<case header> := <case head>
               | <case header> <case head>
<case head> ::= case <constant expression> :
              | default :
  • Syntaksivaihtoehtoja on runsaasti.
  • C-sukuisissa kielissä tämä on määritelty idioottimaisesti. Tämä on parempi tapa.
  • Voi laajentaa Haskell-tyyliseksi pattern matchiksi jos haluaa.

Esittelyt ja määrittelyt

  • Esittely (engl. declaration) kertoo, että jokin nimi on olemassa ja millainen se on
  • Määrittely (engl. definition) on esittely, joka lisäksi luo otuksen (arvo, olio, luokka, funktio tms), johon esitelty nimi viittaa
  • Sekä esittely että määrittely voi olla
    • paikallinen eli lokaali, jonka näkyvyys on rajattu sisimpään lohkoon, jonka sisällä esittely tai määrittely on
    • päätasolla (at toplevel), jolloin se ei sisälly mihinkään lohkoon
  • Yleensä vain osa esittelyistä on sallittuja paikallisessa määrittelyssä.
    • Lisäksi yleensä rekursio on sallittu vain päätasolla.

Muuttujamäärittelyt

Yksinkertaistettu C-tyyli

<declaration> ::= <type> <identifier> = <expression> ;
                 | auto <identifier> = <expression> ;
                 | <type> <identifier> ;                      

Yksinkertaistettu Pascal/Haskell-tyyli

<declaration> ::= <identifier> : <type> = <expression> ;
                | <identifier> = <expression> ;
                | <identifier> : <type> ;

Lisäksi kummassakin tarvittaessa aloittava avainsana.

Funktiomäärittelyt

Huom! Paikalliset funktiomäärittelyt johtavat helposti ensiluokkaisiin funktioihin (nostaa vaativuutta myöhemmissä vaiheissa, ks OKP 2.2.2017)

Yksinkertaistettu C-tyyli

<declaration> ::= <type> <identifier> ( <optional parameter list> ) <block>
<optional parameter list> ::= <empty>
                            | <parameter list>
<parameter list> ::= <parameter>
                   | <parameter list> , <parameter>
<parameter> ::= <type> <identifier>

Yksinkertaistettu Pascal-tyyli

<declaration> ::= function <identifier> ( <optional parameter list> ) : <type> <block>
                | procedure <identifier> ( <optional parameter list> ) <block>
<parameter> ::= <identifier> : <type>

Tyyppilausekkeet

  • Jo määriteltyihin (ja kieleen pultattuihin) tyyppeihin viitataan tyyppilausekkeilla
    • edellä välikesymboli <type>
  • Tyyppilausekkeet voidaan analysoida operaattorimallin kautta.
  • Atomityyppilausekkeita:
    • kieleen pultattu tyyppi (avainsana), esim. bool, int
    • nimellä viittaus aiemmin määriteltyyn tyyppiin

Taulukkotyyppi

  • Taulukkotyyppiin kuuluu tavallisesti vähintään tieto dimensioiden lukumäärästä sekä alkiotyyppi
  • Joskus tyyppiin kuuluu myös tieto indeksirajoista.

Yksinkertaistettu C-tyyli

<type> ::= <type> [ <optional constant expression> ]

Yksinkertaistettu Pascal-tyyli

<type> ::= array [ <list of ranges> ] of <type>
<list of ranges> ::= <range>
                   | <list of ranges> , <range>
<range> ::= <constant expression> .. <constant expression>

Funktiotyyppi

Huom! Funktiotyyppi voi tuoda mukanaan ensiluokkaisten funktioiden ongelman, joka tekee myöhemmistä vaiheista haastavampia.

Yksinkertaistettu C-tyyli

<type> ::= <type> ( <optional parameter type list> )
<optional parameter type list> ::= <empty>
                                 | <parameter type list>
<parameter type list> ::= <parameter type>
                        | <parameter type list> , <parameter type>
<parameter type> ::= <type>

Yksinkertaistettu Haskell-tyyli

<type> ::= ( <optional parameter type list> ) -> <type>

Tyyppinimet

  • Ohjelmoijan nimeämien tyyppien kannattaa olla leksikaalisesti tunnistettavissa eroon muuttujannimistä, esim.
    • muuttujannimet alkavat pienellä kirjaimella
    • tyypinnimet alkavat isolla kirjaimella
  • Vaihtoehtoisesti jäsentäjä voi kertoa selaajalle, että nyt tuli ohjelmoijalta uusi tyyppinimi.
  • Näin vältetään monia harmaita hiuksia jäsentimen kehityksessä!

Tietuetyypin määrittely

Määritellään tietuetyypin nimi, kenttien nimet ja kenttien tyypit. Esim:

<declaration> ::= struct <typename> { <member declarations> }
<member declarations> ::= <empty>
                        | <member declarations> <member declaration>
<member declaration> ::= <type> <identifier> ;

Tämä olettaa, että tyyppinimet on tunnistettavissa esim. kirjainkoolla muuttujista.

Luokkatyyppi periaatteessa kuten tietue, mutta mukaan tulee metodimäärittelyt, konstruktorien määrittelyt ym. sekä näkyvyysmäärittelyt (public jne).

Varianttityypin määrittely

Variantti on kuten tietue mutta vain yksi kentistä on kerrallaan aktiivinen. Esim:

<declaration> ::= union <typename> { <member declarations> }

Tyypinmäärittelyn lisäksi on määriteltävä, millä syntaksilla variantti luodaan ja kuinka variantin data luetaan. Turvallisuudesta on huolehdittava: variantin luojan tulee kertoa, mikä kenttä tulee aktiiviseksi (ja mielellään alustaa se), ja variantin lukemisen tulee tarkistaa, että haettu kenttä on aktiivinen. Yksi mahdollinen syntaksi:

<expression> ::= <typename>.<identifier> ( <expression> )
<case head> ::= case <identifier> ( <identifier> ) :

These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.