3 điểm bởi GN⁺ 2024-10-25 | 1 bình luận | Chia sẻ qua WhatsApp
  • 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ểu var/let, xuất generic print/println, so sánh generic equal, 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 withvoid * 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

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ử
    • stdbool.h: true, false, bool
    • stdint.h: kiểu số nguyên độ rộng cố định như uint64_t
    • iso646.h: toán tử thay thế như and thay cho &&, or thay cho ||
  • 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ảng
    • default: cung cấp giá trị fallback
    • limit: 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ông
    • divisible: 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
    • stringchar*
    • bytechar
    • byteschar*
    • anyvoid*
    • 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 bit
    • success, fail, failure: alias cho mẫu success == 0
    • below, above, upto, downto: toán tử so sánh
    • even, odd, positive, negative, zero, empty: predicate cho số và dữ liệu
    • nil: NULL
    • until: while phủ định
    • elif: else if
    • ifnt, elifnt: biểu thức kiểu if(!...)
    • repeat: alias kiểu Lua cho do
    • done, finish, pass: alias cho break, continue
    • always, forever, loop, indefinitely: biểu thức vòng lặp vô hạn
    • never, 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);
  • Xuất dữ liệu generic có thể dùng với C11+
    • print in ra giá trị được truyền vào
    • println thê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)
  • 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
    • when mở rộng thành chuỗi rỗng để tăng khả năng đọc
    • unless mở rộng thành not, hoạt động như dạng phủ định của when
    • then?
    • otherotherwise:
    • only dùng khi không cần nhánh otherwise
    • otherwhen dù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 for thườ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, var trở 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 var thuộc type
  • 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 đến target
    • inittarget có thể là số nguyên signed hoặc unsigned bất kỳ
    • Nếu init lớn hơn target, bước lặp sẽ giảm
  • forrangeby (var, type, init, target, by)
    • Duyệt vartype được chỉ định từ init đến target với khoảng cách by

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++ new
    • vector (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 đầu
    • delete (...): giống free như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ên close
      • 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
    • defer: đã bị loại bỏ, và hướng dẫn dùng defer mới có sẵn
    • trycatch: cho phép viết xử lý lỗi trong C theo dạng hoa mỹ hơn
    • NOERR, NOERROR: được cung cấp để tiện cho switch-case lỗi

Cách đóng góp

1 bình luận

 
GN⁺ 2024-10-25
Ý 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...

  • 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

    • Có vẻ là nghiêm túc thật. Vì họ nói “Hãy vứt bỏ Lua, Python, JavaScript, Ruby và hàng chục ngôn ngữ khác. Pretty C là ngôn ngữ scripting tối thượng, nhanh như chớp và còn có kiểu mạnh!!”
    • Hoàn toàn không nghĩ tác giả đang rối. Dĩ nhiên, bản thân việc rối cũng không sao
  • 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

    • Nếu mục đích của repo này là gây đau khổ, thì chẳng phải chỉ cần thêm shebang vào file để biến nó thành file thực thi là được sao [0]
      Trước đây tôi từng thấy một bài blog nói chi tiết về cách ./foo hoạt động và cách chạy file ELF. Có vẻ cũng có thể đăng ký chương trình .c theo cùng cách để biên dịch rồi chạy
      [0] https://gist.github.com/jdarpinian/1952a58b823222627cc1a8b83...
    • Thà dùng tcc còn hơn [0]. Nhẹ hơn con quái vật kia rất nhiều, và là C chứ không phải C++
      [0] https://bellard.org/tcc/tcc-doc.html
    • CERN giờ dùng cling (https://github.com/root-project/cling)
    • Ai quy định ngôn ngữ scripting thì không được biên dịch? Và Clang-REPL cũng là một cách khác để làm cho nó thân thiện với REPL
  • “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

    • “Lấy vô số ý tưởng tệ hại rồi nghiền chúng thành một con quái vật ghê tởm.”
      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

    • Văn bản shell, tức các giá trị trong argv hoặ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 OsStringString
    • Dự án này là pretty.c và nói rằng nó nhắm tới “tương thích ngược với C và mọi thư viện”, vậy có lựa chọn nào hợp lý hơn việc giữ chuỗi multibyte của C không?
      https://en.cppreference.com/w/c/string/multibyte
    • Không đồng ý. Nguyên tắc này dựa trên tất cả các tiền đề sau: dữ liệu chuỗi được mã hóa đúng, chỉ có một encoding chuỗi duy nhất (thường là UTF-8), phải kiểm chứng tại thời điểm tạo dữ liệu chuỗi, tuyệt đối không được cắt rời codepoint logic, không được thực hiện thao tác chuỗi trên các byte “không hợp lệ”, encoding đúng là điểm bắt đầu và kết thúc của việc kiểm chứng
      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ì Utf8StringEmailAddress về 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 encoding
    • Còn tùy bạn định nghĩa “chuỗi” như thế nào
      Nế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

    • Vậy là vẫn chưa thấy thứ thật sự rồi. Cứ xem mã nguồn Bourne shell của Unix Seventh Edition là được
      https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd...
      Khó mà tin được đó không phải ALGOL
    • Camel case, getter/setter, interface trong nhiều trường hợp cũng không khác hướng dẫn là mấy. Ví dụ như mã Windows, tất nhiên không phải đến mức mọi biến. Nhiều phần trong thiết kế của Java cũng được sao chép từ C++
    • Tôi từng thấy một codebase tương tự do những người dùng Java quá lâu viết. Thậm chí còn có cả class String tự viết, thực chất chỉ bọc std::string nhưng gắn thêm các method kiểu Java
    • Có vẻ may là họ không dùng MSVC
      https://learn.microsoft.com/en-us/cpp/cpp/property-cpp?view=...
    • Tôi cũng từng gặp kiểu đó, thêm nữa là họ truyền chuỗi như Java bằng 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

    • Đúng vậy, đó là một trong những nguồn cảm hứng
  • Tên các type rất hay, và lựa chọn macro cho hàm dựng sẵn như min cũng hoàn hảo. Tôi cũng thích Len
    Toá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ác
    Nhì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ữ

    • Không cần phải dùng hết mọi thứ. Các dự án của tôi phần lớn chỉ dùng boolean, 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 đáng
  • Nếu ifnt là dành cho if(!...), thì unless có vẻ dễ đọc hơn ifnt

    • Một chủ đề khác để tranh luận về tên gọi là gọi vòng lặp vô hạn for(;;)always
      Tô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 Qt
    • Ngược lại, ifnt khi đọc thành tiếng nghe cũng vui
    • Đúng. Nhưng tôi để dành unless cho đ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