Nauja programavimo kalba Mash

Keletą metų išbandžiau savo jėgas kurdamas savo programavimo kalbą. Norėjau sukurti, mano nuomone, kuo paprastesnę, visapusiškesnę ir patogesnę kalbą.

Šiame straipsnyje noriu išryškinti pagrindinius savo darbo etapus ir pradžioje aprašyti sukurtą kalbos koncepciją bei pirmąjį jos įgyvendinimą, prie kurios šiuo metu ir dirbu.

Iš anksto pasakysiu, kad visą projektą rašiau „Free Pascal“, nes... jame esančios programos gali būti surinktos daugybei platformų, o pats kompiliatorius sukuria labai optimizuotus dvejetainius failus (aš renku visus projekto komponentus su O2 vėliava).

Kalbos vykdymo laikas

Visų pirma, verta pakalbėti apie virtualią mašiną, kurią turėjau parašyti, kad ateityje paleisčiau programas mano kalba. Nusprendžiau įgyvendinti stekinę architektūrą, galbūt todėl, kad tai buvo lengviausias būdas. Neradau nei vieno normalaus straipsnio, kaip tai padaryti rusų kalba, todėl susipažinęs su medžiaga anglų kalba sėdau kurti ir rašyti savo dviratį. Toliau pateiksiu savo „pažangias“ idėjas ir pokyčius šiuo klausimu.

Stacko įgyvendinimas

Akivaizdu, kad VM viršuje yra krūva. Mano įgyvendinime jis veikia blokais. Iš esmės tai yra paprastas rodyklių masyvas ir kintamasis, skirtas saugoti krūvos viršaus indeksą.
Kai jis inicijuojamas, sukuriamas 256 elementų masyvas. Jei į krūvą nustumiama daugiau rodyklių, jo dydis padidėja kitais 256 elementais. Atitinkamai, išimant elementus iš kamino, koreguojamas jo dydis.

VM naudoja kelis kaminus:

  1. Pagrindinė krūva.
  2. Kiemas grąžinimo taškams saugoti.
  3. Šiukšlių surinkėjas.
  4. Pabandykite/pagauti/galiausiai blokuoti tvarkyklės krūvą.

Konstantos ir kintamieji

Šis paprastas. Konstantos yra tvarkomos atskiroje mažoje kodo dalyje ir yra pasiekiamos būsimose programose naudojant statinius adresus. Kintamieji yra tam tikro dydžio rodyklių masyvas, prieiga prie jo langelių vykdoma indeksu – t.y. statinis adresas. Kintamieji gali būti nustumti į krūvos viršų arba nuskaityti iš ten. Tiesą sakant, nes Nors mūsų kintamieji iš esmės saugo nuorodas į reikšmes VM atmintyje, kalboje dominuoja darbas su numanomomis nuorodomis.

Šiukšlių surinkėjas

Mano VM jis yra pusiau automatinis. Tie. pats kūrėjas nusprendžia, kada skambinti šiukšlių išvežėjui. Jis neveikia naudojant įprastą rodyklės skaitiklį, kaip Python, Perl, Ruby, Lua ir kt. Jis įgyvendinamas per žymeklio sistemą. Tie. kai kintamajam ketinama priskirti laikiną reikšmę, šios reikšmės rodyklė pridedama prie šiukšlių surinkėjo kamino. Ateityje kolekcionierius greitai perbėga jau parengtą rodyklių sąrašą.

Tvarkyti bandom/pagauti/galiausiai blokuoja

Kaip ir bet kurioje šiuolaikinėje kalboje, išimčių tvarkymas yra svarbus komponentas. VM branduolys yra suvyniotas į try..catch bloką, kuris gali grįžti į kodo vykdymą po to, kai pagavo išimtį, įstumdamas tam tikrą informaciją apie ją į krūvą. Programos kode galite apibrėžti try/catch/final kodo blokus, nurodydami įėjimo taškus gaudymo metu (išimčių tvarkytojas) ir galiausiai/pabaigoje (bloko pabaiga).

Daugiagija

Jis palaikomas VM lygiu. Tai paprasta ir patogu naudoti. Jis veikia be pertraukimo sistemos, todėl kodas turėtų būti vykdomas keliomis gijomis atitinkamai kelis kartus greičiau.

Išorinės VM bibliotekos

Be šito negalima išsiversti. VM palaiko importavimą, panašiai kaip jis įgyvendinamas kitomis kalbomis. Dalį kodo galite parašyti Mash, o dalį – gimtąja kalba, tada susieti jas į vieną.

Vertėjas iš aukšto lygio Mash kalbos į VM baitinį kodą

Vidutinė kalba

Norėdami greitai parašyti vertėją iš sudėtingos kalbos į VM kodą, pirmiausia sukūriau tarpinę kalbą. Rezultatas buvo kaip surinkėjas, baisus reginys, apie kurį čia nėra jokios ypatingos prasmės. Pasakysiu tik tiek, kad šiame lygyje vertėjas apdoroja daugumą konstantų ir kintamųjų, apskaičiuoja jų statinius adresus ir įėjimo taškų adresus.

Vertėjo architektūra

Diegimui nepasirinkau geriausios architektūros. Vertėjas nekuria kodų medžio, kaip tai daro kiti vertėjai. Jis žiūri į konstrukcijos pradžią. Tie. jei analizuojamas kodo fragmentas atrodo kaip "while <condition>:", tada akivaizdu, kad tai yra while ciklo konstrukcija ir ją reikia apdoroti kaip while ciklo konstrukciją. Kažkas panašaus į sudėtingą jungiklio korpusą.

Šio architektūrinio sprendimo dėka vertėjas pasirodė ne itin greitas. Tačiau jo modifikavimo paprastumas gerokai išaugo. Reikiamas struktūras pridėjau greičiau, nei mano kava galėjo atvėsti. Visas OOP palaikymas buvo įgyvendintas mažiau nei per savaitę.

Kodo optimizavimas

Čia, žinoma, galėjo būti įgyvendinta ir geriau (ir bus įgyvendinta, bet vėliau, kai tik apsispręs). Kol kas optimizatorius žino tik kaip atjungti nenaudojamą kodą, konstantas ir importus iš surinkimo. Taip pat kelios tos pačios vertės konstantos pakeičiamos viena. Tai viskas.

Masės kalba

Pagrindinė kalbos samprata

Pagrindinė idėja buvo sukurti kuo funkcionalesnę ir paprastesnę kalbą. Manau, kad plėtra su savo užduotimi susidoroja su kaupu.

Kodų blokai, procedūros ir funkcijos

Visos kalbos konstrukcijos atidaromos dvitaškiu. : ir juos uždaro operatorius pabaiga.

Procedūros ir funkcijos atitinkamai deklaruojamos kaip proc ir func. Argumentai išvardyti skliausteliuose. Viskas kaip ir daugelyje kitų kalbų.

operatorius grįžti galite grąžinti reikšmę iš funkcijos, operatoriaus pertrauka leidžia išeiti iš procedūros/funkcijos (jei ji yra už kilpų ribų).

Kodo pavyzdys:

...

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

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

Palaikomi dizainai

  • Kilpos: už..pabaigą, kol..pabaigą, iki..pabaigą
  • Sąlygos: jei..[else..]pabaiga, perjunkite..[atvejis..pabaiga..][else..]pabaiga
  • Metodai: proc <vardas>():... end, func <name>():... end
  • Etiketė ir goto: <vardas>:, šuolis <vardas>
  • Sąrašai ir konstantų masyvai.

Kintamieji

Vertėjas gali juos nustatyti automatiškai arba jei kūrėjas prieš juos apibrėždamas parašo var.

Kodo pavyzdžiai:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Palaikomi pasauliniai ir vietiniai kintamieji.

OOP

Na, mes priėjome prie skaniausios temos. Mash palaiko visas objektinio programavimo paradigmas. Tie. klasės, paveldimumas, polimorfizmas (įskaitant dinaminį), dinaminis automatinis atspindys ir savistaba (pilna).

Be tolesnių veiksmų, geriau tiesiog pateikti kodo pavyzdžius.

Paprasta klasė ir darbas su ja:

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

Išeiga: 30.

Paveldėjimas ir polimorfizmas:

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

Išeiga: 60.

O dinaminis polimorfizmas? Taip, tai atspindys!:

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

Išeiga: 60.

Dabar pažvelkime į paprastas vertybes ir klases:

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

Bus išvesta: tiesa, tiesa.

Apie priskyrimo operatorius ir aiškias nuorodas

Operatorius ?= naudojamas kintamajam priskirti žymeklį reikšmei atmintyje.
Operatorius = keičia reikšmę atmintyje naudodamas žymeklį iš kintamojo.
O dabar šiek tiek apie aiškias nuorodas. Pridėjau juos prie kalbos, kad jie egzistuotų.
@<kintamasis> – perkelkite aiškų žymeklį į kintamąjį.
?<kintamasis> — gauti kintamąjį pagal žymeklį.
@= – priskirti kintamajam reikšmę aiškia jo žymekliu.

Kodo pavyzdys:

uses <bf>
uses <crt>

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

Išves: tam tikras skaičius, 10, 11.

Pabandyk..[pagauti..][pagaliau..]pabaiga

Kodo pavyzdys:

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

Ateities planai

Vis žiūriu ir žiūriu į GraalVM & Truffle. Mano vykdymo aplinkoje nėra JIT kompiliatoriaus, todėl našumo požiūriu ji šiuo metu konkuruoja tik su Python. Tikiuosi, kad man pavyks įdiegti JIT kompiliaciją pagal GraalVM arba LLVM.

saugykla

Galite žaisti su pokyčiais ir patys sekti projektą.

Vieta
„GitHub“ saugykla

Dėkojame, kad perskaitėte iki galo, jei perskaitėte.

Šaltinis: www.habr.com

Добавить комментарий