3 ポイント 投稿者 GN⁺ 2024-10-25 | 1件のコメント | WhatsAppで共有
  • Pretty C は C と互換性のある新しいスクリプト言語で、動的型付け、ジェネリックな反復、リソース追跡といった利便機能を C プログラムに追加する
  • 利用方法は pretty.h を 1 つ含めるヘッダオンリーライブラリであり、既存の C ファイルや C ライブラリと一緒に使える
  • 提供機能には stringanynil のような別名、var/let による型推論、print/println のジェネリック出力、equal のジェネリック比較、読みやすい三項式マクロが含まれる
  • 反復とメモリ操作を減らすために foreachforthesefortimesforrangenewvectordeletewithlambdatry/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 は標準ヘッダを含み、よく使う型や演算子の代替表現をすぐ使えるようにする
    • stdbool.h: truefalsebool
    • stdint.h: uint64_t のような固定幅整数型
    • iso646.h: && の代わりに and|| の代わりに or のような代替演算子
  • よく定義されるシンプルなマクロも提供する
    • max, min: 2 つの数の最大値と最小値
    • len: 配列長
    • default: フォールバック値を提供
    • limit: 値の範囲を制限
    • between: 数値が範囲内にあるか確認
    • divisible: 別の数で割り切れるか確認

型と別名

  • Pretty C は C の型に対する短い型エイリアスを提供する
    • stringchar*
    • bytechar
    • byteschar*
    • anyvoid*
    • 整数の短縮形として ucharushortuintulong を提供
  • Lua や Lisp の影響を受けた構文エイリアスも含まれる
    • eqis: ==
    • bitnotbitxor: ビット演算の別名
    • successfailfailure: success == 0 パターン用の別名
    • belowaboveuptodownto: 比較演算子
    • evenoddpositivenegativezeroempty: 数値やデータの述語
    • nil: NULL
    • until: 否定の while
    • elif: else if
    • ifntelifnt: if(!...) 系の表現
    • repeat: Lua 風の do 別名
    • donefinishpass: breakcontinue の別名
    • alwaysforeverloopindefinitely: 無限ループ表現
    • nevercomment: コードを実行しないままコンパイラが解析できるようにする表現

型推論、出力、比較、三項式

  • 型推論は 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 は可読性のための空文字列に展開される
    • unlessnot に展開され、when の否定形として動作する
    • then?
    • otherotherwise:
    • onlyotherwise 節が不要なときに使う
    • otherwhen は次の条件をつなぐときに使う

反復マクロ

  • Pretty C はよく使われる for ループのパターンをマクロとして抽象化する
  • foreach (var, type, length, ...)
    • 配列またはメモリ領域を走査する
    • 各反復で var は該当する配列要素へのポインタになる
    • ポインタなので必要に応じて要素をその場で変更できる
  • forthese (var, type, ...)
    • 渡された可変引数を走査し、各値を type 型の var に束縛する
  • fortimes (var, times)
    • 0 から正の回数まで繰り返す一般的なパターンを簡潔にする
  • forrange (var, init, target)
    • init から target までの数値範囲を走査する
    • inittarget は任意の signed または unsigned 整数にできる
    • inittarget より大きい場合、反復ステップは減少する
  • forrangeby (var, type, init, target, by)
    • type を指定した varinit から target まで by 間隔で走査する

割り当てとブロックユーティリティ

  • 割り当てマクロは C++ の影響を受けた形で一般的なメモリ割り当てパターンを単純化する
    • new (type, ...): C++ の new に近い形で構造体などを生成する
    • vector (length, type, ...): 指定した長さと型の配列を割り当て、初期内容を入れるパターンを簡潔にする
    • delete (...): free と同じだが、C++ 風の名前を好む場合に使える
  • ブロックユーティリティはローカル束縛、遅延実行、ブロック単位の処理のためのマクロを提供する
    • lambda (ret, name, ...): GCC、Clang、または C++ で、ネストした関数、ラムダ、クロージャの形を C で使えるようにする
    • with (var, close, ...): リソース解放関数 close をあらかじめ提供し、動的割り当てオブジェクトやファイルハンドルに便利
      • 欠点として、束縛された varvoid * なので使用前に望む型へ変換が必要なことがある
    • defer: 削除されており、新たに利用可能な defer を使うよう案内される
    • trycatch: C でのエラー処理をより凝った形で書けるようにする
    • NOERRNOERROR: エラー switch-case を扱いやすくするために提供される

コントリビュート方法

1件のコメント

 
GN⁺ 2024-10-25
Hacker News の意見
  • C の中でドメイン特化言語を作る伝統は昔からある
    Stephen Bourne はシェルを ALGOL で書きたいと思っていて、C プリプロセッサを執拗にいじり回し、最終的に自分の好みの言語のように見えるものにした
    https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh...

  • これが冗談なのか、それとも作者が本当に混乱しているのか、誰か説明してほしい
    何も筋が通っていない。「スクリプティング」でもないし、「強い型付け」を導入すると言いながら型には何もしていないし、Lua と Lisp を手本にしたという演算子エイリアスは、そのどちらの言語にも存在しない。なのに明白なパロディにも見えないので、本当に戸惑う

    • 真面目なもののように見える。「Lua、Python、JavaScript、Ruby とその他何十もの言語を捨てろ。Pretty C こそ究極のスクリプティング言語で、稲妻のように速く、強い型付けまで備えている!!」と言っているので
    • 作者が混乱しているとはまったく思わない。もちろん、混乱していること自体は構わない
  • スクリプティング言語だと主張しているが、相変わらずプログラムをコンパイルしなければならない。ブーイングものだ
    CINT(https://root.cern.ch/root/html534/guides/users-guide/CINT.ht...)を付ければ、即時実行も REPL も可能になる

    • このリポジトリの目的が苦痛を与えることなら、ファイルに shebang を追加して実行可能にすればいいのではと思う [0]
      以前、./foo がどう動作し、ELF ファイルがどう実行されるのかを詳しく扱ったブログ記事を見た。同じ方式で .c プログラムを登録し、コンパイル後に実行させることもできそうだ
      [0] https://gist.github.com/jdarpinian/1952a58b823222627cc1a8b83...
    • むしろ tcc を使うほうがよさそう [0]。あの怪物よりずっと軽量だし、C++ ではなく C だ
      [0] https://bellard.org/tcc/tcc-doc.html
    • CERN は今では cling を使っている(https://github.com/root-project/cling
    • スクリプティング言語がコンパイルされてはいけないと誰が決めたのかと思う。それに Clang-REPL も、REPL フレンドリーにする別の方法だ
  • 「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 は OsStringString でこの部分をおおむねうまく扱っている
    • このプロジェクトは pretty.c で、「C およびすべてのライブラリとの後方互換性」を意図していると言っているのだから、C のマルチバイト文字列を維持する以上に理にかなう選択があるのかと思う
      https://en.cppreference.com/w/c/string/multibyte
    • 同意しない。この原則は次の前提をすべて置いている:文字列データは正しくエンコードされている、文字列エンコーディングは一つだけだ(通常は UTF-8)、文字列データは生成時点で検証すべきだ、論理的なコードポイントを切り落とすことは絶対に許されない、「無効な」バイトに対して文字列操作をしてはならない、正しいエンコーディングこそが検証の始まりであり終わりである
      このうち、常に真であるものは一つもない。検証済みのバイト列を、検証を通じてのみ作れる型で包むのは有用だが、そうすると Utf8StringEmailAddress は実質的に同じ種類のものになる。型システムでエンコーディングだけを特別扱いする理由はない
    • 「文字列」をどう定義するか次第だ
      「人間が読めるテキスト」なら、文字列は任意のバイト配列と同じではないという点に同意する。だが多くの言語はその定義を強制していない
  • 以前レビューしなければならなかった C++ コードベースを思い出した。全体が Java で書かれたかのようになっていて、あらゆるものに キャメルケース、すべてのクラス変数に getter/setter、あちこちにインターフェースがあった

    • まだ本物を見ていないだけ。Unix Seventh Edition の Bourne shell ソースを見るといい
      https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd...
      ALGOL ではないとは信じがたい
    • キャメルケース、getter/setter、インターフェースは、多くの場合ガイドラインと大して変わらない。たとえば Windows のコードがそうで、もちろんすべての変数までではないが。Java の設計の多くも C++ からコピーされたものだ
    • Java を長く使いすぎた人たちが書いた、似たようなコードベースを見たことがある。std::string をラップしただけなのに、Java 風のメソッドを付けた独自の String クラスまであった
    • MSVC を使っていなくてよかったようだ
      https://learn.microsoft.com/en-us/cpp/cpp/property-cpp?view=...
    • 私もそういうのを経験したが、それに加えて Java のように文字列を 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 を作った人!ファンです