Uus programmeerimiskeel Mash

Olen mitu aastat proovinud oma programmeerimiskeele arendamist. Soovisin luua vÔimalikult lihtsa, tÀieliku ja mugava keele.

Selles artiklis tahan tutvustada oma töö peamisi etappe ja alustada loodud keele kontseptsiooni ja selle esialgse rakenduse kirjeldamisest, mille kallal praegu töötan.

Kuna kirjutasin kogu projekti Free Pascalis, ĂŒtlen kohe, et sellel keeles saab programme kokku panna paljudele platvormidele ning kompilator loob ĂŒsna optimeeritud binaarfaile (kĂ”ik projekti komponendid kogun O2 lipuga).

Keelte tÀitmisring

Esmalt tasub rÀÀkida virtuaalsest masinast, mille pidin kirjutama oma keeles tulevaste rakenduste tĂ€itmiseks. Otsustasin rakendada stakialust arhitektuuri, kuna see oli kĂ”ige lihtsam. Ühtegi korralikku artiklit selles osas vene keeles ei leidnud, nii et pĂ€rast ingliskeelsete materjalidega tutvumist asusin oma jalgratta projekteerimise ja kirjutamise kallale. Edasi toon oma "uuenduslikud" ideed ja arendused sellel alal.

Staki rakendamine

Ilmselt asub VM-i tipus virn. Minu teostuses töötab see blokkidena. Tegelikult on see lihtne pointerite massiiv ja muutuja, mis salvestab virna tipu indeksi.
Selle initsialiseerimisel luuakse 256 elemendiga massiiv. Kui virn lĂ€heb ĂŒle rohkemate pointerite, siis selle suurus suureneb jĂ€rgmise 256 elemendi vĂ”rra. Vastavalt, kui elemente virnast eemaldatakse, reguleeritakse selle suurust.

VM-is kasutatakse mitmeid virnu:

  1. Peamine virn.
  2. Tagasi pöördumispunktide virn.
  3. Kogumishalduri virn.
  4. Try/catch/finally plokkide töötleja virn.

Constantid ja muutujad

Selle osas on kĂ”ik lihtne. Constantid töödeldakse eraldi vĂ€ikese kooditĂŒkiga ja on tulevikus rakendustes kergesti saadaval staatiliste aadresside kaudu. Muutujad on teatud suurusega pointerite massiiv, mille elementide juurde pÀÀseb indeksi kaudu – s.t. staatilise aadressi kaudu. Muutujad saab paigutada virna tippu vĂ”i lugeda sealt. Tegelikult, kuna meie muutujad pĂ”himĂ”tteliselt hoiavad pointereid VM-i mĂ€lu vÀÀrtustele, siis keeles domineerib töötamine varjatud pointeritega.

KÔrvalvÀrvide kogu

Minu virtuaalses masinas on see poolautomaalne. See tĂ€hendab, et arendaja otsustab ise, millal tuleb koristustöötlust vĂ€lja kutsuda. See ei tööta nagu tavaliselt viidatud loenduri jĂ€rgi, nagu nĂ€iteks Pythonis, Perl'is, Ruby's, Lua's jne. See on rakendatud markerite sĂŒsteemi kaudu. See tĂ€hendab, et kui eeldatakse, et muutujale antakse ajutine vÀÀrtus — siis lisatakse viidatud vÀÀrtus koristustöötluse steki.

Try/catch/finally plokkide töötlemine

Nagu igas kaasaegses keeles, on erandite töötlemine selle oluline osa. Virtuaalse masina sĂŒda on ĂŒmbritsetud try..catch plokiga, mis vĂ”ib naasta koodi tĂ€itmise juurde pĂ€rast erandi pĂŒĂŒdmist, paigutades steki natuke teavet selle kohta. Rakenduskoodis saab mÀÀrata try/catch/finally koodiplokke, mĂ€rkides sissepÀÀsu punktid catch (erandi töötleja) ja finally/end (ploki lĂ”pp).

Mitme niidi olemasolu

Seda toetatakse virtuaalse masina tasemel. See on lihtne ja mugav kasutada. See töötab ilma katkestussĂŒsteemita, seega peab kood tööle olema mitmes niidis mitu korda kiiremini vastavalt.

Virtuaalse masina vÀlised raamatukogud

Seda on hĂ€davajalik. VM toetab imporde, sarnaselt sellele, kuidas see on rakendatud teistes keeltes. VĂ”ib kirjutada osa koodist Mashis ja osa koodist natiivsetes keeltes, seejĂ€rel siduda need ĂŒheks tervikuks.

TÔlkija kÔrgtasemest keele Mashi baidikoodiks VM jaoks

Vahekeel

Kiire tĂ”lkija koostamiseks keerulisest keelest VM koodiks arendasin ma esmalt vahekeele. Selle tulemusena sai sellest assembleerijapoodne hirmsa vĂ€limusega nĂ€htus, mida siin eriti pole mĂ”tet vaadata. Ütlen vaid, et sellel tasemel tĂ”lkija töötleb enamikku viimaseid, muutujaid, arvutab nende staatilisi aadresse ja sisenemispunktide aadresse.

TÔlkija arhitektuur

Valisin mitte kĂ”ige parema arhitektuuri rakendamiseks. TĂ”lkija ei ehita koodipuu, nagu peaksid teised tĂ”lkijad. Ta vaatab konstruktsiooni algust. St, kui analĂŒĂŒsitav koodijupp nĂ€eb vĂ€lja nagu «while :», siis on ilmne, et see on while-tsĂŒkli konstruktsioon ja seda tuleb töödelda kui while-tsĂŒklit. Midagi keerulist switch-case sarnast.

Sellise arhitektuurilise lahenduse tĂ”ttu ei ole tĂ”lkija eriti kiire. Kuid selle ĂŒmbertegemise lihtsus on kordades paranenud. Vajalikud konstruktsioonid lisasin kiiremini, kui suutsin oma kohvi jahtuda. TĂ€ielik OOP tugi saadi valmis vĂ€hem kui nĂ€dalaga.

Koodi optimeerimine

Siin oleks muidugi saanud paremini teha (ja tehakse ka, aga hiljem, kui jĂ”ud kĂ€es). Praegu suudab optimeerija ainult eemaldada kasutamata koodi, konstandid ja impordid kogumisest. Samuti asendatakse mitu konstandi, millel on sama vÀÀrtus, ĂŒhega. Sellega asi piirdub.

Mash keele

Keele pÔhikontseptsioon

PĂ”hieesmĂ€rk oli vĂ€lja arendada vĂ”imalikult funktsionaalne ja lihtne keel. Arvan, et arendus tĂ€idab oma ĂŒlesande suurepĂ€raselt.

Koodiblokid, protseduurid ja funktsioonid

KÔik konstruktsioonid keeles avatakse kolonni mÀrkiga : ja suletakse operaatoriga lÔpp.

Protseduure ja funktsioone deklareeritakse vastavalt proc ja func. Sulgudes loetletakse argumendid. KÔik nagu enamikus teistes keeltes.

Operaatori return abil saab funktsioonist vÀÀrtuse tagasi anda, operaator break lubab protseduurist/funktsioonist vÀljuda (kui see on vÀljaspool silmuseid).

KoodinÀide:

...

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

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

Toetatud konstruktsioonid

  • TsĂŒklid: for..end, while..end, until..end
  • Tingimused: if..[else..]end, switch..[case..end..][else..]end
  • Meetodid: proc <nimi>():
 end, func <nimi>():
 end
  • Label & goto: <nimi>:, jump <nimi>
  • Enum, enum ja konstantide massiivid.

Muutujad

TÔlkija vÔib neid automaatselt mÀÀrata vÔi arendaja kirjutab var nende mÀÀratlemise ette.

KoodinÀidised:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Toetatakse globaalseid ja kohalikke muutujaid.

OOP

NĂŒĂŒd oleme jĂ”udnud kĂ”ige huvitavama teema juurde. Mash keeles toetatakse kĂ”iki objektorienteeritud programmeerimise paradigmasid. St. klassid, pĂ€randumine, polĂŒmorfism (sealhulgas dĂŒnaamiline), dĂŒnaamiline automaatne refleksioon ja introspektsioon (tĂ€ielik).

Ilma liigsete sÔnadeta, parim on anda koodinÀidised.

Lihtne klass ja töö selle kallal:

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Ă€ljastab: 30.

PĂ€rimine ja polĂŒmorfism:

kasutab 
kasutab 

klass MyClass:
  muutuja a, b
  prots Create, Free
  funktsioon Summ
lÔpp

prots MyClass::Create(a, b):
  $a = uus(a)
  $b = uus(b)
lÔpp

prots MyClass::Free():
  Vabasta($a, $b)
  $rem()
lÔpp

funktsioon MyClass::Summ():
  tagasta $a + $b
lÔpp

klass MyNewClass(MyClass):
  funktsioon Summ
lÔpp

funktsioon MyNewClass::Summ():
  tagasta ($a + $b) * 2
lÔpp

prots main():
  x ?= uus MyNewClass(10, 20)
  println(x->Summ())
  x->Free()
lÔpp

VĂ€ljund: 60.

Aga mida öelda dĂŒnaamilise polĂŒmorfismi kohta? See on ju refleksioon!:

kasutab 
kasutab 

klass MyClass:
  muutuja a, b
  prots Create, Free
  funktsioon Summ
lÔpp

prots MyClass::Create(a, b):
  $a = uus(a)
  $b = uus(b)
lÔpp

prots MyClass::Free():
  Vabasta($a, $b)
  $rem()
lÔpp

funktsioon MyClass::Summ():
  tagasta $a + $b
lÔpp

klass MyNewClass(MyClass):
  funktsioon Summ
lÔpp

funktsioon MyNewClass::Summ():
  tagasta ($a + $b) * 2
lÔpp

prots main():
  x ?= uus MyClass(10, 20)
  x->Summ ?= MyNewClass::Summ
  println(x->Summ())
  x->Free()
lÔpp

VĂ€ljund: 60.

NĂŒĂŒd vĂ”tame hetke introspektsiooniks lihtsate vÀÀrtuste ja klasside jaoks:

kasutab 
kasutab 

klass MyClass:
  muutuja a, b
lÔpp

prots main():
  x ?= uus MyClass
  println(BoolToStr(x->type == MyClass))
  x->rem()
  println(BoolToStr(typeof(3.14) == typeReal))
lÔpp

VĂ€ljund: true, true.

Kuna tegemist on mÀÀramisoperatsioonide ja selgete viidete teemaga

Operaator ?= mÀÀrab vÀÀrtuse aadressi muutujale.
Operaator = muudab aadressi vÀÀrtuse mÀlus muutuja kaudu.
Ja nĂŒĂŒd natuke selgetest viidetest. Lisasin need keelde, et need olemas oleksid.
@ — vĂ”tta selge viide muutujale.
?<ĐżĐ”Ń€Đ”ĐŒĐ”ĐœĐœĐ°Ń> — ĐżĐŸĐ»ŃƒŃ‡ĐžŃ‚ŃŒ ĐżĐ”Ń€Đ”ĐŒĐ”ĐœĐœŃƒŃŽ ĐżĐŸ уĐșазатДлю.
@= — mÀÀrata muutujale vÀÀrtus selle otsese viitamise kaudu.

KoodinÀide:

kasutab 
kasutab 

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

VÀljund: mÔni number, 10, 11.

Proovi..[catch..][finally..]end

KoodinÀide:

kasutab 
kasutab 

proc main():
  println("Algus")
  proovi:
    println("Proovin midagi teha...")
    a ?= 10 / 0
  catch:
    println(getError())
  finally:
    println("LÔpuks")
  end
  println("LÔpp")
  inputln()
end

Tulevikuplaanid

Vaatan pidevalt GraalVM-i & Truffle'i. Minu jooksutamisel puudub JIT kompilaator, seega vÔib selle jÔudlus praegu konkurentsi pakkuda vaid Pythoni osas. Loodan, et suudan JIT kompileerimise GraalVMi vÔi LLVM-i baasil ellu viia.

Repo

VÔite mÀngida nendega ja jÀlgida projekti ise.

Veebileht
GitHubi hoidla

AitÀh, et lugesite lÔpuni, kui te seda tegite.

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster