• C3 มีพื้นฐานมาจากภาษา C และมี ความสามารถขั้นสูง เช่น โมดูล, operator overloading, generics และการรันตอนคอมไพล์
  • โดยยังคงไวยากรณ์ C ที่คุ้นเคยไว้ พร้อมเสริม ไวยากรณ์ที่เพิ่มประสิทธิภาพและความเสถียร เช่น การจัดการข้อผิดพลาด, defer, foreach
  • เพิ่ม สัญญาเชิงประกาศ (contracts) และ optional type กับรูปแบบการจัดการข้อผิดพลาด เพื่อยกระดับ ความปลอดภัยและความชัดเจน
  • รองรับ สภาพแวดล้อมการพัฒนาที่ใช้งานได้จริง เช่น standard library และ การรวม build system, การจัดสรรหน่วยความจำชั่วคราว
  • มีความคล้ายกับ ภาษา Zig ในด้านการ build, การสร้างโปรเจกต์, โครงสร้างโค้ด และสะท้อนให้เห็นการทดลองด้าน การออกแบบภาษา แบบใหม่

ภาพรวมและจุดเด่นของ C3

C3 คืออะไร?

  • C3 เป็นภาษาที่สร้างต่อยอดบนภาษา C โดยยังคง ไวยากรณ์ที่คุ้นเคย ไว้ ขณะเดียวกันก็เพิ่มความสามารถที่ C ทำได้ยาก เช่น ระบบโมดูล, operator overloading, generics, การรันตอนคอมไพล์, การจัดการข้อผิดพลาด, defer, value methods, contracts แบบค่อยเป็นค่อยไป, slices, foreach, การรองรับ dynamic type
  • ใช้โครงสร้างโมดูลที่อาศัย namespace เพื่อป้องกัน การชนกันของชื่อ (abc::Context เป็นตัวอย่างของ imperative namespace)
  • เป้าหมายหลักคือ เพิ่มผลิตภาพ และมอบ ความสามารถสมัยใหม่สำหรับ system programming อย่างปลอดภัย

คุณลักษณะของภาษา

ตัวอย่าง Hello World

  • มีความคล้ายกับ C ในเชิงไวยากรณ์
  • ต้องระบุคีย์เวิร์ด fn อย่างชัดเจนในการประกาศฟังก์ชัน
  • ฟังก์ชันใน standard library สำหรับ I/O และงานอื่น ๆ มีความสามารถสูง และสามารถพิมพ์ค่าหลายชนิดได้ทันที

ลูป foreach

  • ต่างจาก C ตรงที่รองรับ ไวยากรณ์ foreach มาให้โดยตรง
  • การวนซ้ำผ่าน reference ต้องใส่ & หน้าชื่อตัวแปร (ความสามารถขั้นสูง)
  • รองรับ break และ continue และมีลักษณะคล้าย foreach ของภาษาอื่น

ลูป while

  • ก่อน C99 ไม่สามารถประกาศตัวแปรภายในเงื่อนไขของ while ได้ แต่ใน C3 สามารถประกาศภายในได้

enum และคำสั่ง switch

  • รองรับ break แบบ implicit ในคำสั่ง switch (การปะปนระหว่าง implicit/explicit break อาจมีทั้งคนชอบและไม่ชอบ)
  • รองรับ การย้ายเคสอย่างชัดเจน ผ่านคีย์เวิร์ด nextcase (ทำให้ทำ jump table ได้ง่าย)
  • ควบคุม flow ของ switch-case ได้กระชับกว่าแนวทางที่เคยซับซ้อนในภาษาอย่าง Zig หรือ C
โฆษณา

คีย์เวิร์ด defer

  • เมื่อ scope สิ้นสุด จะ รันคำสั่งที่จองด้วย defer ย้อนลำดับ เพื่อรับประกันการคืนทรัพยากรอย่างปลอดภัย
  • สามารถใช้ defer ร่วมกับ catch, try ได้ด้วย (ควบคุม flow ของการจัดการข้อผิดพลาด)

struct และ union

  • อนุญาตให้มี sub-struct/sub-union แบบมีชื่อหรือ anonymous ภายใน struct ทำให้ออกแบบแพตเทิร์น tagged union ได้ง่าย
  • แยกความต่างระหว่าง anonymous (ฟิลด์ชื่อซ้ำกัน) กับการชนกันของชื่ออย่างเข้มงวดและชัดเจน

แนวทางการจัดการข้อผิดพลาด

  • รองรับ optional type ผ่านสัญลักษณ์ ? และรวม error กับ value option เข้าไว้ด้วยกันเพื่อความสะดวก
  • ใช้คีย์เวิร์ด catch เพื่อแตกแขนงเมื่ออยู่ในสถานะว่าง (ไม่มี Optional) หรือเกิด error
  • ต่างจาก Rust และ Zig ตรงที่ แยก error กับ optional value ได้ไม่ชัดนัก (ข้อดี: เรียบง่าย, ข้อเสีย: เจตนาชัดเจนน้อยลง)
  • สามารถส่งต่อ exception ได้ด้วยตัวดำเนินการ ! (rethrow)

Contracts

  • เขียนเงื่อนไขก่อน/หลังฟังก์ชัน (Require/Ensure) ภายใน <* .. *> (ตรวจเงื่อนไขตอนคอมไพล์)
  • รองรับการวิเคราะห์ fold ใน compile time ด้วย (แต่ยังไม่ทำ static analysis)

เมธอดของ struct

  • สร้าง associated methods ด้วยการระบุชนิด + dot notation (Foo.next) และมี namespace (รวมถึง primitive)
  • อนุญาตให้มีเมธอดกับทุกชนิด เช่น struct/union/enum

แมโคร

  • แมโครที่อิงการประเมินค่าในช่วงคอมไพล์ (macro keyword)
  • ใช้ $ สำหรับพารามิเตอร์ตอนคอมไพล์ และ # สำหรับส่งผ่านก่อนประเมินค่า
  • เป็นสไตล์แบบ C (ลดปัญหาแมโครพันกัน เน้นความเสถียรของ AST มีการตรวจด้วยคำนำหน้า @ เป็นต้น)
  • ใช้แมโครเพื่อทำ type reflection และการรันตอนคอมไพล์
โฆษณา

คุณสมบัติของชนิดข้อมูล

  • alignof, kindof, extnameof, sizeof, typeid, methodsof, has_tagof, tagof, is_eq, is_ordered, is_substruct เป็นต้น
  • เหมาะกับ metaprogramming และ reflection

Base64/Hex literals

  • สามารถประกาศ ลำดับไบต์โดยตรง ได้ในรูปแบบ b64"..." x"..."
  • มีแมโครในตัว $embed เป็นทางเลือกทดแทน (ในทางปฏิบัติใช้ไม่บ่อย)

Primitive types

  • มีชนิดพื้นฐานหลากหลาย เช่น int, uint, char (เป็น unsigned เสมอ), bool, float, int128/uint128
  • มีชนิดเฉพาะสำหรับ pointer/size เช่น iptr, uptr, isz, usz (อาจไม่ค่อยตรงไปตรงมานัก)
  • ต่างจาก C ตรงที่รับประกัน bit-size

อื่น ๆ

  • มาพร้อมชุดความสามารถที่กว้างขวาง เช่น operator overloading, struct subtyping, generics, runtime dispatch, any type, bitstructs

ภาคปฏิบัติ: ประสบการณ์ใช้งาน C3

การติดตั้ง C3

  • รองรับทั้งไบนารีที่ build ไว้ล่วงหน้าจากเว็บไซต์ทางการ และการ build จากซอร์สโดยตรง
  • ต้องติดตั้ง LLVM และ LLD (หากมีปัญหาเรื่องการลิงก์ ให้ใช้ CMake flags -DLLVM_DIR, -DLLD_DIR)
  • บางดิสโทรมีปัญหาไม่รวมไลบรารี LLD มาด้วย จึงแนะนำให้ดาวน์โหลดไบนารีโดยตรง
  • คอมไพเลอร์ C3 ต้องพึ่งพา libtinfo
โฆษณา

การสร้างโปรเจกต์

  • ใช้คำสั่ง c3c init เพื่อสร้างโครงสร้างโฟลเดอร์มาตรฐาน (LICENSE/README.md/project.json/src เป็นต้น)
  • ตั้งค่าโครงโปรเจกต์พื้นฐาน เช่น Bluild, build target, การตั้งค่าซอร์ส (คล้าย Zig และ Cargo)
  • ไฟล์ main.c3 เริ่มต้นกระชับมาก (ความเห็น: เหมาะกับผู้ใช้ใหม่)

การสร้างเครื่องคิดเลข

การออกแบบและเป้าหมาย

  • ใช้ C3 ฝึกไวยากรณ์หลากหลายด้าน เช่น ฟังก์ชัน, I/O, การจัดการหน่วยความจำ, ลูป ผ่านการทำ recursive descent parser และลอจิกหลักของเครื่องคิดเลข
  • เป้าหมายคือดูข้อดีข้อไม่สะดวกด้วยตนเอง ทั้งเรื่องความตรงไปตรงมาของไวยากรณ์และผลิตภาพในการใช้งานจริง

การจัดการอินพุต

  • ใช้ temporary allocator (tmem) ผ่าน @pool โดยคืนหน่วยความจำอัตโนมัติเมื่อ scope สิ้นสุด (arena allocator)
  • รองรับทั้ง tmem (ชั่วคราว) และ mem (ทั่วไป) ซึ่งเป็นรูปแบบการจัดการหน่วยความจำมาตรฐาน พร้อมแพตเทิร์นการส่ง allocator ระดับฟังก์ชัน (ผสมข้อดีของ Zig กับ C)
  • ฟังก์ชัน main ต้องระบุค่าที่คืนอย่างชัดเจนเสมอ (คอมไพเลอร์บังคับ)
  • ฟังก์ชันที่ไม่เป็นไรหากละเลยค่าที่คืน ต้องติดแอตทริบิวต์ @maydiscard (ป้องกันการเมินผลลัพธ์โดยไม่ตั้งใจ)

การทำ tokenizer

  • แยกอินพุตของผู้ใช้ออกเป็นรายการโทเคน
  • ใช้โครงสร้างควบคุมหลายแบบ เช่น List ใน standard library, ไวยากรณ์ foreach, switch-case (nextcase, การผสม implicit/explicit break)
  • มีความสับสนอยู่บ้างกับไวยากรณ์ slice (รวมดัชนีปลายทั้งสองด้าน) และ slice ความยาว 0 (มีไวยากรณ์ระบุความยาวแยกต่างหาก)
  • เมื่อเทียบกับภาษาอย่าง Rust แล้ว การผสมใช้ temporary/general allocator แสดงให้เห็นถึง ความโปร่งใสและความยืดหยุ่นของการจัดการหน่วยความจำ ที่ดีกว่า

การทำ parser

  • ประสบการณ์เขียน parser โดยตรง (ละไว้)

บทสรุปและความเห็นโดยรวม

  • C3 มุ่งหาจุดร่วมระหว่าง ภาษาระบบแบบดั้งเดิม กับ การออกแบบสมัยใหม่
  • ศึกษาแนวทางของ Zig, Rust, C และออกแบบให้เป็นภาษาที่รักษาสมดุลระหว่าง ประสิทธิภาพกับความเสถียรของโค้ด
  • มีจุดเด่นด้าน modularity, การจัดการหน่วยความจำ/ข้อผิดพลาด/contracts อย่างปลอดภัย, metaprogramming ที่ทรงพลัง, build system ที่ตรงไปตรงมา
  • เส้นโค้งการเรียนรู้ สำหรับผู้มีประสบการณ์ C ถือว่าเริ่มต้นได้อย่างค่อยเป็นค่อยไป
  • ยังต้องปรับปรุงเรื่อง ecosystem ที่ยังไม่โตเต็มที่ เช่น language server, IDE และบางไวยากรณ์ที่อาจมีทั้งคนชอบและไม่ชอบ
  • น่าจับตาในฐานะ ภาษาทางเลือกยุคถัดไป สำหรับงาน low-level/system development

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น