TIES448 Luento 2 (1.11.2018)
Palataas vielä tiistain luennon asiaan
Harjoitustyöstä
Lähdekieli
- suositeltavaa ottaa olemassaoleva ohjelmointikieli pohjaksi
- suunnitelmassa hahmoteltava poikkeamat pohjaksi otetusta kielestä
- Kurssin aikana keretään todennäköisesti toteuttaa vain osa kielestä, joten halutut ominaisuudet on hyvä priorisoida.
Hyviä kohdekieliä
- Voitte valita, että teette tulkin (joko suoraan taikka oman välikielen kautta).
- Standardoitu välikieli (LLVM, Java bytecode, .NET CIL, webassembly)
- Oikea konekieli (lähinnä AMD64 ja ARM)
- sallittua on tuottaa symbolista konekieltä,
- suositeltavaa, että tällöin kääntäjä kutsuu assembleria automaattisesti
- kiinnitä myös käyttöjärjestelmä (esim. Linux tai Windows)
Isäntäkielen valinnasta
- mikä tahansa ohjelmointikieli, jonka tulkkaaminen tai kääntäminen onnistuu luentosalissa (viimeistään omalla läppärillä)
- kiinnitä huomiota:
- Kuinka hyvin kieli tukee laajojen ohjelmien tekemistä?
- Kuinka helppoa sillä on käsitellä puu- ja graafitietorakenteita?
- Onko sille olemassa kielioppityökaluja (vrt. yacc, CUP)?
- Kuinka hyvin osaat kieltä jo valmiiksi?
- Suosittelen Haskellia ja muita ML-suvun kieliä (SML, OCaml),
- vain jos osaat niitä jo valmiiksi.
Vastuun jako
Ryhmässä on hyvä jakaa seuraavat vastuut:
- ryhmänjohtaja, joka huolehtii siitä, että tekeminen etenee ja ratkaisee kiistat siitä, kuka tekee mitäkin
- (lähde)kielitsaari, joka ratkaisee kiistat lähdekielen ominaisuuksista
- testi-inkvisiittori, joka vastaa testauksen kattavuudesta
- kohdekieliguru, joka tutustuu kohdekielen saloihin ja selvittää tarvittaessa vastaukset kohdekieltä koskeviin kysymyksiin
- työkaluguru, joka tutustuu valittuihin työkaluihin ja selvittää tarvittaesa vastaukset työkaluja koskeviin kysymyksiin
Luonnollisesti samalla henkilöllä voi olla useita rooleja. Jokainen tietenkin osallistuu itse tekemiseen!
Harjoitustyöstä selviälminen
- aloita heti
- älä pysähdy
- ajattele mitä teet
- älä pelästy epämukavuuden tunnetta
- pyydä apua ajoissa
- pidä tuntikirjanpitoa
- jos op-määrät ylittyvät perustellusti, voidaan harkita ylimääräisten opintopisteiden antamista toisella koodilla (esim. ohjelmointityö tai erikoistyö)
Muista testaus!
- Kääntäjän tulee toimia (lähes) kaikissa tilanteissa oikein.
- Kääntäjä on monimutkainen ja siksi virheherkkä.
- Automaattinen testaus on siksi erittäin suositeltavaa.
- perustestit ennen koodia tai pian koodin kirjoittamisen jälkeen
- joka bugista testi ennen korjausta, jotta bugin uusiutuminen huomattaisiin
- kaikki testit hyvä ajaa aina käännöksen jälkeen
- feilaavaan testiin puututtava välittömästi (ei saa jättää myöhemmäksi)
Perustestit
- Pyri tekemään ainakin yksi testi jokaisesta toiminnosta.
- Älä kuitenkaan tuhlaa aikaasi sellaisen asian testaamiseen, joka ei voi hajota (ellei osoittaudu, että olet väärässä)
- Jos et ole varma, osaatko koodata jonkin asian oikein, tee siitä testi.
- Priorisoi virheherkkiä tilanteita.
- Rajankäynnit ovat usein virheherkkiä – niihin kannattaa kiinnittää huomiota.
- tarkemmin testauskursseilla (TIES546, TJTS50)
Selaajan tehtävät
- lähdeohjelman lukeminen tietovarastosta
- joskus tosin esikäsittelyvaiheita (esim. C-esikääntäjä)
- mahdollisten merkistömuunnosten tekeminen
- joskus erillinen esikäsittelyvaihe tähän
- kommenttien ja tyhjän tilan eliminointi merkitys säilyttäen
- ohjelman alustava (”leksikaalinen”) analyysi
- rivinumerotiedon ylläpitäminen
Lekseemi
- minimaalinen ohjelmatekstin osamerkkijono, jolla on itsenäistä semanttista sisältöä
- tyypillisesti:
- välimerkit
- erilaiset luku-, merkkijono- ym. vakiot
- erilaiset muuttujien, tyyppien ym. nimet
Sananen
- engl. token (kirjaimellisesti ’poletti’, ’rahake’, ’kuponki’)
- analysoitu lekseemi
- sananen koostuu yleensä
- itse lekseemistä
- sen luokittelusta (sanasluokka, engl. token class, token type)
- mahdollisesti sen semanttisesta sisällöstä
- sen paikkatiedosta (lähdetiedosto, rivinumero ym.)
Sanasten määrittely säännöllisillä lausekkeilla
- Kustakin sanasluokasta annetaan säännöllinen lauseke, joka kuvaa tämän sanasluokan lekseemit
- Yleensä maximum munch rule: jos voidaan muodostaa useampi lekseemi, valitaan niistä pisin.
- Lausekkeet laitetaan järjestykseen niin, että ylin täsmäävä valitaan.
Regular descriptions
- (kirjan termi, en ole nähnyt muualla)
- Annetaan säännöllisille lausekkeille nimiä, joita voi käyttää toisissa säännöllisissä lausekkeissa.
- Rekursiiviset määritelmät kielletään.
Lekseemien luokittelu
- tarkka luokittelu riippuu lähdekielestä
- on tavallisesti jopa määritelty kielen määrittelydokumentissa (standardi tms.)
- idea: jäsentäjä pystyy päättelemään pelkästään lekseemin luokituksesta (tutkimatta lekseemiä tarkemmin), mitä lekseemille pitäisi tehdä
Välimerkit
- jokainen oma lekseemiluokkansa, koska jokaisella eri syntaktinen tehtävä
- myös operaattorit välimerkkejä
- poikkeus: kielet joissa omien operaattoreiden määrittely mahdollista (esim. Haskell)
- tavallisesti yhden merkin pituisia, mutta ei suinkaan aina
- semanttinen merkitys identtinen lekseemiluokan kanssa
- Javassa:
( ) { } [ ] ; , . = > < ! ~ ? : == <= >= != &&
|| ++ -- + - * / & | ^ % << >> >>> += -= *= /= &= |=
^= %= <<= >>= >>>=
Vakiot
- kokonaisluvut, desimaaliluvut, e-notaatio, merkkivakiot, merkkijonovakiot
- rakenne usein monimutkainen – selaajan kannalta hankalaa
- lukujen eri esitysmuodot
- merkki(jono)vakioiden escape-merkinnät (
\n
)
- lekseemiluokkia yleensä yksi per semanttinen tyyppi
- semanttinen arvo: lukuvakion lukuarvo, merkkijonovakion tarkoittama merkkijono ym.
- liukulukujen tapauksessa erittäin epätriviaalia
- toisinaan itse lekseemimerkkijono riittää
Kokonaislukuvakiot
- perusrakenne selkeä:
- aluksi yksi tai useampia numeromerkkejä
- vakio päättyy viimeiseen numeromerkkiin
- komplikaatioita:
- numerojärjestelmän kantaluvun esittäminen (
10H
,010
,0x10
) - mahdolliset suffiksit (esim. C:n tyyppisuffiksit:
10ul
) - lukutyyppiin nähden liian isot tai liian pienet luvut
- numerojärjestelmän kantaluvun esittäminen (
Liukulukuvakiot
- perusrakenne:
- aluksi nolla tai useampia numeromerkkejä (kokonaisosa)
- sitten mahdollisesti desimaalierotin (yleensä
.
) - sitten nolla tai useampia numeromerkkejä (desimaaliosa)
- sitten mahdollisesti kirjain
e
taiE
- sitten (mahdollisesti etumerkillinen) kokonaisluku (eksponentti)
- desimaalierotin on pakollinen jos desimaaliosa löytyy
e
-kirjain on pakollinen, jos eksponentti löytyy- joko kokonaisosa tai desimaaliosa tulee löytyä
- joko desimaaliosa tai eksponentti tulee löytyä
- hankalaksi selaamisen tekee monien osien vapaaehtoisuus
- semanttinen tulkinta myös hankalaa
Merkki(jono)vakiot
- tyypillinen rakenne:
- alkaa ja päättyy lainausmerkillä (yleensä
'
tai"
) - koostuu merkeistä sellaisinaan sekä komentomerkkisarjoista
- alkaa ja päättyy lainausmerkillä (yleensä
- lainausmerkki ei voi sellaisenaan olla osa merkki(jono)vakiota
- paljon vaihtelua eri kielissä
- komentomerkkisarjat hyvin erilaisia
- Voiko merkkijonovakio koostua useasta rivistä?
- Jos voi, kuuluvatko merkkijonon sisällä olevan rivinvaihdon jälkeiset välilyönnit ja tabulaattorit merkkijonon semanttiseen sisältöön?
- Voiko ohjelman muuttujiin viitata merkkijonon sisällä (interpolaatio)?
Merkki(jono)vakioiden komentomerkkisarjoja
- C-sukuisten kielten komentomerkkisarjoja
\l
, missäl
on kirjain, edustaa erityismerkkiä, esim.\t
\m
, missäm
on välimerkki, edustaa ko. välimerkkiä sellaisenaan, esim.\\
,\"
\xhh
, missäh
on heksadesimaalinumero, edustaa heksadesimaalilukuhh
:n tarkoittamaa merkkiä (esim.\x20
on ASCII-koodauksen vallitessa välilyönti)\0ooo
, missäo
on oktaalinumero, edustaa oktaalilukuooo
:n tarkoittamaa merkkiä (esim.\044
on ASCII-koodauksen vallitessa dollarinmerkki)\uhhhh
, missäh
on heksadesimaalinumero, edustaa heksadesimaalilukuhhhh
:n tarkoittamaa Unicode-merkkiä (esim.\u203D
on ns. interrobang‽
)
- joissakin kielissä
""
edustaa lainausmerkkiä sellaisenaan eikä päätä merkki(jono)vakiota
Nimet
- perusrakenne:
- alkaa merkillä, joka luokitellaan nimenaloittajaksi
- jatkuu niin kauan kuin löytyy merkkejä, jotka luokitellaan nimenjatkajiksi
- nimenaloittajia ovat yleensä kirjaimet ja jokunen erikoismerkki (esim. alaviiva ja dollari)
- nimenjatkajia ovat yleensä kaikki nimenaloittajat sekä lisäksi kymmenjärjestelmän numeromerkit
- Kielissä, jotka sallivat omien operaattoreiden määrittelyn, esimerkiksi Haskell, samaa periaatetta käytetään usein myös operaattorin määrittelyssä.
- lekseemiluokkia
- avainsanat (engl. keyword), kukin erikseen
- tyyppinimi (engl. type name)
- tunnus (engl. identifier)
Avainsanat
- esimerkkejä: if, while, class
- nimiä, joilla on syntaktinen erityistehtävä
- tavallisesti varattuja sanoja (engl. reserved words)
- Jos avaisana löytyy lähdekoodista, se käsitellään avainsanana.
- Joissakin (vanhemmissa) kielissä avainsanat eivät ole varattuja, vaan avainsanan tunnistaminen tapahtuu vasta jäsennysvaiheessa.
- sivuutetaan tämä sotku tällä kurssilla kokonaan
- klassinen varoittava esimerkki (PL/I):
IF IF = THEN THEN THEN = ELSE; ELSE ELSE = IF;
Tyyppinimet
- tyyppinimet usein erillinen lekseemiluokka jäsennyksen helpottamiseksi
- Osa tyypeistä nimetään yleensä avainsanoilla (esim. Javan
int
); nyt kyse ohjelmoijan nimeämistä tyypeistä. - Joissakin kielissä tyyppinimet alkavat isolla kirjaimella.
- Toisissa kielissä tyyppinimet määritellään ohjelmatekstissä.
- Selaajalla tulee tällöin olla luettelo kullakin hetkellä määritellyistä tyyppinimistä.
- Jäsentäjä päivittää listaa tarpeen mukaan.
- Tässä systeemissä selaaja ja jäsentäjä eivät voi olla erillisiä.
Entäpä kommentit ja välilyönnit?
- tavallisesti eivät lekseemejä
- selaaja lopettaa lekseemin kommentin tai välilyönnin vastaan tullessa
- seuraavaa lekseemiä etsitään kommentin loputtua tai välilyönnin jälkeen
- välilyönnin veroisia myös rivinvaihdot ja tabulaattorit
Paitsiosääntö
- Useimmissa kielissä tyhjä tila on (lähes) merkityksetöntä, jolloin sisennystavat ym. ovat vain ”hyviä tapoja”.
- Joissakin kielissä rivitys ja sisennys ovat osa syntaksia.
- tunnetuimmat modernit esimerkit ovat Python ja Haskell
- ideana että lohkorakenne esitetään sisennyksellä, ei muuten
- Sisennyksestä lohkorakenteen päättelmisestä käytetään joskus Peter Landinin keksimää termiä paitsiosääntö (engl. off-side rule).
Paitsiosäännön toteutuksen idea
- ks esim AJK-2016
- tee filtteri selaajan ja jäsentäjän väliin
- filtteri generoi sanasia BEGIN, END ja SEP (tms)
- jotkin sanaset aloittavat lohkon (Pythonissa
:
, Haskellissa mm.do
)- generoi lohkon aloittavan sanasen jälkeen BEGIN
- seuraavan oikean sanasen sisennys laitetaan sisennyspinoon
- kun jokin sananen on ensimmäinen uudella rivillä:
- laske sen sisennys
- poista sisennyspinosta sisennykset, jotka ovat tätä isompia
- generoi sanasen eteen kullekin END
- jos sisennys on nyt sama kuin pinon päällimmäinen, generoi sanasen eteen ENDien perään SEP
- syötteen loppuun generoi sisennyspinon koon verran ENDejä
These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.