4 คะแนน โดย GN⁺ 2023-10-09 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • 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 แบบเลือกเปิดใช้
  • ด้วย Native C interface จึงสามารถฝังเข้าไปในโค้ดเดิมได้ในลักษณะเดียวกับไลบรารี โดยมีโค้ดอย่าง Tasmota เป็นตัวอย่าง
    • มี easy mapping แบบเลือกเปิดใช้สำหรับการเรียกโค้ด C จาก Berry
  • ตัวอย่างไวยากรณ์แสดงการวนลูปรายการ การสร้าง map และวนดูคีย์ การประกาศ class และ single inheritance ผ่านโอเปอเรเตอร์ :
  • ดูซอร์สตัวอย่างเพิ่มเติมได้ที่ GitHub

1 ความคิดเห็น

 
GN⁺ 2023-10-09
ความเห็นจาก Hacker News
  • สำหรับ รันไทม์ขนาด 40KB แล้ว ฟีเจอร์ถือว่าครบจนน่าทึ่ง: มีภาษาคล้าย Python/Ruby ทำงานบน VM ที่มี garbage collection และรองรับสไตล์ procedural, object-oriented และ functional
    แค่ดูคร่าว ๆ ก็ดูเขียนได้ค่อนข้างสะดวก โดยเฉพาะฟีเจอร์ที่สร้างอ็อบเจ็กต์คงที่ไว้ล่วงหน้าแล้วใส่ส่วนใหญ่ไว้ใน ROM และใช้ RAM เฉพาะกับข้อมูลที่เปลี่ยนแปลงได้จริงเท่านั้น ซึ่งสะดุดตามาก เท่าที่รู้ MicroPython หรือ Lua ไม่มีแนวทางนี้ และบน MCU ที่ ROM/แฟลชมีเหลือเฟือแต่ RAM จำกัด ความต่างนี้มีผลมาก

    • MicroPython ก็สามารถ freeze โมดูลแล้วเก็บไว้ในแฟลชได้เช่นกัน โดยในแฟลชจะมีเฉพาะ bytecode ที่คอมไพล์ไว้ล่วงหน้า
      ถ้าในโมดูลมีข้อมูลที่เก็บในรูปอ็อบเจ็กต์ immutable อย่างสตริงหรือ bytes() ก็จะอ่านจากแฟลชโดยตรง ไม่ต้องคัดลอกเข้า RAM ก่อน อย่างไรก็ตาม การทำ freeze ต้องรันโค้ดครั้งหนึ่งบนคอมพิวเตอร์เดสก์ท็อป
      https://docs.micropython.org/en/latest/reference/constrained...
    • พออ่านคำอธิบายแล้วกลับไปดูเว็บไซต์อีกครั้ง รู้สึกว่า ครอบคลุมและน่าสนใจ กว่าหน้าโฮมเพจมาก
    • อย่างที่ @snops อธิบายไว้ MicroPython สามารถรันโค้ดจากแฟลชในรูปแบบ frozen code ได้ ผลคือจำเป็นต้องคอมไพล์ใส่เข้าไปในไบนารีเฟิร์มแวร์ ทำให้วงจรพัฒนาแบบรวดเร็วตามปกติของ MicroPython ช้าลง
      ดังนั้นโฟลว์ที่สะดวกคือ deploy ตอนรันไทม์ไปก่อนจนกว่า codebase จะนิ่งหรือใกล้ขั้น productization แล้วค่อย freeze โค้ดเข้าเฟิร์มแวร์ในจุดนั้น โมเดลที่สะดวกกว่านั้นมีข้อเสนอรองรับ mapfs ซึ่งให้จัดสรรแฟลชบางส่วนไว้เก็บโค้ด จากนั้นคอมไพล์ Python เป็น bytecode (mpy_cross) อัปโหลดไปยังพื้นที่นั้น และรันตอนรันไทม์ได้ proof of concept ออกมาแล้ว แต่ยังมีรายละเอียดที่ต้องเก็บกวาดก่อนจะเข้า mainline
      https://github.com/micropython/micropython/pull/8381
    • ดูน่าสนใจทีเดียว และหลายแง่ก็ คล้าย Lua แต่ก็มีความต่างที่เห็นได้ชัดอยู่บ้าง อนึ่ง ใน eLua ก็เคยมีงานที่ทำให้เก็บข้อมูลบางส่วนไว้ใน ROM ได้แบบโปร่งใส
      ดู rotables ได้ที่ https://eluaproject.net/doc/v0.9/en_arch_ltr.html
  • Berry ถูกใช้ใน Tasmota: https://tasmota.github.io/docs/Berry/

    • Tasmota ดูน่าสนใจ ถ้าเคยใช้หรือเคยเทียบกับ toit/jaguar อยากรู้ว่าคิดอย่างไร
  • ดูดีมาก โดยส่วนตัวคิดว่าเอกสารนี้ใกล้เคียงกับ เอกสารระดับยอดเยี่ยมที่สุด เท่าที่เคยเห็นมา
    ชอบ “short manual” เป็นพิเศษ เพราะช่วยให้นักพัฒนาที่มีประสบการณ์จับความรู้สึกของภาษาได้เร็ว ก่อนหน้านี้ไม่รู้จัก Tasmota แต่คงจะเริ่มหาข้ออ้างใช้ในโปรเจกต์สักอย่าง

  • สิ่งที่สงสัยที่สุดคือ ประสิทธิภาพและการใช้หน่วยความจำเมื่อเทียบกับ Lua เป็นอย่างไร และทำ sandboxing ได้แค่ไหน
    อยากรู้ด้วยว่ารันโค้ดที่ไม่น่าเชื่อถือได้หรือไม่

    • การเทียบกับ Lua น่าสนใจที่สุด บริษัทที่ผมเคยทำงานด้วยส่วนใหญ่ใช้ Python เหมือนพร็อกซีหรือเครื่องมือ CLI แบบหยาบ ๆ แต่ Lua แนะนำยากเพราะ OOP แบบ metatable ชวนสับสน
      ถ้าภาษานี้เป็นทางเลือกที่ใช้ได้ โดยมี struct เรียบง่ายและ constructor พื้นฐาน ก็น่าจะแนะนำให้ใช้เพื่อตัดสคริปต์ Python กับ dependency ของมันออกไปได้
    • โดยพื้นฐานแล้ว โค้ดสามารถเปิดและเขียนไฟล์ซิสเต็มได้ ดังนั้นจึงรัน โค้ดที่ไม่น่าเชื่อถือ ตรง ๆ ไม่ได้
      แอปพลิเคชันต้องรับผิดชอบ sandboxing เอง สำหรับ sandboxing แล้ว WebAssembly เป็นทางเลือกที่ดี
    • ถ้าเพิ่ม การจัดการเธรด native ที่มีคีย์เวิร์ด async เข้ามา น่าจะแยกตัวจาก Lua ได้ชัดเจนสำหรับงานเกมเอนจิน
  • ถ้ามีอะไรที่แทบเหมือนแบบนี้ แต่มี paradigm น้อยกว่าและเป็น static typing ก็คงเหมาะมากสำหรับใช้เป็นภาษาคอนฟิก
    ในหลายโปรเจกต์ที่ต้องมีคอนฟิกซับซ้อน เคยมีกรณีที่ให้ใช้ Lua หรือ Starlark/Python แทนการเขียน YAML หลายร้อยบรรทัด ซึ่งรู้สึกดีกว่ามาก แต่ก็ยังขาด autocomplete และ reflection อยู่เสมอ ไม่ค่อยเห็นตัวเลือกที่พอดีกับงานนี้ และภาษาสคริปต์ขนาดเล็กที่ฝังได้แทบทั้งหมดเป็น dynamic typing

    • คิดในแนวคล้ายกันมาเหมือนกัน แต่ใกล้กับ คอนฟิกที่ตรวจสอบอย่างเข้มงวด มากกว่าการเป็น static typing คงดีกว่าถ้าโหลดคอนฟิกทั้งหมดแล้วสร้างรายการปัญหาออกมาได้ และถ้าทำให้ดี ก็ควรรองรับ editor ได้ดีด้วย
      อย่างไรก็ตาม https://dhall-lang.org/ แสดงให้เห็นว่าคอนฟิกจำนวนมากสามารถจัดการด้วย static typing ได้ และมีประโยชน์มาก ดูเหมือนจะฝังแบบโปรแกรมในหลายภาษาได้
      https://docs.dhall-lang.org/howtos/How-to-integrate-Dhall.ht...
  • ดูดี ถึงอย่างนั้นตอนนี้ก็ยังค่อนข้างเอนเอียงไปทาง uLisp(http://www.ulisp.com) อยู่ แต่ก็ดีใจที่มีของแบบนี้ออกมา

    • ตัวเลือกสำหรับไมโครคอนโทรลเลอร์น่าสนใจ และยังมีสาย Forth ด้วย: https://github.com/tabemann/zeptoforth
  • ถ้าเป็นคนที่ชอบดูตัวอย่างโค้ดก่อนเพื่อจับความรู้สึกของภาษา ลองดู ไดเรกทอรีตัวอย่าง ได้: https://github.com/berry-lang/berry/tree/master/examples

  • ถ้ามี binding สำหรับภาษาอื่นก็คงดีมาก เราใช้ quickjs จาก Rust อยู่ และมันทำงานได้ค่อนข้างดี
    สามารถให้ VM เห็นเฉพาะสิ่งที่ต้องการได้ จึงรันโค้ดที่ไม่น่าเชื่อถือได้

  • ดูเหมือนเป็นภาษาที่ปรับมาเพื่อ embedded use case โดยเฉพาะ ออกแบบและทำเอกสารได้ดี และดูเหมือนไม่มีพฤติกรรมแปลก ๆ หรือผิดคาด
    ไวยากรณ์ก็ดู minimal แบบน่าพอใจและตรงรสนิยม คงจะเก็บไว้พิจารณาแน่ ๆ สำหรับ โปรเจกต์ ESP32 ถัดไป

    • ตั้งใจจะลองใช้ทันที กำลังลองเล่นชุดพัฒนา ESP-32 เพื่อทำของตกแต่งฮาโลวีนแบบไฟกระพริบอยู่ เลยตื่นเต้นที่จะได้ทดสอบตัวนี้
      ดูเหมาะมากกับ niche นี้ และชอบเป็นพิเศษตรงที่มันทำสิ่งที่ไม่ค่อยนึกออกว่ามี counterpart ชัด ๆ ในภาษาอื่น
  • สงสัยว่ามีแนวทางมาตรฐานไหมในการแสดง ชื่อฟังก์ชันของสคริปต์ ใน stack frame ของ native stack trace
    เวลา crash หรือใช้ CPU profiler อยากรู้ว่าสามารถแสดง native stack trace ผสมกับ script stack trace ได้หรือไม่