Pretty.c - สคริปต์ที่ทำงานบน C
(github.com/aartaka)- Pretty C เป็นภาษาสคริปต์ใหม่ที่เข้ากันได้กับ C และเพิ่มความสะดวกอย่าง dynamic typing, generic iteration, และ resource tracking ให้กับโปรแกรม C
- วิธีใช้งานคือเป็นไลบรารีแบบ header-onlyที่เพียง include
pretty.hไฟล์เดียว และสามารถใช้ร่วมกับไฟล์ C และไลบรารี C เดิมได้ - ฟีเจอร์ที่มีได้แก่ alias อย่าง
string,any,nil, การอนุมานชนิดด้วยvar/let, การแสดงผลแบบ generic ด้วยprint/println, การเปรียบเทียบแบบ generic ด้วยequal, และ macro ternary ที่อ่านง่าย - เพื่อลดงานซ้ำด้านการวนลูปและการจัดการหน่วยความจำ มีไวยากรณ์แบบ macroอย่าง
foreach,forthese,fortimes,forrange,new,vector,delete,with,lambda,try/catch - บางฟีเจอร์มีเงื่อนไขเช่น GCC, Clang, C23+, C11+ และตัวแปรที่ผูกด้วย
withเป็นvoid *จึงอาจต้องแปลงชนิดก่อนใช้งาน
เป้าหมายและวิธีใช้งานของ Pretty C
- Pretty C เป็นภาษาสคริปต์ที่เข้ากันได้กับ C และเพิ่ม dynamic typing, generic iteration, resource tracking รวมถึงความสะดวกด้านไวยากรณ์หลายอย่างให้กับโปรแกรม C
- ยังคงรักษาความเข้ากันได้ย้อนหลังกับ C และไลบรารี C เดิม
- ได้แรงบันดาลใจจาก Lua, Python, JavaScript, และ Lisp
- การติดตั้งสามารถ clone repository หรือคัดลอกเฉพาะไฟล์
pretty.hไปใช้ก็ได้- สามารถ include จากไฟล์ C ใดก็ได้ด้วย
#include "pretty.h" - หรือจะกำหนด path ของ Pretty C ใน include path ก็ใช้งานได้
- สามารถ include จากไฟล์ C ใดก็ได้ด้วย
เฮดเดอร์ที่ include มาให้พื้นฐานและ macro แบบง่าย
- Pretty C include standard header มาให้เพื่อให้ใช้ชนิดข้อมูลและรูปแบบตัวดำเนินการที่ใช้บ่อยได้ทันที
- ยังมีmacro แบบง่ายที่นิยามไว้ให้ใช้งานบ่อยด้วย
max,min: ค่าสูงสุดและต่ำสุดของตัวเลขสองค่าlen: ความยาวของอาร์เรย์default: ให้ค่า fallbacklimit: จำกัดค่าให้อยู่ในช่วงbetween: ตรวจว่าตัวเลขอยู่ในช่วงหรือไม่divisible: ตรวจว่าหารด้วยอีกจำนวนหนึ่งลงตัวหรือไม่
ชนิดข้อมูลและ alias
- Pretty C มีalias ของชนิดข้อมูล C แบบสั้นให้ใช้
stringคือchar*byteคือcharbytesคือchar*anyคือvoid*- มีรูปย่อของจำนวนเต็มอย่าง
uchar,ushort,uint,ulong
- ยังมีalias ด้านไวยากรณ์ที่ได้รับอิทธิพลจาก Lua และ Lisp
eq,is:==bitnot,bitxor: alias สำหรับการทำงานระดับบิตsuccess,fail,failure: alias สำหรับแพตเทิร์นsuccess == 0below,above,upto,downto: ตัวดำเนินการเปรียบเทียบeven,odd,positive,negative,zero,empty: predicate สำหรับตัวเลขและข้อมูลnil:NULLuntil:whileแบบปฏิเสธelif:else ififnt,elifnt: รูปแบบตระกูลif(!...)repeat: alias ของdoแบบ Luadone,finish,pass: alias ของbreak,continuealways,forever,loop,indefinitely: รูปแบบสำหรับลูปไม่สิ้นสุดnever,comment: รูปแบบที่ไม่รันโค้ดแต่ยังเปิดให้คอมไพเลอร์วิเคราะห์ได้
การอนุมานชนิด การแสดงผล การเปรียบเทียบ และ ternary
- การอนุมานชนิดใช้ได้บน GCC, Clang หรือ C23+
- มีรูปแบบตัวอย่างเช่น
var t = time(0);,let lt = localtime(&t);,local at = asctime(lt);
- มีรูปแบบตัวอย่างเช่น
- การแสดงผลแบบ genericใช้ได้บน C11+
printใช้แสดงค่าที่ส่งเข้าไปprintlnเพิ่มขึ้นบรรทัดใหม่หลังแสดงผล
- การเปรียบเทียบความเท่ากันแบบ genericก็มีให้เป็นฟีเจอร์ของ C11+
- เช่นการเทียบสตริง
equal("NA", line) - และมีตัวอย่างอย่าง
equal(0.3, 0.2 + 0.1)
- เช่นการเทียบสตริง
- ตัวดำเนินการ ternary สามารถทำให้อ่านง่ายขึ้นด้วย macro แบบข้อความ
whenขยายเป็นสตริงว่างเพื่อช่วยด้านการอ่านunlessขยายเป็นnotจึงทำงานเป็นรูปปฏิเสธของwhenthenคือ?otherและotherwiseคือ:onlyใช้เมื่อไม่ต้องการส่วนotherwiseotherwhenใช้สำหรับต่อเงื่อนไขถัดไป
Macro สำหรับการวนลูป
- Pretty C ทำ abstraction ของแพตเทิร์น
forloop ที่ใช้บ่อยด้วยmacro foreach (var, type, length, ...)- ใช้วนผ่านอาร์เรย์หรือพื้นที่หน่วยความจำ
- ในแต่ละรอบ
varจะเป็น pointer ไปยังสมาชิกของอาร์เรย์นั้น - เพราะเป็น pointer จึงสามารถแก้ไขสมาชิกนั้นได้โดยตรงเมื่อจำเป็น
forthese (var, type, ...)- วนผ่านอาร์กิวเมนต์แบบ variadic ที่ส่งเข้ามา และ bind แต่ละค่าเข้ากับ
varชนิดtype
- วนผ่านอาร์กิวเมนต์แบบ variadic ที่ส่งเข้ามา และ bind แต่ละค่าเข้ากับ
fortimes (var, times)- ย่อแพตเทิร์นที่พบบ่อยของการวนซ้ำตั้งแต่ 0 จนถึงจำนวนครั้งบวกที่กำหนด
forrange (var, init, target)- วนผ่านช่วงตัวเลขตั้งแต่
initถึงtarget initและtargetสามารถเป็นจำนวนเต็ม signed หรือ unsigned แบบใดก็ได้- ถ้า
initมากกว่าtargetขั้นของการวนจะลดลง
- วนผ่านช่วงตัวเลขตั้งแต่
forrangeby (var, type, init, target, by)- วน
varที่กำหนดtypeจากinitถึงtargetด้วยช่วงห่างby
- วน
การจัดสรรหน่วยความจำและยูทิลิตีสำหรับบล็อก
- macro สำหรับ allocation ได้รับอิทธิพลจาก C++ และช่วยทำให้แพตเทิร์นการจัดสรรหน่วยความจำทั่วไปง่ายขึ้น
new (type, ...): สร้าง struct เป็นต้น ในรูปแบบคล้าย C++newvector (length, type, ...): ทำให้แพตเทิร์นการจัดสรรอาร์เรย์ตามความยาวและชนิดที่กำหนด พร้อมค่าเริ่มต้น เรียบง่ายขึ้นdelete (...): เหมือนfreeแต่ใช้ได้สำหรับคนที่ชอบชื่อแบบ C++
- ยูทิลิตีสำหรับบล็อก มี macro สำหรับ local binding, deferred execution, และงานแบบเป็นบล็อก
lambda (ret, name, ...): ทำให้ใช้ nested function, lambda, closure ใน C ได้บน GCC, Clang หรือ C++with (var, close, ...): ระบุฟังก์ชันปล่อยทรัพยากรcloseล่วงหน้า จึงมีประโยชน์กับอ็อบเจ็กต์ที่จัดสรรแบบไดนามิกหรือ file handle- ข้อเสียคือ
varที่ bind ไว้จะเป็นvoid *จึงอาจต้อง cast เป็นชนิดที่ต้องการก่อนใช้งาน
- ข้อเสียคือ
defer: ถูกถอดออกแล้ว และแนะนำให้ใช้deferแบบใหม่ที่ใช้งานได้แล้วแทนtryและcatch: ช่วยให้เขียนการจัดการข้อผิดพลาดใน C ได้ในรูปแบบที่หรูขึ้นNOERR,NOERROR: มีไว้เพื่อความสะดวกกับ error switch-case
วิธีการมีส่วนร่วม
- เมื่อต้องเพิ่มฟังก์ชัน มีคำขอให้ทำตาม Linux Kernel Style Guide
- แนะนำให้รัน
make indentก่อน commit
- แนะนำให้รัน
- นิยมให้ทำตาม A’s Commit Message Guide แต่ไม่ได้บังคับ
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ประเพณีการสร้าง ภาษาเฉพาะโดเมน ภายใน C มีมานานแล้ว
Stephen Bourne อยากเขียนเชลล์ให้เป็น ALGOL จึงเคี่ยวเข็ญ C preprocessor อย่างไม่ลดละ จนทำให้มันดูเหมือนภาษาที่เขาชอบ
https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh...
https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh...
https://www.ioccc.org/
อยากให้ใครสักคนอธิบายหน่อยว่านี่เป็นมุกตลก หรือผู้เขียนสับสนจริง ๆ กันแน่
ไม่มีอะไรสมเหตุสมผลเลย ไม่ใช่ “scripting” ด้วยซ้ำ บอกว่าจะนำ “strong typing” เข้ามา แต่ไม่ได้ทำอะไรกับ type เลย และ alias ของ operator ที่บอกว่าได้แรงบันดาลใจจาก Lua กับ Lisp ก็ไม่ได้มีอยู่ในสองภาษานั้นสักภาษา แต่ก็ไม่ได้ดูเหมือนล้อเลียนอย่างชัดเจน เลยสับสนจริง ๆ
อ้างว่าเป็นภาษา scripting แต่ก็ยังต้อง คอมไพล์ โปรแกรมอยู่ดี น่าหัวเราะ
ถ้าเอา CINT(https://root.cern.ch/root/html534/guides/users-guide/CINT.ht...) มาต่อ ก็รันทันทีและใช้ REPL ได้
ก่อนหน้านี้เคยเห็นบล็อกโพสต์ที่เจาะลึกว่า
./fooทำงานอย่างไร และไฟล์ ELF ถูก execute อย่างไร น่าจะใช้วิธีเดียวกันลงทะเบียนโปรแกรม.cให้คอมไพล์แล้วรันได้ด้วย[0] https://gist.github.com/jdarpinian/1952a58b823222627cc1a8b83...
[0] https://bellard.org/tcc/tcc-doc.html
“มอบ syntax sugar มากพอที่จะทำให้นักพัฒนา C เกิดหัวใจวายแบบเบาหวาน”
ชอบประโยคนี้มาก
เห็นมีหลายอย่างที่ควรเพิ่มเข้าไปใน CNoEvil[0] กับ ogw[1] ทุกครั้งที่โปรเจกต์นี้โผล่กลับมาทุก ๆ ไม่กี่เดือน ดูเหมือนจะมีเพิ่มเสมอ
[0] https://git.sr.ht/~shakna/cnoevil3/
[1] https://git.sr.ht/~shakna/ogw
เป็นประโยคที่ทำให้รักจากก้นบึ้งของหัวใจจริง ๆ
นี่กำลังทำผิดพลาดแบบเดียวกับ Python 2 สตริงกับไบต์ ไม่ใช่ type เดียวกัน และไม่ควรถูกปฏิบัติแบบนั้น
argvหรือ output ของ subprocess ควรมองว่าเป็น type อะไรดีในนั้นอาจมีค่าขยะอะไรก็ได้ จึงไม่ใช่สตริง UTF-8 ที่ถูกต้อง แต่เครื่องมืออย่าง awk กับ grep ก็ถูกใช้กันทุกที่
สตริงกับไบต์อาจมองได้ว่าเป็น type ทั่วไปเดียวกัน เพียงแต่บางครั้งการให้ type ภายในที่ต่างออกไปกับไบต์ UTF-8 ที่ถูกต้องก็มีประโยชน์ Rust จัดการส่วนนี้ได้ค่อนข้างดีด้วย OsString กับ String
https://en.cppreference.com/w/c/string/multibyte
ไม่มีข้อใดเป็นจริงเสมอไป การห่อ byte sequence ที่ผ่านการตรวจสอบแล้วไว้ใน type ที่สร้างได้ผ่านการตรวจสอบเท่านั้นมีประโยชน์ แต่พอทำแบบนั้นแล้ว
Utf8StringกับEmailAddressก็แทบเป็นหมวดเดียวกัน ไม่มีเหตุผลที่ type system จะต้องปฏิบัติกับ encoding เป็นกรณีพิเศษเพียงอย่างเดียวถ้าหมายถึง “ข้อความที่มนุษย์อ่านได้” ก็เห็นด้วยว่าสตริงไม่เหมือนกับอาร์เรย์ไบต์ใด ๆ แต่หลายภาษาไม่ได้บังคับนิยามนั้น
ทำให้นึกถึงโค้ดเบส C++ ที่เคยต้องเข้าไปตรวจสอบเมื่อนานมาแล้ว ทั้งหมดเขียนเหมือนกับว่าเป็น Java ทุกอย่างใช้ camelCase ตัวแปรของคลาสทุกตัวมี getter/setter และมี interface อยู่ทั่วไปหมด
https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd...
ยากจะเชื่อว่าไม่ใช่ ALGOL
std::stringไว้ แต่ใส่เมธอดสไตล์ Java เข้าไปด้วยhttps://learn.microsoft.com/en-us/cpp/cpp/property-cpp?view=...
f(String *)แบบ Java ทำให้โค้ด C++ เป็นแนวf(new String("Hello")ถ้าเรื่องนี้น่าสนใจ คุณอาจชอบ libcello.h ด้วย
https://www.libcello.org
ชอบชื่อของชนิดข้อมูลต่าง ๆ และการเลือกมาโครฟังก์ชันในตัวอย่าง
minก็สมบูรณ์แบบLenก็ถูกใจตัวดำเนินการบูลีนแบบมีชื่ออาจจะเกินไปนิด แต่ก็น่าลอง ส่วน ternary operator เดิมทีอ่านยากอยู่แล้ว จึงยังมีพื้นที่ให้ปรับปรุงได้มาก ผมยังไม่ถูกโน้มน้าวเต็มที่กับนิยามลูปทั้งหมด แต่บางอันก็มีเหตุผล การติดตามทรัพยากร น่าประทับใจ และรูปแบบ
for...ดูขี้เหร่นิดหน่อย อาจตั้งชื่ออื่นได้โดยรวมเป็นความพยายามที่ค่อนข้างแข็งแรง ผมอยากให้ดีไซน์ประมาณ 8/10 วิธีที่ร่างด้วยมาโคร C นั้นสง่างามจริง ๆ และดูเหมือนโค้ดที่ดีจริง ๆ ไม่แน่ใจว่าจะใช้เองไหม เพราะมันเป็นภาษาใหม่ และผมก็ชอบ C อยู่แล้ว ถึงอย่างนั้น วิธี implement ก็ชัดเจนมาก จึงอาจช่วยให้ผู้คนเรียนรู้ C และคิดเรื่องการออกแบบภาษาได้
len(),max/minและ มาโครตัวดำเนินการ บางตัว เพราะยังไม่ค่อยต้องใช้ฟีเจอร์น่าอร่อยอื่น ๆ มากนัก ดังนั้นอยากให้ลองใช้แค่มาโครตัวดำเนินการสักไม่กี่ตัวดูก็ได้ถ้า
ifntมีไว้สำหรับif(!...)แล้ว unless ดูอ่านง่ายกว่าifntfor(;;)ว่าalwaysในภาษาอื่นเคยเห็นใช้
loopแต่ Qt เรียกว่าforeverซึ่งสวยจริง ๆ และก็ให้ความรู้สึกเป็น Qt มากifntเวลาอ่านออกเสียงแล้วตลกดีunlessไว้สำหรับเงื่อนไขแบบ ternary เพราะฝั่งนั้นมีประโยชน์กว่ายังไงล่ะอ้อ เดี๋ยวนะ นี่ John Tromp นี่นา คนสร้าง BLC! ผมเป็นแฟนครับ