桁であって符号点ではない

日本語セルの小さな商品テーブルを印字してみると、以前の Unicode の仕事は正しい第一歩であり、この仕事には間違った道具だと分かった。符号点を数えると 5 文字の挨拶は幅 5、だが端末は 10 桁で描く。整形には表示幅 —— East Asian Width —— が要り、符号点数ではない。修正は純ライブラリ Mere の wcwidth-lite で、UTF-8 を素の除算とレンジの表で decode し、ライブラリコードの住処ゆえに 4 backend に一斉着地した。

mereunicodestdlibbackendslanguage-design

以前の話が言語に文字列の符号点ビューを与え、日本語の単語のバイトでなく文字を数えられるように した。この probe は、そのビューが —— 正しいものであるにもかかわらず —— なお間違った測定である 場所を見つけた。そしてこの区別は、テキストを扱うあらゆる言語がいずれ迫られるものだ。

5 文字、10 桁

probe は小さかった: 名前列のある商品テーブルを、ASCII と日本語のエントリを混ぜて印字し、列を 揃える。符号点数の関数で pad すると、行が目に見えて不揃いになった —— 日本語の行が短く座った。 理由は、端末が符号点あたり 1 桁を描かないことだ。CJK 文字、全角形、絵文字は各々 2 桁で描き、 結合記号は 0 桁で描く。5 符号点の挨拶は端末 10 桁を占め、符号点数は —— 「何文字か」には正しいが —— 「これは何桁を取るか」には端的に間違った数だ。これは 2 話前のバイト対文字列の区別と同じ教訓の 一段上だ: バイト、符号点、表示桁は同じ文字列の 3 つの異なる測定であり、プログラムは実際に意味する 方を求めねばならない。整形が意味するのは桁だ。

wcwidth を、ライブラリで、全 backend に

修正は表示幅の関数だ: East Asian Width、wcwidth-lite の流儀で —— CJK、全角、絵文字のレンジは 2、 結合記号は 0、それ以外は 1 と数える —— に、その上に建てた左右の pad 関数。これを一話に値させる のは、その住処だ。3 つとも普通の prelude 関数で、言語そのもので書かれている: UTF-8 は素の除算と 剰余で decode され(2 バイト列は 1 つの算術式)、幅表は主要ブロックに対する 12 個ほどのレンジ チェックだ。新しい builtin は無く、backend 別の C も IR も wasm も無い。そして prelude はどこでも 走らねばならないので、compile した瞬間に interpreter、C、LLVM、wasm で同一に存在した —— Part 全体の 4 方向分岐というテーマが裏返る。ライブラリコードは構成上分岐できないからだ。幅表は Unicode 表が 使う 16 進の境界でなく 10 進のレンジで書かれていて、それはわずかに痛く、そして 16 進で書きたかった 数を 10 進で書く 2 本連続の probe だ —— いまや直すに足る大きさの papercut で、それこそ最終話が やることだ。整形テーブルの example は ASCII、日本語、絵文字、半角カナの混在を、実際に揃う罫線で 描く、interpreter でも C backend でも同じく。

← Back to Mere: 言語を作る