Uus programmeerimiskeel Mash

Mitu aastat proovisin kätt oma programmeerimiskeele arendamisel. Tahtsin luua minu arvates võimalikult lihtsa, täielikult toimiva ja mugava keele.

Käesolevas artiklis tahan välja tuua oma töö põhietapid ja alustuseks kirjeldada loodud keelekontseptsiooni ja selle esmast teostust, millega praegu tegelen.

Etteruttavalt ütlen, et kirjutasin kogu projekti Free Pascalis, sest... sellel olevaid programme saab kokku panna suure hulga platvormide jaoks ja kompilaator ise toodab väga optimeeritud binaarfaile (kogun kõik projekti komponendid O2 lipuga).

Keele käitusaeg

Kõigepealt tasub rääkida virtuaalmasinast, mille pidin kirjutama, et tulevasi rakendusi minu keeles käivitada. Võib-olla otsustasin rakendada virnaarhitektuuri, sest see oli kõige lihtsam viis. Ma ei leidnud ühtegi tavalist artiklit selle kohta, kuidas seda vene keeles teha, nii et pärast ingliskeelse materjaliga tutvumist asusin oma jalgratast kujundama ja kirjutama. Järgmisena tutvustan oma “edasijõudnud” ideid ja arenguid selles küsimuses.

Virna rakendamine

Ilmselgelt on VM-i ülaosas virn. Minu teostuses töötab see plokkidena. Põhimõtteliselt on see lihtne vihjete massiiv ja muutuja virna ülaosa indeksi salvestamiseks.
Selle lähtestamisel luuakse 256 elemendist koosnev massiiv. Kui virnale lükatakse rohkem viiteid, suureneb selle suurus järgmise 256 elemendi võrra. Vastavalt sellele reguleeritakse elementide virnast eemaldamisel selle suurust.

VM kasutab mitut virna:

  1. Peamine virn.
  2. Virn tagastuspunktide hoidmiseks.
  3. Prügikoristus.
  4. Proovige/püüdke/lõpuks blokeerige käitleja virn.

Konstandid ja muutujad

See on lihtne. Konstante käsitletakse eraldi väikeses kooditükis ja need on tulevastes rakendustes staatiliste aadresside kaudu saadaval. Muutujad on teatud suurusega osutite massiiv, mille lahtrite juurde pääseb indeks - st. staatiline aadress. Muutujaid saab lükata virna ülaossa või lugeda sealt. Tegelikult, sest Kuigi meie muutujad salvestavad VM-i mällu väärtustele viiteid, domineerib keeles töötamine kaudsete osutitega.

Prügikorjaja

Minu VM-is on see poolautomaatne. Need. arendaja ise otsustab, millal prügivedajale helistada. See ei tööta tavalise osutiloenduriga, nagu Python, Perl, Ruby, Lua jne. Seda rakendatakse markersüsteemi kaudu. Need. kui muutujale tahetakse määrata ajutine väärtus, lisatakse prügikoristaja virna selle väärtuse osuti. Edaspidi jookseb koguja kiiresti läbi juba koostatud vihjete nimekirja.

Proovi/saagi/lõpuks blokkide käsitlemine

Nagu igas kaasaegses keeles, on erandite käsitlemine oluline komponent. VM-i tuum on mähitud try..catch-plokki, mis võib pärast erandi püüdmist naasta koodi käitamise juurde, lükates selle kohta teatud teabe virna. Rakenduse koodis saate määratleda koodi proovi/püüda/lõpuks plokke, märkides sisenemispunktid püüdmisel (erandikäsitleja) ja lõpuks/lõpus (ploki lõpp).

Mitmelõimeline

Seda toetatakse VM-i tasemel. Seda on lihtne ja mugav kasutada. See töötab ilma katkestussüsteemita, nii et kood tuleks käivitada mitmes lõimes vastavalt mitu korda kiiremini.

VM-ide välised teegid

Ilma selleta ei saa kuidagi hakkama. VM toetab importi, sarnaselt sellega, kuidas seda muudes keeltes rakendatakse. Saate kirjutada osa koodist Mashis ja osa koodist emakeeltes, seejärel linkida need üheks.

Tõlkija kõrgetasemelisest Mash-keelest VM-ide baitkoodini

Vahekeel

Keerulisest keelest VM-koodiks tõlkija kiireks kirjutamiseks töötasin esmalt välja vahepealse keele. Tulemuseks oli monteerija moodi kohutav vaatemäng, mida pole siinkohal eriti mõtet arvestada. Ütlen vaid, et sellel tasemel töötleb tõlkija enamikku konstante ja muutujaid, arvutab nende staatilised aadressid ja sisenemispunktide aadressid.

Tõlkija arhitektuur

Ma ei valinud rakendamiseks parimat arhitektuuri. Tõlkija ei ehita koodipuud, nagu teevad teised tõlkijad. Ta vaatab struktuuri algust. Need. kui parsitav kooditükk näeb välja nagu "while :", siis on ilmne, et tegemist on while-tsükli konstruktsiooniga ja seda tuleb töödelda while-tsükli konstruktsioonina. Midagi keerulise lüliti korpuse sarnast.

Tänu sellele arhitektuursele lahendusele osutus tõlkija mitte eriti kiireks. Selle muutmise lihtsus on aga oluliselt suurenenud. Lisasin vajalikud struktuurid kiiremini, kui mu kohv jahtus. Täielik OOP tugi rakendati vähem kui nädalaga.

Koodi optimeerimine

Siin oleks võinud seda muidugi paremini rakendada (ja rakendatakse, aga hiljem, niipea kui asjani jõutakse). Siiani teab optimeerija ainult, kuidas sõlmest kasutamata koodi, konstandid ja impordid ära lõigata. Samuti asendatakse mitu sama väärtusega konstanti ühega. See on kõik.

Pudru keel

Keele põhimõiste

Põhiidee oli arendada võimalikult funktsionaalset ja lihtsat keelt. Arvan, et arendus tuleb oma ülesandega pauguga toime.

Koodiplokid, protseduurid ja funktsioonid

Kõik keele konstruktsioonid avatakse kooloniga. : ja operaator sulgeb need lõpp.

Protseduurid ja funktsioonid on deklareeritud vastavalt kui proc ja func. Argumendid on loetletud sulgudes. Kõik on nagu enamikus teistes keeltes.

Operaator tagasipöördumine saate tagastada väärtuse funktsioonist, operaatorist murdma võimaldab protseduurist/funktsioonist väljuda (kui see on silmustest väljaspool).

Näidiskood:

...

func summ(a, b):
  return a + b
end

proc main():
  println(summ(inputln(), inputln()))
end

Toetatud kujundused

  • Silmused: for.. end, while.. end, kuni.. end
  • Tingimused: kui..[else..]lõpeta, vaheta..[juhtum..lõpp..][else..]lõpp
  • Meetodid: proc ():... end, func ():... end
  • Silt & goto: :, hüpata
  • Loendloendid ja konstantmassiivid.

Muutujad

Tõlkija saab need määrata automaatselt või kui arendaja kirjutab enne defineerimist var.

Koodi näited:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Toetatakse globaalseid ja kohalikke muutujaid.

OOP

Noh, oleme jõudnud kõige maitsvama teemani. Mash toetab kõiki objektorienteeritud programmeerimise paradigmasid. Need. klassid, pärand, polümorfism (sh dünaamiline), dünaamiline automaatpeegeldus ja sisekaemus (täielik).

Ilma pikema jututa on parem tuua lihtsalt koodinäiteid.

Lihtne klass ja sellega töötamine:

uses <bf>
uses <crt>

class MyClass:
  var a, b
  proc Create, Free
  func Summ
end

proc MyClass::Create(a, b):
  $a = new(a)
  $b = new(b)
end

proc MyClass::Free():
  Free($a, $b)
  $rem()
end

func MyClass::Summ():
  return $a + $b
end

proc main():
  x ?= new MyClass(10, 20)
  println(x->Summ())
  x->Free()
end

Väljund: 30.

Pärand ja polümorfism:

uses <bf>
uses <crt>

class MyClass:
  var a, b
  proc Create, Free
  func Summ
end

proc MyClass::Create(a, b):
  $a = new(a)
  $b = new(b)
end

proc MyClass::Free():
  Free($a, $b)
  $rem()
end

func MyClass::Summ():
  return $a + $b
end

class MyNewClass(MyClass):
  func Summ
end

func MyNewClass::Summ():
  return ($a + $b) * 2
end

proc main():
  x ?= new MyNewClass(10, 20)
  println(x->Summ())
  x->Free()
end

Väljund: 60.

Kuidas on lood dünaamilise polümorfismiga? Jah, see on peegeldus!:

uses <bf>
uses <crt>

class MyClass:
  var a, b
  proc Create, Free
  func Summ
end

proc MyClass::Create(a, b):
  $a = new(a)
  $b = new(b)
end

proc MyClass::Free():
  Free($a, $b)
  $rem()
end

func MyClass::Summ():
  return $a + $b
end

class MyNewClass(MyClass):
  func Summ
end

func MyNewClass::Summ():
  return ($a + $b) * 2
end

proc main():
  x ?= new MyClass(10, 20)
  x->Summ ?= MyNewClass::Summ
  println(x->Summ())
  x->Free()
end

Väljund: 60.

Võtame nüüd hetke, et uurida lihtsaid väärtusi ja klasse:

uses <bf>
uses <crt>

class MyClass:
  var a, b
end

proc main():
  x ?= new MyClass
  println(BoolToStr(x->type == MyClass))
  x->rem()
  println(BoolToStr(typeof(3.14) == typeReal))
end

Väljund: tõsi, tõsi.

Määramise operaatorite ja selgesõnaliste näpunäidete kohta

Operaatorit ?= kasutatakse muutujale mällu salvestatud väärtusele osuti määramiseks.
Operaator = muudab väärtust mälus, kasutades muutuja osutit.
Ja nüüd natuke selgesõnalistest näpunäidetest. Lisasin need keelde, et nad oleksid olemas.
@ — viige muutujale otsene kursor.
? — muutuja hankimine osutiga.
@= — määrake muutujale väärtus selle selgesõnalise osuti abil.

Näidiskood:

uses <bf>
uses <crt>

proc main():
  var a = 10, b
  b ?= @a
  PrintLn(b)
  b ?= ?b
  PrintLn(b)
  b++
  PrintLn(a)
  InputLn()
end

Väljastab: mingi arv, 10, 11.

Proovi..[püüa..][lõpuks..]lõpp

Näidiskood:

uses <bf>
uses <crt>

proc main():
  println("Start")
  try:
    println("Trying to do something...")
    a ?= 10 / 0
  catch:
    println(getError())
  finally:
    println("Finally")
  end
  println("End")
  inputln()
end

Tuleviku plaanid

Vaatan ja vaatan pidevalt GraalVM & Truffle'i. Minu käituskeskkonnas ei ole JIT-kompilaatorit, seega on see jõudluse poolest praegu konkurentsivõimeline ainult Pythoniga. Loodan, et suudan rakendada GraalVM-il või LLVM-il põhinevat JIT-i koostamist.

hoidla

Saate arendustega mängida ja projekti ise jälgida.

veebisait
Hoidla GitHubis

Täname, et lugesite lõpuni, kui lugesite.

Allikas: www.habr.com

Lisa kommentaar