本ライブラリは「1人が全言語へ同一のAPIを提供する」ことを価値としている。 新しい言語を足すときも、その性質を損なわない手順で進める。
docs/spec/が唯一の正である。
既存の言語実装を写経するのではなく、仕様書を実装する。
既存実装は「仕様書の解釈が合っているか」を照らし合わせるときの参考にとどめる。
C#を基準実装としているので、迷ったらcsharp/src/SpringNBTLibrary/を見る(adr/0002)。
レイヤ1 → レイヤ2 → レイヤ3の順に、各レイヤを完成させてから次へ進む。
| 順 | レイヤ | 仕様 |
|---|---|---|
| 1 | NBT(タグ・MUTF-8・圧縮・SNBT) | 10 / 11 |
| 2 | Anvil(リージョンファイル) | 20 |
| 3 | World(ワールド・チャンク・ブロック) | 30 / 31 / 40 |
各レイヤの完了条件は「適合性検証がそのレイヤのベクタで全件通ること」である。
命名変換規則に従い、その言語で自然な綴りにする。他言語の綴りをそのまま持ち込まない。
論理名 read_file
-> Go なら ReadFile / Kotlin なら readFile / Swift なら readFile
概念・型構成・機能は完全に揃える。変えてよいのは綴りだけである。
これが移植で最初に役立つ成果物である。 次のインターフェースを持つCLIを用意する。
<runner> decode <入力パス> <出力JSONパス> [--format network]
<runner> encode <入力パス> <出力バイナリパス> [--format network]
<runner> snbt <入力パス> <出力SNBTパス> [--format network]
<runner> nbt-list <入力パス> <出力テキストパス> [--format network]
<runner> region-list <入力.mcaパス> <出力テキストパス>
<runner> region-rewrite <入力.mcaパス> <出力.mcaパス>
<runner> chunk-report <入力チャンクnbtパス> <出力テキストパス>
<runner> chunk-edit <入力チャンクnbtパス> <出力nbtパス>
<runner> version
spec/tools/run_conformance.pyのbuild_runners()に、その言語のビルド方法と起動方法を持つRunnerを足し、LANGUAGE_ORDERに言語名を足すと、既存言語との比較が自動で始まる。
実装が無い言語は自動的に検証対象から外れるので、1コマンドずつ増やしながら進められる。
移植のたびに実際に踏んだ箇所を挙げる。
| 箇所 | 内容 |
|---|---|
| MUTF-8 | U+0000はC0 80。BMP外は4バイトではなくサロゲートペア2つ(3+3バイト) |
| 孤立サロゲート | UTF-8に写せない文字列をどう保持するか。Rustは2形態の列挙にした(spec/10 2.3) |
| 小数の表記 | 言語の既定書式は使えない。正準な10進表記を実装する |
-0.0 |
符号を落とす言語がある(JavaScriptのtoExponentialなど) |
i64 |
64bit整数を持たない言語では専用の型が要る(adr/0007) |
| 論理右シフト | BitStorageでは符号なしとして扱う。算術シフトだと上位ビットが埋まる |
| 再帰の深さ | 深さ512を安全に扱えるか確かめる(spec/00 5.1) |
| キーの並び | Compoundは挿入順を保持する。ハッシュマップで実装すると書き戻しが一致しない |
dataとpaletteの順 |
実データはdataが先(spec/31 4.1) |
適合性検証はバイト列の一致を見るが、APIの振る舞いまでは見ない。 既存言語のテストと同じ検証項目を用意する。 テストファイルはレイヤごとに分ける。名前の付け方はその言語の慣習に合わせる(次はRustの例)。
rust/tests/nbt.rs NBT レイヤ
rust/tests/anvil.rs Anvil レイヤ
rust/tests/world.rs World レイヤ
各言語のテストファイルは、見比べれば項目の対応が付くようにしてある。
spec/tools/extract_api.pyに抽出器を足し、EXTRACTORSに登録する(抽出器は、その言語のソースから公開型・公開メンバを拾う)spec/tools/extract_api.pyのLANGUAGE_ORDERと、spec/tools/check_docs_sync.pyのLANGUAGE_LABELSに言語を足すpython3 spec/tools/check_docs_sync.py --writeでAPI対応表を再生成するdocs/features.mdに列を足し、spec/tools/check_docs_sync.pyのFEATURE_COLUMNSにも同じ位置に言語を足すdocs/getting-started/<言語>.mdを書く
5が済むまで「対応した」とは言わない。 実装があってもドキュメントから辿れなければ、利用者には存在しないのと同じである。
最後に、自分のMinecraftワールドで検証する。 合成のテストベクタでは、実データほど網羅できない。
python3 spec/tools/scan_world.py "<ワールドのパス>"このツールが使うのはPython実装だけである。
run-conformance.shも実ワールドを扱わない(受け付ける引数は--onlyと--generate-expectだけ)。
新しい言語では、実ワールドから取り出したファイルを3章の検証ツールに通し、既存言語の出力と比べて確かめる(spec/90の検証実績)。