Pretty.c - Scripting chạy trên C
(github.com/aartaka)- Pretty C là một ngôn ngữ scripting mới tương thích với C, bổ sung các tiện ích như dynamic typing, vòng lặp generic và theo dõi tài nguyên cho chương trình C
- Cách dùng là một thư viện header-only chỉ cần include một file
pretty.h, có thể dùng cùng các file C và thư viện C hiện có - Các tính năng cung cấp gồm alias như
string,any,nil, suy luận kiểuvar/let, xuất genericprint/println, so sánh genericequal, và macro biểu thức ba ngôi dễ đọc - Để giảm bớt thao tác lặp và bộ nhớ, cung cấp cú pháp dựa trên macro như
foreach,forthese,fortimes,forrange,new,vector,delete,with,lambda,try/catch - Một số tính năng có điều kiện như GCC, Clang, C23+, C11+; biến được bọc bằng
withlàvoid *nên có thể cần ép kiểu trước khi dùng
Mục tiêu và cách sử dụng Pretty C
- Pretty C là một ngôn ngữ scripting tương thích với C, bổ sung dynamic typing, vòng lặp generic, theo dõi tài nguyên và nhiều tiện ích cú pháp cho chương trình C
- Duy trì tính tương thích ngược với C và các thư viện C hiện có
- Lấy cảm hứng từ Lua, Python, JavaScript và Lisp
- Cài đặt bằng cách clone repository hoặc chỉ sao chép file
pretty.hđể dùng- Có thể include trong bất kỳ file C nào bằng
#include "pretty.h" - Cũng có thể dùng bằng cách chỉ định đường dẫn Pretty C trong include path
- Có thể include trong bất kỳ file C nào bằng
Header được include mặc định và macro đơn giản
- Pretty C include các header chuẩn để có thể dùng ngay các kiểu thường dùng và biểu thức thay thế toán tử
- Cũng cung cấp các macro đơn giản thường được định nghĩa
max,min: giá trị lớn nhất và nhỏ nhất của hai sốlen: độ dài mảngdefault: cung cấp giá trị fallbacklimit: giới hạn phạm vi giá trịbetween: kiểm tra một số có nằm trong phạm vi hay khôngdivisible: kiểm tra có chia hết cho một số khác hay không
Kiểu và alias
- Pretty C cung cấp alias kiểu ngắn cho các kiểu C
stringlàchar*bytelàcharbyteslàchar*anylàvoid*- Cung cấp dạng viết tắt số nguyên
uchar,ushort,uint,ulong
- Cũng bao gồm các alias cú pháp chịu ảnh hưởng từ Lua và Lisp
eq,is:==bitnot,bitxor: alias cho phép toán bitsuccess,fail,failure: alias cho mẫusuccess == 0below,above,upto,downto: toán tử so sánheven,odd,positive,negative,zero,empty: predicate cho số và dữ liệunil:NULLuntil:whilephủ địnhelif:else ififnt,elifnt: biểu thức kiểuif(!...)repeat: alias kiểu Lua chododone,finish,pass: alias chobreak,continuealways,forever,loop,indefinitely: biểu thức vòng lặp vô hạnnever,comment: biểu thức cho phép compiler phân tích nhưng không thực thi code
Suy luận kiểu, xuất dữ liệu, so sánh, biểu thức ba ngôi
- Suy luận kiểu có thể dùng với GCC, Clang hoặc C23+
- Ví dụ cung cấp các dạng như
var t = time(0);,let lt = localtime(&t);,local at = asctime(lt);
- Ví dụ cung cấp các dạng như
- Xuất dữ liệu generic có thể dùng với C11+
printin ra giá trị được truyền vàoprintlnthêm xuống dòng sau khi in
- So sánh bằng generic cũng được cung cấp như một tính năng C11+
- So sánh chuỗi như
equal("NA", line) - Có ví dụ so sánh như
equal(0.3, 0.2 + 0.1)
- So sánh chuỗi như
- Toán tử ba ngôi có thể được làm dễ đọc hơn bằng macro dựa trên văn bản
whenmở rộng thành chuỗi rỗng để tăng khả năng đọcunlessmở rộng thànhnot, hoạt động như dạng phủ định củawhenthenlà?othervàotherwiselà:onlydùng khi không cần nhánhotherwiseotherwhendùng để nối điều kiện tiếp theo
Macro vòng lặp
- Pretty C trừu tượng hóa các mẫu vòng lặp
forthường dùng bằng macro foreach (var, type, length, ...)- Duyệt qua mảng hoặc vùng nhớ
- Trong mỗi lần lặp,
vartrở thành con trỏ tới phần tử mảng tương ứng - Vì là con trỏ, có thể sửa phần tử tại chỗ nếu cần
forthese (var, type, ...)- Duyệt qua các biến đối được truyền vào và bind từng giá trị vào
varthuộctype
- Duyệt qua các biến đối được truyền vào và bind từng giá trị vào
fortimes (var, times)- Rút gọn mẫu phổ biến lặp từ 0 đến số lần dương
forrange (var, init, target)- Duyệt qua phạm vi số từ
initđếntarget initvàtargetcó thể là số nguyên signed hoặc unsigned bất kỳ- Nếu
initlớn hơntarget, bước lặp sẽ giảm
- Duyệt qua phạm vi số từ
forrangeby (var, type, init, target, by)- Duyệt
varcótypeđược chỉ định từinitđếntargetvới khoảng cáchby
- Duyệt
Tiện ích cấp phát và khối
- Macro cấp phát đơn giản hóa các mẫu cấp phát bộ nhớ phổ biến theo dạng chịu ảnh hưởng từ C++
new (type, ...): tạo struct, v.v. theo dạng tương tự C++newvector (length, type, ...): đơn giản hóa mẫu cấp phát mảng có độ dài và kiểu được chỉ định, rồi đặt nội dung ban đầudelete (...): giốngfreenhưng có thể dùng khi muốn tên kiểu C++
- Tiện ích khối cung cấp macro cho binding cục bộ, thực thi trì hoãn và thao tác theo block
lambda (ret, name, ...): cho phép dùng hàm lồng nhau, lambda và closure trong C với GCC, Clang hoặc C++with (var, close, ...): hữu ích cho đối tượng cấp phát động hoặc file handle bằng cách cung cấp trước hàm giải phóng tài nguyênclose- Nhược điểm là
varđược bind làvoid *, nên có thể cần chuyển đổi sang kiểu mong muốn trước khi dùng
- Nhược điểm là
defer: đã bị loại bỏ, và hướng dẫn dùngdefermới có sẵntryvàcatch: cho phép viết xử lý lỗi trong C theo dạng hoa mỹ hơnNOERR,NOERROR: được cung cấp để tiện cho switch-case lỗi
Cách đóng góp
- Khi thêm hàm, được yêu cầu tuân theo Linux Kernel Style Guide
- Khuyến nghị chạy
make indenttrước khi commit
- Khuyến nghị chạy
- Ưu tiên tuân theo A’s Commit Message Guide, nhưng không bắt buộc
1 bình luận
Ý kiến trên Hacker News
Truyền thống tạo ngôn ngữ chuyên biệt theo miền bên trong C đã có từ lâu
Stephen Bourne muốn viết shell theo kiểu ALGOL, nên đã kiên trì “gõ” bộ tiền xử lý C cho đến khi nó trông giống ngôn ngữ ông ưa thích
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/
Không biết có ai giải thích được đây là trò đùa, hay tác giả thật sự đang rối không
Chẳng có gì hợp lý cả. Nó không phải là “scripting”, nói là đưa vào “kiểu mạnh” nhưng chẳng làm gì với kiểu, còn các bí danh toán tử được nói là mô phỏng Lua và Lisp thì thực ra không có trong cả hai ngôn ngữ đó. Nhưng nó cũng không trông như một bản nhại hiển nhiên, nên thật sự gây bối rối
Tự nhận là ngôn ngữ scripting nhưng vẫn phải biên dịch chương trình. Đáng chê thật
Gắn CINT(https://root.cern.ch/root/html534/guides/users-guide/CINT.ht...) vào thì có thể chạy ngay và có REPL
Trước đây tôi từng thấy một bài blog nói chi tiết về cách
./foohoạt động và cách chạy file ELF. Có vẻ cũng có thể đăng ký chương trình.ctheo cùng cách để biên dịch rồi chạy[0] https://gist.github.com/jdarpinian/1952a58b823222627cc1a8b83...
[0] https://bellard.org/tcc/tcc-doc.html
“Cung cấp đủ syntactic sugar để khiến lập trình viên C lên cơn đau tim do tiểu đường.”
Tôi rất thích câu này
Thấy có vài thứ nên thêm vào CNoEvil[0] và ogw[1]. Cứ mỗi vài tháng dự án này lại nổi lên, dường như lúc nào cũng có thêm thứ mới
[0] https://git.sr.ht/~shakna/cnoevil3/
[1] https://git.sr.ht/~shakna/ogw
Một câu khiến tôi thật lòng yêu từ tận đáy lòng
Cái này đang mắc lại sai lầm như Python 2. Chuỗi và byte không phải cùng một kiểu, và cũng không nên được đối xử như vậy
argvhoặc output của tiến trình con, nên được xem là kiểu gì?Trong đó có thể chứa dữ liệu rác tùy ý nên không phải chuỗi UTF-8 hợp lệ, nhưng các công cụ như awk và grep thì được dùng ở khắp nơi
Có thể xem chuỗi và byte là cùng một kiểu tổng quát, chỉ là đôi khi việc gán một kiểu nội bộ khác cho các byte UTF-8 hợp lệ sẽ hữu ích. Rust xử lý phần này khá tốt bằng OsString và String
https://en.cppreference.com/w/c/string/multibyte
Không có điều nào trong số đó luôn đúng. Việc bọc chuỗi byte đã được kiểm chứng trong một kiểu chỉ có thể được tạo thông qua kiểm chứng là hữu ích, nhưng một khi làm vậy thì
Utf8StringvàEmailAddressvề cơ bản thuộc cùng một loại. Không có lý do gì để hệ thống kiểu đối xử đặc biệt chỉ với encodingNếu là “văn bản con người đọc được” thì tôi đồng ý rằng chuỗi không giống một mảng byte tùy ý. Nhưng nhiều ngôn ngữ không áp đặt định nghĩa đó
Gợi nhớ đến một codebase C++ mà trước đây tôi từng phải kiểm tra. Toàn bộ trông như được viết bằng Java, với camel case cho mọi thứ, getter/setter cho mọi biến của class, và interface ở khắp nơi
https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd...
Khó mà tin được đó không phải ALGOL
std::stringnhưng gắn thêm các method kiểu Javahttps://learn.microsoft.com/en-us/cpp/cpp/property-cpp?view=...
f(String *), nên mã C++ kiểu nhưf(new String("Hello")Nếu thấy cái này thú vị thì có thể bạn cũng sẽ thích libcello.h
https://www.libcello.org
Tên các type rất hay, và lựa chọn macro cho hàm dựng sẵn như
mincũng hoàn hảo. Tôi cũng thíchLenToán tử boolean có tên có thể hơi quá, nhưng đáng thử; toán tử ba ngôi vốn khó đọc nên còn nhiều chỗ để cải thiện. Tôi chưa hoàn toàn bị thuyết phục bởi mọi định nghĩa vòng lặp, nhưng một số thì hợp lý. Theo dõi tài nguyên rất ấn tượng, còn dạng
for...thì hơi xấu nên có lẽ có thể đặt tên khácNhìn chung đây là một nỗ lực khá chắc tay. Tôi muốn chấm thiết kế khoảng 8/10. Cách phác thảo bằng macro C thật sự thanh nhã và trông như code tốt trong thực tế. Không rõ tôi có tự dùng không. Vì đây là một ngôn ngữ mới, mà tôi thì vốn đã thích C. Dù vậy cách triển khai rất rõ ràng, nên có thể giúp mọi người học C và suy nghĩ về thiết kế ngôn ngữ
len(),max/min, và vài macro toán tử. Vì tôi vẫn chưa cần nhiều tính năng ngon lành khác. Vì thế, chỉ cần thử dùng vài macro toán tử thôi cũng đángNếu
ifntlà dành choif(!...), thì unless có vẻ dễ đọc hơnifntfor(;;)làalwaysTôi từng thấy ngôn ngữ khác dùng
loop. Nhưng Qt gọi làforever, và cái đó thật sự đẹp. Cũng rất đúng chất Qtifntkhi đọc thành tiếng nghe cũng vuiunlesscho điều kiện ba ngôi. Vì kiểu đó dù sao cũng hữu dụng hơnÀ, khoan đã, John Tromp đây mà. Người tạo ra BLC! Tôi là fan