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:
- Peamine virn.
- Virn tagastuspunktide hoidmiseks.
- Prügikoristus.
- 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.
Täname, et lugesite lõpuni, kui lugesite.
Allikas: www.habr.com