Opiskelumateriaali - Syntaksi ja semantiikka

Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
Ohjelmointikielten syntaksista ja semantiikasta
Tässä osassa esitellään käsitteet syntaksi ja semantiikka sekä tutustutaan
ohjelmointikielen syntaksin kuvaamismenetelmiin. Esimerkiksi Sebestan ([Seb]) luvuissa
3 ja 4 ja Harsun kirjan [Har] luvussa 2 käsitellään seuraavassa esitettäviä asioita.
1. Kielen syntaksi ja semantiikka käsitteinä
Ohjelmointikieltä määriteltäessä tarvitaan täsmällinen ja helposti ymmärrettävä kuvaus
kielestä. Tämä on tärkeää sekä kieltä toteutettaessa että käytettäessä kieltä
ohjelmointiin. On kuvattava kielen rakenne ja kielen konstruktioiden merkitys. Ensin
mainittu koskee kielen syntaksia ja jälkimmäinen sen semantiikkaa.
Kielen syntaksi (syntax) on sen rakenne. Syntaksi määrittelee ne säännöt, joiden
perusteella kielen lailliset ilmaukset voidaan muodostaa. Semantiikka (semantics)
määrittelee puolestaan näiden ilmauksien merkityksen. Esimerkiksi C-kielen if-lauseen
syntaksi (ilman else -osaa) on
if(<expression>) <statement>
Tämän lauseen semantiikka on puolestaan seuraava: Jos lausekkeen <expression>
arvo ei ole nolla, niin lause <statement> suoritetaan.
Ohjelmointikielen syntaksin formaalia määritelmää kutsutaan (analogisesti luonnollisen
kielen kanssa) kieliopiksi (grammar). Kielten syntaksin formaaliin esittämiseen
käytetään yleisesti kontekstista riippumattomia (context-free, kontekstivapaita,
yhteysriippumattomia jne) kielioppeja. Sen sijaan semantiikan formaaliin kuvaamiseen
ei ole olemassa yhtä yleisesti hyväksyttyä tapaa. Tässä käsitellään pääasiassa syntaksin
esittämistä mainittujen kielioppien avulla.
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
2. BNF -kieliopit
Tunnetuin formaali kielioppi kehitettiin vuonna 1960 kuvaamaan ALGOL -kielen
syntaksia; kielioppi nimettiin kehittäjiensä mukaan BNF:ksi (Backus-Naur Form).
Hieman aiemmin (vuonna 1959) kieliteoreetikko Noam Chomsky oli kehittänyt
kontekstista riippumattoman kieliopin. Voidaan osoittaa, että itse asiassa molemmat
kieliopit ovat ekvivalentit, ts. ne määrittelevät täsmälleen saman asian, ainoa ero on
merkintätapa. Tästä syystä termejä "kontekstista riippumaton kielioppi" ja "BNF kielioppi" käytetään synonyymeina. Tässä käytetään enimmäkseen termiä "BNF kielioppi", koska kielen syntaksi kuvataan BNF:n avulla. Nimitystä "kontekstista
riippumaton kielioppi" käytetään, koska tällaisella kieliopilla kuvataan kieli, jonka
määrittelyt eivät saa riippua siitä yhteydestä, missä ne esitetään.
BNF -kielioppi muodostetaan äärellisestä joukosta kielioppisääntöjä, jotka yhdessä
määrittelevät (formaalin) kielen. Tässä pyritään luonnollisesti kuvaamaan
ohjelmointikieliä. Huomaa, että syntaktisesti kuvataan ainoastaan muodollisesti oikein
muodostettuja ohjelmia, semanttisesti tällaiset voivat olla täysin mielettömiä. Aluksi
tarvitaan määrittely kielelle:
Määritelmä. Kieli on joukko äärellisen pituisia jonkin aakkoston sanoja (merkkijonoja).
Tämä määrittely pitää siis sisällään sen, että aakkosto on kiinnitettävä etukäteen ja
saadaan muodostaa ainoastaan äärellisen mittaisia sanoja. Määrittelyn perusteella
mikä tahansa ohjelmointikieli (esimerkiksi FORTRAN, C tai Java) on kieli, aakkostona on
yleensä ASCII- tai UNICODE-merkistö.
Kielen pienimmät perusosaset eli lekseemit (tekstialkiot, leksikaaliset sanat, lexemes)
jätetään usein pois kielen formaalista kuvauksesta yksinkertaisuuden vuoksi; nämä
voidaan luetella sanakirjamaisesti erillään syntaktisesta kuvauksesta. Ohjelmointikielen
lekseemeihin kuuluvat



tunnisteet (identifiers),
literaalit (literals),
operaattorit (operators),
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto


815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
erikoissanat (special words, key words) ja
erikoissymbolit (special symbols).
Literaalit ovat erityyppisten vakioiden arvoja. Ne voivat olla siis esimerkiksi
kokonaislukuvakioita, merkkijonovakioita jne. Lekseemien kategorioita kutsutaan
alkionimiksi (sanaset, tokens). Lekseemit ovat siis alkionimien ilmentymiä. Varatut
sanat ovat erikoissanojen erikoistapaus. Kielen leksikaalinen rakenne on kuitenkin
läheisesti sidoksissa syntaktisen rakenteeseen. Syntaksin tarkistus tapahtuu yleensä
niin, että kielen selausvaiheessa (scanning phase) kerätään lekseemit ja
jäsentelyvaiheessa (parsing) tarkistetaan varsinainen syntaktinen rakenne.
Esimerkiksi C-kielen lauseessa
if( luku < 0) luku++;
lekseemi alkionimi
if
erikoissana
(
erikoissymboli
luku
tunniste
<
erikoissymboli
0
literaali
)
erikoissymboli
+
operaattori
;
erikoissymboli
Kielen kuvauksessa tarvitaan jonkinlainen metakieli, jonka avulla kuvataan kohdekieltä.
Näin ollen on tärkeää erottaa metakielen ja kuvattavan kielen symbolit toisistaan.
Kontekstista riippumaton kielioppi koostuu joukosta kielioppisääntöjä, joiden
vasemmalla puolella esiintyy ainoastaan määriteltävään rakenteen nimi, vasemman ja
oikean puolen erotinmerkkinä toimii symboli ::= ja oikealla puolella voi esiintyä
symboleita ja rakenteen nimiä. Rakenteiden nimiä, jotka yleensä esitetään
kulmasulkeiden sisällä (<rakenne>), nimitetään välisymboleiksi eli nonterminaaleiksi,
koska ne hajaantuvat edelleen pienempiin osiin. Kielen lekseemejä kutsutaan
loppusymboleiksi eli terminaaleiksi, sillä ne eivät enää hajaannu pienempiin osiin.
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
Kielioppisääntöjä sanotaan myös produktioiksi, koska ne tuottavat kieleen kuuluvat
merkkijonot johtamalla ne säännöistä.
Puhtaassa BNF:ssä käytetään ainoastaan seuraavia metasymboleja:
<
>
::=
|
Kolmen ylimmän symbolin merkitys on kerrottu yllä ja symboli | ilmaisee vaihtoehtoa:
Tällä symbolilla erotettuja termejä voidaan jompaakumpaa käyttää johdossa.
Rakenteiden määrittely voi olla rekursiivista, ts. sama rakenteen nimi voi esiintyä
säännössä sekä oikealla että vasemmalla puolella. Esimerkiksi kymmenjärjestelmän
etumerkittömät luvut voitaisiin määritellä syntaktisesti seuraavasti:
<luku> ::= <luku><numero> | <numero>
<numero> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Huomaa kuitenkin, että esimerkiksi 000131 on laillisesti johdettu luku
(ohjelmointikielet myös yleensä sallivat lukujen esittämisen näin). Tällä yksinkertaisella
merkintätavalla voidaan kuvata ohjelmointikielten syntakseja. Mukavuuden vuoksi
usein käytetään laajennettua BNF:ää (extended BNF, EBNF), johon on lisätty
metasymbolit
[
]
{
}
Näiden merkitys on seuraava:
[] tarkoittaa sulkujen sisällä olevan lausekkeen vapaaehtoista valintaa,
ts. määrittelyssä
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
<sana> ::= x[y]
sana voi olla x tai xy. Edelleen
{} tarkoittaa sulkujen sisällä olevan lausekkeen esiintymistä 0 tai useampi kertaa,
ts. nyt määrittelyssä
<sana> ::= x{y}
sana voi olla x, xy, xyy, xyyy, jne. Laajennetussa BNF:ssä voidaan myös käyttää
sulkuja ryhmittelyyn. Muitakin helpottavia merkintöjä saatetaan käyttää EBNF:ksi
nimitettävissä esityksissä.
Esimerkki. C-kielen if -lause voidaan kuvata seuraavasti:
<if_stmt> ::= if(<expr>) <stmt>[else <stmt>];
Tässä määrittelyssä pitää luonnollisesti antaa myöhemmin säännöt
välisymboleille <expr> ja <stmt>.
Valintatilanteessa voidaan käyttää myös metakielessä sulkuja; esimerkkinä Pascal kielen for-lause
<for_stmt> ::= for <var> := <expr> (to|downto) <expr> do <stmt>;
Kielen kaikki syntaktisesti oikeat lauseet voidaan johtaa kielioppisäännöistä;
johtaminen tapahtuu lähtien liikkeelle jostakin kieliopin säännöstä ja korvaamalla
välisymboleita joillakin määrittelyillään. Kontekstista riippumattoman kieliopin
tapauksessa kaikki vaihtoehtoiset määrittelyt sallitaan kaikissa yhteyksissä.
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
Esimerkiksi jossakin ohjelmointikielessä sijoituslause yhteen- ja kertolaskua sisältäville
aritmeettisille operaatioille voitaisiin määritellä seuraavasti:
<assign> ::= <id> = <expr>
<id> ::= X|Y|Z
<expr> ::= <id> + <expr> |
<id> * <expr> |
(<expr>) |
<id>
Tällöin lause X = X*(Y+Z) voitaisiin johtaa seuraavasti:
<assign> ->
->
->
->
->
->
->
->
<id> = <expr>
X = <expr>
X = X*<expr>
X = X*(<expr>)
X = X*(<id> + <expr>)
X = X*(Y + <expr>)
X = X*(Y + <id>)
X = X*(Y + Z)
Lauseen johtaminen voidaan myös esittää graafisesti johtopuuna (parse tree). Tällöin
nähdään lauseiden hierarkkinen rakenne selvemmin. Esimerkiksi yllä olevan
sijoituslauseen johtopuu olisi
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
Kielioppia sanotaan monikäsitteiseksi (ambiguous), mikäli samalla lauseella on useita
erilaisia johtopuita. Jos sijoituslauseen kielioppia muutettaisiin hieman, esimerkiksi
muotoon
<assign> ::= <id> = <expr>
<id> ::= X|Y|Z
<expr> ::= <id> + <expr> |
<expr> * <expr> |
(<expr>) |
<id>
seurauksena olisi monikäsitteinen kielioppi, nimittäin esimerkiksi lauseella
X = X + Y*Z
on kaksi erilaista johtopuuta (muodosta ne!). Tämä on ongelmallista, sillä kääntäjät
pohjaavat usein semanttisen tulkinnan syntaktiseen muotoon. Esimerkiksi tässä
tapauksessa kielioppi ei kerro, suoritetaanko laskutoimitus muodossa (X+Y)*Z vai
(kuten aritmeettisten sääntöjen nojalla on oikein) X+(Y*Z). Vaikka kielioppi ei
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
olisikaan monikäsitteinen, sama lause voidaan yleensä johtaa eri tavoin; tällöin
kuitenkin näitä kaikkia vastaa sama jäsennyspuu.
3. Syntaksikaaviot
BNF:n ja EBNF:n säännöt voidaan esittää myös graafisessa muodossa ns.
syntaksikaaviona (syntax graph, syntax diagram) avulla. Tällöin käytetään suunnattua
polkua, jossa kieliopin loppusymbolit ja välisymbolit merkitään solmuiksi.
Loppusymbolien nimet kirjoitetaan ovaaleihin ja välisymbolit suorakaiteisiin seuraavasti
Muotonsa vuoksi syntaksikaavioita kutsutaan myös ratapihakaavioiksi. Vaihtoehtoinen
toiminto esitetään kirjoittamalla vaihtoehdot rinnakkain, esimerkiksi säännöt
X1 | X2 ja {YN} kirjoitetaan
Näin saadaan esimerkiksi Pascal-kielen case-lause
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
<case_stmt> ::= CASE <expression> OF <case_list> [;] END
<case_list> ::= {<const_list>: <stmt>;}<const_list>: <stmt>
<const_list> ::= <const> {, <const>}
muunnettua syntaksikaavioksi
Huomaa, että kielioppisääntö sisältää vielä kolme välisymbolia (expression, stmt,
const), joille tulisi myös antaa säännöt. Seuraavassa listauksessa on esimerkki
syntaktisesti oikeasta Pascal-kielisestä case-lauseesta
Case SELECTION of
1 :
Begin
Writeln('Selection one');
End;
2 :
Begin
Writeln('Selection two');
End;
3,4 : Begin
Writeln('Selection three');
End;
End;
Edellä SELECTION tunnistetaan lausekkeeksi ja jäsennetään välisymbolilla expression.
Vakiot 1, 2, 3 ja 4 tunnistetaan ensin ensin välisymboliin const_list kuuluviksi ja
lopuksi ne jäsennetään välisymbolilla const. Erikoissana Begin aloittaa ja End
lopettaa lauseen, joten näiden muodostamat kokonaisuudet jäsennetään välisymbolilla
stmt.
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
Edellä kuvatun graafisen esityksen etuja on mm. se, että syntaksidiagrammia voidaan
käyttää varsin suoraviivaisesti kirjoitettaessa jäsentäjä (parser, syntax analyzer)
kieliopille.
4. Jäsentäjät
Seuraavaksi käsitellään lyhyesti erilaisia tapoja konstruoida jäsentäjä. Ohjelmointikieli
voi olla käännettävä, tulkattava tai hybridi. Käännettävästä kielestä muodostetaan
erityisen ohjelman, kääntäjän (compiler) avulla konekielinen ohjelma, joka sitten
suoritetaan. Tulkattavalla kielellä kirjoitetun ohjelman ajaa erillinen tulkki (interpreter)
suoraan ohjelmointikielellä kirjoitetusta koodista. Hybridisysteemissä kääntäjä
muodostaa ohjelmasta välimuodon, joka tulkataan. Esimerkiksi Java on hybridikieli:
alkuperäinen ohjelma käännetään tavukoodiksi, jonka Javan virtuaalikone suorittaa.
Kaikki käännettävät kielet tarvitsevat jäsentäjän osana kääntäjää.
Lähes poikkeuksetta kääntäjät jakavat syntaksianalyysin leksikaaliseen analyysiin ja
varsinaiseen jäsentämiseen. Leksikaalinen analyysi toimii jäsentämisen esioperaationa
ja on oikeastaan osa jäsentämistä. Leksikaalinen analysoija on pääasiassa
hahmontunnistaja: se poimii ohjelmasta lekseemit ja tunnistaa niiden tyypin, ts. mistä
alkionimestä on kysymys. Yleensä alkionimille käytetään (nimettyjä)
kokonaislukutunnistetta jäsentäjän sisällä. Esimerkkinä ohjelmointikielen sijoituslause
luku = toinen_luku + 25;
lekseemi
luku
=
toinen_luku
+
25
;
alkionimi
IDENT (tunniste)
ASSIGN_OP (operaattori)
IDENT (tunniste)
PLUS_OP (operaattori)
INT_LIT (literaali)
SEMICOLON (erikoissymboli)
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
Tässä ei puututa lähemmin leksikaalisen analyysin toteutukseen; periaatteessa kysymys
on kuitenkin varsin suoraviivaisesta hahmontunnistusongelmasta. Seuraavassa
oletetaan, että käytössä on funktio GetToken(), joka hakee jäsennettävästä
merkkijonosta seuraavan lekseemin ja sijoittaa sen globaaliin muuttujaan sekä
paluuarvonaan (kokonaisluku) antaa alkionimen tyyppikoodin.
Jäsentäjän tehtävä on konstruoida syötteenä saamalleen ohjelmalle jäsennyspuu;
yksinkertaisimmillaan jäsentäjä toimii vain syntaksin tarkastajana, ts. se tutkii
ainoastaan, onko ohjelma syntaktisesti oikein muodostettu. Virhetilanteessa jäsentäjän
on luonnollisesti raportoitava asianmukaisesti havaitusta virheestä. Jäsentäjät jaetaan
kahteen pääluokkaan sen mukaan, miten jäsennyspuu rakennetaan. Osittavat
(ylätasolta lähtevät) jäsentäjät (top-down parsers) etenevät puun juuresta lehtiin päin,
kun taas kokoavat (alatasolta lähtevät) jäsentäjät (bottom-up parsers) rakentavat puun
päinvastaisessa järjestyksessä.
Kaikki yleisesti käytettävät jäsentäjät toimivat kuitenkin siinä suhteessa samalla
periaatteella, että ne tutkivat ainoastaan yhden lekseemin eteenpäin kerrallaan.
Kokoavien jäsentäjien ideana on sovittaa käsiteltävän merkkijonon loppuosa (oikea
puoli) jonkin kielioppisäännön oikeaan puoleen ja tämä redusoidaan sitten kyseisen
säännön vasemmaksi puoleksi, minkä vuoksi jäsennystä kutsutaan myös LRjäsennykseksi. Sebestan kirjan [Seb] luvussa 4.5 on käsitelty tarkemmin tällaisia
jäsentäjiä. Osittavat jäsentäjät toimivat päinvastoin: ne päättelevät merkkijonon
vasemmasta päästä lähtien, onko tutkittava lauseke loppu- vai välisymboli ja redusoivat
välisymbolit sovittaen ne sopivan kielioppisäännön vasemmaksi puoleksi.
Tällöin jäsennystä sanotaan LL-jäsennykseksi. (Ks myös [Har], 2.4)
(E)BNF:n tai vastaavan syntaksikaavion avulla esitetylle kieliopille luonnollisimmin
rakentuu jäsentäjä, joka noudattaa osittavaa ns. rekursiivisesti laskeutuvaa (recursive
descent) algoritmia. EBNF sopii erityisen hyvin rekursiivisesti laskeutuvan jäsentäjän
konstruoimiseen. Tällaisessa jäsentäjässä on kokoelma (yleensä rekursiivisia) funktioita,
jotka tuottavat jäsennyspuun ylhäältä lähtien. Tarvitaan leksikaalinen analysoija
(aiemmin mainittu funktio GetToken()) ja jokaista kieliopin välisymbolia kohti oma
funktio, joka käsittelee kyseisen välisymbolin.
Esimerkki valaisee asiaa. Rakennetaan aiemmin esitetylle Pascal -kielen case -lauseelle
rekursiivisesti laskeutuva jäsentäjä. Oletetaan, että ohjelmassa on funktio GetToken(),
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
joka hakee jäsennettävästä merkkijonosta seuraavan lekseemin ja sijoittaa sen
globaaliin muuttujaan sym. Oletetaan lisäksi, että välisymboleille "expression", "const"
ja "stmt" on kullekin oma funktio, joka huolehtii kyseisen kielioppisäännön
tarkastamisesta. Oletetaan vielä, että virhetilanteessa kutsutaan metodia error().
Tällöin pseudokoodi yllä olevan case -lauseen jäsentämiseksi voisi olla
PROGRAM CASE_STATEMENT
String sym
GetToken()
if( sym != "CASE")
error()
else
GetToken()
expression()
GetToken();
if( sym != "OF")
error()
else
GetToken()
case_list()
if(sym != "END")
error()
return
function case_list()
const_list()
if(sym != ":")
error()
else
GetToken()
stmt()
GetToken()
if(sym == ";")
GetToken()
if(sym != "END")
case_list()
else if(sym != "END")
error()
return
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
function const_list()
const()
GetToken()
if(sym ==",")
GetToken()
const_list()
else if(sym != ":")
error()
return
function expression()
// Parse expression according to rule
return
const()
// Parse const according to rule
return
stmt ()
// Parse statement according to rule
return
END PROGRAM CASE_STATEMENT
Rekursiivisesti laskeutuva menetelmä on varsin tehokas, mutta sisältää erään
rajoituksen: kielioppisäännöt eivät saa sisältää vasemmanpuoleista rekursiota.
Esimerkiksi säännön
<expr> ::= <expr> + <term>
<term> ::= …
muuntaminen jäsentäjäksi johtaisi seuraavan kaltaiseen ohjelmaan:
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
PROGRAM EXPR
String sym
GetToken()
expr();
return
function expr()
expr()
GetToken();
if( sym == "+")
GetToken()
term()
else
error()
return
function term()
// Parse term
return
END PROGRAM EXPR
Nyt huomataan välittömästi, että funktion expr() kutsuminen johtaa
päättymättömään rekursioon. Ahon, Sethin ja Ullmanin kirjassa ([Aho]) kääntäjiä ja
niiden toteutusalgoritmeja käsitellään laajasti. Tässä on ainoastaan pyritty antamaan
pintapuolinen kuva kielen syntaksin ja jäsennysprosessin välisestä yhteydestä.
4. Semantiikka
Palataan vielä lopuksi lyhyesti ohjelmointikielten semantiikkaan. Usein puhutaan
staattisesta ja dynaamisesta semantiikasta. Näistä oikeastaan ainoastaan dynaaminen
semantiikka on varsinaista semantiikkaa, ts. ohjelmointikielen merkitysoppia.
Staattisen semantiikan ongelmat eivät liity merkitykseen vaan koskevat paremminkin
ohjelmien sallittua muotoa (ts. lähestyvät syntaktisia kysymyksiä). Staattisen
semantiikan kysymykset ovat sellaisia muotoseikkoja, joita on vaikea tai mahdoton
kuvata BNF:n avulla. Esimerkiksi vaatimus siitä, että muuttuja on määriteltävä ennen
arvon sijoittamista siihen, on tällainen ominaisuus. Staattisen semantiikan nimitys
johtuu siitä, että sen vaatimukset voidaan tarkistaa jo käännösaikana.
Varsinainen eli dynaaminen semantiikka on varsin hankala aihe. Ei nimittäin ole
yleisesti hyväksyttyä formaalia järjestelmää kuvaamaan ohjelmien merkitysoppia.
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
Luonnollisesti ohjelmointikielen määrittelyn yhteydessä sen konstruktioiden merkitys
olisi tarkasti kuvattava. Yleensä tämä kuitenkin tapahtuu luonnollista kieltä käyttämällä
eikä formaalisti. Usein semantiikka jaetaan tarkastelunäkökulman perusteella
operationaaliseen, denotationaaliseen ja aksiomaattiseen semantiikkaan. Tässä ei
paneuduta ohjelmointikielten merkitysoppiin syvällisemmin vaan tyydytään
kuvaamaan kyseiset käsitteet.
Operationaalinen semantiikka pyrkii kuvaamaan annetun ohjelman merkityksen
suorittamalla ohjelman joko reaalisessa tai virtuaalisessa tietokoneessa; koneen tilat
ohjelman suorituksen aikana määrittelevät tällöin ohjelman merkityksen. Formaalia
operationaalista semantiikkaa käytettiin ohjelmointikielen PL/I merkitysopin
kuvaamiseen jo 1960-luvulla. Tämä semantiikan laji pohjautuu algoritmeihin eikä
niinkään matemaattiseen esitykseen; operationaalinen semantiikka voi olla hyödyllinen
tapa kuvata merkitysoppi kielen käyttäjille ja toteuttajille, kunhan esitystapa pidetään
riittävän selkeänä ja yksinkertaisena.
Aksiomaattinen semantiikka kehitettiin, kun pyrittiin konstruoimaan menetelmä
todistaa ohjelmien korrektisuutta. Nimensä mukaisesti aksiomaattinen semantiikka
pohjautuu matemaattiseen logiikkaan. Aksiomaattisen semantiikan tuntemus voi olla
hyödyksi ohjelmoijalle, jonka on todistettava aukottomasti ohjelmansa korrektius.
Myös denotationaalinen semantiikka perustuu matematiikkaan, nimittäin rekursiivisten
funktioiden teoriaan. Yleisesti katsotaan, että tämä semantiikan muoto kuvaa
käytettävistä menetelmistä tarkimmin ohjelmien merkitysopin. Denotationaalisen
semantiikan avulla lähes mikä tahansa ohjelmointikielen piirre voidaan kuvata
matemaattisen funktion avulla. Denotationaalista semantiikkaa voidaan käyttää
hyödyksi ohjelmointikielten suunnittelussa; ohjelmoijan kannalta sitä ei voitane pitää
erityisen käyttökelpoisena. Sebesta ([Seb]) käsittelee hieman perusteellisemmin
semantiikan kysymyksiä kirjansa luvussa 3.5. (Ks. myös [Har], 2.5 ja 2.6)
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Syntaksi ja semantiikka
Lähteet
[Aho] Aho, A.V., Sethi, R. & Ullman, J.D. Compilers: Principles, Techniques and Tools.
Addison-Wesley 1986.
[Har] Harsu, Maarit. Ohjelmointikielet, Periaatteet, käsitteet,
valintaperusteet, Talentum 2005.
[Seb] Sebesta, Robert W. Concepts of Programming Languages 10th edition, Pearson
2013.