Yeni programlama dili Mash

Birkaç yıl boyunca kendi programlama dilimi geliştirmeye çalıştım. Bana göre mümkün olan en basit, tamamen işlevsel ve kullanışlı dili yaratmak istedim.

Bu yazıda çalışmamın ana aşamalarını vurgulamak ve öncelikle şu anda üzerinde çalıştığım dilin oluşturulan konseptini ve ilk uygulamasını anlatmak istiyorum.

Projenin tamamını Free Pascal'da yazdığımı şimdiden söyleyeyim, çünkü... üzerindeki programlar çok sayıda platform için bir araya getirilebilir ve derleyicinin kendisi oldukça optimize edilmiş ikili dosyalar üretir (projenin tüm bileşenlerini O2 bayrağıyla topluyorum).

Dil çalışma zamanı

Öncelikle kendi dilimde gelecekteki uygulamaları çalıştırmak için yazmak zorunda kaldığım sanal makineden bahsetmeye değer. Belki de en kolay yol olduğu için yığın mimarisini uygulamaya karar verdim. Bunun nasıl yapılacağına dair Rusça'da tek bir normal makale bulamadım, bu yüzden İngilizce materyale aşina olduktan sonra kendi bisikletimi tasarlamaya ve yazmaya koyuldum. Daha sonra bu konudaki “ileri” fikirlerimi ve gelişmeleri sunacağım.

Yığın uygulaması

Açıkçası, VM'nin en üstünde yığın bulunur. Benim uygulamamda bloklar halinde çalışıyor. Esasen bu, basit bir işaretçi dizisi ve yığının en üstündeki indeksi saklayan bir değişkendir.
Başlatıldığında 256 öğeden oluşan bir dizi oluşturulur. Yığına daha fazla işaretçi itilirse boyutu sonraki 256 öğe kadar artar. Buna göre yığından elemanlar çıkarılırken boyutu ayarlanır.

VM birkaç yığın kullanır:

  1. Ana yığın.
  2. Dönüş noktalarını depolamak için bir yığın.
  3. Çöp toplayıcı yığını.
  4. İşleyici yığınını deneyin/yakalayın/sonunda engelleyin.

Sabitler ve Değişkenler

Bu çok basit. Sabitler ayrı küçük bir kod parçasında işlenir ve gelecekteki uygulamalarda statik adresler aracılığıyla kullanılabilir. Değişkenler belirli bir boyuttaki işaretçilerin bir dizisidir, hücrelerine erişim indeks ile gerçekleştirilir - yani. statik adres. Değişkenler yığının en üstüne itilebilir veya oradan okunabilir. Aslında çünkü Değişkenlerimiz esasen VM belleğindeki değerlere yönelik işaretçileri depolarken, dile örtülü işaretçilerle çalışma hakimdir.

Çöp toplayıcı

VM'mde yarı otomatiktir. Onlar. çöp toplayıcının ne zaman aranacağına geliştiricinin kendisi karar verir. Python, Perl, Ruby, Lua vb.'de olduğu gibi normal bir işaretçi sayacı kullanılarak çalışmaz. Marker sistemi ile uygulanır. Onlar. Bir değişkene geçici bir değer atanması istendiğinde, çöp toplayıcının yığınına bu değere bir işaretçi eklenir. Gelecekte, toplayıcı önceden hazırlanmış işaretçi listesini hızlı bir şekilde gözden geçirecektir.

Try/catch/finally bloklarını yönetme

Her modern dilde olduğu gibi istisna yönetimi de önemli bir bileşendir. VM çekirdeği, bir istisna yakaladıktan sonra bununla ilgili bazı bilgileri yığına göndererek kod yürütmeye dönebilen bir try..catch bloğuna sarılmıştır. Uygulama kodunda, try/catch/finally kod bloklarını tanımlayabilir, catch (istisna işleyicisi) ve nihayet/end (blok sonu) giriş noktalarını belirtebilirsiniz.

Çoklu kullanım

VM düzeyinde desteklenir. Kullanımı basit ve kullanışlıdır. Bir kesme sistemi olmadan çalışır, bu nedenle kodun birkaç iş parçacığında sırasıyla birkaç kat daha hızlı yürütülmesi gerekir.

VM'ler için harici kitaplıklar

Bu olmadan yapmanın bir yolu yok. VM, diğer dillerde uygulandığına benzer şekilde içe aktarmayı destekler. Kodun bir kısmını Mash'te, bir kısmını da yerel dillerde yazabilir ve ardından bunları bir araya getirebilirsiniz.

VM'ler için üst düzey Mash dilinden bayt koduna çevirmen

Orta dil

Karmaşık bir dilden VM koduna hızlı bir şekilde çevirmen yazmak için önce bir ara dil geliştirdim. Sonuç, burada dikkate almanın özel bir anlamı olmayan, montajcı benzeri korkunç bir gösteriydi. Sadece bu seviyede çevirmenin çoğu sabiti ve değişkeni işlediğini, bunların statik adreslerini ve giriş noktalarının adreslerini hesapladığını söyleyeceğim.

Çevirmen mimarisi

Uygulama için en iyi mimariyi seçmedim. Çevirmen diğer çevirmenlerin yaptığı gibi bir kod ağacı oluşturmaz. Yapının başlangıcına bakıyor. Onlar. ayrıştırılan kod parçası “while <condition>:” gibi görünüyorsa, bunun bir while döngüsü yapısı olduğu ve bir while döngüsü yapısı olarak işlenmesi gerektiği açıktır. Karmaşık bir anahtar durumu gibi bir şey.

Bu mimari çözüm sayesinde çevirmenin çok hızlı olmadığı ortaya çıktı. Ancak modifikasyonunun kolaylığı önemli ölçüde arttı. Gerekli yapıları kahvemin soğuyabileceğinden daha hızlı ekledim. Tam OOP desteği bir haftadan kısa bir sürede uygulandı.

Kod optimizasyonu

Burada elbette daha iyi uygulanabilirdi (ve uygulanacaktır, ancak daha sonra, alışır alışmaz). Şu ana kadar optimizer yalnızca kullanılmayan kodun, sabitlerin ve derlemeden içe aktarmaların nasıl kesileceğini biliyor. Ayrıca aynı değere sahip birden fazla sabitin yerini bir tane alır. Bu kadar.

Mash dili

Dilin temel kavramı

Ana fikir mümkün olan en işlevsel ve basit dili geliştirmekti. Gelişimin göreviyle bir patlama ile başa çıktığını düşünüyorum.

Kod blokları, prosedürler ve işlevler

Dildeki tüm yapılar iki nokta üst üste ile açılır. : ve operatör tarafından kapatılır son.

Prosedürler ve işlevler sırasıyla proc ve func olarak bildirilir. Argümanlar parantez içinde listelenmiştir. Her şey diğer dillerin çoğu gibidir.

Şebeke dönüş bir fonksiyondan, operatörden bir değer döndürebilirsiniz kırılma prosedürden/işlevden çıkmanıza olanak tanır (döngülerin dışındaysa).

Örnek кода:

...

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

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

Desteklenen Tasarımlar

  • Döngüler: for..end, while..end, Until..end
  • Koşullar: if..[else..]end, switch..[case..end..][else..]end
  • Yöntemler: proc <name>():... end, func <name>():... end
  • Etiketle ve git: <isim>:, <isim>'e atla
  • Enum numaralandırmaları ve sabit diziler.

Değişkenler

Çevirmen bunları otomatik olarak belirleyebilir veya geliştirici bunları tanımlamadan önce var yazarsa belirleyebilir.

Kod örnekleri:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Global ve yerel değişkenler desteklenir.

FKÖ

Evet geldik en lezzetli konuya. Mash, tüm nesne yönelimli programlama paradigmalarını destekler. Onlar. sınıflar, kalıtım, polimorfizm (dinamik dahil), dinamik otomatik yansıma ve iç gözlem (tam).

Lafı fazla uzatmadan kod örneklerini vermekte fayda var.

Basit bir sınıf ve onunla çalışmak:

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

Çıktı: 30.

Kalıtım ve polimorfizm:

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

Çıktı: 60.

Peki ya dinamik polimorfizm? Evet, bu bir yansıma!:

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

Çıktı: 60.

Şimdi basit değerler ve sınıflar için iç gözlem yapmaya biraz zaman ayıralım:

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

Çıktısı: doğru, doğru.

Atama operatörleri ve açık işaretçiler hakkında

?= operatörü, bir değişkene bellekteki bir değere işaretçi atamak için kullanılır.
= operatörü, bir değişkenden gelen bir işaretçiyi kullanarak bellekteki bir değeri değiştirir.
Ve şimdi açık işaretçiler hakkında biraz. Var olsunlar diye dile ekledim.
@<variable> - bir değişkene açık bir işaretçi alın.
?<değişken> — işaretçiyle bir değişken elde edin.
@= - açık bir işaretçiyle bir değişkene bir değer atayın.

Örnek кода:

uses <bf>
uses <crt>

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

Çıktı olarak: bir sayı, 10, 11.

Deneyin..[yakala..][nihayet..]son

Örnek кода:

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

Gelecek için planlar

GraalVM & Truffle'a bakmaya ve bakmaya devam ediyorum. Çalışma zamanı ortamımda bir JIT derleyicisi yok, dolayısıyla performans açısından şu anda yalnızca Python ile rekabet edebilir. GraalVM veya LLVM'ye dayalı JIT derlemesini uygulayabileceğimi umuyorum.

depo

Gelişmelerle oynayabilir ve projeyi kendiniz takip edebilirsiniz.

web sitesi
GitHub'daki depo

Eğer okuduysanız sonuna kadar okuduğunuz için teşekkür ederim.

Kaynak: habr.com

Yorum ekle