Miten voisin luoda ohjelmointikielen?

Tämän artikkelin otsikko kuvastaa kysymystä, jonka kuulen yhä uudestaan ja uudestaan foorumeilla tai saamissani sähköposteissa.

Luulen, että kaikki uteliaat kehittäjät ovat kysyneet sitä ainakin kerran. On normaalia olla kiehtoutunut siitä, miten ohjelmointikielet toimivat. Valitettavasti useimmat lukemamme vastaukset ovat hyvin akateemisia tai teoreettisia. Jotkut toiset sisältävät liikaa toteutuksen yksityiskohtia. Niiden lukemisen jälkeen ihmettelemme edelleen, miten asiat toimivat käytännössä.

Niinpä me aiomme vastata siihen. Kyllä, katsomme, mikä on prosessi, jolla luodaan oma kokonainen kieli ja sille kääntäjä ja mitä ei.

Yleiskatsaus

Suuri osa henkilöistä, jotka haluavat oppia, miten ”luoda ohjelmointikieli”, etsivät tosiasiassa tietoa siitä, miten rakentaa kääntäjä. He haluavat ymmärtää mekaniikan, joka mahdollistaa uuden ohjelmointikielen suorittamisen.
Kääntäjä on palapelin olennainen osa, mutta uuden ohjelmointikielen tekeminen vaatii muutakin:

1) Kieli on suunniteltava: Kielen luojan on tehtävä joitakin perustavanlaatuisia päätöksiä käytettävistä paradigmoista ja kielen syntaksista
2) Kääntäjä on luotava
3) Standardikirjasto on toteutettava
4) Tukityökaluja, kuten editoreita ja build-järjestelmiä, on tarjottava

Katsotaanpa yksityiskohtaisemmin, mitä kukin näistä kohdista merkitsee.

Ohjelmointikielen suunnittelu

Jos haluat vain kirjoittaa oman kääntäjän oppiaksesi, miten nämä asiat toimivat, voit ohittaa tämän vaiheen. Voit vain ottaa osajoukon olemassa olevasta kielestä tai keksiä yksinkertaisen variaation siitä ja aloittaa. Jos sinulla on kuitenkin suunnitelmissa luoda ikioma ohjelmointikieli, sinun täytyy miettiä asiaa.

Ajattelen ohjelmointikielen suunnittelun jakautuvan kahteen vaiheeseen:

  1. Suuren kuvan vaihe
  2. Hienosäätövaihe

Ensimmäisessä vaiheessa vastaamme kielemme perustavanlaatuisiin kysymyksiin.

  • Minkälaista suoritusparadigmaa haluamme käyttää? Onko se imperatiivinen vai funktionaalinen? Vai perustuuko se kenties tilakoneisiin vai liiketoimintasääntöihin?
  • Haluammeko staattisen vai dynaamisen tyypityksen?
  • Millaisiin ohjelmiin tämä kieli soveltuu parhaiten? Käytetäänkö sitä pieniin skripteihin vai suuriin järjestelmiin?
  • Millä on meille eniten merkitystä: suorituskyvyllä? Luettavuus?
  • Haluammeko sen olevan samanlainen kuin olemassa oleva ohjelmointikieli? Onko se suunnattu C-kehittäjille vai onko se helppo oppia niille, jotka tulevat Pythonista?
  • Tahdommeko sen toimivan tietyllä alustalla (JVM, CLR)?
  • Millaisia metaohjelmointiominaisuuksia haluamme tukea, jos sellaisia on? Makrot? Mallit? Reflection?

Toisessa vaiheessa kehitämme kieltä sitä mukaa kuin käytämme sitä. Törmäämme ongelmiin, asioihin, joita on hyvin vaikeaa tai mahdotonta ilmaista kielellämme, ja päädymme kehittämään sitä. Toinen vaihe ei ehkä ole yhtä hohdokas kuin ensimmäinen, mutta se on vaihe, jossa jatkamme kielemme virittämistä, jotta se olisi käyttökelpoinen käytännössä, joten sitä ei pidä aliarvioida.

Kääntäjän rakentaminen

Kääntäjän rakentaminen on jännittävin vaihe ohjelmointikielen luomisessa. Kun meillä on kääntäjä, voimme todella herättää kielemme henkiin. Kääntäjä antaa meille mahdollisuuden alkaa leikkiä kielellä, käyttää sitä ja tunnistaa, mitä meiltä puuttuu alkuperäisestä suunnittelusta. Sen avulla voimme nähdä ensimmäiset tulokset. On vaikea voittaa sitä iloa, joka syntyy, kun suoritetaan ensimmäinen upouudella ohjelmointikielellämme kirjoitettu ohjelma, olipa se kuinka yksinkertainen tahansa.

Mutta miten rakennamme kääntäjän?

Kuten kaikki monimutkainen, teemme sen vaiheittain:

  1. Rakennamme jäsentäjän: jäsentäjä on kääntäjämme osa, joka ottaa ohjelmiemme tekstin haltuunsa ja tajuaa, mitä komentoja ne ilmaisevat. Se tunnistaa lausekkeet, lausekkeet ja luokat ja luo sisäisiä tietorakenteita niiden esittämiseksi. Loput jäsentimestä työskentelee näiden tietorakenteiden kanssa, ei alkuperäisen tekstin kanssa
  2. (valinnainen) Käännämme jäsennyspuun abstraktiksi syntaksipuuksi. Tyypillisesti jäsentäjän tuottamat tietorakenteet ovat hieman matalan tason rakenteita, koska ne sisältävät paljon yksityiskohtia, jotka eivät ole kääntäjämme kannalta ratkaisevia. Tämän vuoksi haluamme usein järjestää tietorakenteet uudelleen johonkin hieman korkeamman tason
  3. Ratkaisemme symbolit. Koodissa kirjoitamme asioita kuten a + 1. Kääntäjämme on selvitettävä, mihin a viittaa. Onko se kenttä? Onko se muuttuja? Onko se metodin parametri? Tutkimme koodia vastataksemme tähän
  4. Validoimme puun. Meidän on tarkistettava, ettei ohjelmoija tehnyt virheitä. Yrittääkö hän laskea yhteen booleanin ja intin? Tai käyttää ei-olevaa kenttää? Meidän on tuotettava asianmukaiset virheilmoitukset
  5. Generoimme konekoodin. Tässä vaiheessa käännämme koodin joksikin, mitä kone voi suorittaa. Se voi olla varsinaista konekoodia tai bytecodea jollekin virtuaalikoneelle
  6. (valinnainen) Suoritamme linkityksen. Joissakin tapauksissa meidän on yhdistettävä ohjelmillemme tuotettu konekoodi niiden staattisten kirjastojen koodiin, jotka haluamme sisällyttää, jotta voimme tuottaa yhden suoritettavan ohjelman

Tarvitsemmeko aina kääntäjää? Ei. Voimme korvata sen muilla keinoilla koodin suorittamiseksi:

  • Voidaan kirjoittaa tulkki: Tulkki on olennaisesti ohjelma, joka tekee kääntäjän vaiheet 1-4 ja suorittaa sitten suoraan sen, mitä abstraktissa syntaksipuussa on määritelty
  • Voidaan kirjoittaa transpileri:

Nämä kaksi vaihtoehtoa ovat täysin kelvollisia, ja usein on järkevää valita jompikumpi näistä kahdesta, koska tarvittava työmäärä on yleensä pienempi.

Kirjoitimme artikkelin, jossa selitetään, miten transpileri kirjoitetaan. Vilkaise sitä, jos haluat nähdä käytännön esimerkin koodin kera.

Tässä artikkelissa selitämme tarkemmin kääntäjän ja tulkin eron.

Ohjelmointikielesi standardikirjasto

Jokainen ohjelmointikieli tarvitsee muutamia asioita:

  • Tulostaminen ruudulle
  • Käyttö tiedostojärjestelmässä
  • Verkkoyhteyksien käyttäminen
  • GUI:n luominen

Nämä ovat perustoiminnallisuuksia, joilla voi olla vuorovaikutuksessa muun järjestelmän kanssa. Ilman niitä kieli on periaatteessa hyödytön. Miten tarjoamme nämä toiminnallisuudet? Luomalla standardikirjasto. Se on joukko funktioita tai luokkia, joita voidaan kutsua ohjelmointikielellämme kirjoitetuissa ohjelmissa, mutta jotka kirjoitetaan jollakin muulla kielellä. Esimerkiksi monilla kielillä on standardikirjastoja, jotka on kirjoitettu ainakin osittain C-kielellä.

Standardikirjasto voi sitten sisältää paljon muutakin. Esimerkiksi luokkia tärkeimpien kokoelmien, kuten listojen ja karttojen, esittämiseen tai yleisten formaattien, kuten JSON tai XML, käsittelyyn. Usein se sisältää kehittyneitä toiminnallisuuksia merkkijonojen ja säännöllisten lausekkeiden käsittelyyn.

Vakiokirjaston kirjoittaminen on siis paljon työtä. Se ei ole hohdokasta, se ei ole käsitteellisesti yhtä mielenkiintoista kuin kääntäjän kirjoittaminen, mutta se on silti perustavanlaatuinen osa, joka tekee ohjelmointikielestä elinkelpoisen.

On olemassa keinoja välttää tämä vaatimus. Yksi keino on tehdä kielestä käyttökelpoinen jollakin alustalla ja tehdä mahdolliseksi toisen kielen standardikirjaston uudelleenkäyttö. Esimerkiksi kaikki JVM:llä toimivat kielet voivat yksinkertaisesti käyttää uudelleen Javan standardikirjastoa.

Tukityökalut uudelle ohjelmointikielelle

Voidaksemme tehdä kielestä käytännössä käyttökelpoisen joudumme usein kirjoittamaan muutamia tukityökaluja.

Näyttävin on editori. Erikoistunut editori, jossa on syntaksin korostus, inline-virheiden tarkistus ja automaattinen täydennys, on nykyään välttämätön, jotta kuka tahansa kehittäjä olisi tuottava.

Mutta nykyään kehittäjät ovat hemmoteltuja ja he odottavat kaikenlaisia muitakin tukityökaluja. Esimerkiksi debuggeri voi olla todella hyödyllinen ikävän bugin käsittelyssä. Tai mavenin tai gradlen kaltainen build-järjestelmä voisi olla jotain, mitä käyttäjät kysyvät myöhemmin.

Alussa editori voisi riittää, mutta käyttäjäkunnan kasvaessa myös projektien monimutkaisuus kasvaa ja tarvitaan lisää tukityökaluja. Toivottavasti tuolloin löytyy yhteisö, joka on valmis auttamaan niiden rakentamisessa.

Yhteenveto

Ohjelmointikielen luominen on prosessi, joka tuntuu monista kehittäjistä mystiseltä. Tässä artikkelissa pyrimme osoittamaan, että se on vain prosessi. Se on kiehtovaa eikä helppoa, mutta se on mahdollista.

Haluat ehkä rakentaa ohjelmointikielen monista eri syistä. Yksi hyvä syy on hauskanpito, toinen on kääntäjien toiminnan oppiminen. Kielestäsi voi tulla lopulta hyvin hyödyllinen tai sitten ei, riippuen monista tekijöistä. Jos sinulla on kuitenkin hauskaa ja/tai opit sitä rakentaessasi, kannattaa siihen panostaa aikaa.

Ja tietysti voit kehuskella muiden kehittäjien kanssa.

Jos haluat oppia lisää kielen luomisesta, katso muita luomiamme resursseja: Opi rakentamaan kieliä.

Olet ehkä kiinnostunut myös joistakin artikkeleistamme:

Vastaa

Sähköpostiosoitettasi ei julkaista.