Pretty.c - C 上で動作するスクリプティング
(github.com/aartaka)- Pretty C は C と互換性のある新しいスクリプト言語で、動的型付け、ジェネリックな反復、リソース追跡といった利便機能を C プログラムに追加する
- 利用方法は
pretty.hを 1 つ含めるヘッダオンリーライブラリであり、既存の C ファイルや C ライブラリと一緒に使える - 提供機能には
string、any、nilのような別名、var/letによる型推論、print/printlnのジェネリック出力、equalのジェネリック比較、読みやすい三項式マクロが含まれる - 反復とメモリ操作を減らすために
foreach、forthese、fortimes、forrange、new、vector、delete、with、lambda、try/catchなどのマクロベース構文を提供する - 一部機能には GCC、Clang、C23+、C11+ などの条件があり、
withで束縛された変数はvoid *のため使用前に型変換が必要な場合がある
Pretty C の目標と使い方
- Pretty C は C と互換性のあるスクリプト言語であり、C プログラムに動的型付け、ジェネリックな反復、リソース追跡、さまざまな文法上の利便機能を追加する
- 既存の C および C ライブラリとの後方互換性を維持する
- Lua、Python、JavaScript、Lisp から着想を得ている
- インストールはリポジトリをクローンするか、
pretty.hファイルだけをコピーして利用できる#include "pretty.h"で任意の C ファイルから取り込める- include パスに Pretty C のパスを指定しても利用できる
基本の同梱ヘッダとシンプルなマクロ
- Pretty C は標準ヘッダを含み、よく使う型や演算子の代替表現をすぐ使えるようにする
- よく定義されるシンプルなマクロも提供する
max,min: 2 つの数の最大値と最小値len: 配列長default: フォールバック値を提供limit: 値の範囲を制限between: 数値が範囲内にあるか確認divisible: 別の数で割り切れるか確認
型と別名
- Pretty C は C の型に対する短い型エイリアスを提供する
stringはchar*byteはcharbytesはchar*anyはvoid*- 整数の短縮形として
uchar、ushort、uint、ulongを提供
- Lua や Lisp の影響を受けた構文エイリアスも含まれる
eq、is:==bitnot、bitxor: ビット演算の別名success、fail、failure:success == 0パターン用の別名below、above、upto、downto: 比較演算子even、odd、positive、negative、zero、empty: 数値やデータの述語nil:NULLuntil: 否定のwhileelif:else ififnt、elifnt:if(!...)系の表現repeat: Lua 風のdo別名done、finish、pass:break、continueの別名always、forever、loop、indefinitely: 無限ループ表現never、comment: コードを実行しないままコンパイラが解析できるようにする表現
型推論、出力、比較、三項式
- 型推論は GCC、Clang または C23+ で利用できる
- 例として
var t = time(0);、let lt = localtime(&t);、local at = asctime(lt);の形を提供する
- 例として
- ジェネリック出力は C11+ で利用できる
printは渡された値を出力するprintlnは出力後に改行を追加する
- ジェネリックな等価比較も C11+ の機能として提供される
equal("NA", line)のような文字列比較equal(0.3, 0.2 + 0.1)のような比較例が含まれる
- 三項演算子はテキストベースのマクロでより読みやすくできる
whenは可読性のための空文字列に展開されるunlessはnotに展開され、whenの否定形として動作するthenは?otherとotherwiseは:onlyはotherwise節が不要なときに使うotherwhenは次の条件をつなぐときに使う
反復マクロ
- Pretty C はよく使われる
forループのパターンをマクロとして抽象化する foreach (var, type, length, ...)- 配列またはメモリ領域を走査する
- 各反復で
varは該当する配列要素へのポインタになる - ポインタなので必要に応じて要素をその場で変更できる
forthese (var, type, ...)- 渡された可変引数を走査し、各値を
type型のvarに束縛する
- 渡された可変引数を走査し、各値を
fortimes (var, times)- 0 から正の回数まで繰り返す一般的なパターンを簡潔にする
forrange (var, init, target)initからtargetまでの数値範囲を走査するinitとtargetは任意の signed または unsigned 整数にできるinitがtargetより大きい場合、反復ステップは減少する
forrangeby (var, type, init, target, by)typeを指定したvarをinitからtargetまでby間隔で走査する
割り当てとブロックユーティリティ
- 割り当てマクロは C++ の影響を受けた形で一般的なメモリ割り当てパターンを単純化する
new (type, ...): C++ のnewに近い形で構造体などを生成するvector (length, type, ...): 指定した長さと型の配列を割り当て、初期内容を入れるパターンを簡潔にするdelete (...):freeと同じだが、C++ 風の名前を好む場合に使える
- ブロックユーティリティはローカル束縛、遅延実行、ブロック単位の処理のためのマクロを提供する
lambda (ret, name, ...): GCC、Clang、または C++ で、ネストした関数、ラムダ、クロージャの形を C で使えるようにするwith (var, close, ...): リソース解放関数closeをあらかじめ提供し、動的割り当てオブジェクトやファイルハンドルに便利- 欠点として、束縛された
varはvoid *なので使用前に望む型へ変換が必要なことがある
- 欠点として、束縛された
defer: 削除されており、新たに利用可能なdeferを使うよう案内されるtryとcatch: C でのエラー処理をより凝った形で書けるようにするNOERR、NOERROR: エラー switch-case を扱いやすくするために提供される
コントリビュート方法
- 関数追加時には Linux Kernel Style Guide に従うよう求めている
- コミット前に
make indentの実行を推奨
- コミット前に
- A’s Commit Message Guide に従うのが望ましいが必須ではない
1件のコメント
Hacker News の意見
C の中でドメイン特化言語を作る伝統は昔からある
Stephen Bourne はシェルを ALGOL で書きたいと思っていて、C プリプロセッサを執拗にいじり回し、最終的に自分の好みの言語のように見えるものにした
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/
これが冗談なのか、それとも作者が本当に混乱しているのか、誰か説明してほしい
何も筋が通っていない。「スクリプティング」でもないし、「強い型付け」を導入すると言いながら型には何もしていないし、Lua と Lisp を手本にしたという演算子エイリアスは、そのどちらの言語にも存在しない。なのに明白なパロディにも見えないので、本当に戸惑う
スクリプティング言語だと主張しているが、相変わらずプログラムをコンパイルしなければならない。ブーイングものだ
CINT(https://root.cern.ch/root/html534/guides/users-guide/CINT.ht...)を付ければ、即時実行も REPL も可能になる
以前、
./fooがどう動作し、ELF ファイルがどう実行されるのかを詳しく扱ったブログ記事を見た。同じ方式で.cプログラムを登録し、コンパイル後に実行させることもできそうだ[0] https://gist.github.com/jdarpinian/1952a58b823222627cc1a8b83...
[0] https://bellard.org/tcc/tcc-doc.html
「C 開発者に糖尿病性心臓発作を起こさせるほどの構文糖を提供する。」
この文句は本当にいい
CNoEvil[0] と ogw[1] に追加すべきものがいくつか見える。このプロジェクトが数か月ごとに再浮上するたび、いつも増えている気がする
[0] https://git.sr.ht/~shakna/cnoevil3/
[1] https://git.sr.ht/~shakna/ogw
心の底から愛してしまう一文だ
これは Python 2 と同じ過ちを犯している。文字列とバイトは同じ型ではないし、そのように扱うべきでもない
argv内の値や子プロセス出力の型は何だと考えるべきなのだろうそこには任意のゴミ値が入る可能性があるので正しい UTF-8 文字列ではないが、awk や grep のようなツールはどこでも使われている
文字列とバイトは同じ一般的な型と見なせるし、ただ正しい UTF-8 バイトに内部的に別の型を与えるのが有用な場合があると思う。Rust は OsString と String でこの部分をおおむねうまく扱っている
https://en.cppreference.com/w/c/string/multibyte
このうち、常に真であるものは一つもない。検証済みのバイト列を、検証を通じてのみ作れる型で包むのは有用だが、そうすると
Utf8StringとEmailAddressは実質的に同じ種類のものになる。型システムでエンコーディングだけを特別扱いする理由はない「人間が読めるテキスト」なら、文字列は任意のバイト配列と同じではないという点に同意する。だが多くの言語はその定義を強制していない
以前レビューしなければならなかった C++ コードベースを思い出した。全体が Java で書かれたかのようになっていて、あらゆるものに キャメルケース、すべてのクラス変数に getter/setter、あちこちにインターフェースがあった
https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd...
ALGOL ではないとは信じがたい
std::stringをラップしただけなのに、Java 風のメソッドを付けた独自の String クラスまであったhttps://learn.microsoft.com/en-us/cpp/cpp/property-cpp?view=...
f(String *)で渡していて、C++ コードがf(new String("Hello")みたいになっていたこれが面白いなら libcello.h も気に入るかもしれない
https://www.libcello.org
型名が良く、
minのような組み込み関数マクロの選び方も完璧。Lenも気に入った名前付きのブール演算子は少しやりすぎかもしれないが、試す価値はあるし、三項演算子はもともと読みにくいので改善の余地が大きい。すべてのループ定義に完全に納得しているわけではないが、一部は理にかなっている。リソース追跡は印象的で、
for...形式は少し不格好なので別の名前を付けてもよさそうだ全体としてかなり堅実な試みだ。設計には 8/10 くらいを付けたい。C マクロでスケッチしたやり方が本当にエレガントで、実際に良いコードのように見える。自分で使うかは分からない。新しい言語だし、すでに C が好きだからだ。それでも実装方法が非常に明快なので、人々が C を学び、言語設計について考える助けになるかもしれない
len()、max/min、それにいくつかの 演算子マクロだけを使っている。まだ他のおいしい機能があまり必要になっていないからだ。だから演算子マクロをいくつかだけでも試してみるといいif(!...)のためのifntなら、unless のほうがifntより読みやすそうだfor(;;)ループをalwaysと呼ぶのもある他の言語では
loopを見たことがある。だが Qt はforeverと呼んでいて、これは本当にきれいだ。いかにも Qt らしくもあるifntは声に出すと面白いunlessは三項条件用に取っておいた。そちらのほうが何にせよ便利だからだあ、ちょっと待って、John Tromp だ。BLC を作った人!ファンです