(เอกสารนี้เป็นงานที่อยู่ระหว่างดำเนินการ…)
หมายเหตุ: สาขา“ CPP” มีพอร์ต“ ใบ้” ของโครงการนี้จาก C ถึง C ++ ซึ่งฉันทำด้วยเหตุผลหลายประการ:
J2 เป็นระบบที่รวมภาษาการเขียนโปรแกรมแบบมินิมอลลิสต์ (“ EDICT” สำหรับ“ พจนานุกรมที่ดำเนินการได้”) กับระบบ C Reflection/FFI ช่วยให้คุณสามารถนำเข้าไลบรารีที่ใช้ร่วมกันได้อย่างง่ายดายและใช้ข้อมูลประเภทตัวแปรและฟังก์ชั่นภายใน (อย่างน้อยที่สุดในขอบเขตทั่วโลก) โดยตรง โดยไม่จำเป็นต้องเขียนรหัส "กาว" ใด ๆ
ตัวอย่างเช่นหากฉันมีไฟล์“ Inc.c” ที่มี:
typedef struct mystruct { int i; float f; } mystruct;
extern mystruct increment(mystruct x) { x.i+=1; x.f+=1; return x; }
... และฉันสร้างห้องสมุดที่ใช้ร่วมกัน:
gcc --shared -g inc.c -o inc.so
... จากนั้นฉันสามารถนำเข้าไลบรารีที่ใช้ร่วมกันไปยัง Edict Interpreter และได้รับสิทธิ์ในการใช้งาน:
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)
มันดูเรียบง่าย แต่มี หลายอย่าง เกิดขึ้นที่นี่ภายใต้ผ้าห่มที่นี่ ...
“ EDICT” เป็นภาษาการเขียนโปรแกรมที่เรียบง่ายซึ่งประกอบขึ้นเพื่อความเรียบง่ายโดยมีความสามารถในตัวในการทำความเข้าใจและผูกกับไลบรารี C แบบไดนามิกให้การเข้าถึงประเภท C แบบ C, ตัวแปรและวิธีการของความซับซ้อนโดยพลการโดยไม่ต้องเขียน ห่อหุ้มหรือรหัสกาว อีกทางเลือกหนึ่งคุณสามารถมองว่าเป็นไลบรารีสะท้อนแสงสำหรับโปรแกรม C ที่ช่วยให้พวกเขาสามารถเปิดเผยการเข้าถึงภายในของตัวเองที่รันไทม์
ภาษาถูกสร้างขึ้นบนพื้นฐานขององค์ประกอบสามประการ:
องค์ประกอบทั้งสามนี้รวมตัวกันที่รันไทม์ในสภาพแวดล้อมที่ตั้งโปรแกรมได้อเนกประสงค์ ระบบ bootstraps ของตัวเองโดย:
วิวัฒนาการของคำสั่งได้รับอิทธิพลจาก Forth, Lisp, Joy, Factor, TCL, Mathematica และอื่น ๆ
ประเด็นสำคัญ:
while (call=vm_dispatch(call)); // VM's inner loop
(หมายเหตุ: ฉันพบโครงสร้าง "ซิกแซก" นี้ที่คล้ายกัน มาก (และวันพิเศษ) Listree ของฉัน: https://www.nongnu.org/gzz/gi/gi.html)
“ Listree” เป็นโครงสร้างข้อมูลหลักของ EDICT และ VM ซึ่งใช้มัน ตัวอย่างของ Listree ประกอบด้วยค่า Listree ซึ่งมี:
ความเป็นไปได้ของค่า Listree ที่จะมีการอ้างอิง (ติดป้าย) ไปยังค่า Listree อื่น ๆ คือสิ่งที่ทำให้ Listree เป็นโครงสร้างข้อมูล "ลำดับชั้น" หรือ "เรียกซ้ำ" Listree เป็นทั้งแกนหลักของ EDICT และระบบที่ใช้งาน
*มันไม่ชัดเจนในคำอธิบายง่ายๆนี้ (และนั่นคือจุดประสงค์) แต่แต่ละฉลากจริง ๆ แล้วหมายถึง รายการ การอ้างอิงถึงค่า Listree อื่น ๆ
ดังที่ได้กล่าวไว้ VM รักษาสถานะทั้งหมดของตนไว้ในค่า Listree รวมถึง“ Data Stack” และ“ Dictionary” ผู้ถือหุ้นย่อยในหมู่คนอื่น ๆ อีกหลายคน
ใน EDICT มีค่าข้อมูลสองประเภท แต่ค่าของทั้งสองประเภทจะถูกเก็บไว้โดยใช้ค่า Listree ซึ่งมีอยู่ในสแต็กข้อมูลของ VM หรือภายในพจนานุกรม
ค่าที่ง่ายที่สุดคือ "ตัวอักษร" ตัวอักษรเป็นเพียงข้อความที่อธิบายโดยวงเล็บเหลี่ยม:
[This is a literal]
หากคุณมาจากพื้นหลังที่กระฉับกระเฉงคุณอาจคิดว่าล่ามจะแบ่งตัวอักษรออกเป็น s-expressions แต่นี่ไม่ใช่กรณี ทุกอย่างระหว่างวงเล็บจะแสดง“ ตัวอักษร” ใน“ บัฟเฟอร์ข้อมูล” ของค่า Listree
Edict Interpreter ติดตามวงเล็บสี่เหลี่ยมซ้อนกันดังนั้น:
[This is a [nested] literal]
ถูกตีความว่าเป็นตัวอักษรเดียวที่มีค่า "นี่คือ [ซ้อนกัน] ตัวอักษร"
ตัวละคร "" ที่ปรากฏในคำจำกัดความของตัวอักษร "หลบหนี" ตัวละครถัดไปช่วยให้ล่ามสามารถสร้างตัวอักษรที่มี (ตัวอย่าง) วงเล็บเหลี่ยมที่ไม่สมดุล:
[This is a literal containing an unbalanced [ bracket]
เมื่อล่ามมาถึงหนึ่งค่าตัวอักษร (หรือมากกว่านั้น การอ้างอิง ถึงค่า - ความแตกต่างที่สำคัญ) จะถูกวางไว้บนสแต็กข้อมูล
Otoh ล่ามจะประมวลผลอะไรมากกว่าสิ่งที่ ไม่ใช่ ตัวอักษร (มีค่าอื่น ๆ ที่ ไม่ใช่ ตัวอักษรซึ่งฉันจะพูดถึงในส่วนต่อมา…)
โปรแกรมเมอร์ EDICT สามารถกำหนดชื่อให้กับค่าบนสแต็กและต่อมาอ้างถึงค่าเหล่านั้นด้วยชื่อเหล่านั้น งานที่ได้รับมอบหมายเป็นแบบนี้:
@mylabel
การกำหนดชื่อให้กับค่าจริง ๆ แล้วทำหลายสิ่ง:
เมื่อล่ามเห็นการอ้างอิงถึงฉลากการอ้างอิงนั้นจะถูกแทนที่ด้วยการอ้างอิงถึงค่าที่เกี่ยวข้องกับป้ายกำกับนั้น ... กล่าวอีกนัยหนึ่ง:
EDICT นั้นแตกต่างจากภาษาอื่น ๆ ด้วยวิธีที่สำคัญ หลายภาษาเป็น“ homoiconic” คือรหัสและข้อมูลที่แสดงโดยใช้โครงสร้างพื้นฐานเดียวกัน (LISP เป็นตัวอย่างดั้งเดิมของภาษา homoiconic) คำสั่งไม่ได้เป็น homoiconic หรือ non -homoiconic: มันไม่มี "ฟังก์ชั่น" เลย มันมีตัวดำเนินการประเมินผลซึ่งสามารถนำไปใช้กับค่าได้
สิ่งพื้นฐาน "ฟังก์ชั่น" เหมือนในคำสั่งดูอาจมีลักษณะ:
[1@x]
(กำหนดฉลาก“ x” ให้กับค่า“ 1”)
โปรดทราบว่า มันเป็นเพียงตัวอักษร
ใน การเรียกใช้ มันจะใช้ ตัวดำเนินการประเมินผล :
[1@x]!
ผลลัพธ์ของสิ่งนี้เหมือนกับว่าล่ามได้อ่านสิ่งต่อไปนี้โดยตรง:
1@x
ผู้ดำเนินการประเมินผลทั้งหมดทำคือป้อนเนื้อหาของด้านบนของด้านบนของสแต็กไปยังล่าม
ตอนนี้: จำได้ว่าฉลากสามารถกำหนดให้กับค่านิยมและฉลากสามารถเรียกใช้เพื่อเรียกคืนค่าของพวกเขาและค่าเหล่านั้นจะถูกส่งไปยังสแต็กและผู้ดำเนินการประเมินผลจะป้อนเนื้อหาของสแต็กกลับเข้าไปในล่าม:
[1@x]@f
f!
ลำดับเล็ก ๆ นี้ทำดังต่อไปนี้:
EDICT สามารถนำเข้าประเภทไลบรารี C และข้อมูลตัวแปร/ฟังก์ชั่นทั่วโลกผ่านส่วนการดีบัก (DWARF) ของห้องสมุดหากมีอยู่ ข้อมูลแคระถูกประมวลผลและเก็บไว้ในพจนานุกรมและ VM สามารถ“ เข้าใจ” ข้อมูลนี้และนำเสนอภายในล่าม EDICT โดยใช้ไวยากรณ์“ ดั้งเดิม” แบบง่าย ๆ ที่ทำงานกับค่าตัวอักษร
int! @x
MyCGlobalInt @y
xy Multiply!
(WIP ด้านล่าง…)
ผู้อ้างอิง | อ้างอิง |
-ref | อ้างอิง (หาง) |
- | มอบหมายให้ TOS |
@ref | มอบหมายให้อ้างอิง |
- | ปล่อย TOS |
/อ้างอิง | รีลีสอ้างอิง |
^ref | การพิจารณา |
ref^ | ผสานเลเยอร์สแต็กด้านบนเพื่ออ้างอิง |
^ref^(^ref +^) | Metareference และผสานเลเยอร์สแต็คด้านบนเพื่ออ้างอิง |
- | ประเมิน TOS |
tos <... > | “ ประเมินใน dict-context”: ผลักดัน TOS ไปยัง DICT ประเมินเนื้อหาของ <... >, ป๊อปด้านบนของ Dict Stack ถึง TOS |
tos (... ) | “ ประเมินในรหัสบริบท”: Push Null Stack/Dict Layers, Push TOS ไปยัง Code Stack, ประเมินเนื้อหาของ Parens, ประเมินด้านบนของ Code Stack, ทิ้งด้านบนของ dict, concatenate ด้านบนของสแต็กไปยังชั้นก่อนหน้า |
โปรแกรมสะท้อนแสงที่เรียบง่าย:
int! [3]@ square! stack!
รายละเอียด:
int | การค้นหาและการผลักดันค่าของ“ int” (ประเภท C ดั้งเดิม) ลงบนสแต็ก |
! | ประเมินค่าสูงสุดของสแต็คในกรณีนี้จัดสรรอินสแตนซ์ของ "int" |
[3] | กดตัวอักษร“ 3” ลงบนสแต็กตัวอักษร |
@ | งานที่มอบหมาย; ในกรณีนี้สตริง“ 3” จะถูกบังคับโดยอัตโนมัติใน C“ Int” |
square | การค้นหาและผลักดันค่าของ“ สี่เหลี่ยม” (วิธี C ดั้งเดิม) ลงบนสแต็ก |
! | ประเมินค่าสูงสุดของสแต็คในกรณีนี้การเรียก FFI ไปยังวิธี C พื้นเมือง“ สแควร์” |
stack | การค้นหาและผลักดันค่าของ“ สแต็ค” (วิธี C ดั้งเดิม) ลงบนสแต็ก |
! | ประเมินด้านบนของสแต็ก ... |
คุณจะเห็น int 0x9 คือ 3 กำลังสอง
การสร้างที่ชัดเจนและการกำหนดจำนวนเต็ม C จะแสดงเฉพาะสำหรับตัวอย่าง; เวอร์ชันที่ง่ายกว่าจะเป็น:
[3] square! stack!
การบีบบังคับแบบเดียวกันนี้จะดำเนินการโดยอัตโนมัติในระหว่างการโต้แย้งของ FFI
โปรดทราบว่า "รหัส" ไม่ได้ประเมินโดยอัตโนมัติ การประเมินผลจะถูกเรียก อย่างชัดเจน ผ่าน "!" รหัสเป็นเพียงข้อมูลจนกว่าคุณจะตัดสินใจ "เรียกใช้":
[3] square stack! | ข้อมูลและ "รหัส" บนสแต็กไม่ผ่านการประเมิน |
! stack! | ประเมิน TOS และสังเกตผลลัพธ์ |
แฟคทอเรียล:
[@n int_iszero(n) 1 | int_mul(fact(int_dec(n)) n)]@fact
ใน Listree ค่ามีพจนานุกรมของคู่คีย์/CLL ซึ่งแต่ละ CLL ("รายการเชื่อมโยงแบบวงกลม" การใช้คิวสองปลาย) มีการอ้างอิงอย่างน้อยหนึ่งรายการซึ่งแต่ละรายการมีพจนานุกรม .. และอื่น ๆ ส่วนประกอบสำคัญ/deq ของ Listree คือการใช้งาน BST โดยใช้การเปลี่ยนแปลง RBTree "Simple" ของ Arne Andersson (http://user.it.uu.se/~arnea/ps/simp.pdf)
VM นั้นง่ายมาก มันประเมิน bytecodes ซึ่งแต่ละตัวเป็นดัชนีเป็นอาร์เรย์ของวิธี C ที่ใช้ไบต์
รีเซ็ต | แสงใส |
ต่อ | ถอดรหัสลำดับอักขระและตั้งค่า Lit ("[One Two 3]", "ABC") |
ext_push | ดันไฟลงบนสแต็กข้อมูล |
ผู้อ้างอิง | สร้างการอ้างอิงพจนานุกรมอ้างอิงจาก LIT |
ดีเรฟ | แก้ไขอ้างอิงในพจนานุกรม |
กำหนด | ป๊อปด้านบนของสแต็กข้อมูลและแทรกลงในพจนานุกรมที่ Location Ref ("@") |
ลบ | มูลค่าการวางจำหน่ายที่อ้างอิง |
การประเมิน | ป๊อปด้านบนของ Data Stack และ A) Call FFI หรือ B) กดไปยัง Code Stack และให้ VM ให้ผลผลิต |
ctx_push | ป๊อปด้านบนของสแต็กข้อมูลและผลักมันไปที่หัวของสแต็กพจนานุกรมผลักเลเยอร์ใหม่ไปยังสแต็กข้อมูล |
ctx_pop | ฟิวส์สองชั้นของสแต็กข้อมูลป๊อปพจนานุกรมสแต็กพจนานุกรมและผลักมันไปยังสแต็กข้อมูล |
fun_push | ป๊อปด้านบนของสแต็คข้อมูลและผลักไปยังสแต็ก func เพิ่มเลเยอร์ลงในสแต็กข้อมูลเพิ่มเลเยอร์โมฆะลงในพจนานุกรม |
fun_eval | กด {fun_pop} ไปยังรหัสสแต็กป๊อปด้านบนของ Func Stack และทำ "eval" |
fun_pop | ฟิวส์สองชั้นสแต็คทิ้งเลเยอร์พจนานุกรม Null |
โยน | โยนข้อยกเว้น* |
จับ | จับข้อยกเว้น* |
*ข้อยกเว้นเป็นวิธีการใช้ข้อผิดพลาดและเงื่อนไขของ VM และพวกเขาเล่นซอกับสถานะของ VM มากกว่าการดำเนินการเฉลี่ยเล็กน้อยซึ่งสมควรได้รับย่อหน้าของตัวเอง
ชุดปฏิบัติการง่าย ๆ นี้เพียงพอที่จะโต้ตอบกับพจนานุกรมประเมินการสร้างภาษาซ้ำ (VM คือการใช้งานแบบไร้สแต็ค) และที่สำคัญที่สุดคือการใช้ประโยชน์จากประเภท C ฟังก์ชั่นและข้อมูล มันเป็นกรอบกระดูกเปลือยที่ต้องอาศัยการมีเพศสัมพันธ์อย่างแน่นหนากับ C เพื่อขยายความสามารถของมันอย่างง่ายดาย
(distro gnu/linux ใด ๆ ที่คุ้มค่าเกลือของมันมีสิ่งเหล่านี้:
ในการเรียกใช้: สมมติว่า GCC, CMAKE และห้องสมุดไม่ได้โบราณเกินไปเพียงแค่เรียกใช้ "Make" เพื่อสร้างและเข้าสู่ Repl