Berry คือภาษาสคริปต์แบบฝังตัวชนิดไดนามิกที่มีน้ำหนักเบามาก
(berry-lang.github.io)- Berry เป็นภาษาสคริปต์แบบชนิดไดนามิกสำหรับอุปกรณ์แบบฝังตัวสมรรถนะต่ำ โดยเมื่ออ้างอิง ARM Cortex M4 ขนาดของ interpreter core จะต่ำกว่า 40KiB และสามารถทำงานได้ด้วย heap ต่ำกว่า 4KiB
- รันไทม์ประกอบด้วย คอมไพเลอร์แบบ one-pass และ VM แบบ register-based โดยโค้ดทั้งหมดเขียนด้วย ANSI C99 จึงผนวกเข้ากับโค้ดแบบฝังตัวที่มีอยู่เดิมได้ง่าย
- เพื่อประสิทธิภาพ
int,real,boolean,stringจะไม่ถูกทำเป็นอ็อบเจ็กต์คลาส และมีเพียงlist,map,rangeเท่านั้นที่เป็น อ็อบเจ็กต์คลาส - ความสามารถของภาษา รองรับทั้งการเขียนโปรแกรมเชิงคำสั่ง เชิงวัตถุ และเชิงฟังก์ชัน พร้อม GC, FFI, การจัดการข้อยกเว้น, ไฟล์ไบต์โค้ด และโมดูลส่วนขยาย
- ด้วยการประกอบอ็อบเจ็กต์ตั้งแต่คอมไพล์ไทม์และความสามารถในการตรึงไว้ใน flash จึงช่วยลด การใช้ RAM ของโค้ด คลาส และโมดูล ทำให้เหมาะกับสภาพแวดล้อมที่มีข้อจำกัดด้านหน่วยความจำสูง
รันไทม์ที่ออกแบบมาสำหรับสภาพแวดล้อมแบบฝังตัว
- Berry เป็นภาษาสคริปต์แบบชนิดไดนามิกที่ออกแบบมาเพื่อใช้งานบนอุปกรณ์แบบฝังตัวสมรรถนะต่ำ เช่น ไมโครโปรเซสเซอร์
- บน ARM Cortex M4 CPU, Thumb ISA และ ARMCC compiler ขนาดโค้ดของ interpreter core จะต่ำกว่า 40KiB
- ภายใต้เงื่อนไขเดียวกัน สามารถทำงานได้ด้วย heap ต่ำกว่า 4KiB
- อินเทอร์พรีเตอร์เขียนด้วย ANSI C99 และมีทั้งคอมไพเลอร์ไบต์โค้ดแบบ one-pass กับเครื่องเสมือนแบบ register-based
- การออกแบบประเภทข้อมูลให้ความสำคัญกับประสิทธิภาพการทำงาน โดยแยกประเภทค่าออกจากประเภทอ็อบเจ็กต์
- ประเภทที่ไม่ใช่อ็อบเจ็กต์คลาส: int, real, boolean, string
- ประเภทที่เป็นอ็อบเจ็กต์คลาส: list, map, range
- การประกอบอ็อบเจ็กต์ตั้งแต่คอมไพล์ไทม์จะเก็บอ็อบเจ็กต์ค่าคงที่ส่วนใหญ่ไว้ในส่วนข้อมูลโค้ดแบบอ่านอย่างเดียว ช่วยลด การใช้ RAM ตอนเริ่มต้นอินเทอร์พรีเตอร์
ความสามารถของภาษาและโมเดลการทำงาน
- ประเภทพื้นฐานประกอบด้วย nil, boolean, integer, real, string, class, instance, module, list, map, range
- นิพจน์และโอเปอเรเตอร์รองรับการกำหนดค่า ความสัมพันธ์ ตรรกะ คณิตศาสตร์ บิต ฟิลด์ ดัชนี การเชื่อมสตริง conditional operator วงเล็บ และ bytes buffer
- โครงสร้างควบคุมมี
if elif else end,while,for,break,continue - ความสามารถด้านฟังก์ชันครอบคลุมตัวแปรภายในและ block scope, return statement, การนิยามฟังก์ชันซ้อน, closure แบบ Upvalue, anonymous function และ lambda expression
- คลาสรองรับเฉพาะ single inheritance แบบ public พร้อมเมธอด การโอเวอร์โหลดโอเปอเรเตอร์ constructor method และ destructor method
- GC ใช้ Mark-Sweep GC
- การจัดการข้อยกเว้นสามารถโยนค่า exception ใดก็ได้ด้วยคำสั่ง
raiseและรองรับ catch ได้หลายโหมด - ความสามารถด้านไฟล์ไบต์โค้ดทำให้สามารถ export ฟังก์ชันเป็นไฟล์ไบต์โค้ด แล้วโหลดกลับมาเพื่อรันได้
โมดูล การเชื่อมต่อกับ C และตัวอย่าง
- การจัดการโมดูลรองรับทั้งโมดูลในตัวและโมดูลส่วนขยาย
- โมดูลส่วนขยายรวมถึง script module, bytecode file module และ shared library module เช่น
*.soและ*.dll - สามารถตรึงโค้ด คลาส และโมดูลไว้ใน flash เพื่อลดการใช้ RAM
- มีการรองรับ Regex แบบเลือกเปิดใช้ และ LVGL mapping แบบเลือกเปิดใช้
- โมดูลส่วนขยายรวมถึง script module, bytecode file module และ shared library module เช่น
- ด้วย Native C interface จึงสามารถฝังเข้าไปในโค้ดเดิมได้ในลักษณะเดียวกับไลบรารี โดยมีโค้ดอย่าง Tasmota เป็นตัวอย่าง
- มี easy mapping แบบเลือกเปิดใช้สำหรับการเรียกโค้ด C จาก Berry
- ตัวอย่างไวยากรณ์แสดงการวนลูปรายการ การสร้าง map และวนดูคีย์ การประกาศ
classและ single inheritance ผ่านโอเปอเรเตอร์: - ดูซอร์สตัวอย่างเพิ่มเติมได้ที่ GitHub
1 ความคิดเห็น
ความเห็นจาก Hacker News
สำหรับ รันไทม์ขนาด 40KB แล้ว ฟีเจอร์ถือว่าครบจนน่าทึ่ง: มีภาษาคล้าย Python/Ruby ทำงานบน VM ที่มี garbage collection และรองรับสไตล์ procedural, object-oriented และ functional
แค่ดูคร่าว ๆ ก็ดูเขียนได้ค่อนข้างสะดวก โดยเฉพาะฟีเจอร์ที่สร้างอ็อบเจ็กต์คงที่ไว้ล่วงหน้าแล้วใส่ส่วนใหญ่ไว้ใน ROM และใช้ RAM เฉพาะกับข้อมูลที่เปลี่ยนแปลงได้จริงเท่านั้น ซึ่งสะดุดตามาก เท่าที่รู้ MicroPython หรือ Lua ไม่มีแนวทางนี้ และบน MCU ที่ ROM/แฟลชมีเหลือเฟือแต่ RAM จำกัด ความต่างนี้มีผลมาก
ถ้าในโมดูลมีข้อมูลที่เก็บในรูปอ็อบเจ็กต์ immutable อย่างสตริงหรือ
bytes()ก็จะอ่านจากแฟลชโดยตรง ไม่ต้องคัดลอกเข้า RAM ก่อน อย่างไรก็ตาม การทำ freeze ต้องรันโค้ดครั้งหนึ่งบนคอมพิวเตอร์เดสก์ท็อปhttps://docs.micropython.org/en/latest/reference/constrained...
ดังนั้นโฟลว์ที่สะดวกคือ deploy ตอนรันไทม์ไปก่อนจนกว่า codebase จะนิ่งหรือใกล้ขั้น productization แล้วค่อย freeze โค้ดเข้าเฟิร์มแวร์ในจุดนั้น โมเดลที่สะดวกกว่านั้นมีข้อเสนอรองรับ
mapfsซึ่งให้จัดสรรแฟลชบางส่วนไว้เก็บโค้ด จากนั้นคอมไพล์ Python เป็น bytecode (mpy_cross) อัปโหลดไปยังพื้นที่นั้น และรันตอนรันไทม์ได้ proof of concept ออกมาแล้ว แต่ยังมีรายละเอียดที่ต้องเก็บกวาดก่อนจะเข้า mainlinehttps://github.com/micropython/micropython/pull/8381
ดู rotables ได้ที่ https://eluaproject.net/doc/v0.9/en_arch_ltr.html
Berry ถูกใช้ใน Tasmota: https://tasmota.github.io/docs/Berry/
ดูดีมาก โดยส่วนตัวคิดว่าเอกสารนี้ใกล้เคียงกับ เอกสารระดับยอดเยี่ยมที่สุด เท่าที่เคยเห็นมา
ชอบ “short manual” เป็นพิเศษ เพราะช่วยให้นักพัฒนาที่มีประสบการณ์จับความรู้สึกของภาษาได้เร็ว ก่อนหน้านี้ไม่รู้จัก Tasmota แต่คงจะเริ่มหาข้ออ้างใช้ในโปรเจกต์สักอย่าง
สิ่งที่สงสัยที่สุดคือ ประสิทธิภาพและการใช้หน่วยความจำเมื่อเทียบกับ Lua เป็นอย่างไร และทำ sandboxing ได้แค่ไหน
อยากรู้ด้วยว่ารันโค้ดที่ไม่น่าเชื่อถือได้หรือไม่
ถ้าภาษานี้เป็นทางเลือกที่ใช้ได้ โดยมี struct เรียบง่ายและ constructor พื้นฐาน ก็น่าจะแนะนำให้ใช้เพื่อตัดสคริปต์ Python กับ dependency ของมันออกไปได้
แอปพลิเคชันต้องรับผิดชอบ sandboxing เอง สำหรับ sandboxing แล้ว WebAssembly เป็นทางเลือกที่ดี
asyncเข้ามา น่าจะแยกตัวจาก Lua ได้ชัดเจนสำหรับงานเกมเอนจินถ้ามีอะไรที่แทบเหมือนแบบนี้ แต่มี paradigm น้อยกว่าและเป็น static typing ก็คงเหมาะมากสำหรับใช้เป็นภาษาคอนฟิก
ในหลายโปรเจกต์ที่ต้องมีคอนฟิกซับซ้อน เคยมีกรณีที่ให้ใช้ Lua หรือ Starlark/Python แทนการเขียน YAML หลายร้อยบรรทัด ซึ่งรู้สึกดีกว่ามาก แต่ก็ยังขาด autocomplete และ reflection อยู่เสมอ ไม่ค่อยเห็นตัวเลือกที่พอดีกับงานนี้ และภาษาสคริปต์ขนาดเล็กที่ฝังได้แทบทั้งหมดเป็น dynamic typing
อย่างไรก็ตาม https://dhall-lang.org/ แสดงให้เห็นว่าคอนฟิกจำนวนมากสามารถจัดการด้วย static typing ได้ และมีประโยชน์มาก ดูเหมือนจะฝังแบบโปรแกรมในหลายภาษาได้
https://docs.dhall-lang.org/howtos/How-to-integrate-Dhall.ht...
ดูดี ถึงอย่างนั้นตอนนี้ก็ยังค่อนข้างเอนเอียงไปทาง uLisp(http://www.ulisp.com) อยู่ แต่ก็ดีใจที่มีของแบบนี้ออกมา
ถ้าเป็นคนที่ชอบดูตัวอย่างโค้ดก่อนเพื่อจับความรู้สึกของภาษา ลองดู ไดเรกทอรีตัวอย่าง ได้: https://github.com/berry-lang/berry/tree/master/examples
ถ้ามี binding สำหรับภาษาอื่นก็คงดีมาก เราใช้ quickjs จาก Rust อยู่ และมันทำงานได้ค่อนข้างดี
สามารถให้ VM เห็นเฉพาะสิ่งที่ต้องการได้ จึงรันโค้ดที่ไม่น่าเชื่อถือได้
ดูเหมือนเป็นภาษาที่ปรับมาเพื่อ embedded use case โดยเฉพาะ ออกแบบและทำเอกสารได้ดี และดูเหมือนไม่มีพฤติกรรมแปลก ๆ หรือผิดคาด
ไวยากรณ์ก็ดู minimal แบบน่าพอใจและตรงรสนิยม คงจะเก็บไว้พิจารณาแน่ ๆ สำหรับ โปรเจกต์ ESP32 ถัดไป
ดูเหมาะมากกับ niche นี้ และชอบเป็นพิเศษตรงที่มันทำสิ่งที่ไม่ค่อยนึกออกว่ามี counterpart ชัด ๆ ในภาษาอื่น
สงสัยว่ามีแนวทางมาตรฐานไหมในการแสดง ชื่อฟังก์ชันของสคริปต์ ใน stack frame ของ native stack trace
เวลา crash หรือใช้ CPU profiler อยากรู้ว่าสามารถแสดง native stack trace ผสมกับ script stack trace ได้หรือไม่