Selama beberapa tahun saya mencuba tangan saya dalam membangunkan bahasa pengaturcaraan saya sendiri. Saya mahu mencipta, pada pendapat saya, bahasa yang paling mudah, berfungsi sepenuhnya dan mudah yang mungkin.
Dalam artikel ini saya ingin menyerlahkan peringkat utama kerja saya dan, sebagai permulaan, menerangkan konsep bahasa yang dicipta dan pelaksanaan pertamanya, yang sedang saya usahakan.
Biar saya katakan terlebih dahulu bahawa saya menulis keseluruhan projek dalam Free Pascal, kerana... program di atasnya boleh dipasang untuk sejumlah besar platform, dan pengkompil itu sendiri menghasilkan binari yang sangat dioptimumkan (saya mengumpul semua komponen projek dengan bendera O2).
Masa jalan bahasa
Pertama sekali, patut dibincangkan tentang mesin maya yang perlu saya tulis untuk menjalankan aplikasi masa depan dalam bahasa saya. Saya memutuskan untuk melaksanakan seni bina tindanan, mungkin, kerana ia adalah cara yang paling mudah. Saya tidak menemui satu pun artikel biasa tentang cara melakukan ini dalam bahasa Rusia, jadi selepas membiasakan diri dengan bahan bahasa Inggeris, saya duduk untuk mereka bentuk dan menulis basikal saya sendiri. Seterusnya saya akan membentangkan idea dan perkembangan saya yang "maju" dalam perkara ini.
Pelaksanaan tindanan
Jelas sekali, di bahagian atas VM ialah timbunan. Dalam pelaksanaan saya ia berfungsi dalam blok. Pada asasnya ini ialah tatasusunan mudah penunjuk dan pembolehubah untuk menyimpan indeks bahagian atas timbunan.
Apabila ia dimulakan, tatasusunan 256 elemen dicipta. Jika lebih banyak penunjuk ditolak ke tindanan, saiznya meningkat sebanyak 256 elemen seterusnya. Sehubungan itu, apabila mengeluarkan elemen dari timbunan, saiznya diselaraskan.
VM menggunakan beberapa tindanan:
- Timbunan utama.
- Timbunan untuk menyimpan mata pulangan.
- Timbunan pemungut sampah.
- Cuba/tangkap/akhirnya sekat timbunan pengendali.
Pemalar dan Pembolehubah
Yang ini mudah sahaja. Pemalar dikendalikan dalam sekeping kod kecil yang berasingan dan tersedia dalam aplikasi masa hadapan melalui alamat statik. Pembolehubah ialah tatasusunan penunjuk saiz tertentu, akses kepada selnya dijalankan oleh indeks - i.e. alamat statik. Pembolehubah boleh ditolak ke bahagian atas timbunan atau dibaca dari sana. Sebenarnya, kerana Walaupun pembolehubah kami pada asasnya menyimpan penunjuk kepada nilai dalam memori VM, bahasa dikuasai dengan bekerja dengan penunjuk tersirat.
Pengumpul sampah
Dalam VM saya ia adalah separa automatik. Itu. pemaju sendiri yang memutuskan bila hendak menghubungi pengutip sampah. Ia tidak berfungsi menggunakan kaunter penunjuk biasa, seperti dalam Python, Perl, Ruby, Lua, dll. Ia dilaksanakan melalui sistem penanda. Itu. apabila pembolehubah bertujuan untuk diberikan nilai sementara, penunjuk kepada nilai ini ditambahkan pada timbunan pemungut sampah. Pada masa hadapan, pengumpul dengan cepat menjalankan senarai penunjuk yang telah disediakan.
Mengendalikan cubaan/tangkap/akhirnya blok
Seperti dalam mana-mana bahasa moden, pengendalian pengecualian adalah komponen penting. Teras VM dibungkus dalam blok try..catch, yang boleh kembali ke pelaksanaan kod selepas menangkap pengecualian dengan menolak beberapa maklumat tentangnya ke dalam tindanan. Dalam kod aplikasi, anda boleh menentukan cuba/tangkap/akhirnya blok kod, menentukan titik masuk pada tangkapan (pengendali pengecualian) dan akhirnya/akhir (hujung blok).
Multithreading
Ia disokong pada peringkat VM. Ia mudah dan senang digunakan. Ia berfungsi tanpa sistem gangguan, jadi kod itu harus dilaksanakan dalam beberapa utas beberapa kali lebih cepat, masing-masing.
Perpustakaan luaran untuk VM
Tidak ada cara untuk melakukannya tanpa ini. VM menyokong import, sama seperti cara ia dilaksanakan dalam bahasa lain. Anda boleh menulis sebahagian daripada kod dalam Mash dan sebahagian daripada kod dalam bahasa ibunda, kemudian memautkannya menjadi satu.
Penterjemah daripada bahasa Mash peringkat tinggi kepada bytecode untuk VM
bahasa perantaraan
Untuk menulis penterjemah dari bahasa kompleks ke dalam kod VM dengan cepat, saya mula-mula membangunkan bahasa perantaraan. Hasilnya adalah tontonan dahsyat seperti pemasang yang tidak ada gunanya untuk dipertimbangkan di sini. Saya hanya akan mengatakan bahawa pada tahap ini penterjemah memproses kebanyakan pemalar dan pembolehubah, mengira alamat statik mereka dan alamat titik masuk.
Seni bina penterjemah
Saya tidak memilih seni bina terbaik untuk pelaksanaan. Penterjemah tidak membina pokok kod, seperti yang dilakukan oleh penterjemah lain. Dia melihat pada permulaan struktur. Itu. jika sekeping kod yang dihuraikan kelihatan seperti "while <condition>:", maka jelas sekali bahawa ini adalah binaan gelung sementara dan perlu diproses sebagai binaan gelung sementara. Sesuatu seperti sarung suis yang kompleks.
Terima kasih kepada penyelesaian seni bina ini, penterjemah ternyata tidak begitu pantas. Walau bagaimanapun, kemudahan pengubahsuaiannya telah meningkat dengan ketara. Saya menambah struktur yang diperlukan lebih cepat daripada kopi saya boleh menyejukkan. Sokongan OOP penuh telah dilaksanakan dalam masa kurang dari seminggu.
Pengoptimuman kod
Di sini, sudah tentu, ia boleh dilaksanakan dengan lebih baik (dan akan dilaksanakan, tetapi kemudian, sebaik sahaja seseorang mencapainya). Setakat ini, pengoptimum hanya tahu cara memotong kod, pemalar dan import yang tidak digunakan daripada pemasangan. Juga, beberapa pemalar dengan nilai yang sama digantikan dengan satu. Itu sahaja.
Bahasa tumbuk
Konsep asas bahasa
Idea utama adalah untuk membangunkan bahasa yang paling berfungsi dan mudah yang mungkin. Saya fikir pembangunan itu mengatasi tugasnya dengan cepat.
Blok kod, prosedur dan fungsi
Semua binaan dalam bahasa dibuka dengan titik bertindih. : dan ditutup oleh pengendali akhir.
Prosedur dan fungsi diisytiharkan sebagai proc dan func, masing-masing. Hujah-hujah disenaraikan dalam kurungan. Semuanya seperti kebanyakan bahasa lain.
Operator pulangan anda boleh mengembalikan nilai daripada fungsi, operator memecahkan membolehkan anda keluar dari prosedur/fungsi (jika ia berada di luar gelung).
Contoh kod:
...
func summ(a, b):
return a + b
end
proc main():
println(summ(inputln(), inputln()))
end
Reka Bentuk yang Disokong
- Gelung: untuk..akhir, sementara..akhir, sehingga..akhir
- Syarat: jika..[lain..]tamat, tukar..[kes..tamat..][lain..]tamat
- Kaedah: proc <name>():... end, func <name>():... end
- Label & pergi: <nama>:, lompat <nama>
- Penghitungan enum dan tatasusunan malar.
Pembolehubah
Penterjemah boleh menentukannya secara automatik, atau jika pembangun menulis var sebelum mentakrifkannya.
Contoh kod:
a ?= 10
b ?= a + 20
var a = 10, b = a + 20
Pembolehubah global dan tempatan disokong.
OOP
Nah, kami telah sampai ke topik yang paling lazat. Mash menyokong semua paradigma pengaturcaraan berorientasikan objek. Itu. kelas, pewarisan, polimorfisme (termasuk dinamik), pantulan automatik dinamik dan introspeksi (penuh).
Tanpa berlengah lagi, lebih baik berikan contoh kod sahaja.
Kelas yang mudah dan bekerja dengannya:
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
Akan keluaran: 30.
Warisan dan polimorfisme:
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
Akan keluaran: 60.
Bagaimana pula dengan polimorfisme dinamik? Ya, ini adalah refleksi!:
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
Akan keluaran: 60.
Sekarang mari kita luangkan sedikit masa untuk introspeksi nilai dan kelas mudah:
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
Akan output: benar, benar.
Mengenai pengendali tugasan dan petunjuk eksplisit
Operator ?= digunakan untuk menetapkan pembolehubah penunjuk kepada nilai dalam ingatan.
Operator = menukar nilai dalam ingatan menggunakan penunjuk daripada pembolehubah.
Dan sekarang sedikit tentang petunjuk eksplisit. Saya menambahkannya pada bahasa supaya ia wujud.
@<pembolehubah> β ambil penunjuk eksplisit kepada pembolehubah.
?<pembolehubah> β dapatkan pembolehubah dengan penuding.
@= β memberikan nilai kepada pembolehubah dengan penunjuk eksplisit kepadanya.
Contoh kod:
uses <bf>
uses <crt>
proc main():
var a = 10, b
b ?= @a
PrintLn(b)
b ?= ?b
PrintLn(b)
b++
PrintLn(a)
InputLn()
end
Akan mengeluarkan: beberapa nombor, 10, 11.
Cuba..[tangkap..][akhirnya..]tamat
Contoh kod:
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
ΠΠ»Π°Π½Ρ Π½Π° Π±ΡΠ΄ΡΡΠ΅Π΅
Saya terus melihat dan melihat GraalVM & Truffle. Persekitaran masa jalan saya tidak mempunyai pengkompil JIT, jadi dari segi prestasi ia kini hanya bersaing dengan Python. Saya berharap saya akan dapat melaksanakan kompilasi JIT berdasarkan GraalVM atau LLVM.
repositori
Anda boleh bermain dengan perkembangan dan mengikuti projek itu sendiri.
Terima kasih kerana membaca sehingga akhir jika anda membacanya.
Sumber: www.habr.com