Mash programazio lengoaia berria

Hainbat urtez saiatu nintzen nire programazio-lengoaia garatzen. Nire ustez, ahalik eta hizkuntza errazena, guztiz funtzional eta erosoena sortu nahi nuen.

Artikulu honetan nire lanaren etapa nagusiak nabarmendu nahi ditut eta, hasteko, hizkuntzaren sortutako kontzeptua eta bere lehen ezarpena deskribatu, egun lantzen ari naizena.

Esan dezadan aldez aurretik proiektu osoa Free Pascal-en idatzi nuela, zeren... bertan dauden programak plataforma kopuru handi baterako munta daitezke, eta konpilatzaileak berak bitar oso optimizatuak sortzen ditu (O2 banderarekin biltzen ditut proiektuaren osagai guztiak).

Hizkuntzaren exekuzioa

Lehenik eta behin, merezi du nire hizkuntzan etorkizuneko aplikazioak exekutatzeko idatzi behar nuen makina birtualaz hitz egitea. Pila arkitektura bat ezartzea erabaki nuen, agian, modurik errazena zelako. Ez nuen artikulu arrunt bakar bat aurkitu errusieraz nola egin, beraz, ingelesezko materiala ezagutu ondoren, nire bizikleta diseinatzen eta idazten jarri nintzen. Jarraian, gai honi buruzko nire ideia eta garapen "aurreratuak" aurkeztuko ditut.

Pila inplementatzea

Jakina, VM-aren goialdean pila dago. Nire inplementazioan blokeetan funtzionatzen du. Funtsean, erakusle sorta sinple bat eta aldagai bat da pilaren goiko indizea gordetzeko.
Hasieratzen denean, 256 elementuko array bat sortzen da. Erakusle gehiago pilara bultzatzen badira, haren tamaina hurrengo 256 elementuetan handituko da. Horren arabera, elementuak pilatik kentzean, bere tamaina egokitzen da.

VM-k hainbat pila erabiltzen ditu:

  1. Pila nagusia.
  2. Itzultzeko puntuak gordetzeko pila bat.
  3. Zabor biltzaileen pila.
  4. Saiatu/harrapatu/azkenean blokeatu kudeatzaileen pila.

Konstanteak eta aldagaiak

Hau sinplea da. Konstanteak kode zati txiki batean kudeatzen dira eta etorkizuneko aplikazioetan eskuragarri daude helbide estatikoen bidez. Aldagaiak tamaina jakin bateko erakusle sorta bat dira, bere gelaxketarako sarbidea indizearen bidez egiten da, hau da. helbide estatikoa. Aldagaiak pilaren goiko aldera eraman daitezke edo hortik irakur daitezke. Egia esan, zeren Gure aldagaiek funtsean VM memorian balioen erakusleak gordetzen dituzten bitartean, hizkuntzan erakusle inplizituekin lan egiten da nagusi.

Zabor biltzailea

Nire VMn erdi-automatikoa da. Horiek. garatzaileak berak erabakitzen du noiz deitu zabor-biltzaileari. Ez du funtzionatzen ohiko erakusle-kontagailu bat erabiliz, Python, Perl, Ruby, Lua, etab. Markatzaile sistema baten bidez ezartzen da. Horiek. aldagai bati aldi baterako balio bat esleitu nahi denean, balio horren erakuslea gehitzen zaio zabor-biltzaileen pilara. Etorkizunean, biltzaileak lehendik prestatutako erakusleen zerrenda azkar exekutatzen du.

Saiatu/harrapatu/azkenik blokeak maneiatzea

Edozein hizkuntza modernotan bezala, salbuespenen kudeaketa osagai garrantzitsua da. VM core try..catch bloke batean bilduta dago, eta kodearen exekuziora itzul daiteke salbuespen bat harrapatu ondoren horri buruzko informazio batzuk pilara bultzatuz. Aplikazio-kodean, try/catch/finally kode-blokeak defini ditzakezu, catch-en (salbuespen-kudeatzailea) eta azkenik/end (blokearen amaiera) sarrera-puntuak zehaztuz.

Hari anitzekoa

VM mailan onartzen da. Erabiltzeko erraza eta erosoa da. Eten-sistemarik gabe funtzionatzen du, beraz, kodea hainbat haritan exekutatu behar da hainbat aldiz azkarrago, hurrenez hurren.

VMentzako kanpoko liburutegiak

Ez dago hau gabe egin beharrik. VM-k inportazioak onartzen ditu, beste hizkuntza batzuetan inplementatzen den antzera. Kodearen zati bat Mash-en eta kodearen zati bat jatorrizko hizkuntzan idatz ditzakezu, gero bakar batean lotu.

Maila handiko Mash hizkuntzatik VMetarako bytekoderako itzultzailea

Erdiko hizkuntza

Lengoaia konplexu batetik VM kodera itzultzaile bat azkar idazteko, lehenik eta behin bitarteko hizkuntza bat garatu nuen. Emaitza muntatzaile moduko ikuskizun ikaragarria izan zen, hemen kontuan hartzeko puntu berezirik ez duena. Bakarrik esango dut maila honetan itzultzaileak konstante eta aldagai gehienak prozesatzen dituela, haien helbide estatikoak eta sarrera puntuen helbideak kalkulatzen dituela.

Itzultzaileen arkitektura

Ez nuen inplementatzeko arkitektura onena aukeratu. Itzultzaileak ez du kode zuhaitzik eraikitzen, beste itzultzaile batzuek egiten duten bezala. Egituraren hasierari begiratzen dio. Horiek. Analizatzen ari den kode zatiak "while <baldintza>::" itxura badu, bistakoa da hau while begizta eraikuntza bat dela eta while begizta eraikuntza gisa prozesatu behar dela. Etengailu-kasu konplexu baten antzeko zerbait.

Konponbide arkitektoniko honi esker, itzultzailea ez zen oso azkarra izan. Hala ere, aldatzeko erraztasuna nabarmen handitu da. Nire kafeak hoztu zezakeen baino azkarrago gehitu nituen beharrezko egiturak. OOP laguntza osoa astebete baino gutxiagoan ezarri zen.

Kodearen optimizazioa

Hemen, noski, hobeto inplementatu zitekeen (eta inplementatuko da, baina gerora, norberak bertara iritsi bezain pronto). Orain arte, optimizatzaileak erabiltzen ez diren kodea, konstanteak eta inportazioak muntaiatik mozten besterik ez daki. Gainera, balio bereko hainbat konstante batek ordezkatzen dira. Hori da dena.

Mash hizkuntza

Hizkuntzaren oinarrizko kontzeptua

Ahalik eta hizkuntzarik funtzional eta errazena garatzea zen ideia nagusia. Uste dut garapenak bere zereginari kolpe batez aurre egiten diola.

Kode blokeak, prozedurak eta funtzioak

Hizkuntzaren eraikuntza guztiak bi puntuz irekitzen dira. : eta operadoreak itxi egiten ditu amaiera.

Prozedurak eta funtzioak proc eta func gisa deklaratzen dira, hurrenez hurren. Argumentuak parentesi artean ageri dira. Dena beste hizkuntza gehienak bezalakoa da.

Eragilea bueltan funtzio batetik, operadore batetik balio bat itzul dezakezu apurtu prozedura/funtziotik irteteko aukera ematen du (begiztetatik kanpo badago).

Adibidea kodea:

...

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

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

Onartutako diseinuak

  • Begiztak: for..end, while..end, arte..amaiera
  • Baldintzak: bada..[bestela..]amaitu, aldatu..[kasu..amaitu..][bestela..]amaitu
  • Metodoak: proc <izena>():... end, func <izena>():... end
  • Etiketatu eta joan: <izena>:, salto egin <izena>
  • Enumerazioak eta matrize konstanteak.

aldagai

Itzultzaileak automatikoki zehaztu ditzake, edo garatzaileak var idazten badu definitu aurretik.

Kode adibideak:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Aldagai globalak eta lokalak onartzen dira.

OOP

Tira, gairik goxoenera heldu gara. Mash-ek objektuetara zuzendutako programazio paradigma guztiak onartzen ditu. Horiek. klaseak, herentzia, polimorfismoa (dinamikoa barne), hausnarketa automatiko dinamikoa eta introspekzioa (osoa).

Gehiagorik gabe, hobe da kode-adibideak ematea.

Klase sinple bat eta honekin lan egitea:

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

Irteera: 30.

Herentzia eta polimorfismoa:

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

Irteera: 60.

Zer gertatzen da polimorfismo dinamikoarekin? Bai, hau hausnarketa da!:

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

Irteera: 60.

Orain har dezagun une bat balio eta klase errazak barneratzeko:

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

Irteera izango da: egia, egia.

Esleipen-operadoreei eta erakusle esplizituei buruz

?= eragilea aldagai bati erakuslea memoriako balio bati esleitzeko erabiltzen da.
= operadoreak memoriako balio bat aldatzen du aldagai bateko erakuslea erabiliz.
Eta orain erakusle esplizituei buruz pixka bat. Hizkuntzari gehitu nizkion, existitzeko.
@<aldagaia> β€” hartu erakusle esplizitu bat aldagai bati.
?<aldagaia> β€” lortu aldagai bat erakusle bidez.
@= β€” esleitu balio bat aldagai bati erakusle esplizitu baten bidez.

Adibidea kodea:

uses <bf>
uses <crt>

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

Irteera izango du: zenbaki batzuk, 10, 11.

Saiatu..[harrapatu..][azkenean..]amaitu

Adibidea kodea:

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

Etorkizunerako planak

GraalVM eta Truffle-ra begira eta begira jarraitzen dut. Nire exekuzio-inguruneak ez du JIT konpilatzailerik, beraz, errendimenduari dagokionez, Python-ekin soilik lehiakorra da. Espero dut GraalVM edo LLVMn oinarritutako JIT konpilazioa inplementatzeko gai izango naizela.

biltegia

Garapenekin jolastu eta zuk zeuk jarraitu dezakezu proiektua.

Web
Biltegia GitHub-en

Eskerrik asko amaierara arte irakurtzeagatik.

Iturria: www.habr.com

Gehitu iruzkin berria