summaryrefslogtreecommitdiff
path: root/chapter25
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-07 10:14:20 -0700
committerGitHub <noreply@github.com>2026-10-07 10:14:20 -0700
commit8a20d5238855de5479d84e7fd8e84561844e2dc4 (patch)
treebbeab7de6ac8b556911d117989ab4bbc0324ecb5 /chapter25
parent81b654d76444d747fdf1d053760e507b50f26e18 (diff)
parentb2862f980f3d78012066ef1cc09d4ad58f886c0f (diff)
downloadSimple-8a20d5238855de5479d84e7fd8e84561844e2dc4.tar.gz
Simple-8a20d5238855de5479d84e7fd8e84561844e2dc4.zip
Merge pull request #262 from com-junkawasaki/docs/japanese-chapters
docs: add Japanese translations for all chapters
Diffstat (limited to 'chapter25')
-rw-r--r--chapter25/README.ja.md123
-rw-r--r--chapter25/README.md10
-rw-r--r--chapter25/ROADMAP.ja.md451
-rw-r--r--chapter25/ROADMAP.md2
-rw-r--r--chapter25/docs/18-grammar.md129
-rw-r--r--chapter25/docs/25-grammar.ja.md175
-rw-r--r--chapter25/docs/25-grammar.md175
-rw-r--r--chapter25/module.ja.md275
-rw-r--r--chapter25/module.md2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java9
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/README.ja.md50
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/README.md2
12 files changed, 1266 insertions, 137 deletions
diff --git a/chapter25/README.ja.md b/chapter25/README.ja.md
new file mode 100644
index 00000000..cfb953c6
--- /dev/null
+++ b/chapter25/README.ja.md
@@ -0,0 +1,123 @@
+# 第25章: モジュール、分割コンパイル、SSA 構築
+
+[English](README.md) | 日本語
+
+この文書は [原文](README.md) の日本語訳です。
+
+[前の章: 第24章](../chapter24/README.ja.md) |
+[章の一覧](../README.ja.md#各章)
+
+この章では Simple のソースファイルを再利用可能なオブジェクトファイルへコンパイルし、その型と理想 IR を後のコンパイルに読み込み、別に構築したシステムライブラリとネイティブプログラムをリンクします。また、不完全な情報を扱うため、パーサー、型、メモリ SSA を大幅に再設計します。
+
+これらが現在のスナップショットの内容です。独立した正しさの修正を先に以前の章へ移します。大きな SSA 再設計を以前へ移すことや、この章の分割は延期します。[バックポート待ち一覧](../docs/chapter-backports.md) を参照してください。
+
+第21〜24章のメモリ処理はここにも引き継ぎます。Load 検索は読み取りをループ外へ移す前にマージの全枝を証明し、Phi の因数分解はメモリ破壊を確認し、スケジューリングはメモリ集合体を通じてエイリアスをたどります。検索は `EscapeNode` から非公開または公開メモリへ進み、未解決の Store エイリアスを待ちます。配列長の読み取りには要素の境界検査がなく、配列要素の読み取りは制御を保持します。整数値の Phi は、既存の型付き変換機構で、ループの拡大を通じてもロード幅を保持します。New は引き続き非公開のコンストラクタメモリを生成し、公開は Escape が扱います。以前の章の割り当て入力設計は、これを置き換えません。
+
+[直線的な履歴](https://github.com/SeaOfNodes/Simple/tree/linear) で [この章](https://github.com/SeaOfNodes/Simple/tree/linear-chapter25) を読み、[第24章と比較](https://github.com/SeaOfNodes/Simple/compare/linear-chapter24...linear-chapter25) することもできます。
+
+この章の[完全な言語文法](docs/25-grammar.ja.md)はこちらです。
+
+## コンパイル単位と名前
+
+`CompUnit` はソースまたはオブジェクトファイルを表します。`ParseAll` は依存関係を発見し、必要なソースを解析し、以前コンパイルした単位を読み込みます。ソースツリーとビルドツリーは対応する相対パスを使い、クラス名はドット区切りのパスを使います。たとえば `a/b/foo.smp` は名前空間 `a.b.foo` を供給します。
+
+各単位は Start/Stop ノードを持ちます。関数がコンパイル単位に所有されることと、未知の外部呼び出し側を表す辺とは別です。出力のためにコードを保持することが、任意の呼び出し側から到達できることを意味してはいけません。
+
+ドライバは、プロジェクトルートの `--root`、出力の `-o`、外部 Simple オブジェクト検索パスの `-L` を受け付けます。検索パスにはオブジェクトファイルやディレクトリを指定できます。コンパイラはネイティブリンク用の外部 C シンボルも解決します。`_` で始まる名前は非公開性を制御し、公開到達性は外側の名前にも依存します。
+
+明示的な数値型の C 宣言は、可変のネイティブ記憶領域へ直接束縛します。
+
+```java
+i32 counter = "C";
+counter += 1;
+return counter;
+```
+
+ネイティブリンカが `counter` を解決します。読み書きはその C 変数にアクセスし、宣言はコピーも初期化もしません。繰り返した宣言とモジュールインポートは同じ記憶領域エイリアスを共有します。整数と浮動小数点データの束縛をサポートし、ポインタと集合体のデータ束縛はまだサポートしません。外部フィールドは記憶領域フラグを持ち、宣言元クラス内でバイトを占めません。配置はフィールドオフセットを `ExternOffset` シンボルへ解決し、命令選択はクラス先頭+オフセットをネイティブシンボルアドレスへ置き換えます。ネイティブオブジェクト出力は既存の AMD64 ELF ライタを使い、ARM と RISC-V はメモリ内リンカを通じてこれらのアドレスをサポートします。スレッドローカル変数と C マクロにはアクセサ関数が必要です。システムライブラリは小さなネイティブランタイムアクセサを通じ、`libc.errno()` を公開します。
+
+## オブジェクトファイルは理想 IR を含む
+
+ELF ライタは、ネイティブコードと、正規化された型、理想グラフノード、依存関係を含む `.simple` セクションを保存します。リーダはそのグラフを再構築し、インポートした関数をインライン化などの最適化に参加させます。インライン化せず残った呼び出しは、ネイティブリンクで解決します。
+
+`GlobalBits` はファイルの同一性とファイルごとのインデックスを、エイリアス、関数対象、戻り位置の局所的な密なインデックスへ対応付けます。独立したコンパイルの等しい局所番号を、等しい同一性と取り違えてはいけません。読み込み後に型を閉じて更新し、単位をまたぐ循環参照も扱います。
+
+シリアライズは、宣言型を精密化されたフロー型とは別に保持します。意味を持つすべてのノード/型のフィールドには、対応する読み書きサポートが必要です。ソースにある高コストなシリアライズの全単射検査は現在無効で、より大きな増分再ビルドテスト `testModule0` は無視されています。どちらも有効な検証保証ではありません。
+
+外部フィールドのフラグと記号的オフセットは、シリアライズされた配置を変更します。マジックは `C0DE` のままで、独立した形式バージョンはまだありません。このコンパイラへインポートする前に、すべての Simple オブジェクトファイルを再ビルドしてください。
+
+## 型が完成する前の SSA 構築
+
+Simple は引き続き AST を介さず直接 SSA に解析します。前方参照により、解析時点では式の最終的な型が利用できない場合があります。そのため構文と解決済みの字句的束縛でトポロジーを選び、後から型でグラフを精密化、最適化、検証します。
+
+- 算術ノードは整数モードか浮動小数点モードかの選択を延期し、SCCP 後の再帰的グラフ成分を通じた解決も扱います。
+- Call は呼び出し先の種類が決まる前に、構文上のレシーバスロットを保持します。記号的なフィールド情報は配置依存の判断を延期します。
+- Load と通常の Phi は、パーサーが供給した下限型を持たなくなります。
+- `TypeScalar` は整数、浮動小数点、メモリポインタ、関数ポインタ値を、大域的な制御/メモリのボトムと区別して表します。
+- `TypeStruct._open` は不完全なフィールド集合を記述し、`_fref` は権威ある定義の欠如を記録します。フィールドを発見したことは、型を定義したことではありません。
+- ガードは入力の種類がわかる前に、分岐が証明したゼロ/非ゼロの事実を運びます。`FunPtrNode` は関数の戻りグラフへの明示的な辺を保持します。
+
+ドライバはシリアライズとマシン選択の前に、解析、悲観的反復、SCCP、最終型検査を行います。単純でないインライン化は、常に即時のピープホール最適化として行うのではなく、反復と調整します。
+
+## メモリと構築
+
+パーサーは通常のバルクメモリを保持します。最適化器は `MemMerge`、`MemPhi`、`BulkMemPhi` で正確なエイリアスを復元します。バルク Phi は、並列の正確な Phi で表す除外分以外のすべてのエイリアスを覆います。各スライスはちょうど1回だけ覆われなければなりません。
+
+ユーザーコンストラクタは次のように宣言し、呼び出します。
+
+```java
+struct Box {
+ i64[] values;
+ new Box = { i64[] initial -> values = initial; };
+};
+return new Box(new i64[1]);
+```
+
+宣言で割り当て補助関数を生成します。これは割り当てを行い、隠れた `<init>` とユーザーコンストラクタを順に呼び、`EscapeNode` を通じて初期化済みの非公開メモリを公開します。パーサーの `Var` メタデータは、早すぎるフィールド読み取りやコンストラクタ出口での未初期化を検出します。Store の幅は保存する値ではなく、対象の宣言から得ます。
+
+ファイルレベルのコードは、インスタンス初期化とは異なるクラス初期化子(`<clinit>`)で表します。古い [モジュール設計ノート](module.ja.md) と [ロードマップ](ROADMAP.ja.md) には、特に初期化順序に関する提案や代替案があります。実装済み規則すべての仕様ではありません。
+
+## システムライブラリと例
+
+以前の単一の `sys.smp` ライブラリは、libc 束縛、I/O、文字、スキャナ、配列、伸長可能なバッファ、ビット集合の単位に整理されました。ネイティブドライバは実際のプログラム引数をサポートします。`docs/examples` の例には Bubble Sort、Capitalize、Dijkstra、FileIO があります。
+
+`Chapter25Test` は、意図的にインライン化せず残す Hello World 呼び出しを含め、ライブラリ読み込みとネイティブリンクを検証します。そのテストは `sys.o` とリンクする前に、クライアントの外部シンボルを確認します。ほかのテストは前方コンストラクタ、呼び出し経由のフィールド更新、リテラルのエスケープ、縮小したコンパイラ失敗を検証します。独立した回帰テストは以前の章へ移す候補です。
+
+## ビルドと確認
+
+このディレクトリで `make lib` は Java テスト依存関係を用意し、`make tests` は章の生のスイート、独立スイート、システムテスト、ファザー回帰テストを実行します。`make release` はコンパイラ jar とネイティブライブラリ成果物を構築し、`make tags` はエディタ用タグを構築します。ネイティブツールと選択した CPU/ABI は環境と一致する必要があります。Makefile は現在、既定で x86-64/win64 を使います。
+
+シリアライズ IR は `C0DE` ヘッダを使います。ポインタ型は独立した読み取り専用アクセスビットを含みます。形式にはまだバージョン番号がないため、形式を変更したらオブジェクトファイルを再ビルドしてください。現在、後方互換性は保証していません。
+
+コンパイラ変更は `sys.o` のシリアライズ IR とネイティブコードの両方を無効にする場合があります。リンクしたプログラムのテスト結果を解釈する前に再ビルドしてください。ソースだけの部分集合は、この章の完全な `make tests` と同等ではありません。
+
+## RegAlloc の改良: 生存区間の面積と分割コスト
+
+第24章はループ Phi の高頻度辺を分割する前に、低頻度のコピーを試しました。この章ではさらに、スピルがスケジュール済みコードのどれだけをレジスタ圧力から解放するかを推定し、新しいコピーの推定コストで割ります。callee-save 区間は関数全体を覆います。複製可能な定数は使用の近くで再構築できます。ループ内のコピーは高コストなので、`8^loopDepth` で重み付けします。特に多数の使用を持つ値については、低コストな近似です。
+
+複数の固定レジスタ使用を直接分割する規則や、一部の複数定義区間をより深く分割する規則など、第25章のほかの衝突処理はここに残します。サイズのしきい値はヒューリスティックであり、最初の割り当て器へバックポートすべき一般的不変条件ではありません。共有される合法性修正は、引き続き最も早い該当章から始めます。最終監査は第20〜25章の固定隣接ノードの色バイアス、第20〜21章の狭いストアマスク、第21〜25章の ARM レジスタバンク/浮動小数点メモリエンコーディングを修正しました。
+
+第21章の隣接コピー転送も引き継ぎます。次の命令だけで使うコピーは、オペランドがコピー元レジスタを受け入れ、命令の結果に結び付けられていなければ除去します。
+
+以下は、必須の `cs` フィールドに明示的なコンストラクタを与えて保存済み String ソース3件を修正した後の、完全な2026-10-02再実行です。過去の212項目すべてが成功します。これはフィクスチャ調整と最近のコンパイラ修正後の新たな基準値を確立します。以前の監査との割り当て器だけの比較ではありません。[バックポート記録](../docs/chapter-backports.md) に古い部分比較を残しています。
+
+`make spill-stats` を実行してください。各行は **第25章のコンパイラ** を使います。最初の5行は以前の212のプログラム/対象項目を、この章の構文とライブラリ構成に適応して保持しています。[フィクスチャノート](src/test/java/com/seaofnodes/simple/spill/README.ja.md) にその調整と測定の制限を記載しています。最適化器のシードは123です。最後の行には、既存シードで記録した `Chapter25Test` の13割り当てと、ドライバのシード456での新規システムライブラリコンパイルを含みます。`sys.o` を再利用して、この大きな処理対象を測定から消してはいけません。この結果は Windows x86-64 のものです。以前の群には RISC-V/ARM SystemV と x86 SystemV も含みます。報告器は対象ごとの合計も表示します。
+
+| プログラム群 | コンパイル数 | 分割/移動数 | ループ重み付き数 |
+|---|---:|---:|---:|
+| 第20章 | 39 | 375 | 606 |
+| 第21章 | 52 | 479 | 1,172 |
+| 第22章 | 24 | 103 | 103 |
+| 第23章 | 30 | 129 | 353 |
+| 第24章 | 67 | 696 | 1,410 |
+| 第25章 | 14 | 786 | 1,815 |
+| **合計** | **226** | **2,568** | **5,459** |
+
+`_spills` はレジスタ間移動を含む残存 SplitNode を数え、`_spillScaled` はループ重みを適用します。どちらもメモリアクセスだけを測るものではありません。古い群はレジスタ合法性確認を伴う割り当ての再実行で、ネイティブハーネスの再実行ではありません。`Chapter25Test` の23テストすべてが、ネイティブ/結果確認を含め、スピル期待値の失敗なしに成功します。継承したエミュレータテストを含む章全体には `make -j 4 tests` を使ってください。
+
+### 測定についてのコメント
+
+以前の割り当て器監査では、一様な改善は見つかりませんでした。そのコンパイラのほかの部分を変えず、第24章の順位付けに置き換えると、221のクライアントコンパイルで5移動減りました(2,211/4,724に対し2,206/4,719)。しかしシステムライブラリを8ラウンド以内に割り当てられませんでした。したがって不完全な比較であり、有効なスイート全体の低い合計ではありません。既存の面積/コスト順位付けを保持し、ラウンド上限と正しさの確認も変えません。その監査でシステムライブラリ自体は386移動/855重み付き移動で、現在の再実行は373/828です。
+
+複数使用を持つすべての複製可能定数を優先する試行は、それらのクライアントで以前の順位付けより1移動減りましたが、やはり新規ライブラリビルドに失敗し、却下されました。これが、集計統計と完全なコンパイル・実行確認の両方が重要な理由です。有利な小計だけでは変更を正当化できません。
+
+その以前の監査では、過去の群の合計が第24章の1,332/2,956から、第25章の1,814/3,753へ増えました。第25章はクラス初期化子、モジュール所有権、異なる脱出解析、構文/ライブラリの調整を追加します。これらは異なるマシングラフなので、増加を割り当て器の品質だけに帰することはできません。章の表から結論を出す前に、1つのコンパイラと固定した入力集合でヒューリスティックを比較してください。
diff --git a/chapter25/README.md b/chapter25/README.md
index 7ab7c89e..bfe48809 100644
--- a/chapter25/README.md
+++ b/chapter25/README.md
@@ -1,5 +1,7 @@
# Chapter 25: Modules, Separate Compilation, and SSA Construction
+English | [日本語](README.ja.md)
+
[Previous: Chapter 24](../chapter24/README.md) |
[Chapter index](../README.md#chapters)
@@ -26,6 +28,8 @@ You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linea
in the [linear history](https://github.com/SeaOfNodes/Simple/tree/linear) and
[compare it with Chapter 24](https://github.com/SeaOfNodes/Simple/compare/linear-chapter24...linear-chapter25).
+Here is the [complete language grammar](docs/25-grammar.md) for this chapter.
+
## Compilation units and names
`CompUnit` represents a source or object file. `ParseAll` discovers dependencies,
@@ -156,9 +160,9 @@ regressions. `make release` builds the compiler jar and native library artifacts
`make tags` builds editor tags. Native tools and the selected CPU/ABI must match
the environment; the Makefile currently defaults to x86-64/win64.
-Serialized IR now uses the `C0D2` header: pointer types include an independent
-read-only access bit. Older object files must be rebuilt; the reader rejects
-their old header rather than interpreting their types with the new layout.
+Serialized IR uses the `C0DE` header; pointer types include an independent
+read-only access bit. The format is not versioned yet, so rebuild object files
+after format changes. Backward compatibility is not currently supported.
Compiler changes can invalidate both serialized IR and native code in `sys.o`.
Rebuild it before interpreting linked-program test results. A source-only subset
diff --git a/chapter25/ROADMAP.ja.md b/chapter25/ROADMAP.ja.md
new file mode 100644
index 00000000..222f228a
--- /dev/null
+++ b/chapter25/ROADMAP.ja.md
@@ -0,0 +1,451 @@
+# ロードマップ
+
+[English](ROADMAP.md) | 日本語
+
+この文書は [ROADMAP.md](ROADMAP.md) の日本語訳です。原文の設計案と未解決の問いを、そのまま設計案として記しています。
+
+## 分割コンパイル
+
+未知の前方参照をどう扱うか? H-M は使わないので、単一化も行わない。
+関数以外の前方参照を認めないことにする……関数なら、定義した時点ですぐにシグネチャを提供できる。
+
+案 A:FRefNode を削除し、すべてのバグを直す。
+案 A の問題:相互再帰を使えるようにしたい。
+例:`val even = { int x -> x ? odd(x-1) : true; }; val odd = { int x -> x ? even(x-1) : false; };`
+
+案 B:型は順序どおりに定義する必要があるが、それ以外の関数には順序を要求しない。
+`new T` ならエラーにできるが、`fref.field`、`fref()`、`fref op fref`、`fref[]` ではできない。
+
+あらゆる場所で Type.BOTTOM をパーサーの要件に合わせて「引き上げる」必要がある。
+FRef からフィールドをロードするには、FREF を FIELD のある TSTRUCT への TMP に引き上げる必要がある……。
+実際には、FREF が正しくなると局所的に仮定するだけだ。型は任意のコードから来るので、
+アクセスできず、引き上げられない。TMP は nullable かもしれない(後でエラーにすべき)。
+TSTRUCT は無名にでき、無名になるだろう。これは、すべての TSTRUCT を無名にすることを意味する。
+
+問題は、未知・遠隔・外部・前方の参照にある。
+
+FRef を*少しでも*認めると、すべての処理が FRef 型に遭遇することを考慮しなければならない。
+同じスコープの関数で認めると、上の odd/even の例が使える。
+同じスコープの構造体で認めると、相互再帰的な構造体が使える。
+スコープの外へ昇格させる際には外部の検索を試み、見つからなければ失敗する。
+
+任意の使い方の例:`sys.fld`。`sys` の検索に失敗したら、次のどちらかを推測しなければならない。
+前方参照で問題なく、間もなく定義されるのか……それとも今すぐ外部を検索するのか。
+さらに、使い方によっては問題の発覚が遅れる。
+```java
+// Extern or Future `sys` being returned.
+val foo { -> return sys; }
+
+// foo() returns FRef, so Bottom, so the `.bar` has no type
+// to do field lookups
+val main { arg -> foo().bar }
+
+// HERE: Parser finds `sys` OR NOT?!?!?!
+val sys??? = user_stuff....;
+
+```
+
+行き着く先は、パーサーが一切エラーを出さず、盲目的な AST のように SSA を構築し、
+不適切な型だらけのグラフを結線するだけになることだ。
+必要なこと:パーサーを導く型をなくす! 構文だけを使う!
+
+(明らかな小さな問題:浮動小数点数へ拡張するかどうか。)
+修正:たとえば整数演算を挿入する。両方の入力が isa-Int であり、isa-Float に引き上げられなくなるまでは、
+戻り値の型を Int と Float の MEET とする。
+
+メソッド呼び出しで起こり得る問題:
+`UNKNOWN_STRUCT_BOT.UNKNOWN_FIELD ( ARGS ); // Insert SELF OR NOT?`
+— SELF を挿入するか?
+修正案:常に SELF 引数があると仮定し、静的と証明できる場合は null にする。
+最適化:定数パラメータをシグネチャから削除するか? *定数*パラメータは、
+*パーサー*の時点で定数でなければならない。そうでなければ未知の呼び出し元がないことが必要になる
+(したがって局所的な最適化になる)。
+修正案:SELF 引数を*最初*ではなく*最後*に置くと、不要になった場合に削除しやすいか?
+
+Load は ConFldOff を捨て、名前を保持する。
+Load はスケーリング前の添字を保持し、添字入力を持つ。構造体ではこの入力は null。
+Ptr Base が*確定*した後で基底の配置を行い、添字の計算を畳み込める。
+
+- `Load(ctrl, base, null );` — フィールド名は内部に持つ。
+- `Load(ctrl, base, index_math );` — フィールド名は `[]`。
+- `Store(ctrl, base, null , val);`
+
+問題:エイリアス情報がない!
+
+案 C:直接呼び出し `odd()` と型では FRef を認める。それ以外はすべて*引き上げ*が必要。
+
+## 構造体と配列の統合
+
+構造体のフィールドは名前で索引付けされ、配列の要素は数値で索引付けされる。
+この2つの考え方は両立する、という発想です。
+C 言語では古くからこの考え方を使っています。通常の C の構造体の後に配列を続け、
+通常の配列構文で索引アクセスできます。
+
+別の方法として、配列へのポインタをフィールドに保持すると、間接参照が1つ増える代わりに柔軟性が得られます。
+特に、配列を(通常はより大きな配列に)交換できます。
+これは、広く使われる汎用コンテナ `Vector` や `ArrayList` の基礎です。
+
+```java
+struct str {
+ int hash; // A cache of the strings hash code
+ u32 #; // The array length
+ u8 []; // Array of bytes
+};
+
+str X = "abc";
+print(X.hash); // Read the hash field
+X[0]=='a'; // Read array element 0
+X#; // Length of the array
+
+```
+
+ここでは、`#` というフィールドが配列の長さを保持し、`[]` というフィールドが実際の配列です。
+メモリ内では、この配列がほかのフィールドの直後に続きます。
+可変長配列の別の例を示します。
+
+```java
+struct vecInt {
+ u32 size; // Actual number of elements
+ u32 #; // The array length or capacity
+ int []; // Array of integers.
+};
+```
+
+明らかな制約として、配列のサイズを直接変更することはできません。
+先頭のフィールドと同じメモリブロックに実際に配置されているからです。
+このような `vecInt` を、より大きい新しい `vecInt` に*再割り当て*することはできますが、
+元の `vecInt` は解放されます。同じ `vecInt` の参照を永遠に使い続けることはできません。
+
+名前付きフィールドはいくつあってもかまいません。
+
+```java
+struct Class {
+ !str !className;
+ !str !professor;
+ int time;
+ int credits;
+ !Class !prerequisite;
+ u16 #; // Limit of 65535 students per class
+ Student [];
+};
+```
+
+自己参照も使えます。
+
+```java
+struct NTree {
+ Some val;
+ u8 #; // Length is 0-255
+ NTree []; // More NTrees
+};
+```
+
+構文規則は次のとおりです。
+
+- 最後のフィールドの名前は `[]` で、どの型でもよい。
+- 最後から2番目のフィールドの名前は `#` で、配列の最大長を表す符号なし整数型でなければならない。
+- ` []` の前に空白が必要になるかもしれない。
+
+### コンストラクタと配列
+
+割り当てには配列の長さを含め、コンストラクタも含められます。
+`new NTree{val=17;}[2];`
+
+コンストラクタは省略できますが、配列の長さは省略できません。
+
+配列を初期化するための任意の関数を使うと、一度だけ書き込む final 配列を作れます。
+`new u8[buffer#,{ u32 idx -> buffer[idx].toUpperCase() }]`
+
+### 配列の最大長の変更
+
+範囲チェックのある小さな配列を大量に使う場合、配列の長さのために丸々4バイトを確保すると、
+配列のオーバーヘッドが大きく増えることが問題になります。
+Simple では最大長を小さくできます。ただし、最大長が異なる配列は別の型として扱います。
+
+```java
+struct x0 { u8 #; u8[]; }; // Array limited to 0-255 length
+struct x1 { u16 #; u8[]; }; // Array limited to 0-65535 length
+struct x2 { u32 #; u8[]; }; //
+struct x3 { u64 #; u8[]; }; //
+
+x0 x = "abc"; // 4 bytes total: [3, 'a', 'b', 'c']
+x1 y = "def"; // 6 bytes total: [3, 0 , 'd', 'e', 'f', 0,]; 1 pad byte to acheieve 2-byte alignment
+x = y; // ERROR, "x1 is not x0", no auto-widening
+x += y; // OK, 7 bytes total: [6, 'a', 'b', 'c', 'd', 'e', 'f']
+y += x; // OK, 8 bytes total: [6, 0 , 'd', 'e', 'f', 'a', 'b', 'c']
+
+x += "VeryLongString.... ....255_chars"; // Runtime RangeCheck
+
+// Explicit conversion to widen string
+y = "" + x;
+```
+
+### 拡張可能な配列・文字列の2つの形:間接参照を増やすかどうか
+
+間接参照を1つ増やすと、Java の `ArrayList` や C++ の `Vector` のようになります。
+
+増やさない場合、拡張可能な配列を伸ばす呼び出しは、新しいオブジェクトを返す可能性があります。
+拡張は必要な場合だけ行います。
+
+```java
+val vec1 = new vecInt[0].add(2); // len=1, capacity=1
+val vec2 = vec1.add(3); // NEW !vecInt !returned, only is deleted; len=2; capacity=2;
+vec1[0]; // ERROR, vec1 has been freed by call to `vec1.add(3)`
+vec2[0]; // OK
+vec2[1]; // OK
+vec2[2]; // Runtime error, AIOOBE
+```
+
+## 文字列の実装
+
+既定の文字列は「str」「cstr」「ustr」などの名前を持ちます。
+
+常に unicode8 を使い、u8 を明示的に操作します。
+Unicode の「文字」は、基礎となる実装の上に関数呼び出しとして提供します。
+おそらく別の文字列クラスで実装することになります。
+
+## 既定の文字列
+
+連結によって効率的に拡張でき、さまざまな IO/printf 関数をサポートします。
+明示的に要求すれば、基礎となる配列を可変な状態で共有できます。
+既定では常に、非共有時は可変、共有時は読み取り専用です。
+
+```java
+struct str {
+
+ // Add a string to the string, possibly growing
+ val add = { str str ->
+ val self = this;
+ for( int i=0; i<str#; i++ )
+ self = self.add(str[i]);
+ return self;
+ };
+
+ // Add an char to the string, possibly growing
+ val add = { u8 e ->
+ if( len >= # ) return copy(# ? #*2 : 1).add(e);
+ [size++] = e;
+ return this;
+ };
+
+ // Copy 'this' to a new larger size
+ val copy = { int sz2 ->
+ val v2 = new str[sz2];
+ for( int i=0; i<len; i++ )
+ v2[i] = [i];
+ v2.len = len;
+ sys.libc.free(this); // Needs a better Mem Management solution
+ return v2;
+ };
+
+ val print { -> sys.libc.write(1/*stdout*/,[]/*array base as argument to C*/,len); return this; }
+
+ u32 len; // In-use size
+ u32 #; // Max length is 4Gig, although variants can request smaller lengths
+ u8 []; // Character data
+};
+```
+
+### Final な文字列
+
+**不変**な文字列を意図しており、**安全な**文字列の基礎にも使えるかもしれません。
+一度に全体を作成する必要があり、おそらく防御的コピーも必要になります。
+
+```java
+struct sstr {
+ // Possible static call syntax; no references to "this"
+ val make { str str -> new sstr[str#,str.at] };
+
+ // Array contents are immutable.
+ // Still figuring out good syntax for this.
+ u8 #;
+ u8 [~]; // Middlin '~'? Makes primitive array contents *immutable* and requires constructor syntax
+};
+
+// Construction requires a function that produces the array contents.
+// Here we are defining a defensive copy over `buf`.
+sstr safe = new sstr[buf.len,{ int idx -> buf[idx]; }];
+sstr safe = sstr.make("abc"); // Defensive copy is made
+```
+
+### 拡張可能な文字列(StringBuilder または StringBuffer)
+
+この API は `StringBuilder` スタイルの代替としてそのまま使えることを意図しており、
+使うたびに間接参照が1つ増えます。
+
+```java
+struct xstr {
+ !str !_str; // The extra indirection is here
+ val add = { str str -> _str = str.add2(str); };
+ val write = { int fd -> _str.write(fd); };
+};
+```
+
+### Unicode 文字列
+
+この API は、通常の `str` の上で Unicode の文字を操作できるようにすることを意図しています。
+
+```java
+struct ustr {
+ // Assuming idx is at the start of a character, return the character
+ // as a 32b integer. Returns junk integer if not at a character start.
+ // For all ASCII strings this will amount to a simple byte load.
+ // For bytes with high order bits set, this may read 1-4 characters.
+ val at = { int idx -> ... };
+};
+// Unicode character iterator
+struct ucharator {
+// Return successive unicode character codes
+};
+```
+
+### 「C」文字列
+
+ゼロ終端の文字列ですが、長さを直接取得できる必要があります。
+
+```java
+struct cstr {
+ u8 #; // length including the trailing zero
+ u8[]; // always ends in a zero byte; suitable for direct passing to C string functions
+};
+```
+
+### 「german string」:レジスタに格納できる非常に短い文字列
+
+メモリ内では、長さを表す1バイト(3ビット?)に続いて0〜7文字を格納し、最大8文字です。
+64ビットのレジスタに収まります。
+
+```java
+struct gstr { // aka germanString, or blend string and register "streg"
+ u3 #; // Size is 0-7 bytes???
+ u8 []; // Up to 7 bytes
+}
+// Using inlined object (see below). All these strings are short and are
+// represented as packed integers in a 64b register.
+*gstr prize1 = "gold";
+*gstr prize2 = "silver";
+*gstr prize3 = "bronze";
+
+```
+
+### Main は `val main = { str[] args -> ...}` になる
+
+## 構造体のインライン化
+
+構造体は通常、参照として扱いますが、「内容(contents of)」と読む `*` 演算子を使って
+値として扱う形へ変換できます(この名前は、たとえば `inline` に変えるかもしれません)。
+
+これは名前空間の変更だけです。内部の構造体の寿命を完全に消費しない限り、そのアドレスは取得できません。
+「外部への流出」は認めません。
+
+```java
+// 2 64-bit words, no other overheads
+struct Complex { f64 x,y; val len = { ->Math.sqrt(x*x+y*y); }; };
+
+// Current Simple rules: always by-reference
+struct ByRef {
+ !Complex !c; // 4-byte pointer to a Complex
+};
+print(new ByRef.c.y); // Lookup requires 1 extra memory load from ref to c
+
+// New behavior: by value (inlining a struct)
+// The '*' syntax indicator can be something else, e.g. a keyword "inline"
+// I am pronouncing '*' as "contents of"
+struct ByValue {
+ *!Complex !c; // c is inlined, full 16 bytes into ByValue
+};
+print(new ByValue.c.y); // x,y inlined into ByValue, no extra memory load
+
+// Mixing Refs and Struct Values. Basically, a ref can be converted
+// to a value by taking the "contents of" the ref, and values cannot
+// be *converted* to a ref, although they can be copied over a ref.
+ref. c = val.c; // Error, mixing refs and values
+ref.*c = val.c; // Allowed, whole structure copy. Does not allocate, requires ref.c not-null
+val. c = ref.c; // Error, mixing refs and values
+val. c = *ref.c; // Allowed, whole structure copy
+ref. c = ref.c; // Allowed, pointer copy
+val. c = val.c; // Allowed, whole structure copy.
+val *c = val.c; // Error, cannot take "contents of" a value
+
+// Alternative syntax, to avoid the "contents of" being on LHS and field applying to being on RHS:
+ref.c = val.*c; // Assign into ref.c the "contents of" val.c
+
+// Arrays of inlined structures
+var ary = new *Complex[99]; // Array of 99 !Complex !objects, inlined
+
+// Calling methods has the same syntax
+ref.c.len();
+val.c.len();
+
+// Function arguments use the same typing, so can pass-by-value
+val math.sin = { *Complex c -> ... }; // Passes a complex by value
+math.sin(ref.*c); // Requires de-reference to pass by value
+math.sin(val. c); // Pass by value
+
+```
+
+## ほかの章のロードマップ
+
+`&&` と `||`。
+`int` を `i32` に縮小する。
+`TypeMemPtr` を4バイトに縮小する。`mmap` を使い、ヒープを下位4Gの領域に置く。
+パーサーの既定の for ループ構築、または部分的なループ剥離。
+`for( init; test; next ) body` は、次のようになる。
+```java
+{ init; // Normal scope entry to bound lifetime of index variable
+if( !test ) { // Zero-trip count test
+ do {
+ body;
+ next;
+ } while( test ); // Exit test at loop bottom by default
+} };
+```
+
+## `&&` と `||`
+
+論理演算子 &&(AND)と ||(OR)は短絡評価を行います。
+最初の条件だけで結果が決まるときは、第2の条件の評価を省略できます。
+
+- `&&` は最初のオペランドが偽なら評価を止めます。
+- `||` は最初のオペランドが真なら評価を止めます。
+
+これらは制御フローを読みやすくする糖衣構文であり、実行モデルに新しいノードを作りません。
+
+`&&` と `||` の構文は、制御フローだけを変える単純な糖衣構文であり、新しいノードを導入しません。
+
+次の例を考えます。
+```java
+int a = 1;
+int b = 0;
+
+if(a++ || b++ ) {
+ if(b == 0 && a == 2) {
+ sys.io.p("Or");
+ }
+} else{
+ sys.io.p("And");
+}
+return 0;
+```
+
+- `a++` は1(真)と評価されるので、`b++` は評価されません。
+- その結果、`b` は0のままで、`a` は2になり、「Or」が表示されます。
+
+```java
+int a = 1;
+int b = 1;
+
+int x=1;
+int y=1;
+int z=0;
+
+int g = x++ && y++ && z++;
+...
+```
+
+この例では、次のようになります。
+
+- `x++` は真(1)なので、`y++` を評価します。それも真です。
+- 続いて `z++` を評価します。これは偽(0)です。
+- `&&` はすべての条件が真であることを要求するため、最終結果 `g` は0になります。
+- 偽になるオペランドまでのすべてのオペランドを評価するため、`x`、`y`、`z` はすべてインクリメントされます。
diff --git a/chapter25/ROADMAP.md b/chapter25/ROADMAP.md
index 30495a69..749a1614 100644
--- a/chapter25/ROADMAP.md
+++ b/chapter25/ROADMAP.md
@@ -1,5 +1,7 @@
## Separate Compilation
+English | [日本語](ROADMAP.ja.md)
+
How to handle unknown forward ref?
No H-M, so no unification.
diff --git a/chapter25/docs/18-grammar.md b/chapter25/docs/18-grammar.md
deleted file mode 100644
index 2c98f562..00000000
--- a/chapter25/docs/18-grammar.md
+++ /dev/null
@@ -1,129 +0,0 @@
-# Grammar for Chapter 16
-
-```antlrv4
-grammar SimpleLanguage;
-
-program : block EOF ;
-
-block : statement+ ;
-
-
-statement
- : '{' block '}'
- | returnStatement
- | ifStatement
- | whileStatement
- | breakStatement
- | continueStatement
- | structDeclaration
- | expressionStatement
- ;
-
-
-returnStatement : 'return' expression ';' ;
-
-ifStatement : 'if' '(' expression ')' statement ('else' statement)? ;
-
-whileStatement : 'while' '(' expression ')' statement ;
-
-breakStatement : 'break' ';' ;
-
-continueStatement : 'continue' ';' ;
-
-structDeclaration : 'struct' IDENTIFIER '{' block '}' ;
-
-expressionStatement
- : type IDENTIFIER ';'
- | type IDENTIFIER '=' expression ';'
- | IDENTIFIER '=' expression ';'
- | expression
- ;
-
-PRIMTYPE
- : 'int'
- | 'i8'
- | 'i16'
- | 'i32'
- | 'i64'
- | 'u8'
- | 'u16'
- | 'u32'
- | 'flt'
- | 'f32'
- | 'f64'
- | 'bool'
- ;
-
-type
- : PRIMTYPE
- | typeName '?'?
- | typeName nestedArray+
- ;
-
-nestedArray : '?'? '[]'* ;
-
-typeName : IDENTIFIER ;
-
-
-expression : bitWiseExpression ;
-
-bitWiseExpression
- : comparisonExpression ( '&' | '|' | '^' ) comparisonExpression)*
- ;
-
-comparisonExpression
- : shiftExpression (('==' | '!='| '>'| '<'| '>='| '<=') shiftExpression)*
- ;
-
-shiftExpression
- : additiveExpression ( '<<' | '>>>' | '>>' ) additiveExpression)*
- ;
-
-additiveExpression
- : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
- ;
-
-multiplicativeExpression
- : unaryExpression (('*' | '/') unaryExpression)*
- ;
-
-unaryExpression
- : ('-') unaryExpression
- | '!' unaryExpression
- | primaryExpression postAssign
- ;
-
-primaryExpression
- : INTEGER_LITERAL
- | FLOAT_LITERAL
- | '(' expression ')'
- | 'true'
- | 'false'
- | 'null'
- | newExpression
- | IDENTIFIER
- ;
-
-newExpression
- : 'new' IDENTIFIER [ '{' block '}' ]
- | 'new' IDENTIFIER '[' expression ']'
- ;
-
-postAssign : postFix [ '=' expression ] | [ '#' ];
-
-postFix
- : '.' IDENTIFIER postFix
- | '[' expression ']' postFix
- ;
-
-
-IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ;
-
-INTEGER_LITERAL
- : [1-9]DIGIT*
- | [0]
- ;
-
-NON_DIGIT: [a-zA-Z_];
-DIGIT: [0-9];
-```
diff --git a/chapter25/docs/25-grammar.ja.md b/chapter25/docs/25-grammar.ja.md
new file mode 100644
index 00000000..9e9fb820
--- /dev/null
+++ b/chapter25/docs/25-grammar.ja.md
@@ -0,0 +1,175 @@
+# 第25章の文法
+
+[English](25-grammar.md) | 日本語
+
+```antlrv4
+grammar SimpleLanguage;
+
+// Name lookup, assignable operands, type compatibility, and access permissions
+// are checked by the compiler in addition to these syntax rules.
+// A file is a compilation unit; its path supplies its namespace.
+// Qualified names resolve nested types and other units without an import keyword.
+program : statement* EOF ;
+
+block : '{' statement* '}' ;
+
+statement
+ : block
+ | returnStatement
+ | ifStatement
+ | whileStatement
+ | forStatement
+ | breakStatement
+ | continueStatement
+ | structDeclaration
+ | declaration ';'
+ | assignment ';'
+ | ';'
+ ;
+
+returnStatement : 'return' assignment ';' ;
+ifStatement : 'if' '(' assignment ')' statement ('else' statement)? ;
+whileStatement : 'while' '(' assignment ')' statement ;
+breakStatement : 'break' ';' ;
+continueStatement : 'continue' ';' ;
+
+forStatement
+ : 'for' '(' (declaration | assignment)? ';' assignment? ';' assignment? ')' statement
+ ;
+
+structDeclaration : 'struct' IDENTIFIER '{' structMember* '}' ';' ;
+structMember : constructorDeclaration | methodDeclaration | statement ;
+
+// The constructor name must match the enclosing struct's name.
+constructorDeclaration : 'new' IDENTIFIER '=' functionExpression ';' ;
+
+// A method is an ordinary fixed function binding in a struct definition.
+// The compiler supplies self; it is not written in parameterList.
+// Function types and expressions both receive their implicit receiver here.
+methodDeclaration : 'val' bindingModifier? IDENTIFIER '=' functionExpression ';' ;
+
+declaration : declarationType binding (',' binding)* ;
+declarationType : type | 'var' | 'val' ;
+binding : bindingModifier? IDENTIFIER ('=' initializer)? ;
+bindingModifier : '!' | '~' ;
+// A declaration initializer consisting of the string "C" declares an extern
+// symbol: a function, or explicit numeric native storage. Ordinary strings
+// elsewhere are array-valued expressions.
+initializer : assignment ;
+
+// The prefix qualifies the named struct reference, not an enclosing array.
+// Each [] / [~] suffix qualifies its own array layer. ? requires a reference.
+type
+ : functionType
+ | primitiveType typeSuffix*
+ | accessModifier? typeName typeSuffix*
+ ;
+accessModifier : '!' | '~' ;
+typeSuffix : '?' | '[]' | '[~]' ;
+typeName : IDENTIFIER ('.' IDENTIFIER)* ;
+
+primitiveType
+ : 'int' | 'i8' | 'i16' | 'i32' | 'i64'
+ | 'u1' | 'u8' | 'u16' | 'u32' | 'byte' | 'bool'
+ | 'flt' | 'f32' | 'f64'
+ ;
+
+assignment : expression (assignmentOperator assignment)? ;
+assignmentOperator
+ : '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^='
+ | '<<=' | '>>=' | '>>>='
+ ;
+
+expression : logicalExpression ('?' assignment (':' assignment)?)? ;
+
+// Both logical operators short-circuit and associate to the right here.
+logicalExpression : bitwiseExpression (('&&' | '||') logicalExpression)? ;
+
+bitwiseExpression
+ : equalityExpression (('&' | '|' | '^') equalityExpression)*
+ ;
+
+equalityExpression : comparisonExpression (('==' | '!=') comparisonExpression)* ;
+
+// Chained comparisons test adjacent operands and short-circuit. A chain must
+// keep its direction; equality has lower precedence and does not join a chain.
+comparisonExpression
+ : shiftExpression
+ ( (('<' | '<=') shiftExpression)+ | (('>' | '>=') shiftExpression)+ )?
+ ;
+
+shiftExpression
+ : additiveExpression (('<<' | '>>' | '>>>') additiveExpression)*
+ ;
+
+additiveExpression
+ : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
+ ;
+
+multiplicativeExpression
+ : unaryExpression (('*' | '/') unaryExpression)*
+ ;
+
+unaryExpression
+ : ('-' | '!') unaryExpression
+ | updateOperator IDENTIFIER
+ | postfixExpression
+ ;
+
+// Prefix updates require a local name; postfix updates also allow fields
+// and array elements. All updates require an assignable operand.
+updateOperator : '++' | '--' ;
+postfixExpression : primaryExpression postfixSuffix* updateOperator? ;
+postfixSuffix : '.' IDENTIFIER | '[' assignment ']' | '#' | callSuffix | methodCallSuffix ;
+
+// For a fixed function-valued member, object.name(args) passes object as self.
+// This overlaps ordinary field selection followed by a call syntactically;
+// name/type resolution determines which call form applies.
+methodCallSuffix : '.' IDENTIFIER callSuffix ;
+
+primaryExpression
+ : INTEGER_LITERAL
+ | FLOAT_LITERAL
+ | STRING_LITERAL
+ | CHARACTER_LITERAL
+ | 'true'
+ | 'false'
+ | 'null'
+ | IDENTIFIER
+ | '(' assignment ')'
+ | newExpression
+ | functionExpression
+ ;
+
+// A function is an expression bound with an ordinary declaration.
+// Its last statement supplies the implicit result; return can exit earlier.
+functionExpression : '{' parameterList? '->' statement* '}' ;
+parameterList : (parameter ','?)+ ;
+parameter : type bindingModifier? IDENTIFIER ;
+
+// Function types have unnamed, space-separated argument types.
+// {int} is the zero-argument type; its function expression still uses ->.
+functionType
+ : '{' type+ '->' type '}' '?'?
+ | '{' type '}' '?'?
+ ;
+
+callSuffix : '(' argumentList? ')' ;
+argumentList : assignment (',' assignment)* ','? ;
+
+// Allocation invokes the declared constructor; inline initializer blocks from
+// earlier chapters are replaced by constructor arguments.
+newExpression : 'new' type ('[' assignment ']' | callSuffix)? ;
+
+INTEGER_LITERAL : '0' | [1-9] DIGIT* ;
+FLOAT_LITERAL : DIGIT+ ('.' DIGIT* ('e' DIGIT+)? | 'e' '-'? DIGIT+) ;
+STRING_LITERAL : '"' (ESCAPE | ~["\\])* '"' ;
+CHARACTER_LITERAL : '\'' (ESCAPE | ~['\\]) '\'' ;
+fragment ESCAPE : '\\' [0ntr\\'"] ;
+IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ;
+fragment NON_DIGIT : [a-zA-Z_] ;
+fragment DIGIT : [0-9] ;
+WHITESPACE : [ \t\r\n]+ -> skip ;
+LINE_COMMENT : '//' ~[\r\n]* -> skip ;
+BLOCK_COMMENT : '/*' .*? '*/' -> skip ;
+```
diff --git a/chapter25/docs/25-grammar.md b/chapter25/docs/25-grammar.md
new file mode 100644
index 00000000..065cf051
--- /dev/null
+++ b/chapter25/docs/25-grammar.md
@@ -0,0 +1,175 @@
+# Grammar for Chapter 25
+
+English | [日本語](25-grammar.ja.md)
+
+```antlrv4
+grammar SimpleLanguage;
+
+// Name lookup, assignable operands, type compatibility, and access permissions
+// are checked by the compiler in addition to these syntax rules.
+// A file is a compilation unit; its path supplies its namespace.
+// Qualified names resolve nested types and other units without an import keyword.
+program : statement* EOF ;
+
+block : '{' statement* '}' ;
+
+statement
+ : block
+ | returnStatement
+ | ifStatement
+ | whileStatement
+ | forStatement
+ | breakStatement
+ | continueStatement
+ | structDeclaration
+ | declaration ';'
+ | assignment ';'
+ | ';'
+ ;
+
+returnStatement : 'return' assignment ';' ;
+ifStatement : 'if' '(' assignment ')' statement ('else' statement)? ;
+whileStatement : 'while' '(' assignment ')' statement ;
+breakStatement : 'break' ';' ;
+continueStatement : 'continue' ';' ;
+
+forStatement
+ : 'for' '(' (declaration | assignment)? ';' assignment? ';' assignment? ')' statement
+ ;
+
+structDeclaration : 'struct' IDENTIFIER '{' structMember* '}' ';' ;
+structMember : constructorDeclaration | methodDeclaration | statement ;
+
+// The constructor name must match the enclosing struct's name.
+constructorDeclaration : 'new' IDENTIFIER '=' functionExpression ';' ;
+
+// A method is an ordinary fixed function binding in a struct definition.
+// The compiler supplies self; it is not written in parameterList.
+// Function types and expressions both receive their implicit receiver here.
+methodDeclaration : 'val' bindingModifier? IDENTIFIER '=' functionExpression ';' ;
+
+declaration : declarationType binding (',' binding)* ;
+declarationType : type | 'var' | 'val' ;
+binding : bindingModifier? IDENTIFIER ('=' initializer)? ;
+bindingModifier : '!' | '~' ;
+// A declaration initializer consisting of the string "C" declares an extern
+// symbol: a function, or explicit numeric native storage. Ordinary strings
+// elsewhere are array-valued expressions.
+initializer : assignment ;
+
+// The prefix qualifies the named struct reference, not an enclosing array.
+// Each [] / [~] suffix qualifies its own array layer. ? requires a reference.
+type
+ : functionType
+ | primitiveType typeSuffix*
+ | accessModifier? typeName typeSuffix*
+ ;
+accessModifier : '!' | '~' ;
+typeSuffix : '?' | '[]' | '[~]' ;
+typeName : IDENTIFIER ('.' IDENTIFIER)* ;
+
+primitiveType
+ : 'int' | 'i8' | 'i16' | 'i32' | 'i64'
+ | 'u1' | 'u8' | 'u16' | 'u32' | 'byte' | 'bool'
+ | 'flt' | 'f32' | 'f64'
+ ;
+
+assignment : expression (assignmentOperator assignment)? ;
+assignmentOperator
+ : '=' | '+=' | '-=' | '*=' | '/=' | '&=' | '|=' | '^='
+ | '<<=' | '>>=' | '>>>='
+ ;
+
+expression : logicalExpression ('?' assignment (':' assignment)?)? ;
+
+// Both logical operators short-circuit and associate to the right here.
+logicalExpression : bitwiseExpression (('&&' | '||') logicalExpression)? ;
+
+bitwiseExpression
+ : equalityExpression (('&' | '|' | '^') equalityExpression)*
+ ;
+
+equalityExpression : comparisonExpression (('==' | '!=') comparisonExpression)* ;
+
+// Chained comparisons test adjacent operands and short-circuit. A chain must
+// keep its direction; equality has lower precedence and does not join a chain.
+comparisonExpression
+ : shiftExpression
+ ( (('<' | '<=') shiftExpression)+ | (('>' | '>=') shiftExpression)+ )?
+ ;
+
+shiftExpression
+ : additiveExpression (('<<' | '>>' | '>>>') additiveExpression)*
+ ;
+
+additiveExpression
+ : multiplicativeExpression (('+' | '-') multiplicativeExpression)*
+ ;
+
+multiplicativeExpression
+ : unaryExpression (('*' | '/') unaryExpression)*
+ ;
+
+unaryExpression
+ : ('-' | '!') unaryExpression
+ | updateOperator IDENTIFIER
+ | postfixExpression
+ ;
+
+// Prefix updates require a local name; postfix updates also allow fields
+// and array elements. All updates require an assignable operand.
+updateOperator : '++' | '--' ;
+postfixExpression : primaryExpression postfixSuffix* updateOperator? ;
+postfixSuffix : '.' IDENTIFIER | '[' assignment ']' | '#' | callSuffix | methodCallSuffix ;
+
+// For a fixed function-valued member, object.name(args) passes object as self.
+// This overlaps ordinary field selection followed by a call syntactically;
+// name/type resolution determines which call form applies.
+methodCallSuffix : '.' IDENTIFIER callSuffix ;
+
+primaryExpression
+ : INTEGER_LITERAL
+ | FLOAT_LITERAL
+ | STRING_LITERAL
+ | CHARACTER_LITERAL
+ | 'true'
+ | 'false'
+ | 'null'
+ | IDENTIFIER
+ | '(' assignment ')'
+ | newExpression
+ | functionExpression
+ ;
+
+// A function is an expression bound with an ordinary declaration.
+// Its last statement supplies the implicit result; return can exit earlier.
+functionExpression : '{' parameterList? '->' statement* '}' ;
+parameterList : (parameter ','?)+ ;
+parameter : type bindingModifier? IDENTIFIER ;
+
+// Function types have unnamed, space-separated argument types.
+// {int} is the zero-argument type; its function expression still uses ->.
+functionType
+ : '{' type+ '->' type '}' '?'?
+ | '{' type '}' '?'?
+ ;
+
+callSuffix : '(' argumentList? ')' ;
+argumentList : assignment (',' assignment)* ','? ;
+
+// Allocation invokes the declared constructor; inline initializer blocks from
+// earlier chapters are replaced by constructor arguments.
+newExpression : 'new' type ('[' assignment ']' | callSuffix)? ;
+
+INTEGER_LITERAL : '0' | [1-9] DIGIT* ;
+FLOAT_LITERAL : DIGIT+ ('.' DIGIT* ('e' DIGIT+)? | 'e' '-'? DIGIT+) ;
+STRING_LITERAL : '"' (ESCAPE | ~["\\])* '"' ;
+CHARACTER_LITERAL : '\'' (ESCAPE | ~['\\]) '\'' ;
+fragment ESCAPE : '\\' [0ntr\\'"] ;
+IDENTIFIER : NON_DIGIT (NON_DIGIT | DIGIT)* ;
+fragment NON_DIGIT : [a-zA-Z_] ;
+fragment DIGIT : [0-9] ;
+WHITESPACE : [ \t\r\n]+ -> skip ;
+LINE_COMMENT : '//' ~[\r\n]* -> skip ;
+BLOCK_COMMENT : '/*' .*? '*/' -> skip ;
+```
diff --git a/chapter25/module.ja.md b/chapter25/module.ja.md
new file mode 100644
index 00000000..2746ad1e
--- /dev/null
+++ b/chapter25/module.ja.md
@@ -0,0 +1,275 @@
+# Simple のモジュール
+
+[English](module.md) | 日本語
+
+この文書は [Modules for Simple](module.md) の日本語訳です。原文はモジュールの設計案です。
+
+目標:
+
+- 有効なプログラムを1行で書けるようにする(チュートリアルや初心者の導入を簡単にする)。
+ - 最小限の手間でコンパイルして実行する。
+- 分割コンパイル、名前空間、外部ライブラリを扱う。
+ - ELF など、リンク可能・実行可能な形式へコンパイルする。
+- フィールドやクラスを簡単に非公開にする。
+- 出力ファイルを、既存の .o/.exe/lib/dll の仕組みと互換にする。
+- `#include` や `import` 用のファイルを不要にする。
+- モジュールごとに構造・ファイルシステムの階層を持たせる。
+ - 複数のファイルで1つのモジュールを構成することも考えられる。
+ - モジュール内で簡単に名前空間を使う。
+ - 親ファイルまたは入れ子のファイルに、入れ子の名前を置く。
+- クラス初期化子について次を保証する。
+ - 正確に1回だけ実行する。
+ - 最初にアクセスされた時点で遅延実行する。
+ - 最初のインスタンスが作られる前、またはその時点までに完了する。
+ - `<clinit>` 内の特別な原型インスタンス(golden instance)は例外として認める。
+
+---
+### 目標:最小限の手間で有効な1行プログラムを書く
+
+現在の作業ディレクトリの名前から、モジュールとクラス `hello` を作ります。
+追加の引数なしでコンパイルすると、リンクと実行も行います。
+
+ファイル:`hello.smp`
+```
+sys.io.p("Hello World!");
+```
+
+コンパイルして実行します。
+```
+> smp hello.smp
+Hello World!
+```
+
+モジュール名とクラス名は、ファイル名から取り出した `hello` です。
+メモリ内でコンパイルし、`exec` で実行します。ファイルは作りません。
+これは、その場でコンパイルして実行するためだけのモードです。
+
+---
+### 目標:分割コンパイル
+
+コンパイル時に出力先のビルドディレクトリを指定すると、`.o` ファイルを得られます。
+出力先ディレクトリの構造はソースと一致し、場所は自由に選べます。
+
+前と同様に、コンパイルのコマンドでモジュール名を指定していないため、
+`Pet` がモジュールにもクラスにもなります。
+
+ファイル:Pet.smp
+```
+// Class initializer <clinit> is any code outside a struct declaration
+// and is guaranteed to run-once on first touch.
+
+// static fields listed here:
+int default_leggies = 4;
+
+// A default (no argument) <init> is code inside struct with same name as the
+// file; this is the same behavior as field initializers for Java, although
+// any code is allowed, not just expressions.
+
+struct Pet {
+ str name;
+ int leggies = default_leggies;
+}
+
+float yet_another_static_field;
+```
+
+`build/` ディレクトリ内の `Pet.o` へコンパイルします。
+```
+> smp Pet.smp -o build/
+> ls build
+Pet.o
+```
+
+---
+### 目標:入れ子の名前空間
+
+`myRepo` 内のファイル名から、モジュールとクラス `Pet` を作ります。
+入れ子のクラス `Pet.Vet` も作ります。これらを別のモジュール `Owner` から使います。
+
+ディレクトリ:`~/myRepo/`
+```
+~/myRepo/
+ Pet.smp
+ build/
+
+~/otherRepo/
+ Owner.smp
+```
+
+ファイル:`myRepo/Pet.smp`
+```
+int default_leggies = 4;
+
+struct Pet {
+ str name;
+ int leggies = default_leggies;
+}
+
+// Nested class Vet inside of file Pet
+struct Vet {
+ str name;
+ str address;
+ long credit_card_num;
+}
+```
+
+ファイル:`otherRepo/Owner.smp`
+```
+Pet dog = Pet{name="Fido"};
+Pet snake = Pet{name="slither", leggies = 0; };
+Pet.Vet vet = Pet.Vet{name="Linda"; address="101 Main St"; credit_card_num=1234};
+
+sys.io.p("I took "+dog.name+" to "+vet.name+"\n");
+```
+
+`build/` ディレクトリ内の `Pet.o` へコンパイルします。
+コマンドで別のモジュールのルートを指定していないため、`Pet` がモジュールであり、クラスでもあります。
+`Pet` がモジュールなので、`Vet` はモジュール `Pet` 内のクラスです。
+
+```
+> smp Pet.smp -o build/
+> ls build
+Pet.o
+```
+
+`Pet` と `Pet.Vet` のコードは、同じ `Pet.o` ファイルに入ります。
+
+自由変数 `Pet` と `Pet.Vet` を解決するために `Pet` のリポジトリを指定して、
+`Owner.smp` をコンパイルし、実行します。
+
+```
+> smp Owner.smp -lib ~/myRepo/build
+I took Fido to Linda
+```
+
+---
+### 目標:入れ子のファイル内の入れ子の名前空間
+
+前の例と同じですが、ファイルを入れ子にします。
+
+```
+~/myRepo
+ Pet.smp
+ Pet/
+ Vet.smp
+ build/
+ Pet.o
+ Pet/
+ Vet.o
+
+~/otherRepo
+ Owner.smp
+```
+
+ファイル:Pet.smp
+```
+int default_leggies = 4;
+
+struct Pet {
+ str name;
+ int leggies = default_leggies;
+}
+```
+
+ファイル:Pet/Vet.smp
+```
+// Nested class Vet inside of directory Pet
+struct Vet {
+ str name;
+ str address;
+ long credit_card_num;
+}
+```
+
+`Owner.smp` をコンパイルし、実行します。
+```
+> smp Owner.smp -lib ~/myRepo/build
+I took Fido to Linda
+```
+
+---
+### 目標:クラス初期化子の遅延実行
+
+```
+Parent.smp
+Parent/
+ ChildA.smp
+ ChildB.smp
+```
+
+ファイル:`Parent.smp`
+```
+sys.io.p("In Parent <clinit>");
+sys.io.p(ChildB.str);
+```
+
+ファイル:`ChildA.smp`
+```
+// <clinit> for ChildA
+sys.io.p("In ChildA <clinit>");
+str str = "A";
+```
+
+ファイル:`ChildB.smp`
+```
+// <clinit> for ChildB
+sys.io.p("In ChildB <clinit>");
+str str = "B";
+```
+
+コンパイルして実行します。
+```
+> smp Parent.smp
+In Parent <clinit>
+In ChildB <clinit>
+B
+
+```
+
+### 目標:簡単な非公開フィールド
+
+名前の先頭にアンダースコアを付けると、フィールドやクラスが非公開になります。
+
+### 目標:C コンパイラのドライバに似た構文
+
+```
+Simple --cpu x86_64_v2 --abi Win64 --norun -o lib/lib/sys_x86_64_v2_win64.o src/main/smp/sys.smp
+```
+
+モジュールを指定していないため、最上位のファイルがモジュール `sys` になります。
+モジュールディレクトリも指定していないため、ファイル名のディレクトリ部分 `src/main/smp` になります。
+ビルドディレクトリを指定していないため、出力ファイルのディレクトリ部分 `lib/` になります。
+出力ファイル名の既定値は `smp.o` ですが、ここでは別名を指定しています。
+
+ある `<clinit>` は別の `<clinit>` を参照でき、最初のアクセス時に実行されます。
+設計上、子より先に親へアクセスする必要があります。ただし、子 A は子 B を参照できます。
+Parent.B へのアクセスは、次を意味します。
+
+- 最初に Parent にアクセスし、`<clinit>` を開始する。
+- そこからアクセスするものにも、A や B を含め、再帰的に `<clinit>` を実行する。
+- Parent の処理が完了する。
+- `<clinit>` 内では別の*モジュール*を参照できない。
+- 静的関数は別のモジュールを参照*できる*が、その場合、実行される可能性がある……。
+
+目標:モジュール内の `<clinit>` を決定的にする。
+
+- 背景:複雑な `<clinit>` の順序のせいで、子が使う時点で参照先の static final フィールドがまだ初期化されていないことが、何度もあった。
+- 副目標:親が子より先になるなど、`<clinit>` の*順序*を決定的にする。
+ - 問題:Parent の `<clinit>` の途中で Child を参照すると、Parent が完了する前に Child の `<clinit>` が走る。
+ - 部分的な回答:親が先で「親は子を知っている」ため、これはよい。
+ - 追加の問題:モジュールをまたぐと成り立たない。あるモジュールは別のモジュールの親ではない。
+ - 問題:`<clinit>` 中に呼び出す Parent の関数が、非決定的に子を初期化できる。
+ - `fcn = { -> rand ? A.fld : B.fld }`
+- 回答案:Parent の `<clinit>` は、直接にも間接にも子を参照しては*ならない*。
+- 例:`B b = new B; // Illegal ref to child in Parent.<clinit>`
+ — Parent の初期化子から子への不正な参照。
+- 例:`B? b = fcn(); fcn = { -> rand ? null : new B; } // Illegal call to fcn referring to B inside Parent <clinit>`
+ — Parent の初期化子内で B を参照する関数への不正な呼び出し。
+- 例:`B? b; fcn = { -> new B; } // OK, no B.<clinit> during Parent.<clinit>`
+ — Parent の初期化中に B の初期化子を実行しないため、これは許される。
+
+目標:外部とのリンクでも、1回限りの初期化機能を使えるようにする。
+
+回答案:外部から `Foo.foo()` を呼ぶときは、まず `Foo.<clinit>` が実行済みであることを確認してから、
+`Foo.foo()` を呼び出す。任意の最適化として、成功後は呼び出し元を変更し、
+`<clinit>` の確認を省略する。
diff --git a/chapter25/module.md b/chapter25/module.md
index d7bed18c..4cd801e7 100644
--- a/chapter25/module.md
+++ b/chapter25/module.md
@@ -1,6 +1,8 @@
# Modules for Simple
+English | [日本語](module.ja.md)
+
Goals:
- 1-liner valid program (for easy tutorial/newbie starts)
- - Compile and run with minimal effort
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
index a8cd315f..27253c5e 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
@@ -63,9 +63,8 @@ abstract public class Serialize {
Type.TAGOFFS();
BAOS baos = new BAOS();
- // C0D2 adds independent pointer access permissions to serialized types.
- // A - Print a header
- baos.write('C').write('0').write('D').write('2');
+ // A - Print the C0DE header. The format is not versioned yet.
+ baos.write('C').write('0').write('D').write('E');
// Count unique Types
var types = new HashMap<Type,Integer>();
@@ -218,7 +217,7 @@ abstract public class Serialize {
Type.TAGOFFS();
// A - Read a header
- if( bais.read()!='C' || bais.read()!='0' || bais.read()!='D' || bais.read()!='2' )
+ if( bais.read()!='C' || bais.read()!='0' || bais.read()!='D' || bais.read()!='E' )
throw new IllegalArgumentException("Unsupported serialized IR format; rebuild the object file");
// B - Packed read of #strings, then strings
@@ -247,7 +246,7 @@ abstract public class Serialize {
String[] strs = elf._strs;
if( strs == null ) {
// A - Read a header
- if( bais.read() != 'C' || bais.read() != '0' || bais.read() != 'D' || bais.read() != '2' )
+ if( bais.read() != 'C' || bais.read() != '0' || bais.read() != 'D' || bais.read() != 'E' )
throw new IllegalArgumentException( "Unsupported serialized IR format; rebuild the object file" );
// B - Packed read of #strings, then strings
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/README.ja.md b/chapter25/src/test/java/com/seaofnodes/simple/spill/README.ja.md
new file mode 100644
index 00000000..5ae019dd
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/README.ja.md
@@ -0,0 +1,50 @@
+# 過去の割り当てコホート
+
+[English](README.md) | 日本語
+
+この文書は [Historical allocation cohorts](README.md) の日本語訳です。
+
+`cohorts.tsv` は、第24章の `SpillStats` で測定した212件のコンパイル項目を固定したものです。
+コホート20〜24の項目数は、それぞれ39、52、24、30、67です。
+ソースファイルは内容で重複を除き、最初に参照したテストの名前を付けています。
+マニフェストには、テストの識別情報、ターゲット、ABI、元の停止フェーズを保持しています。
+`Win64` は、第25章で使う ABI の表記 `win64` に変更しています。
+引数の型は i64、再実行のシードは全項目で123です。
+
+これらの入力は、この最終監査で新たな正しさの回帰テストを追加する前に、
+第24章のテストから取得しました。比較のための安定したコーパスです。
+変更する場合は、比較可能性が失われる理由を説明する必要があります。
+人工的なアロケータの回帰テストや命令の回帰テストでの割り当ては、これらのコホートには含めません。
+
+第25章では、以前のソースをすべて無変更で解析できるわけではありません。適応のための変更を明記します。
+
+- String、Scan、連結された S のコンストラクタでは、`new S { field=...; }` を、
+ 宣言されたコンストラクタとその引数に置き換えています。
+ レシーバーを明示するスキャナ関数は、操作を保ったままインスタンスメソッドにしています。
+- 保存した `Chapter21Test-testStringExport`、`Chapter21AllocTest-testString`、
+ `Chapter23AllocTest-testString` のソースは、必須の非 null フィールド `cs` を初期化するために、
+ `new String = { u8[] data -> cs=data; };` を宣言しています。
+ これによって10件の再実行項目(4件、3件、3件)を修正していますが、
+ 等価性・ハッシュの本体やマニフェストの構成項目は変えていません。
+ これらの適応済みソースが新しい比較基準です。以前のコンストラクタなしのフィクスチャと
+ 同一入力として、その集計値を比較することはできません。
+- 短絡評価の右辺で初期化する2つのループは、同等のガード付きブロックにしています。
+ 割り当て、副作用、最終結果を保持しています。
+- 単純な I/O のケースには、以前の `write` の束縛と表示用の補助関数を直接含めています。
+ Bubble Sort には、名前空間の接頭辞を平坦化し、コンストラクタを明示し、
+ 置換用バッファを可変にした、以前のライブラリの本体を含めています。
+ これにより、第25章で大幅に改訂した例に差し替えるのではなく、以前のワークロードを保持しています。
+- 元の Newton の浮動小数点数版2種類を含め、ほかのソース本文は保持しています。
+
+すべての項目を **RegAlloc まで**再実行し、スケジューリング済みのレジスタ制約を確認します。
+過去のフェーズの列は来歴情報であり、第25章で以前のネイティブ実行ハーネスを動かすことを約束するものではありません。
+適応済み Bubble Sort は、割り当ての比較可能性のために、過去の `i32 errno="C"` 宣言を保持しています。
+直接のデータ束縛は現在、3つのターゲットすべてでエンコードできますが、
+errno がスレッドローカルまたはアクセサ経由の libc とは、この宣言をリンクできません。
+現在のシステムライブラリのコードは、代わりに `libc.errno()` を使います。
+このコーパスを、過去のランタイムの実行カバレッジとして数えてはいけません。
+
+その後 `SpillStats` は、シード456で現在のシステムライブラリをエンコーディングまでコンパイルし、
+既存のシードとネイティブ検査を使って Chapter25Test を実行します。
+これらを合わせてコホート25とします。ライブラリまたはテストが失敗すると、レポートも失敗します。
+部分的な集計値では改善を示せません。現在のコンパイラの実行検証は、通常の章全体のテストスイートが担います。
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/README.md b/chapter25/src/test/java/com/seaofnodes/simple/spill/README.md
index 57435402..3a064486 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/README.md
+++ b/chapter25/src/test/java/com/seaofnodes/simple/spill/README.md
@@ -1,5 +1,7 @@
# Historical allocation cohorts
+English | [日本語](README.ja.md)
+
`cohorts.tsv` freezes the 212 compilation entries measured by Chapter 24's
`SpillStats`: 39, 52, 24, 30, and 67 entries for cohorts 20-24. The source files
are deduplicated by contents and named for their first referring test. The