(Dokter ini sedang dalam proses ...)
Catatan: Cabang "CPP" berisi port "bodoh" dari proyek ini dari C ke C ++, yang saya lakukan karena beberapa alasan:
J2 adalah sistem yang menggabungkan bahasa pemrograman minimalis ("Edict", untuk "Kamus yang Dapat Dieksekusi") dengan sistem refleksi/FFI C. Ini memungkinkan Anda untuk dengan mudah mengimpor perpustakaan bersama dan menggunakan tipe data, variabel, dan fungsi di dalamnya (setidaknya yang ada di ruang lingkup global) secara langsung , tanpa perlu menulis kode "lem" apa pun.
Misalnya, jika saya memiliki file “Inc.c” yang berisi:
typedef struct mystruct { int i; float f; } mystruct;
extern mystruct increment(mystruct x) { x.i+=1; x.f+=1; return x; }
... dan saya membuat perpustakaan bersama dari itu:
gcc --shared -g inc.c -o inc.so
... Lalu saya dapat mengimpor perpustakaan bersama ke dalam penerjemah dekrit dan berhak menggunakannya:
e> loadlib([inc.so]) @mylib
Finished curating module
e> mylib<mystruct! <2@i 4@f> @x increment(x)>/ stack!
VMRES_STACK
<null>
structure_type "struct mystruct"
member "i"
base_type "int" 0x3 (0x7f361c0121a0)
member "f"
base_type "float" 5 (0x7f361c0121a4)
Terlihat sederhana, tapi ada banyak hal yang terjadi di sini di bawah sampul di sini ...
"Edict" adalah bahasa pemrograman minimalis yang menebus kesederhanaannya dengan memiliki kemampuan bawaan untuk memahami dan secara dinamis mengikat dengan perpustakaan C, menyediakan akses "asli" ke jenis C, variabel, dan metode, kompleksitas sewenang-wenang, tanpa menulis pembungkus atau kode lem. Atau, Anda dapat melihatnya sebagai perpustakaan refleksi untuk program C yang memungkinkan mereka untuk mengekspos akses dinamis ke internal mereka sendiri saat runtime.
Bahasa ini dibangun di atas fondasi tiga elemen:
Ketiga elemen ini merakit diri mereka pada saat runtime menjadi lingkungan yang dapat diprogram multi-tujuan. Sistem bootstrap itu sendiri dengan:
Evolusi dekrit telah dipengaruhi oleh Forth, Lisp, Joy, Factor, TCL, Mathematica, dan lainnya.
Poin -Poin Kunci:
while (call=vm_dispatch(call)); // VM's inner loop
(Catatan: Saya menemukan struktur "zigzag" ini yang sangat mirip (dan sudah ada sebelumnya) listree saya: https://www.nongnu.org/gzz/gi/gi.html)
"Listree" adalah struktur data inti dari dekrit dan VM yang mengimplementasikannya. Sebuah contoh dari seorang listree hanya terdiri dari nilai listree, yang berisi:
Kemungkinan nilai listree untuk berisi (berlabel) referensi ke nilai -nilai Listree lainnya adalah apa yang membuat Listree menjadi struktur data "hierarkis" atau "rekursif". Listree adalah inti dari dekrit dan sistem yang mengimplementasikannya.
*Ini tidak eksplisit dalam penjelasan sederhana ini (dan itu sengaja), tetapi setiap label sebenarnya mengacu pada daftar referensi ke nilai -nilai Listree lainnya.
Seperti yang disebutkan, VM mempertahankan semua keadaan dalam nilai Listree, termasuk sub-listree “Data Stack” dan “Dictionary”, di antara beberapa lainnya.
Dalam dekrit, ada dua jenis nilai data, tetapi nilai kedua jenis disimpan menggunakan nilai Listree yang ada baik pada tumpukan data VM atau di dalam kamusnya.
Jenis nilai yang paling sederhana adalah "literal". Literal hanya teks, digambarkan oleh tanda kurung persegi:
[This is a literal]
Jika Anda berasal dari latar belakang LISP, Anda mungkin berpikir bahwa penerjemah memecah literal menjadi S-Expressions, tetapi ini bukan masalahnya. Segala sesuatu di antara tanda kurung diwakili "secara harfiah" dalam "buffer data" nilai Listree.
The Edict Interpreter melacak tanda kurung persegi bersarang, jadi:
[This is a [nested] literal]
ditafsirkan sebagai literal tunggal dengan nilai "Ini adalah [bersarang] literal".
Karakter "" yang muncul dalam definisi literal "lolos dari" karakter berikutnya, yang memungkinkan penerjemah untuk membuat literal yang mengandung (misalnya) kurung persegi yang tidak seimbang:
[This is a literal containing an unbalanced [ bracket]
Ketika penerjemah menemukan satu, nilai literal (atau lebih tepatnya, referensi ke nilai - perbedaan penting) hanya ditempatkan pada tumpukan data.
OTOH, penerjemah melakukan sedikit lebih banyak pemrosesan pada apa pun yang bukan literal. (Ada jenis nilai lain yang bukan literal, yang akan saya bahas di bagian selanjutnya ...)
Programmer Edict dapat menetapkan nama ke nilai -nilai pada tumpukan, dan kemudian merujuk pada nilai -nilai tersebut dengan nama -nama tersebut. Tugas terlihat seperti ini:
@mylabel
Penugasan nama ke nilai sebenarnya melakukan beberapa hal:
Ketika penerjemah melihat referensi ke label, referensi itu digantikan oleh referensi ke nilai yang terkait dengan label itu ... dengan kata lain:
Dekrik berbeda dari bahasa lain dalam satu cara penting. Banyak bahasa adalah "homoikonik", kode dan data diwakili menggunakan struktur yang mendasari yang sama. (Lisp adalah contoh tradisional dari bahasa homoikonik.) Dekrik bukan homoikonik atau non -homoikonik: tidak memiliki "fungsi" sama sekali. Ini hanya memiliki operator evaluasi yang dapat diterapkan pada nilai.
Hal "fungsi-seperti" dasar dalam dekrit mungkin terlihat seperti:
[1@x]
(Tetapkan label "X" ke nilai "1".)
Perhatikan bahwa itu hanya literal .
Untuk memohon , operator evaluasi digunakan:
[1@x]!
Hasil dari ini persis sama seperti jika penerjemah baru saja membaca yang berikut:
1@x
Yang dilakukan oleh Operator Evaluasi adalah memberi makan isi bagian atas tumpukan ke penerjemah.
Sekarang: ingat bahwa label dapat ditetapkan untuk nilai -nilai, dan bahwa label dapat dipanggil untuk mengingat nilai -nilainya, dan nilai -nilai itu didorong ke tumpukan, dan bahwa operator evaluasi memberi makan isi tumpukan kembali ke penerjemah:
[1@x]@f
f!
Urutan kecil ini melakukan hal berikut:
Edict dapat mengimpor jenis perpustakaan C dan informasi variabel/fungsi global melalui bagian debugging (kerdil) perpustakaan, jika tersedia. Informasi kerdil diproses dan disimpan dalam kamus, dan VM dapat "memahami" informasi ini dan menyajikannya di dalam interpreter dekrit menggunakan sintaks "asli" sederhana yang sama yang beroperasi pada nilai -nilai literal.
int! @x
MyCGlobalInt @y
xy Multiply!
(WIP di bawah ...)
Ref | Referensi |
-Ref | Referensi (ekor) |
@ | Tugas ke TOS |
@Ref | Tugas untuk referensi |
/ | Lepaskan TOS |
/Ref | Rilis Ref |
^Ref | Metareference |
Ref^ | Gabungkan Lapisan Tumpukan Atas ke Ref |
^Ref^(^ref +^) | Lapisan Stack Top Metareference dan Gabungkan ke Ref |
Lai | Mengevaluasi TOS |
Tos <...> | “Evaluasi dalam Dict-Context”: Push TOS untuk Dikt, evaluasi konten <...>, Pop Top of Dict Stack ke TOS. |
Tos (...) | “Evaluasi dalam Kode-Konteks”: Lapisan Push Null Stack/Dict, Push TOS ke Tumpukan Kode, Mengevaluasi Isi Parens, Mengevaluasi Tumpukan Tumpukan Kode, Buang Dikt dari Dikt, Concatenate Top of Stack ke Lapisan Sebelumnya. |
Program reflektif sederhana:
int! [3]@ square! stack!
Perincian:
int | Cari dan nilai dorong "int" (tipe C asli) ke tumpukan |
! | Mengevaluasi top-of-stack, dalam hal ini mengalokasikan contoh "int" |
[3] | Dorong literal "3" ke tumpukan |
@ | Penugasan; Dalam hal ini, String "3" secara otomatis dipaksa menjadi c "int" |
square | Cari dan dorong nilai "kuadrat" (metode C asli) ke tumpukan |
! | Mengevaluasi Top-of-Stack, dalam hal ini panggilan FFI ke metode C asli "Square" |
stack | Cari dan dorong nilai "tumpukan" (metode C asli) ke tumpukan |
! | Evaluasi Top Of Stack ... |
Anda akan melihat int 0x9, yaitu 3 kuadrat.
Penciptaan eksplisit dan penugasan integer C hanya ditampilkan untuk contoh; Versi yang lebih sederhana adalah:
[3] square! stack!
Paksaan yang sama akan dilakukan secara otomatis selama marshalling argumen FFI.
Perhatikan bahwa "kode" tidak mengevaluasi secara otomatis; Evaluasi dipanggil secara eksplisit melalui "!". Kode hanyalah data sampai Anda memutuskan untuk "menjalankan" itu:
[3] square stack! | data dan "kode" di tumpukan, tidak dievaluasi |
! stack! | Mengevaluasi TOS dan mengamati hasil |
Faktorial:
[@n int_iszero(n) 1 | int_mul(fact(int_dec(n)) n)]@fact
Dalam listree, nilai berisi kamus pasangan kunci/CLL, di mana masing-masing CLL ("daftar circular-linked", mengimplementasikan antrian ganda) berisi satu atau lebih referensi nilai, yang masing-masing berisi kamus. .. dan seterusnya. Komponen kunci/deq dari Listree adalah implementasi BST berdasarkan variasi RBTree "sederhana" Arne Andersson. (http://user.it.uu.se/~arnea/ps/simp.pdf)
VM sangat sederhana; Ini mengevaluasi bytecode, yang masing -masing merupakan indeks ke array metode C yang mengimplementasikan bytecode.
MENGATUR ULANG | Jelas menyala |
Ext | Urutan Karakter Decode dan Set Lit ("[Satu Dua 3]", "ABC") |
Ext_push | Dorong menyalakan ke tumpukan data |
Ref | Buat Referensi Kamus Ref dari Lit |
Deref | Menyelesaikan ref di kamus |
MENETAPKAN | Pop atas tumpukan data dan masukkan ke dalam kamus di lokasi ref ("@") |
MENGHAPUS | Nilai rilis di REF |
Evaluasi | Pop Top Data Stack dan A) Panggil FFI, atau B) Dorong ke Tumpukan Kode dan Hasilkan VM |
Ctx_push | Pop atas tumpukan data dan dorong ke kepala tumpukan kamus, dorong lapisan baru ke tumpukan data |
Ctx_pop | Fuse Top Dua Lapisan Data Stack, Kepala Pop Dictionary Stack dan Dorong ke Data Stack |
Fun_push | Pop atas tumpukan data dan dorong ke stack func, tambahkan lapisan ke tumpukan data, tambahkan lapisan nol ke kamus |
Fun_eval | Push {fun_pop} ke Code Stack, Pop Top of Func Stack dan Do "Eval" |
Fun_pop | Fuse Top Dua Lapisan Tumpukan, Buang Lapisan Kamus Null |
MELEMPARKAN | Lemparkan pengecualian* |
MENANGKAP | Menangkap pengecualian* |
*Pengecualian adalah bagaimana kesalahan dan kondisi VM diimplementasikan, dan mereka mengutak -atik keadaan VM sedikit lebih dari operasi rata -rata, layak mendapatkan paragraf mereka sendiri.
Rangkaian operasi sederhana ini cukup untuk berinteraksi dengan kamus, secara rekursif mengevaluasi konstruksi bahasa (VM adalah implementasi tanpa tumpukan), dan yang paling penting, mengeksploitasi jenis, fungsi, dan data C. Ini adalah kerangka kerja telanjang yang bergantung pada kopling ketat untuk C untuk memperluas kemampuannya dengan mudah.
(Distro GNU/Linux yang sepadan dengan garamnya memiliki ini:
Untuk menjalankan: dengan asumsi GCC, CMake, dan perpustakaan tidak terlalu kuno, cukup jalankan "buat" untuk membangun dan masuk ke repl.