Siirry sisältöön

Ohjelmointikielen kääntäjä

Wikipediasta
Ideaalinen tapaus useaa kieltä ja useaa kohdetta tukevasta kääntäjästä.

Ohjelmointikielen kääntäjä on tietokoneohjelma, joka luo tietokoneohjelman ohjelmointikielisen lähdekoodin perusteella konekielisen ajettavan binääritiedoston (eli kääntää ohjelman). Kääntäminen on siis muunnos, jossa ihmiselle helpossa muodossa oleva lähdekoodi muutetaan tietokoneen suorittimen ymmärtämään muotoon myöhempää suorittamista varten.

Kääntäjän lisäksi voidaan tarvita useita muita ohjelmia suoritettavan ohjelman tuottamiseksi kuten esikääntäjä, assembler ja linkkeri.[1] Englanninkielinen nimitys (engl. compiler) on Hopperin teoksessa A Programmer's Glossary (1. toukokuuta 1954) määritelty pseudokoodin muuttamisessa staattisiin ja dynaamisiin aliohjelmiin ennen laskentaa.[2][3] Yleisesti ottaen kääntäjä ei ole sama asia kuin ohjelma, joka muuttaa lähdekoodin yhdestä ohjelmointikielestä toiseen (engl. translator).[4]

Vaihtoehtona kääntäjälle on ohjelmointikielen tulkki (engl. interpreter), joka myös suorittaa ohjelman.[1] Joskus ohjelmat käännetään ensin kääntäjällä tavukoodiksi, jonka sitten suorittaa yksinkertainen tulkki käsky kerrallaan. Tavukoodi voidaan muuntaa ensin konekieleksi, jolloin suoritetaan suoraan konekieltä ja kutsutaan ajonaikaiseksi kääntämiseksi (engl. just-in-time compiling, JIT).[5] Tavukoodina suoritettavia ohjelmointikieliä ovat Java, Python, Ruby ja PHP.

Tavukoodia käytetään myös välivaiheen käännöksenä, jotta eri kielistä saadaan kohdennettua eri laitteille käännettävää alustakohtaista ohjelmakoodia. Vastaavia ovat muun muassa LLVM kääntäjäympäristössä sekä useissa GPGPU-ohjelmointiin suunnatuissa menetelmissä (esim. SPIR-V[6]).

Käännettävä ohjelma voi olla jaettuna moduuleihin, jotka ovat tallennettuna erillisiin tiedostoihin.[1] Suuret ohjelmat käännetään usein osissa, jolloin uudelleen sijoitettava konekielinen koodi voidaan linkittää yhteen toisten objektitiedostojen kanssa.[1]

Käännöksen vaiheet

[muokkaa | muokkaa wikitekstiä]
Esimerkki skannerin (sanastollinen analyysi) ja parserin (syntaksi) vaiheista.

Käännös voidaan jakaa neljään vaiheeseen:[7]

  1. Lähdekoodin merkkijono muutetaan vastaavaan sarjaan symboleita kielen sanastossa. Muun muassa tunnisteet jotka koostuvat kirjaimista ja numeroista, luvut jotka koostuvat numeroista, erotinmerkit ja operaattorit jotka koostuvat erikoismerkeista tunnistetaan tässä vaiheessa.
  2. Symbolien sarja muutetaan esitysmuotoon, joka vastaa kielen syntaksia.
  3. Ylemmän tason kielissä tunnistetaan tyypit sekä niiden yhteensopivuudet operaattoreiden ja operandien välillä.
  4. Toisen vaiheen esitysmuodosta tuotetaan konekieltä kohteen käskykannalle, joka on usein vaativin osa ja pilkotaan vielä pienempiin vaiheisiin.

Analyysivaihe voidaan jakaa kolmeen analyysiin: sanastollinen (engl. lexical), syntaksin mukainen ja semanttinen merkitys.[1] Kääntäjä voi tuottaa välikielellä käännöksen, johon voidaan soveltaa alustariippumattomia optimointeja ennen varsinaista konekielisen koodin tuottamista ja sen optimointia.[1]

Kääntäjän etuosa jaetaan usein selaajaan (engl. scanner, tokenizer, lexer)[8] ja jäsentimeen (engl. parser). Selaaja tunnistaa ohjelmointikielestä alkionimet (engl. token) esimerkiksi säännöllisten lausekkeiden avulla. Jäsennin (myös jäsentäjä) tunnistaa kielen rakenteen vaikkapa sisäkkäisiä rakenteita tunnistavan LR-jäsennystä (left-right, eli läpikäynti vasemmalta oikealle, ymmärtäminen oikealta vasemmalle) käyttäen. Myös LL-jäsennystä käytetään sen yksinkertaisuuden vuoksi, mutta sellaisen avulla ei voida jäsentää kieltä, missä esiintyy nk. vasen rekursio. Esimerkiksi kielioppisääntöön

 A ::= A + B

ei voida soveltaa LL-jäsentäjää, koska se joutuisi ikuiseen rekursiosilmukkaan - aliohjelmakutsut menisivät seuraavaan tapaan:

 ParseA()
    ParseA()
    ParseB()
    # tee jotain A + B:lle

Unix-maailmassa yleisesti käytetyt työkalut ovat Lex ja Yacc: Lex tekee sanastollisen analyysin (selaajan) ja Yacc lausemuodon analyysin (jäsentimen).[9]

Kolmas käännösvaihe on semanttinen (eli merkityksen) analyysi. Tässä vaiheessa tutkitaan mm. muuttujien ja metodien nimet, tyypit ja niiden käytön oikeellisuus.

Käännöksen optimointi

[muokkaa | muokkaa wikitekstiä]
Pääartikkeli: Ohjelman optimointi

Kääntäjä voi usein suorittaa käännettävän ohjelman optimointia sille sallituissa rajoissa.

C-kielen standardi sallii kääntäjän olettaa ettei käsiteltäviä muuttujia muuteta lataus/tallennus-operaation aikana (kuten lauseen a=b; aikana).[10][11] Muun muassa rinnakkain suoritettavan ohjelmakoodin tapauksessa tämä voi aiheuttaa ongelmia.[11] Kääntäjä voi olettaa ettei tieto muutu sitä käyttävän toistorakenteen aikana, joka voi olla virhetilanne lukituksen vapautumista odottaessa.[12]

Standardifunktiot

[muokkaa | muokkaa wikitekstiä]

Standardifunktiot (engl. intrinsic function)[13] ovat kääntäjän itsensä tarjoamia tai tunnistamia funktioita, jotka sisältävät korkealle optimoidut toteutuksen tietyille yleisille algoritmeille. Esimerkiksi sini- ja kosinifunktiot ovat usein käytettyjä.[13]

Kääntäjässä voi olla toteutuksia alustakohtaisille toiminnoille tietyillä käskykantalaajennuksilla toteutettuna, joita voidaan käyttää ohjelmissa assembly-kielellä tehtyjen rutiinien asemesta kuten SSE-käskykannan hyödyntämisessä. Esimerkiksi Microsoftin ja Intelin C/C++ kääntäjät sekä GCC toteuttavat standardifunktiot, jotka vastaavat suoraan x86-käskykannan SIMD laajennuksia.[14][15]

Kääntäjien tyypit

[muokkaa | muokkaa wikitekstiä]

Ohjelmointikielen kääntäjiä voidaan käyttää samalla alustalla jossa käännettävä ohjelma suoritetaan (natiivi tai isäntäalusta).lähde? Toinen tyyppi on ristiinkääntäjä, jossa kohdealusta voi olla eri kuin käännösympäristön, esimerkiksi sulautettu järjestelmä voi olla suorituskyvyltään liian rajoittunut kääntäjälle.[16][17] Ristiinkääntäjä voi kohdistaa eri suorittimelle tai käyttöjärjestelmälle.[16][18] Ristiinkääntäjä voidaan kääntää eri ympäristössä kuin missä se tullaan ajamaan ja käännetyn kääntäjän kohde voi olla myös eri: tätä tapausta kutsutaan "Kanadalaiseksi ristiksi" (engl. canadian cross).[19][20]

Arizonan yliopiston professorin Todd A. Proebstingin esittämän Proebstingin lain mukaan kääntäjien kehitys tuplaa suorituskyvyn 18 vuoden välein. Tämä eroaa tunnetusta Mooren laista, jonka mukaan transistorien määrä tuplaantuu 18 kuukauden välein.[21][22]

  1. a b c d e f Aho, Alfred V. & Lam, Monica S. & Sethi, Ravi & Ullman, Jeffrey D.: Compilers - Principles, Techniques & Tools, s. 2–5. (Second Edition) Addison Wesley, 2007. ISBN 0-321-48681-1 (englanniksi)
  2. Peter Sestoft: A history of compilers (PDF) itu.dk. 23.1.2014. Viitattu 30.12.2025. (englanniksi)
  3. First Glossary of Programming Terminology (PDF) archive.computerhistory.org. kesäkuu 1954. Viitattu 30.12.2025. (englanniksi)
  4. Aho, Alfred V. & Ullman, Jeffrey D.: Principles of Compiler Design, s. 1. Addison-Wesley, 1977. ISBN 0-201-00022-9 (englanniksi)
  5. Douglas Thain: Introduction to Compilers and Language Design, s. 1. second edition painos. University of Notre Dame, 2023. ISBN 979-8-655-18026-0 Teoksen verkkoversio. (englanniksi)
  6. The first open standard intermediate language for parallel compute and graphics Khronos Group. Viitattu 3.3.2017.
  7. Niklaus Wirth: Compiler Construction (PDF) inf.ethz.ch. toukokuu 2017. Viitattu 10.2.2020. (englanniksi)
  8. Farrell, James Alan: Anatomy of a Compiler cs.man.ac.uk. Viitattu 3.3.2017.
  9. Naomi Hamilton: The A-Z of Programming Languages: AWK (sivu 2) 27.5.2008. Computerworld. Arkistoitu 23.3.2019. Viitattu 27.5.2019. (englanniksi)
  10. Who's afraid of a big bad optimizing compiler? lwn.net. 15.7.2019. Viitattu 5.3.2020. (englanniksi)
  11. a b Calibrating your fear of big bad optimizing compilers lwn.net. 11.10.2019. Viitattu 5.3.2020. (englanniksi) 
  12. Jonathan Corbet: ACCESS_ONCE() lwn.net. 1.8.2012. Viitattu 5.3.2020. (englanniksi) 
  13. a b Haataja, Juha & Rahola, Jussi & Ruokolainen, Juha: Fortran 95 / 2003 Tieteen tietotekniikan keskus (CSC). Viitattu 16.3.2017.
  14. Compiler Intrinsics Microsoft. Viitattu 16.3.2017.
  15. Built-in Functions Specific to Particular Target Machines GNU. Viitattu 16.3.2017.
  16. a b How to Build a GCC Cross-Compiler preshing.com. 19.11.2014. Viitattu 10.2.2020. (englanniksi)
  17. Building GCC as a cross compiler for Raspberry Pi solarianprogrammer.com. Viitattu 11.2.2020. (englanniksi)
  18. Cross Compiling (PDF) disi.unitn.it. Viitattu 11.2.2020. (englanniksi)
  19. Cross compilation mesonbuild.com. Viitattu 2.3.2021. (englanniksi)
  20. 4.9 Canadian Crosses objsw.com. Arkistoitu Viitattu 2.3.2021. (englanniksi)
  21. Proebsting's Law: Compiler Advances Double Computing Power Every 18 Years proebsting.cs.arizona.edu. Viitattu 30.12.2025. (englanniksi)
  22. David Cassel: Rust Creator Graydon Hoare Recounts the History of Compilers thenewstack.io. 12.5.2019. Viitattu 30.12.2025. (englanniksi)

Kirjallisuutta

[muokkaa | muokkaa wikitekstiä]

Aiheesta muualla

[muokkaa | muokkaa wikitekstiä]