Novi programski jezik Mash

Nekoliko sam se godina okušao u razvoju vlastitog programskog jezika. Želio sam stvoriti, po mom mišljenju, najjednostavniji, potpuno funkcionalan i najprikladniji mogući jezik.

U ovom članku želim istaknuti glavne faze svog rada i, za početak, opisati stvoreni koncept jezika i njegovu prvu implementaciju, na kojoj trenutno radim.

Unaprijed ću reći da sam cijeli projekt napisao u Free Pascalu, jer... programi na njemu se mogu sastaviti za ogroman broj platformi, a sam prevodilac proizvodi vrlo optimizirane binarne datoteke (sve komponente projekta skupljam s O2 zastavom).

Izvođenje jezika

Prije svega, vrijedi razgovarati o virtualnom stroju koji sam morao napisati kako bih pokretao buduće aplikacije na mom jeziku. Odlučio sam implementirati arhitekturu hrpa, možda zato što je to bio najlakši način. Nisam našao niti jedan normalan članak o tome kako to učiniti na ruskom, pa sam, nakon što sam se upoznao s materijalom na engleskom jeziku, sjeo na dizajn i pisanje vlastitog bicikla. Zatim ću predstaviti svoje "napredne" ideje i razvoj po ovom pitanju.

Implementacija stoga

Očito, na vrhu VM-a je stog. U mojoj implementaciji radi u blokovima. U biti ovo je jednostavan niz pokazivača i varijabla za pohranjivanje indeksa vrha stoga.
Kada se inicijalizira, stvara se niz od 256 elemenata. Ako se više pokazivača gurne na stog, njegova se veličina povećava za sljedećih 256 elemenata. Sukladno tome, prilikom uklanjanja elemenata iz hrpe, njegova veličina se prilagođava.

VM koristi nekoliko skupova:

  1. Glavni stog.
  2. Stog za pohranu povratnih točaka.
  3. Skupljač smeća.
  4. Pokušajte/uhvatite/konačno blokirajte snop rukovatelja.

Konstante i varijable

Ovaj je jednostavan. Konstante se obrađuju u zasebnom malom komadu koda i dostupne su u budućim aplikacijama putem statičkih adresa. Varijable su niz pokazivača određene veličine, pristup njegovim ćelijama vrši se indeksom - tj. statička adresa. Varijable se mogu gurnuti na vrh stoga ili odatle čitati. Zapravo, jer Dok naše varijable u biti pohranjuju pokazivače na vrijednosti u VM memoriju, jezikom dominira rad s implicitnim pokazivačima.

Sakupljač smeća

U mom VM-u je poluautomatski. Oni. programer sam odlučuje kada će pozvati sakupljača smeća. Ne radi korištenjem uobičajenog brojača pokazivača, kao u Pythonu, Perlu, Rubyju, Lui itd. Provodi se putem sustava markera. Oni. kada se varijabli namjerava dodijeliti privremena vrijednost, pokazivač na tu vrijednost dodaje se na stog skupljača smeća. U budućnosti, sakupljač brzo prolazi kroz već pripremljenu listu pokazivača.

Rukovanje blokovima try/catch/finally

Kao u svakom modernom jeziku, rukovanje iznimkama je važna komponenta. Jezgra VM-a omotana je blokom try..catch, koji se može vratiti na izvršavanje koda nakon hvatanja iznimke guranjem nekih informacija o tome na stog. U kodu aplikacije možete definirati blokove koda try/catch/finally, navodeći ulazne točke na catch (rukovatelj iznimkama) i finally/end (kraj bloka).

Višenitnost

Podržano je na razini VM-a. Jednostavan je i praktičan za korištenje. Radi bez sustava prekida, tako da bi se kod trebao izvršavati u nekoliko niti nekoliko puta brže.

Vanjske biblioteke za VM

Nema načina bez ovoga. VM podržava uvoze, slično kao što je to implementirano u drugim jezicima. Možete napisati dio koda u Mash-u, a dio koda na izvornim jezicima, a zatim ih povezati u jedan.

Prevoditelj s Mash jezika visoke razine na bajt kod za VM

Srednji jezik

Kako bih brzo napisao prevoditelj iz složenog jezika u VM kod, prvo sam razvio srednji jezik. Rezultat je bio užasan spektakl nalik asembleru koji ovdje nema posebnog smisla razmatrati. Reći ću samo da na ovoj razini prevoditelj obrađuje većinu konstanti i varijabli, izračunava njihove statičke adrese i adrese ulaznih točaka.

Arhitektura prevoditelja

Nisam odabrao najbolju arhitekturu za implementaciju. Prevoditelj ne gradi kodno stablo, kao što to rade drugi prevoditelji. Gleda na početak građevine. Oni. ako dio koda koji se raščlanjuje izgleda kao "while <uvjet>:", onda je očito da je to konstrukcija while petlje i treba se obraditi kao konstrukcija while petlje. Nešto poput složene sklopke.

Zahvaljujući ovom arhitektonskom rješenju, prevoditelj se pokazao ne baš brzim. Međutim, lakoća njegove izmjene značajno se povećala. Dodao sam potrebne strukture brže nego što mi se kava mogla ohladiti. Potpuna OOP podrška implementirana je za manje od tjedan dana.

Optimizacija koda

Ovdje se, naravno, moglo bolje provesti (i provodit će se, ali kasnije, čim se dođe do toga). Za sada, optimizator zna samo kako odrezati neiskorišteni kod, konstante i uvoze iz sklopa. Također, nekoliko konstanti s istom vrijednošću zamjenjuju se jednom. To je sve.

Mash jezik

Osnovni pojam jezika

Glavna ideja bila je razviti što funkcionalniji i najjednostavniji jezik. Mislim da se razvoj sa svojim zadatkom nosi s praskom.

Blokovi koda, postupci i funkcije

Sve konstrukcije u jeziku otvaraju se dvotačkom. : a zatvara ih operater kraj.

Procedure i funkcije su deklarirane kao proc odnosno func. Argumenti su navedeni u zagradama. Sve je kao u većini drugih jezika.

Operater povratak možete vratiti vrijednost iz funkcije, operatora razbiti omogućuje izlazak iz procedure/funkcije (ako je izvan petlji).

Primjer koda:

...

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

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

Podržani dizajni

  • Petlje: za..kraj, dok..kraj, dok..kraj
  • Uvjeti: if.[else..]end, switch..[case..end..][else..]end
  • Metode: proc <name>():... end, func <name>():... end
  • Oznaka i idi: <ime>:, skok <ime>
  • Enum enumeracije i konstantni nizovi.

varijable

Prevoditelj ih može odrediti automatski ili ako programer napiše var prije nego ih definira.

Primjeri koda:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Podržane su globalne i lokalne varijable.

OOP

Pa, došli smo do najukusnije teme. Mash podržava sve paradigme objektno orijentiranog programiranja. Oni. klase, nasljeđe, polimorfizam (uključujući dinamički), dinamička automatska refleksija i introspekcija (potpuna).

Bez daljnjeg odlaganja, bolje je dati samo primjere koda.

Jednostavna klasa i rad s njom:

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

Izdat će: 30.

Nasljeđe i polimorfizam:

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

Izdat će: 60.

Što je s dinamičkim polimorfizmom? Da, ovo je odraz!:

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

Izdat će: 60.

Sada uzmimo trenutak da pogledamo jednostavne vrijednosti i 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

Hoće izlaz: istina, istina.

O operatorima dodjele i eksplicitnim pokazivačima

Operator ?= koristi se za dodjeljivanje pokazivača varijabli na vrijednost u memoriji.
Operator = mijenja vrijednost u memoriji pomoću pokazivača iz varijable.
A sada malo o eksplicitnim pokazivačima. Dodao sam ih u jezik tako da postoje.
@<varijabla> — uzeti eksplicitni pokazivač na varijablu.
?<varijabla> — dobivanje varijable pomoću pokazivača.
@= — dodijelite vrijednost varijabli eksplicitnim pokazivačem na nju.

Primjer koda:

uses <bf>
uses <crt>

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

Izbacit će: neki broj, 10, 11.

Pokušajte.[catch..][finally..]end

Primjer koda:

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

Planovi za budućnost

Stalno gledam i gledam GraalVM & Truffle. Moje runtime okruženje nema JIT kompajler, tako da je u smislu performansi trenutno konkurentno samo Pythonu. Nadam se da ću moći implementirati JIT kompilaciju temeljenu na GraalVM ili LLVM.

spremište

Možete se igrati s razvojem i sami pratiti projekt.

web stranica
Repozitorij na GitHubu

Hvala vam što ste pročitali do kraja ako jeste.

Izvor: www.habr.com

Dodajte komentar