Bagong programming language Mash

Sa loob ng ilang taon sinubukan ko ang aking kamay sa pagbuo ng sarili kong programming language. Nais kong lumikha, sa aking opinyon, ang pinakasimple, ganap na gumagana at maginhawang wika na posible.

Sa artikulong ito gusto kong i-highlight ang mga pangunahing yugto ng aking trabaho at, sa simula, ilarawan ang nilikhang konsepto ng wika at ang unang pagpapatupad nito, na kasalukuyang ginagawa ko.

Hayaan akong sabihin nang maaga na isinulat ko ang buong proyekto sa Libreng Pascal, dahil... Ang mga programa dito ay maaaring tipunin para sa isang malaking bilang ng mga platform, at ang compiler mismo ay gumagawa ng napaka-optimized na mga binary (Kinakolekta ko ang lahat ng mga bahagi ng proyekto na may O2 flag).

Runtime ng wika

Una sa lahat, sulit na pag-usapan ang virtual machine na kailangan kong isulat para magpatakbo ng mga application sa hinaharap sa aking wika. Nagpasya akong magpatupad ng isang stack architecture, marahil, dahil ito ang pinakamadaling paraan. Wala akong nakitang isang normal na artikulo kung paano ito gagawin sa Russian, kaya pagkatapos na maging pamilyar sa materyal sa wikang Ingles, umupo ako sa pagdidisenyo at pagsulat ng sarili kong bisikleta. Susunod na ilalahad ko ang aking "advanced" na mga ideya at mga pag-unlad sa bagay na ito.

Pagpapatupad ng stack

Malinaw, sa tuktok ng VM ay ang stack. Sa aking pagpapatupad ito ay gumagana sa mga bloke. Mahalaga ito ay isang simpleng hanay ng mga pointer at isang variable upang iimbak ang index ng tuktok ng stack.
Kapag nasimulan ito, isang hanay ng 256 na elemento ang nalilikha. Kung mas maraming pointer ang itutulak sa stack, tataas ang laki nito ng susunod na 256 na elemento. Alinsunod dito, kapag nag-aalis ng mga elemento mula sa stack, ang laki nito ay nababagay.

Gumagamit ang VM ng ilang stack:

  1. Pangunahing stack.
  2. Isang stack para sa pag-iimbak ng mga return point.
  3. Salansan ng kolektor ng basura.
  4. Subukan/huli/sa wakas ay harangan ang stack ng handler.

Mga Constant at Variable

Ang isang ito ay simple. Ang mga constant ay pinangangasiwaan sa isang hiwalay na maliit na piraso ng code at magagamit sa hinaharap na mga aplikasyon sa pamamagitan ng mga static na address. Ang mga variable ay isang hanay ng mga pointer ng isang tiyak na laki, ang pag-access sa mga cell nito ay isinasagawa sa pamamagitan ng index - i.e. static na address. Maaaring itulak ang mga variable sa tuktok ng stack o basahin mula doon. Actually, kasi Habang ang aming mga variable ay mahalagang nag-iimbak ng mga pointer sa mga halaga sa memorya ng VM, ang wika ay pinangungunahan ng pagtatrabaho sa mga implicit na pointer.

Basurero

Sa aking VM ito ay semi-awtomatikong. Yung. ang developer mismo ang nagdedesisyon kung kailan tatawagan ang basurero. Hindi ito gumagana gamit ang isang regular na pointer counter, tulad ng sa Python, Perl, Ruby, Lua, atbp. Ito ay ipinatupad sa pamamagitan ng isang marker system. Yung. kapag ang isang variable ay nilayon na magtalaga ng isang pansamantalang halaga, isang pointer sa halagang ito ay idaragdag sa stack ng kolektor ng basura. Sa hinaharap, mabilis na tatakbo ang kolektor sa nakahandang listahan ng mga pointer.

Paghawak ng try/catch/finally blocks

Tulad ng sa anumang modernong wika, ang paghawak ng exception ay isang mahalagang bahagi. Ang VM core ay nakabalot sa isang try..catch block, na maaaring bumalik sa code execution pagkatapos makakuha ng exception sa pamamagitan ng paglalagay ng ilang impormasyon tungkol dito sa stack. Sa application code, maaari mong tukuyin ang try/catch/finally blocks ng code, na tumutukoy sa mga entry point sa catch (exception handler) at sa wakas/end (end of the block).

Multithreading

Ito ay suportado sa antas ng VM. Ito ay simple at maginhawang gamitin. Gumagana ito nang walang interrupt system, kaya ang code ay dapat na isagawa sa ilang mga thread nang maraming beses nang mas mabilis, ayon sa pagkakabanggit.

Mga panlabas na aklatan para sa mga VM

Walang paraan kung wala ito. Sinusuportahan ng VM ang mga pag-import, katulad ng kung paano ito ipinapatupad sa ibang mga wika. Maaari mong isulat ang bahagi ng code sa Mash at bahagi ng code sa mga katutubong wika, pagkatapos ay i-link ang mga ito sa isa.

Tagasalin mula sa mataas na antas na wika ng Mash hanggang sa bytecode para sa mga VM

Intermediate na wika

Upang mabilis na magsulat ng isang tagasalin mula sa isang kumplikadong wika sa VM code, una akong bumuo ng isang intermediate na wika. Ang resulta ay isang assembler-like na kahila-hilakbot na palabas na walang partikular na punto sa pagsasaalang-alang dito. Sasabihin ko lang na sa antas na ito ang tagasalin ay nagpoproseso ng karamihan sa mga constant at variable, kinakalkula ang kanilang mga static na address at ang mga address ng mga entry point.

Arkitektura ng tagasalin

Hindi ko pinili ang pinakamahusay na arkitektura para sa pagpapatupad. Ang tagasalin ay hindi gumagawa ng isang code tree, tulad ng ginagawa ng ibang mga tagapagsalin. Tinitingnan niya ang simula ng istraktura. Yung. kung ang piraso ng code na na-parse ay mukhang "habang <kondisyon>:", kung gayon ay malinaw na ito ay isang while loop construct at kailangang iproseso bilang isang while loop construct. Isang bagay na tulad ng isang kumplikadong switch-case.

Salamat sa solusyon sa arkitektura na ito, naging hindi masyadong mabilis ang tagasalin. Gayunpaman, ang kadalian ng pagbabago nito ay tumaas nang malaki. Idinagdag ko ang mga kinakailangang istruktura nang mas mabilis kaysa sa maaaring lumamig ang aking kape. Ang buong suporta sa OOP ay ipinatupad nang wala pang isang linggo.

Pag-optimize ng code

Dito, siyempre, maaari itong maipatupad nang mas mahusay (at ipapatupad, ngunit sa paglaon, sa sandaling maabot ito ng isa). Sa ngayon, alam lang ng optimizer kung paano putulin ang hindi nagamit na code, constants at import mula sa assembly. Gayundin, maraming mga constant na may parehong halaga ay pinapalitan ng isa. Iyon lang.

Mash na wika

Pangunahing konsepto ng wika

Ang pangunahing ideya ay upang bumuo ng pinaka-functional at simpleng wika na posible. Sa tingin ko, ang pag-unlad ay nakayanan ang gawain nito sa isang putok.

Mga bloke ng code, pamamaraan at pag-andar

Ang lahat ng mga konstruksiyon sa wika ay binubuksan gamit ang isang tutuldok. : at isinara ng operator dulo.

Ang mga pamamaraan at function ay idineklara bilang proc at func, ayon sa pagkakabanggit. Ang mga argumento ay nakalista sa panaklong. Ang lahat ay tulad ng karamihan sa iba pang mga wika.

Operator pagbabalik maaari kang magbalik ng isang halaga mula sa isang function, operator masira ay nagbibigay-daan sa iyong lumabas sa procedure/function (kung ito ay nasa labas ng mga loop).

Halimbawang code:

...

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

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

Mga Sinusuportahang Disenyo

  • Loops: para..wakas, habang..wakas, hanggang..wakas
  • Kundisyon: kung..[iba..]tapos, lumipat..[kaso..tapos..][iba..]tapos
  • Paraan: proc <name>():... end, func <name>():... end
  • Label at goto: <name>:, tumalon sa <name>
  • Enum enumerations at pare-parehong array.

Mga variable

Maaaring awtomatikong matukoy ng tagasalin ang mga ito, o kung magsusulat ang developer ng var bago tukuyin ang mga ito.

Mga halimbawa ng code:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Ang mga global at lokal na variable ay suportado.

OOP

Well, nakarating na tayo sa pinaka masarap na paksa. Sinusuportahan ng Mash ang lahat ng object-oriented programming paradigms. Yung. mga klase, pamana, polymorphism (kabilang ang dynamic), dynamic na awtomatikong pagmuni-muni at introspection (buo).

Nang walang karagdagang ado, mas mabuting magbigay na lamang ng mga halimbawa ng code.

Isang simpleng klase at nagtatrabaho dito:

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

Ilalabas: 30.

Pamana at polymorphism:

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

Ilalabas: 60.

Paano ang tungkol sa dynamic na polymorphism? Oo, ito ay pagmuni-muni!:

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

Ilalabas: 60.

Ngayon ay maglaan tayo ng ilang sandali upang introspect para sa mga simpleng halaga at klase:

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

Maglalabas: totoo, totoo.

Tungkol sa mga operator ng pagtatalaga at tahasang mga payo

Ang ?= operator ay ginagamit upang magtalaga ng isang variable ng isang pointer sa isang halaga sa memorya.
Ang = operator ay nagbabago ng isang halaga sa memorya gamit ang isang pointer mula sa isang variable.
At ngayon ng kaunti tungkol sa tahasang mga payo. Idinagdag ko sila sa wika upang umiral sila.
@<variable> β€” kumuha ng tahasang pointer sa isang variable.
?<variable> β€” kumuha ng variable sa pamamagitan ng pointer.
@= β€” magtalaga ng value sa isang variable sa pamamagitan ng isang tahasang pointer dito.

Halimbawang code:

uses <bf>
uses <crt>

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

Maglalabas: ilang numero, 10, 11.

Subukan..[catch..][finally..]end

Halimbawang code:

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

ΠŸΠ»Π°Π½Ρ‹ Π½Π° Π±ΡƒΠ΄ΡƒΡ‰Π΅Π΅

Patuloy akong tumitingin at tumitingin sa GraalVM & Truffle. Ang aking runtime na kapaligiran ay walang JIT compiler, kaya sa mga tuntunin ng pagganap ito ay kasalukuyang nakikipagkumpitensya lamang sa Python. Umaasa ako na maipapatupad ko ang JIT compilation batay sa GraalVM o LLVM.

imbakan

Maaari mong paglaruan ang mga pag-unlad at sundin ang proyekto sa iyong sarili.

Π‘Π°ΠΉΡ‚
Imbakan sa GitHub

Salamat sa pagbabasa hanggang dulo kung ginawa mo.

Pinagmulan: www.habr.com

Magdagdag ng komento