Novi programski jezik Mash

Nekoliko godina sam se okušao u razvoju vlastitog programskog jezika. Želeo sam da stvorim, po mom mišljenju, najjednostavniji, najfunkcionalniji i najprikladniji mogući jezik.

U ovom članku želim da istaknem glavne faze svog rada i za početak opišem kreirani koncept jezika i njegovu prvu implementaciju, na kojoj trenutno radim.

Unaprijed da kažem da sam cijeli projekat napisao u Free Pascalu, jer... programi na njemu mogu se sastaviti za ogroman broj platformi, a sam kompajler proizvodi vrlo optimizirane binarne datoteke (sve komponente projekta skupljam sa O2 zastavicom).

Vrijeme izvođenja jezika

Prije svega, vrijedi razgovarati o virtuelnoj mašini koju sam morao da napišem za pokretanje budućih aplikacija na svom jeziku. Odlučio sam da implementiram arhitekturu steka, možda zato što je to bio najlakši način. Nisam našao ni jedan normalan članak o tome kako se to radi na ruskom, pa sam nakon upoznavanja sa materijalom na engleskom jeziku prionuo dizajnirati i napisati svoj vlastiti bicikl. Zatim ću predstaviti svoje „napredne“ ideje i razvoje po ovom pitanju.

Implementacija steka

Očigledno, na vrhu VM-a je stek. U mojoj implementaciji radi u blokovima. U suštini ovo je jednostavan niz pokazivača i varijabla za pohranjivanje indeksa vrha steka.
Kada se inicijalizira, kreira se niz od 256 elemenata. Ako se više pokazivača gurne na stek, njegova veličina se povećava za sljedećih 256 elemenata. U skladu s tim, prilikom uklanjanja elemenata iz hrpe, prilagođava se njegova veličina.

VM koristi nekoliko stekova:

  1. Glavni stog.
  2. Stog za pohranjivanje povratnih tačaka.
  3. Kolektor za smeće.
  4. Pokušajte/uhvatiti/konačno blokirati stek rukovaoca.

Konstante i varijable

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

Sakupljač smeća

U mom VM-u je poluautomatski. One. programer sam odlučuje kada će pozvati sakupljača smeća. Ne radi pomoću običnog brojača pokazivača, kao u Python, Perl, Ruby, Lua, itd. Realizira se kroz sistem markera. One. kada se varijabli namjerava dodijeliti privremena vrijednost, pokazivač na ovu vrijednost se dodaje u stog sakupljača smeća. U budućnosti, kolektor brzo prolazi kroz već pripremljenu listu pokazivača.

Rukovanje blokovima try/catch/finally

Kao iu svakom modernom jeziku, rukovanje izuzecima je važna komponenta. Jezgro VM-a je umotano u blok try..catch, koji se može vratiti na izvršenje koda nakon što uhvati izuzetak guranjem nekih informacija o njemu u stog. U kodu aplikacije možete definirati try/catch/finally blokove koda, specificirajući ulazne točke na catch (upravljač izuzetkom) i konačno/kraj (kraj bloka).

Multithreading

Podržan je na nivou VM-a. Jednostavan je i zgodan za upotrebu. Radi bez sistema prekida, tako da kod treba da se izvršava u nekoliko niti nekoliko puta brže, respektivno.

Eksterne biblioteke za VM

Bez ovoga se ne može. VM podržava uvoz, slično kao što je implementiran u drugim jezicima. Možete napisati dio koda u Mash, a dio koda na maternjim jezicima, a zatim ih povezati u jedan.

Prevodilac sa Mash jezika visokog nivoa u bajt kod za VM

Srednji jezik

Da bih brzo napisao prevodilac sa složenog jezika u VM kod, prvo sam razvio srednji jezik. Rezultat je bio užasan spektakl nalik asembleru koji ovdje nema posebne svrhe razmatrati. Reći ću samo da na ovom nivou prevodilac obrađuje većinu konstanti i varijabli, izračunava njihove statičke adrese i adrese ulaznih tačaka.

Arhitektura prevodioca

Nisam izabrao najbolju arhitekturu za implementaciju. Prevodilac ne gradi kodno stablo, kao što to rade drugi prevodioci. On gleda na početak strukture. One. ako dio koda koji se raščlanjuje izgleda kao “while <uslov>:”, onda je očigledno da je ovo konstrukcija petlje while i da se treba obraditi kao konstrukcija while petlje. Nešto poput složenog prekidača.

Zahvaljujući ovom arhitektonskom rješenju, prevodilac se pokazao ne baš brzim. Međutim, lakoća njegove modifikacije značajno se povećala. Dodao sam potrebne strukture brže nego što je moja kafa mogla da se ohladi. Potpuna OOP podrška implementirana je za manje od nedelju dana.

Optimizacija koda

Ovdje se, naravno, moglo i bolje implementirati (i implementiraće se, ali kasnije, čim se dođe do toga). Do sada, optimizator samo zna kako da odsiječe neiskorišteni kod, konstante i uvoze iz sklopa. Takođe, nekoliko konstanti sa istom vrednošću se zamenjuju jednom. To je sve.

Language Mash

Osnovni koncept jezika

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

Kodni blokovi, procedure i funkcije

Sve konstrukcije u jeziku otvaraju se dvotočkom. : i zatvara ih operater Kraj.

Procedure i funkcije su deklarirane kao proc i func, respektivno. Argumenti su navedeni u zagradama. Sve je kao i većina drugih jezika.

Operater povratak možete vratiti vrijednost iz funkcije, operatora pauza omogućava vam da izađete iz procedure/funkcije (ako je izvan petlje).

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, do..kraj
  • Uslovi: if..[else..]end, switch..[case..end..][else..]end.
  • Metode: proc <name>():... end, func <name>():... end
  • Oznaka & idi: <ime>:, skoči <ime>
  • Enumeracije i konstantni nizovi.

Varijable

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

Primjeri kodova:

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 objektno orijentirane paradigme programiranja. One. klase, nasljeđivanje, polimorfizam (uključujući dinamički), dinamička automatska refleksija i introspekcija (puna).

Bez daljeg odlaganja, bolje je samo dati 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

Izlaz će: 30.

Nasljeđivanje 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

Izlaz će: 60.

Šta je sa 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

Izlaz će: 60.

Sada odvojimo trenutak za introspekciju jednostavnih vrijednosti i klasa:

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

Izlaz će: istina, istina.

O operatorima dodjeljivanja i eksplicitnim pokazivačima

Operator ?= se koristi za dodjelu varijabli pokazivača vrijednosti u memoriji.
Operator = mijenja vrijednost u memoriji koristeći pokazivač iz varijable.
A sada malo o eksplicitnim pokazivačima. Dodao sam ih u jezik tako da postoje.
@<varijabla> — uzeti eksplicitni pokazivač na varijablu.
?<varijabla> — dobiti varijablu po pokazivaču.
@= — dodijeliti 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

Izdat će: neki broj, 10, 11.

Pokušajte..[uhvati..][konačno..]kraj

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 pogledu performansi trenutno konkurentno samo Pythonu. Nadam se da ću moći implementirati JIT kompilaciju zasnovanu na GraalVM-u ili LLVM-u.

spremište

Možete se igrati sa razvojem i sami pratiti projekat.

sajt
Repozitorijum na GitHubu

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

izvor: www.habr.com

Dodajte komentar