summaryrefslogtreecommitdiff
path: root/chapter28b
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-24 15:31:01 -0700
committerCliff Click <cliffc@acm.org>2026-10-24 15:31:01 -0700
commitfeb98e1bb6c973428b94591ed5b0aed5e4137bd0 (patch)
treeee104333658a452590546c483fda1cae315ce134 /chapter28b
parente2b042bd9721995486f4f0cf96847c02d27908e3 (diff)
downloadSimple-feb98e1bb6c973428b94591ed5b0aed5e4137bd0.tar.gz
Simple-feb98e1bb6c973428b94591ed5b0aed5e4137bd0.zip
Finish separate compilation snapshot in chapter 28b
Diffstat (limited to 'chapter28b')
-rw-r--r--chapter28b/README.ja.md158
-rw-r--r--chapter28b/README.md382
-rw-r--r--chapter28b/ROADMAP.ja.md451
-rw-r--r--chapter28b/ROADMAP.md523
-rw-r--r--chapter28b/ainotes.txt4
-rw-r--r--chapter28b/docs/28b-grammar.ja.md (renamed from chapter28b/docs/25-grammar.ja.md)4
-rw-r--r--chapter28b/docs/28b-grammar.md (renamed from chapter28b/docs/25-grammar.md)4
-rw-r--r--chapter28b/docs/Makefile12
-rw-r--r--chapter28b/docs/font-awesome-4.7.0.zipbin669808 -> 0 bytes
-rw-r--r--chapter28b/docs/ifg_trim.pdfbin155190 -> 0 bytes
-rw-r--r--chapter28b/docs/lattice.gv203
-rw-r--r--chapter28b/docs/lattice.svg691
-rw-r--r--chapter28b/docs/lattice2.gv85
-rw-r--r--chapter28b/docs/lattice2.svg260
-rw-r--r--chapter28b/docs/pardoe.docxbin22583 -> 0 bytes
-rw-r--r--chapter28b/docs/webtcp.min.js65
-rw-r--r--chapter28b/helloWorld.exe.stackdump16
-rw-r--r--chapter28b/pom.xml4
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/Parser.java185
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java4
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java3
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/codegen/Opto.java3
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java13
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java19
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java2
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/node/CallNode.java22
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/node/Node.java1
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java31
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/node/StopCUNode.java9
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/node/StoreNode.java10
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/BoundaryTest.java86
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/ChainedComparisonTest.java37
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java2
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java7
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java7
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java8
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java12
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/FinalConstructionTest.java65
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/SeparateCompilationTest.java90
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/Simple.java25
-rw-r--r--chapter28b/src/test/smp/examples/A_helloWorld.smp (renamed from chapter28b/docs/examples/A_helloWorld.smp)0
-rw-r--r--chapter28b/src/test/smp/examples/BubbleSort.smp (renamed from chapter28b/docs/examples/BubbleSort.smp)0
-rw-r--r--chapter28b/src/test/smp/examples/Capitalize.smp (renamed from chapter28b/docs/examples/Capitalize.smp)0
-rw-r--r--chapter28b/src/test/smp/examples/Dijkstra.smp (renamed from chapter28b/docs/examples/Dijkstra.smp)0
-rw-r--r--chapter28b/src/test/smp/examples/FileIO.smp (renamed from chapter28b/docs/examples/FileIO.smp)0
-rw-r--r--chapter28b/src/test/smp/lazy/App.smp3
-rw-r--r--chapter28b/src/test/smp/lazy/App/Main.smp1
-rw-r--r--chapter28b/src/test/smp/lazy/App/Stats.smp1
-rw-r--r--chapter28b/src/test/smp/lazy/App/Tables.smp4
-rw-r--r--chapter28b/src/test/smp/separate/client/Main.smp6
-rw-r--r--chapter28b/src/test/smp/separate/library/Counters.smp12
51 files changed, 822 insertions, 2708 deletions
diff --git a/chapter28b/README.ja.md b/chapter28b/README.ja.md
index 4feaee93..d04e9ccb 100644
--- a/chapter28b/README.ja.md
+++ b/chapter28b/README.ja.md
@@ -1,110 +1,108 @@
-# 第25章: モジュール、分割コンパイル、SSA 構築
+# 第28b章: 分離コンパイル
[English](README.md) | 日本語
-この文書は [原文](README.md) の日本語訳です。
+[Previous: Chapter 28a](../chapter28a/README.ja.md)
-[前の章: 第24章](../chapter24/README.ja.md) |
-[章の一覧](../README.ja.md#各章)
+前の章では、見つかったソースを一度にコンパイルしました。この章ではライブラリと
+クライアントを別々にコンパイルします。クライアントにはライブラリのオブジェクト
+ファイルが必要ですが、そのソースは不要です。
-この章では Simple のソースファイルを再利用可能なオブジェクトファイルへコンパイルし、その型と理想 IR を後のコンパイルに読み込み、別に構築したシステムライブラリとネイティブプログラムをリンクします。また、不完全な情報を扱うため、パーサー、型、メモリ SSA を大幅に再設計します。
+## オブジェクトに理想 IR を保存する
-この番号付きスナップショットは比較用に残しています。章の分割は進行中で、型未確定時の SSA 構築、オブジェクトの構築、エスケープ解析、モジュールを名前付きの章に分けました。[分割計画](../docs/chapter-split.md) と [バックポート待ち一覧](../docs/chapter-backports.md) を参照してください。
+通常の ELF オブジェクトには機械語、データ、シンボル、再配置情報があります。
+Simple はさらに `.simple` セクションへ型と理想 IR を保存します。クライアントは
+宣言を読み込んで型を検査し、関数本体も読み込んで SCCP やインライン化に使います。
+残った呼び出しは、ネイティブリンクでライブラリの機械語につなぎます。
-第21〜24章のメモリ処理はここにも引き継ぎます。Load 検索は読み取りをループ外へ移す前にマージの全枝を証明し、Phi の因数分解はメモリ破壊を確認し、スケジューリングはメモリ集合体を通じてエイリアスをたどります。検索は `EscapeNode` から非公開または公開メモリへ進み、未解決の Store エイリアスを待ちます。配列長の読み取りには要素の境界検査がなく、配列要素の読み取りは制御を保持します。整数値の Phi は、既存の型付き変換機構で、ループの拡大を通じてもロード幅を保持します。New は引き続き非公開のコンストラクタメモリを生成し、公開は Escape が扱います。以前の章の割り当て入力設計は、これを置き換えません。
+宣言型と解析中の型は別々に保存します。ある時点でフィールドの値が 0 でも、
+その宣言を定数 0 に変えてはいけません。引数の型の上限、数値演算のモード、
+Store の幅、元のソース位置も保存します。入れ子の構造体には、関数本体だけでなく
+割り当て関数とコンストラクタの宣言も必要です。
-[直線的な履歴](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) することもできます。
+別々のコンパイルで同じ番号になったエイリアスや関数は、同じものとは限りません。
+`GlobalBits` は定義元のファイルとファイル内の番号を保存し、インポート時にその
+組み合わせを今回の番号へ写します。型とノードに同じ写像を使い、複数ファイルを
+またぐ再帰型も復元します。
-この章の[完全な言語文法](docs/25-grammar.ja.md)はこちらです。
+読み込みの単位は現在オブジェクトです。無関係なオブジェクトは読み込まずに済みますが、選ばれた
+オブジェクトの保存済み IR は全体を復元し、記録された依存先も読み込みます。`sys.io.p` だけを
+呼ぶ場合でも、`sys.o` にまとめられた十個の単位をすべて読み込み、`aryu8` と `aryi64` も含みます。
+必要なメンバーだけの読み込みは今後の目標です。IR の読み込みだけでクラスが初期化されたり、
+その本体がクライアントの出力にコピーされたりするわけではありません。元のソースの解析は省けますが、
+インポートしたグラフも新しい呼び出し元とともに最適化し、型検査します。
-## コンパイル単位と名前
+## 実行できる例
-`CompUnit` はソースまたはオブジェクトファイルを表します。`ParseAll` は依存関係を発見し、必要なソースを解析し、以前コンパイルした単位を読み込みます。ソースツリーとビルドツリーは対応する相対パスを使い、クラス名はドット区切りのパスを使います。たとえば `a/b/foo.smp` は名前空間 `a.b.foo` を供給します。
+英語版の例は次の二つのソースルートにあります。
-各単位は Start/Stop ノードを持ちます。関数がコンパイル単位に所有されることと、未知の外部呼び出し側を表す辺とは別です。出力のためにコードを保持することが、任意の呼び出し側から到達できることを意味してはいけません。
-
-ドライバは入力パスからプロジェクトルートを推定し、出力の `-o` と外部 Simple オブジェクト検索パスの `-L` を受け付けます。検索パスにはオブジェクトファイルやディレクトリを指定できます。ヘルプには `--root` もありますが、この番号付きスナップショットでは未実装です。名前付きのモジュール章では使用できます。コンパイラはネイティブリンク用の外部 C シンボルも解決します。`_` で始まる名前と外側の名前は、未知の呼び出し元に公開するインターフェースを決めます。
-
-明示的な数値型の C 宣言は、可変のネイティブ記憶領域へ直接束縛します。
-
-```java
-i32 counter = "C";
-counter += 1;
-return counter;
+```text
+src/test/smp/separate/library/Counters.smp
+src/test/smp/separate/client/Main.smp
```
-ネイティブリンカが `counter` を解決します。読み書きはその C 変数にアクセスし、宣言はコピーも初期化もしません。繰り返した宣言とモジュールインポートは同じ記憶領域エイリアスを共有します。整数と浮動小数点データの束縛をサポートし、ポインタと集合体のデータ束縛はまだサポートしません。外部フィールドは記憶領域フラグを持ち、宣言元クラス内でバイトを占めません。配置はフィールドオフセットを `ExternOffset` シンボルへ解決し、命令選択はクラス先頭+オフセットをネイティブシンボルアドレスへ置き換えます。ネイティブオブジェクト出力は既存の AMD64 ELF ライタを使い、ARM と RISC-V はメモリ内リンカを通じてこれらのアドレスをサポートします。スレッドローカル変数と C マクロにはアクセサ関数が必要です。システムライブラリは小さなネイティブランタイムアクセサを通じ、`libc.errno()` を公開します。
-
-## オブジェクトファイルは理想 IR を含む
-
-ELF ライタは、ネイティブコードと、正規化された型、理想グラフノード、依存関係を含む `.simple` セクションを保存します。リーダはそのグラフを再構築し、インポートした関数をインライン化などの最適化に参加させます。インライン化せず残った呼び出しは、ネイティブリンクで解決します。
-
-`GlobalBits` はファイルの同一性とファイルごとのインデックスを、エイリアス、関数対象、戻り位置の局所的な密なインデックスへ対応付けます。独立したコンパイルの等しい局所番号を、等しい同一性と取り違えてはいけません。読み込み後に型を閉じて更新し、単位をまたぐ循環参照も扱います。
-
-シリアライズは、宣言型を精密化されたフロー型とは別に保持します。意味を持つすべてのノード/型のフィールドには、対応する読み書きサポートが必要です。ソースにある高コストなシリアライズの全単射検査は現在無効で、より大きな増分再ビルドテスト `testModule0` は無視されています。どちらも有効な検証保証ではありません。
-
-外部フィールドのフラグと記号的オフセットは、シリアライズされた配置を変更します。マジックは `C0DE` のままで、独立した形式バージョンはまだありません。このコンパイラへインポートする前に、すべての Simple オブジェクトファイルを再ビルドしてください。
-
-## 型が完成する前の SSA 構築
+ライブラリは、ループで final フィールドを計算する `Counter` と共有カウンタを
+公開します。クライアントの `run(3)` は最初に 107 を返します。同じプロセスで
+続けて `run(5)` を呼ぶと 231 になります。インスタンスは別々ですが、クラスの
+カウンタは共有されます。`run(0)` はライブラリを使わずに戻ります。
-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]);
+```sh
+make lib
+make build/classes/test/.ttag
+java -ea -cp "build/classes/main:build/classes/test:lib/*" \
+ com.seaofnodes.simple.Simple --norun -o build/separate-example/lib/ \
+ src/test/smp/separate/library/Counters.smp
```
-宣言で割り当て補助関数を生成します。これは割り当てを行い、隠れた `<init>` とユーザーコンストラクタを順に呼び、`EscapeNode` を通じて初期化済みの非公開メモリを公開します。パーサーの `Var` メタデータは、早すぎるフィールド読み取りやコンストラクタ出口での未初期化を検出します。Store の幅は保存する値ではなく、対象の宣言から得ます。
+次にオブジェクトを使ってクライアントを評価します。
-ファイルレベルのコードは、インスタンス初期化とは異なるクラス初期化子(`<clinit>`)で表します。遅延初期化と循環検査については[英語版の説明](README.md#lazy-class-initialization)を参照してください。今後の計画は[英語版ロードマップ](ROADMAP.md#class-initialization-and-compiler-follow-ups)にあります。
-
-## システムライブラリと例
-
-以前の単一の `sys.smp` ライブラリは、libc 束縛、I/O、文字、スキャナ、配列、伸長可能なバッファ、ビット集合の単位に整理されました。ネイティブドライバは実際のプログラム引数をサポートします。`docs/examples` の例には Bubble Sort、Capitalize、Dijkstra、FileIO があります。
+```sh
+java -ea -cp "build/classes/main:build/classes/test:lib/*" \
+ com.seaofnodes.simple.Simple --eval \
+ -L build/separate-example/lib/Counters.o \
+ src/test/smp/separate/client/Main.smp 3
+```
-`Chapter25Test` は、意図的にインライン化せず残す Hello World 呼び出しを含め、ライブラリ読み込みとネイティブリンクを検証します。そのテストは `sys.o` とリンクする前に、クライアントの外部シンボルを確認します。ほかのテストは前方コンストラクタ、呼び出し経由のフィールド更新、リテラルのエスケープ、縮小したコンパイラ失敗を検証します。独立した回帰テストは以前の章へ移す候補です。
+結果は `107` です。Windows のクラスパス区切りには `;` を使います。クライアントの
+ソースルートには `Counters.smp` がありません。`SeparateCompilationTest` はさらに
+二つのオブジェクトを C ドライバとリンクし、繰り返し呼び出す動作を検査します。
-## ビルドと確認
+## 解析とコードの所有者
-このディレクトリで `make lib` は Java テスト依存関係を用意し、`make tests` は章の生のスイート、独立スイート、システムテスト、ファザー回帰テストを実行します。`make release` はコンパイラ jar とネイティブライブラリ成果物を構築し、`make tags` はエディタ用タグを構築します。ネイティブツールと選択した CPU/ABI は環境と一致する必要があります。Makefile は現在、既定で x86-64/win64 を使います。
+インポートした本体は解析に参加しても、クライアントの出力には属しません。
+`Serialize` は `Lower` がクラス初期化を展開する前に理想 IR を保存します。
+`Unlink` は機械語生成に向けてインポートした本体と解析用の呼び出し辺を切り離します。
+インライン化でクライアントへコピーされたコードは、クライアント自身の本体として
+出力されます。
-シリアライズ IR は `C0DE` ヘッダを使います。ポインタ型は独立した読み取り専用アクセスビットを含みます。形式にはまだバージョン番号がないため、形式を変更したらオブジェクトファイルを再ビルドしてください。現在、後方互換性は保証していません。
+初期化規則は前の章と同じです。型のインポートだけでは初期化しません。能動的な使用は
+`CLInit` を通して初期化を要求し、ライブラリが持つクラスオブジェクトとフラグを使います。
+依存関係の検査にはインポートした本体も含めます。ネイティブコードへ渡した関数の
+未知の呼び出し元も保持します。
-コンパイラ変更は `sys.o` のシリアライズ IR とネイティブコードの両方を無効にする場合があります。リンクしたプログラムのテスト結果を解釈する前に再ビルドしてください。ソースだけの部分集合は、この章の完全な `make tests` と同等ではありません。
+コンストラクタのフィールド更新はローカル値と Phi のままで、ヘルパーの出口に Store を
+置きます。未完成のオブジェクトでも初期化済みフィールドは読めます。
+アンダースコアは公開インターフェースの選択に使いますが、ソースのアクセス制限は
+[レビュー待ち](../docs/chapter-backports.md)です。
-## RegAlloc の改良: 生存区間の面積と分割コスト
+## 検証と今後
-第24章はループ Phi の高頻度辺を分割する前に、低頻度のコピーを試しました。この章ではさらに、スピルがスケジュール済みコードのどれだけをレジスタ圧力から解放するかを推定し、新しいコピーの推定コストで割ります。callee-save 区間は関数全体を覆います。複製可能な定数は使用の近くで再構築できます。ループ内のコピーは高コストなので、`8^loopDepth` で重み付けします。特に多数の使用を持つ値については、低コストな近似です。
+リポジトリのルートから次を実行します。
-複数の固定レジスタ使用を直接分割する規則や、一部の複数定義区間をより深く分割する規則など、第25章のほかの衝突処理はここに残します。サイズのしきい値はヒューリスティックであり、最初の割り当て器へバックポートすべき一般的不変条件ではありません。共有される合法性修正は、引き続き最も早い該当章から始めます。最終監査は第20〜25章の固定隣接ノードの色バイアス、第20〜21章の狭いストアマスク、第21〜25章の ARM レジスタバンク/浮動小数点メモリエンコーディングを修正しました。
+```sh
+make tests CHAPTERS=chapter28b
+```
-第21章の隣接コピー転送も引き継ぎます。次の命令だけで使うコピーは、オペランドがコピー元レジスタを受け入れ、命令の結果に結び付けられていなければ除去します。
+保存したコンストラクタ、インポートをまたぐ初期化循環、共有データ、ネイティブ関数の
+サンク、ライブラリとクライアントの実行を検査します。Make はテスト用の `sys` も
+オブジェクトにします。`make release` は jar と標準ライブラリを作り、`make view` は
+共通のグラフビューアーを起動します。
-現在の比較は、固定したテスト群を行、コンパイラの章を列にした
-[英語版の測定表](README.md#regalloc-improvements-live-range-area-and-split-cost) を参照してください。
-各セルは ARM/RISC-V の実行命令数、そのうち割り当て器が生成したコピーなどの命令数、
-x86 のループ重み付き移動数を示します。これらは異なる指標です。
-対象、入力、除外項目は [測定方法](../docs/regalloc-spills.md) にあります。
-古い測定群の集計値と混ぜて比較しないでください。
+保存形式は `C0DE` 以外のバージョン番号をまだ持ちません。コンパイラや形式を変えたら
+オブジェクトを再構築してください。高コストの往復一致検査は無効のままで、古い増分
+ビルドの実験 `testModule0` も実行対象外です。
-クラス初期化、インライン化、エスケープ解析、ライブラリの変更もグラフを変えます。
-章をまたぐ差を割り当て器だけの品質差と解釈せず、同じ入力とコンパイラで
-ヒューリスティックを比較してください。
+[分割計画](../docs/chapter-split.md)の主要な七つのスナップショットはこれで揃います。
+ネイティブライブラリの構築と最適化の調整は[付録](../docs/appendices/README.md)にまとめています。
diff --git a/chapter28b/README.md b/chapter28b/README.md
index d6ab5666..189f2b7b 100644
--- a/chapter28b/README.md
+++ b/chapter28b/README.md
@@ -1,267 +1,177 @@
-# Chapter 25: Modules, Separate Compilation, and SSA Construction
+# Chapter 28b: Separate Compilation
English | [日本語](README.ja.md)
-[Previous: Chapter 24](../chapter24/README.md) |
-[Chapter index](../README.md#chapters)
+[Previous: Chapter 28a](../chapter28a/README.md)
-This chapter compiles Simple source files into reusable object files, loads
-their types and ideal IR into later compilations, and links native programs
-against a separately built system library. It also contains a substantial
-parser, type, and memory-SSA redesign to support incomplete information.
+The previous chapter compiled every discovered source file together. This
+chapter lets a library and other code compile separately, and have full type
+information. The client needs the library's *object* file, but not its source.
+In spirit this is similar to Java's *class* file, with its full execution and
+type semantics stored in the file; we happen to be using an ELF file for the
+same reason. The ahead-of-time compiled code is *also* available for the
+linking step.
-You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter25)
-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).
+An ordinary native object contains machine code, data, symbols, and
+relocations. Simple adds a `.simple` section with types and the ideal IR.
+Importing `.simple` lets the client check types and optimize calls into the
+library. Calls that remain out of line use the library's machine code at
+native link time.
-Here is the [complete language grammar](docs/25-grammar.md) for this chapter.
+## A library and a client
-## Compilation units and names
+The runnable example has two separate source roots:
-`CompUnit` represents a source or object file. `ParseAll` discovers dependencies,
-parses needed sources, and loads previously compiled units. Source and build
-trees use corresponding relative paths; class names use dotted paths. For
-example, `a/b/foo.smp` supplies the namespace `a.b.foo`.
-
-Each unit has Start/Stop nodes. A function's ownership by a compilation unit is
-distinct from an edge representing unknown external callers: retaining code
-for emission must not imply that arbitrary callers can reach it.
+```text
+src/test/smp/separate/library/Counters.smp
+src/test/smp/separate/client/Main.smp
+```
-The driver infers the project root from the input path and accepts `-o` for
-output and `-L` for external Simple object search paths, including an object
-file or a directory. Its help advertises `--root`, but this numbered snapshot
-does not yet parse that option; the named modules snapshot does.
-The compiler also resolves external C symbols for native linking. Names beginning
-with `_` control the public interface; enclosing names also affect which
-entries are exposed to unknown callers.
+`Counters.smp` declares shared state and an instance constructor:
-Explicit numeric C declarations bind directly to mutable native storage:
+```simple
+int calls=0;
+val next_noInline = { -> calls++; return calls; };
-```java
-i32 counter = "C";
-counter += 1;
-return counter;
+struct Counter {
+ val value=1;
+ new Counter = { int n ->
+ value=0;
+ for( int i=0; i<n; i++ ) value=value*2+1;
+ };
+ val get = { -> value; };
+};
```
-The native linker resolves `counter`. Reads and writes access that C variable;
-the declaration neither copies nor initializes it. Repeated declarations and
-module imports share the same storage alias. Integer and floating-point data
-bindings are supported; pointer and aggregate data bindings are not yet supported.
-External fields carry a storage flag and occupy no bytes in their declaring
-class. Layout resolves their field offsets to `ExternOffset` symbols; instruction
-selection replaces class-base-plus-offset with the native symbol address.
-Native object output uses the existing AMD64 ELF writer; ARM and RISC-V support
-these addresses through the in-memory linker. Thread-local variables and C macros
-need accessor functions: the system library exposes `libc.errno()` through a small
-native runtime accessor.
-
-Native function pointers still occupy four bytes. A native library can live
-above that address range, so the AMD64 ELF writer gives each address-taken
-native function a small entry in Simple's low-address code. The entry tail-jumps
-through the native symbol's PLT entry; arguments and the return address pass
-through unchanged. Direct calls still use the native symbol directly.
-
-Static class fields and addresses computed in code both refer to that entry.
-Its stable weak symbol, `__simple_native$<name>`, lets separately compiled
-objects agree on one pointer when linked into an executable. The class image
-uses a four-byte relocation to this defined entry, instead of a four-byte
-relocation to a dynamic native symbol in writable data, which GNU ld rejects.
-Constant field Stores can still fold away. The entry is a Simple function
-identity; it need not equal the raw address obtained by native code.
-
-`StaticDataTest.testNativeFunctionIdentity` reads two separately compiled class
-images before their initializers run, compares their stored and computed
-addresses, and calls the native function. It checks both object-link orders.
-
-## Object files contain ideal IR
-
-The ELF writer stores native code and a `.simple` section containing canonical
-types, ideal graph nodes, and dependencies. The reader reconstructs that graph
-so imported functions can participate in optimization, including inlining.
-Calls that remain out of line are resolved by native linking.
-
-`GlobalBits` maps file identities and per-file indices to local dense indices
-for aliases, function targets, and return points. Equal local numbers from
-independent compilations must not be mistaken for equal identities. Types are
-closed and upgraded after loading, including cross-unit cyclic references.
-
-Serialization preserves declaration types separately from sharpened flow types.
-Every semantic node/type field needs matching read/write support. The source's
-expensive serialization bijection check is currently disabled, and the larger
-incremental-rebuild test `testModule0` is ignored. Neither is an active
-validation guarantee.
-
-The external-field flag and symbolic offsets change the serialized layout.
-The magic remains `C0DE`; there is no separate format version yet. Rebuild all
-Simple object files before importing them with this compiler.
-
-## Constructing SSA before types are complete
-
-Simple still parses directly to SSA without an AST. Forward references mean an
-expression's eventual type may be unavailable while parsing it. Syntax and
-resolved lexical binding therefore choose topology; types subsequently refine,
-optimize, and validate the graph.
-
-- Arithmetic nodes defer integer versus floating-point mode selection, including
- resolution through recursive graph components after SCCP.
-- Calls preserve their syntactic receiver slot before the callee kind is settled.
- Symbolic field information defers layout-dependent decisions.
-- Loads and ordinary Phis no longer carry parser-supplied lower-bound types.
-- `TypeScalar` represents integer, float, memory-pointer, and function-pointer
- values separately from global control/memory bottom.
-- `TypeStruct._open` describes an incomplete field set; `_fref` records the
- absence of an authoritative definition. Discovering fields does not define a type.
-- Guards carry branch-proven zero/nonzero facts before the input family is known.
- `FunPtrNode` retains an explicit edge to its function's return graph.
-
-The driver separates optimization from lowering:
+`Main.smp` uses both parts of the library:
-```text
-Parse -> Iter -> Opto -> IterPostOpto -> FinalizeIR -> TypeCheck
- -> Serialize (when producing an object) -> Lower -> LoopTree
- -> Unlink -> Select -> Schedule -> LocalSched -> RegAlloc -> Encoding
+```simple
+val run = { int n ->
+ if( !n ) return 0;
+ val c = new Counters.Counter(n);
+ return c.get()+100*Counters.next_noInline();
+};
+return run(arg);
```
-`Opto` runs SCCP and resolves numeric modes. `IterPostOpto` applies peepholes
-and inlining using those results; `FinalizeIR` settles field layouts and offsets.
-Type checking sees the optimized ideal graph. Serialization saves that graph
-before lowering introduces implementation details such as initialization guards.
-For machine compilation, `Unlink` detaches imported function bodies before
-instruction selection; calls retain the symbols needed for linking. Ideal-IR
-evaluation keeps those bodies available and skips machine selection.
+At runtime, `run(0)` returns without reaching or initializing `Counters`.
-## Memory and construction
+For `run(3)`, `Counters` initializes, the constructor computes 7 and
+`calls`==1, so we get `(7+100*1)` or 107.
-The parser carries ordinary bulk memory. The optimizer recovers precise aliases
-using `MemMerge`, `MemPhi`, and `BulkMemPhi`. A bulk Phi covers all aliases
-except its exclusions, represented by parallel precise Phis. Every slice must
-be covered exactly once.
+Calling `run(3)`, then `run(5)`, in one process increments the shared `calls`
+twice. The first call gives `(7+100*1)`, or 107; the second gives
+`(31+100*2)`, or 231.
-User constructors are declared and called as follows:
+(The `_noInline` suffix prevents inlining, so the example exercises native
+linking as well as imported IR.)
-```java
-struct Box {
- i64[] values;
- new Box = { i64[] initial -> values = initial; };
-};
-return new Box(new i64[1]);
+From this chapter directory, build the compiler and compile the library:
+
+```sh
+make lib
+make build/classes/test/.ttag
+java -ea -cp "build/classes/main:build/classes/test:lib/*" \
+ com.seaofnodes.simple.Simple --norun -o build/separate-example/lib/ \
+ src/test/smp/separate/library/Counters.smp
+```
+
+Then compile and evaluate the client using that object:
+
+```sh
+java -ea -cp "build/classes/main:build/classes/test:lib/*" \
+ com.seaofnodes.simple.Simple --eval \
+ -L build/separate-example/lib/Counters.o \
+ src/test/smp/separate/client/Main.smp 3
```
-An allocation helper is generated at the declaration. It allocates, calls the
-hidden `<init>` and then the user constructor, and publishes initialized private
-memory through `EscapeNode`s. Parser `Var` metadata detects early field reads
-and missing initialization on constructor exits. Store widths come from target
-declarations, not from the values being stored.
-## Lazy class initialization
+## What crosses the separate compilation boundary
-File-level code is represented by a class initializer (`<clinit>`), distinct
-from instance initialization. An active class-field access ensures that the
-class is initialized. Taking a class reference is passive: it identifies the
-class without running its body. Initialization runs once, on the path that
-first needs it, and a parent initializes before its child.
+The optimized IR owned by the object is compactly encoded in the `.simple`
+section and fully reconstructed when read back in. The saved state includes
+canonical declarations as well as optimized flow types. E.g. `int x=7` has
+declared type `int` and initial value `7`. Struct field types (both declared
+and optimized), function argument types (both declared and optimized), numeric
+modes, store widths, and source locations also survive serialization.
-For example, if `B.smp` computes `value`, then this code initializes B only
-when `take` is true; the second access reuses the initialized class:
+Each loaded `.simple` section restores its units into the shared `CompUnit`
+tree, mirroring the original code layout. A dependency shared by several
+objects uses the same `CompUnit`. Loaded aliases and function indices are
+renumbered to avoid collisions; all the dense indices end up in the same
+namespace. `GlobalBits` has the mapping from the imported file indices to the
+local compilation indices; this remapping extends everywhere through the loaded
+code, including recursive types that span several files. Importing skips
+parsing the original source, but it still analysed and type checked with its
+new callers.
-```java
-return take ? B.value + B.value : 0;
-```
+`ParseAll` is the workhorse that discovers source and object dependencies. It
+tracks missing symbol references, looking them up in the search path one by one,
+and loading the matching files.
+
+The end result combines locally parsed and imported IR in one graph, while
+keeping their separate `CompUnit`s. This combined compilation can take
+advantage of e.g. constants in the library, or inline library code using the
+normal inlining heuristics. The combined graph goes through the normal
+compilation phases, including peepholes, Opto and TypeCheck.
+
+Dependencies between `CompUnit`s are recorded. An explicitly requested source
+file is always compiled. Other source files are recompiled if their object is
+missing, their source is newer, or a recorded dependency's object in the build
+directory is newer. These timestamp checks are not yet a complete incremental
+build system, there is some modest future work needed to finish this.
+
+Loading currently works at the object granularity. Unrelated objects can stay
+unloaded, but selecting an object loads its entire saved graph and follows its
+recorded dependencies. Future work will look at loading sub-parts more
+incrementally.
+
+
+
+## Analysis, ownership, and initialization
+
+An imported function can participate in analysis without belonging to the
+client's output. Loading the `io` library, with its `p` print function, means
+that `p` is part of the analysis - but when writing out the `helloWorld` app,
+`p` will not end up in the `helloWorld.o` file directly. It might be inlined
+fully, or called out of line, but there won't be a stand-alone copy of it.
+
+After optimization, `Serialize` saves ideal IR before `Lower` expands class
+initialization. `Unlink` then detaches imported bodies for machine compilation
+and removes analysis call edges. The client emits its own bodies and any code
+copied into them by inlining; the
+remaining calls refer to symbols supplied by the library object.
+
+Class initialization follows the previous chapter's rules. Importing a type
+does not initialize its class. Active uses retain `CLInit` checks until
+lowering (unless optimization clears them out). The library supplies its
+shared class object and flag. Initialization checks appear at all the active-touch
+locations, whether local or remote; including source locations for error
+reporting.
-The compiler represents the obligation with an opaque `CLInit` node. It carries
-control, whole-memory effects, the class pointer, and a load of the hidden
-`$init` flag. Its results include a memory slice where that flag is known true.
-Ordinary memory SSA and load simplification can therefore remove repeated
-checks, including after a merge when every incoming path has initialized the
-class. Calls conservatively account for arbitrary memory effects.
-
-After optimization, the compiler builds a graph containing function calls,
-class touches, and parent requirements. A strongly connected component with
-more than one class initializer is an error. Ordinary recursion and helper
-cycles involving just one initializer are allowed. Checking after optimization
-means eliminated branches and call targets do not create false dependencies.
-Diagnostics show the cyclic edges and their source locations, including calls
-through helpers and imported code.
-
-Object files preserve opaque initialization nodes and their source locations.
-Imported ideal bodies participate in the combined cycle check before lowering.
-Lower then expands each remaining obligation into a flag test, a conditional
-initializer call, and control/memory merges. The initializer owns the flag store;
-the store is at body entry to support permitted same-class reentry. Cross-class
-cycles have already been rejected. This is a single-threaded protocol.
-
-The selected entry class receives the current integer `arg`; other initializers
-receive zero. Planned argument arrays and further initialization optimizations
-are described in the [roadmap](ROADMAP.md).
-
-## System library and examples
-
-The former single `sys.smp` library is organized into units for libc bindings,
-I/O, characters, scanners, arrays, growable buffers, and bitsets. The native
-driver supports real program arguments. Examples in `docs/examples` include
-Bubble Sort, Capitalize, Dijkstra, and FileIO.
-
-`Chapter25Test` exercises library loading and native linking, including a Hello
-World call deliberately kept out of line. That test checks the client's external
-symbol before linking against `sys.o`. Other tests cover forward constructors,
-field updates through calls, literal escapes, and reduced compiler failures.
-Independent regressions are candidates for earlier chapters.
## Building and checking
-From this directory, `make lib` supplies Java test dependencies and `make tests`
-runs the raw chapter suites, standalone suites, system tests, and fuzzer
-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 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
-is not equivalent to this chapter's full `make tests`.
-
-## RegAlloc improvements: live-range area and split cost
-
-Chapter 24 tried cold copies before splitting a loop Phi's hot edges. This
-chapter also estimates how much scheduled code a spill frees from register
-pressure, divided by the estimated cost of the new copies. Callee-save ranges
-cover a whole function. Cloneable constants can be rebuilt nearer their uses.
-Copies inside loops cost more: the estimate weights them by `8^loopDepth`.
-These are inexpensive approximations, especially for values with many uses.
-
-A single-use function address in the same block gets priority for
-rematerialization when it is separated from its use. Moving that address
-calculation next to the use adds no instruction and avoids preserving a
-callee-save register merely to hold the constant. Adjacent uses and multi-use
-addresses retain the area/cost ranking.
-
-Rows are fixed test cohorts; columns are compiler chapters. Each cell gives
-**Ops / RA / X**: executed ARM+RISC-V instructions, the subset emitted for
-allocator copies/rematerializations, and x86's loop-weighted split-move estimate.
-**Cases D / X** counts measured dynamic program/target cases and x86 compilations;
-membership and inputs stay fixed across columns. Native-library work is excluded.
-Chapter 20 has no encoder, hence no dynamic counts. Lower is better within each
-metric; see the [measurement details](../docs/regalloc-spills.md) for coverage.
-
-<!-- spill-matrix:start -->
-| Test cohort | Cases D / X | Ch 20: Ops / RA / X | Ch 21: Ops / RA / X | Ch 22: Ops / RA / X | Ch 23: Ops / RA / X | Ch 24: Ops / RA / X | Ch 25: Ops / RA / X |
-|---|---:|---:|---:|---:|---:|---:|---:|
-| Ch 20 | 22 / 13 | — / — / 161 | 55,173 / 5,407 / 174 | 58,751 / 9,099 / 175 | 58,736 / 9,084 / 184 | 58,720 / 9,068 / 183 | 58,965 / 9,076 / 208 |
-| Ch 21 | 20 / 18 | | 6,207 / 261 / 461 | 6,324 / 379 / 475 | 6,326 / 381 / 475 | 6,325 / 380 / 470 | 6,196 / 219 / 534 |
-| Ch 22 | 14 / 8 | | | 198 / 60 / 7 | 194 / 56 / 7 | 194 / 56 / 7 | 252 / 92 / 11 |
-| Ch 23 | 14 / 8 | | | | 630 / 160 / 36 | 630 / 160 / 36 | 864 / 358 / 36 |
-| Ch 24 | 36 / 20 | | | | | 4,923 / 1,341 / 790 | 4,646 / 1,335 / 609 |
-| Ch 25 | 2 / 12 | | | | | | 20 / 0 / 1,856 |
-<!-- spill-matrix:end -->
-
-
-Chapter 25 introduces several features, which when run on the older tests
-generate a lot more code, which in turn generates more spills.
-
-Class initialization, inlining, escape analysis, and library adaptations
-all change the graphs. To help with this, we adjust the inlining
-heuristics to try to inline and optimize the boilerplate.
+From the repository root:
+
+```sh
+make tests CHAPTERS=chapter28b
+```
+The suite includes the preceding constructor and escape regressions, imported
+constructors, initialization cycles across objects, shared static data, native
+function thunks, and native library/client execution. Make also builds the
+modular `sys` object used by the library tests. Clients can import that object
+with `-L`; they do not need to parse the standard library's source again.
+`make release` builds the launcher jar and standard library; `make view`
+starts the shared graph viewer.
+
+The serialized format is internal and currently unversioned beyond its `C0DE`
+magic. Rebuild Simple objects when the compiler or layout changes. The expensive
+serialization bijection check remains disabled, and `testModule0`, the older
+incremental-rebuild experiment, remains ignored. They are not validation
+guarantees for this snapshot.
+
+This completes the seven main snapshots in the [approved split](../docs/chapter-split.md).
+See the appendices on [native libraries](../docs/appendices/native-libraries.md)
+and [optimizer/backend tuning](../docs/appendices/optimizer-tuning.md).
diff --git a/chapter28b/ROADMAP.ja.md b/chapter28b/ROADMAP.ja.md
deleted file mode 100644
index 222f228a..00000000
--- a/chapter28b/ROADMAP.ja.md
+++ /dev/null
@@ -1,451 +0,0 @@
-# ロードマップ
-
-[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/chapter28b/ROADMAP.md b/chapter28b/ROADMAP.md
deleted file mode 100644
index b5647587..00000000
--- a/chapter28b/ROADMAP.md
+++ /dev/null
@@ -1,523 +0,0 @@
-## Separate Compilation
-
-English | [日本語](ROADMAP.ja.md)
-
-
-How to handle unknown forward ref?
-No H-M, so no unification.
-Demand no forward refs beyond functions... which will provide immediate signatures when def'd.
-
-Plan A: Remove FRefNode; fix all my bugs.
-Fail A: I want mutual co-recurusion to work
-Example: `val even = { int x -> x ? odd(x-1) : true; }; val odd = { int x -> x ? even(x-1) : false; };`
-
-Plan B: Require types ordered, but not any other fcn. `new T` can complain,
-but not `fref.field` or `fref()` or `fref op fref` or `fref[]`
-
-Requires "lifting" Type.BOTTOM to the parser requirements everywhere.
-So loading a field from an FRef requires: lift the FREF to a
-TMP-to-TSTRUCT-with-FIELD... actually, just locally assume FREF will become
-correct. Can't Lift it, no access, since type comes from random code.
-TMP might be nullable (should error later).
-TSTRUCT can/will be unnamed. Implies ALL TSTRUCT is unnamed.
-
-Issue is some unknown/remote/external/forward-ref.
-
-Allowing an FRef *at all* means everybody has to deal with seeing an FRef Type.
-Allowing it for functions in same scope allow the above odd/even case .
-Allowing it for structs in the same scope allows for co-recursive structs.
-Promoting out-of-scope attempts the external lookup, then fails.
-
-Random junk usage: `sys.fld`. Looking up `sys` fails, now has to
-guess: fref is ok, will be defined shortly... OR extern lookup now.
-Then some usage delays trouble:
-```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....;
-
-```
-
-Which gets to: parser never complains, builds SSA like a blind AST,
-just wires up a graph with bad types all around. Requires: No Types
-to guide parser! Just Syntax!
-
- (Obvious minor fail: widen floats- or-not) Fix: insert e.g. int-oper, return
- type is MEET of Int,Float until both inputs are isa-Int and cannot lift to be
- isa-Float
-
- Possible method call fail:
- UNKNOWN_STRUCT_BOT.UNKNOWN_FIELD ( ARGS ); // Insert SELF OR NOT?
- Possible fix: always assume a SELF arg, which is null for provable statics.
- Optimization: constant parms get dropped from sig? *Constant* parms
- must be constant from *Parser*, or else no unknown callers (so local opts).
- Possible fix: SELF arg is *last* not *first*, easier to drop if dead?
-
- Load drops ConFldOff, keeps name.
- Load keeps index unscaled, has index input, which is null for structs.
- After Ptr Base is *closed*, base layout can happen, can fold index math.
- - Load(ctrl, base, null ); // field name internal
- - Load(ctrl, base, index_math ); // field name=='[]'
- - Store(ctrl, base, null , val);
-
- FAIL: No Alias Info!
-
-
-Plan C:
- FRefs allowed for direct calls `odd()`, and for Types.
- All other cases must *lift*.
-
-
-## Unifying Structs and Arrays
-
-The idea is that structs have fields indexed by names, and arrays have fields
-indexed by numbers, and these ideas are compatible with each other. This idea
-has been used by the C language for a long time - an array can follow a normal
-struct C and can index it with the usual array syntax.
-
-The alternative, using a field to hold a pointer to an array, exchanges some
-flexibility for an extra indirection. The obvious flexibility allows swapping
-the array out (generally for a larger one) and this is the basis of very common
-`Vector` or `ArrayList` generic containers.
-
-
-```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
-
-```
-
-Here, the field named `#` holds the array length and the field named `[]` is
-the actual array, which follows the other fields directly in memory.
-Here is another example for variable length array:
-
-```java
-struct vecInt {
- u32 size; // Actual number of elements
- u32 #; // The array length or capacity
- int []; // Array of integers.
-};
-```
-
-The obvious limit here is that the array cannot be directly resized; it is
-literally in the same memory block as the leading fields. Such a `vecInt` can
-be *re-allocated* to a new larger `vecInt`, but the original `vecInt` will be
-reclaimed - you cannot use the same `vecInt` reference forever.
-
-Any number of named fields are allowed:
-
-```java
-struct Class {
- !str !className;
- !str !professor;
- int time;
- int credits;
- !Class !prerequisite;
- u16 #; // Limit of 65535 students per class
- Student [];
-};
-```
-
-Or self-reference:
-
-```java
-struct NTree {
- Some val;
- u8 #; // Length is 0-255
- NTree []; // More NTrees
-};
-```
-
-The syntax rules are:
-- the last field is named `[]` and can be of any type.
-- the second-to-last field is named `#` and must be some unsigned integer type
- representing the maximum array length.
-- may require a leading space in front of ` []`
-
-
-
-### Constructors and Arrays
-
-The allocation includes the array length, and can include a constructor:
-`new NTree{val=17;}[2];`
-
-The constructor is optional, the array length is not.
-
-Optional function for initializing arrays, allows for write-once final arrays:
-`new u8[buffer#,{ u32 idx -> buffer[idx].toUpperCase() }]`
-
-
-
-### Changing array max length
-
-An issue for large numbers of range-checked small arrays is that reserving a
-full 4 bytes for array length can substantially increase the array overhead.
-Simple allows for arrays to have smaller max lengths, although these are
-treated as a different type than arrays with different max lengths.
-
-```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;
-```
-
-
-### Two flavors of extendable arrays/strings: an extra indirection or not
-
-With an extra indirection you get e.g. Java `ArrayList` or C++ `Vector`.
-
-Without, then any call which extends the extendable array return a potentially
-new object, only growing as needed.
-
-```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
-```
-
-
-## A String Implementation
-
-Default strings have names like "str", "cstr", "ustr" or such.
-
-Always unicode8 with explicit u8 manipulations. Unicode "characters" will come
-as function calls over the base implementation, probably in another string class.
-
-## Default string
-
-Efficiently growable via concantenation. Supports a variety of IO/printf functions.
-The underlying array can be shared mutable if you ask for this.
-The defaults will always be private mutable and read-only shared.
-
-```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
-};
-```
-
-
-### A Final String
-
-Intended to be a **immutable** string, possibly used as a base for a **secure**
-string. This needs to be created all at once, and probably requires a
-defensive copy.
-
-```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
-```
-
-
-### A Extendable String (aka StringBuilder or StringBuffer)
-
-This API is intended to be a drop-in for a `StringBuilder` style, and takes an
-extra indirection with each usage.
-
-```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); };
-};
-```
-
-### A Unicode String
-
-This API is intended to support unicode character manipulation over a plain `str`
-
-```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
-};
-```
-
-### A "C" string
-
-A zero-terminated string, but the length has to be available directly
-
-```java
-struct cstr {
- u8 #; // length including the trailing zero
- u8[]; // always ends in a zero byte; suitable for direct passing to C string functions
-};
-```
-
-### A "german string": a very short string that can exist in a register
-
-Length in memory is 1 byte (3 bits?) of length, then 0-7 chars; max 8 chars.
-Fits in a 64 bit register.
-
-```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 becomes `val main = { str[] args -> ...}`
-
-
-## Structs can be inlined
-
-Structs are normally by-reference, but can be converted to by-value by using
-the `*` operator, pronounced "contents of" (this name might be changed to
-e.g. `inline`).
-
-This is a name-space change only, you cannot take the address of the internal
-struct without entirely consuming its lifetime. No "escaping".
-
-```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
-
-```
-
-
-
-## RoadMap for other chapters
-
-`&&` and `||`
-Drop `int` to `i32`.
-Drop `TypeMemPtr` to 4 bytes; using `mmap` to make heap in low 4G.
-Default parser for-loop construction, or partial peel.
- `for( init; test; next ) body`
-becomes:
-```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 `||`
-
-The logical operators && (AND) and || (OR) are short-circuiting, meaning they may skip evaluating the second condition if the first one already determines the result:
-
-- `&&` stops if the first operand is false.
-- `||` stops if the first operand is true.
-
-They are syntactic sugar—used for cleaner control flow—but they don't create new nodes in the execution model.
-
-The `&&` and `||` constructs are simple syntactic sugars that only modify the control flow and do not
-introduce new nodes.
-
-Consider this example:
-```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++` evaluates to 1 (true), so `b++` is not evaluated.
-- As a result, `b` stays 0, `a` becomes 2, and "Or" is printed.
-
-```java
-int a = 1;
-int b = 1;
-
-int x=1;
-int y=1;
-int z=0;
-
-int g = x++ && y++ && z++;
-...
-```
-Here:
-- `x++` is true (1), so it evaluates `y++`, which is also true.
-- Then it evaluates `z++`, which is false (0).
-- Since `&&` requires all conditions to be true, the final result `g` is 0.
-- All operands up to the false one are evaluated, so `x`, `y`, and `z` are all incremented.
-
-## Class initialization and compiler follow-ups
-
-### Entry arguments
-
-Replace the integer initializer argument with an argument array. The selected
-entry class receives a non-null array, empty when there are no arguments;
-other class initializers receive null. Coordinate the parser, entry signature,
-runtime driver, and separately compiled interfaces.
-
-### Pre-initialize classes from ELF data
-
-Prove when a class's entire initialization can be represented by ELF static
-data and relocations, with no remaining runtime effects. Emit its initial
-contents and start its `$init` flag as true, so no initializer call is needed.
-Carry that fact through separate compilation so importing compilers can remove
-checks too. A constant field alone is not proof that the whole class can be
-pre-initialized; I/O, argument-dependent work, and other observable effects
-still require execution. Preserve any dynamic parent requirement, and do not
-let cyclic dependencies justify their own pre-initialization.
-
-Mutable fields may have static initial contents without becoming constants.
-Choose read-only storage only when no runtime writes remain. Static reference
-cycles can use relocations without requiring a dynamic initialization cycle.
-
-For a wholly immutable, pre-initialized class, eliminate the mutable `$init`
-state as well as the checks so the class image can reside in ROM. The native
-function thunks preserve static initialization of function fields.
-
-### Compact code and data arenas
-
-Keep four-byte Simple references into bounded code and data arenas; the arenas
-need not overlap or live near zero. Choose absolute addresses versus offsets
-from an arena base when implementing placement and indirect calls. Native
-function thunks can occupy the code arena while native libraries live elsewhere.
-The current thunk implementation uses the existing low-address executable;
-ordinary data pointers still occupy eight bytes.
-
-Give full-width native pointers separate types and foreign lifetime contracts.
-Opaque device addresses may need to be passed through without CPU dereference.
-Simple object lifetimes should be managed automatically without a GC;
-ownership, borrowing, cycles, and native retention still need design. Compact
-storage alone does not establish those rules. Preserve static class images and
-explicit native boundary conversions when choosing the arena representation.
-
-### CFG frequency estimates
-
-After lowering and loop-tree construction, estimate block frequencies for GCM
-and code layout. Treat initialization and known-failure paths as rare; use
-bounded loop-trip estimates and branch probabilities when no profile is
-available. Keep estimates consistent with control flow and shared between
-scheduling and layout. These are backend estimates, not ideal-IR types.
-
-### Constant instance-field storage
-
-When a final instance field has the same constant value in every instance,
-fold accesses or store the value on the class object and omit the instance
-slot. Preserve field-access semantics and public function reachability:
-externally accessible function fields remain callable regardless of storage.
diff --git a/chapter28b/ainotes.txt b/chapter28b/ainotes.txt
index 625acddc..c0b03a4d 100644
--- a/chapter28b/ainotes.txt
+++ b/chapter28b/ainotes.txt
@@ -1,4 +1,8 @@
+Historical design notes from the former combined Chapter 25, now Chapter 28b.
+Current status and remaining work are in ../docs/stage-audit.md and
+../docs/chapter-backports.md. The original intent below is preserved.
+
Meta-info: I intend to keep extending and re-editing this "ainotes.txt" file so
I can reprompt the you (the AI) between AI restarts, As such this file will
contain a mix of high-level design decisions, goals, intents and wisdom, and
diff --git a/chapter28b/docs/25-grammar.ja.md b/chapter28b/docs/28b-grammar.ja.md
index 9e9fb820..65944fe2 100644
--- a/chapter28b/docs/25-grammar.ja.md
+++ b/chapter28b/docs/28b-grammar.ja.md
@@ -1,6 +1,6 @@
-# 第25章の文法
+# 第28b章の文法
-[English](25-grammar.md) | 日本語
+[English](28b-grammar.md) | 日本語
```antlrv4
grammar SimpleLanguage;
diff --git a/chapter28b/docs/25-grammar.md b/chapter28b/docs/28b-grammar.md
index 065cf051..c7a279fa 100644
--- a/chapter28b/docs/25-grammar.md
+++ b/chapter28b/docs/28b-grammar.md
@@ -1,6 +1,6 @@
-# Grammar for Chapter 25
+# Grammar for Chapter 28b
-English | [日本語](25-grammar.ja.md)
+English | [日本語](28b-grammar.ja.md)
```antlrv4
grammar SimpleLanguage;
diff --git a/chapter28b/docs/Makefile b/chapter28b/docs/Makefile
deleted file mode 100644
index 12659b8f..00000000
--- a/chapter28b/docs/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-GV := $(wildcard *.gv)
-SVG := $(patsubst %.gv,%.svg,$(GV))
-
-.PHONY: all
-all: $(SVG)
-
-%.svg: %.gv
- dot -Tsvg $< > $@
-
-.PHONY: clean
-clean:
- @rm -f $(SVG)
diff --git a/chapter28b/docs/font-awesome-4.7.0.zip b/chapter28b/docs/font-awesome-4.7.0.zip
deleted file mode 100644
index b6bdb4bd..00000000
--- a/chapter28b/docs/font-awesome-4.7.0.zip
+++ /dev/null
Binary files differ
diff --git a/chapter28b/docs/ifg_trim.pdf b/chapter28b/docs/ifg_trim.pdf
deleted file mode 100644
index d952fd4f..00000000
--- a/chapter28b/docs/ifg_trim.pdf
+++ /dev/null
Binary files differ
diff --git a/chapter28b/docs/lattice.gv b/chapter28b/docs/lattice.gv
deleted file mode 100644
index 8661082d..00000000
--- a/chapter28b/docs/lattice.gv
+++ /dev/null
@@ -1,203 +0,0 @@
-digraph Lattice {
- compound = true;
- // Define nodes
- splines=false;
- node [shape=circle, style=filled];
- nodesep=0.2;
- ranksep=0.2;
-
- TOP [label=<<B>⊤</B >>, fillcolor=black, fontcolor=white];
- BOT [label=<<B>⊥</B>>, fillcolor=black, fontcolor=white];
-
- // ----------------------
- // Control
- XCTRL [label=<<B>⊤</B >:ctrl>, fillcolor=yellow, shape=oval];
- CTRL [label=<<B>⊥</B>:ctrl>, fillcolor=yellow, shape=oval];
-
- TOP -> XCTRL;
- XCTRL -> CTRL;
- CTRL -> BOT;
-
- // ----------------------
- // Integers
- INT_TOP [label=<<B>⊤</B >:int>, fillcolor=lightblue, shape=oval];
- INT_BOT [label=<<B>⊥</B>:int>, fillcolor=lightblue, shape=oval];
-
- INT_HI [label="[min>max]" , fillcolor=lightblue, shape=oval];
- INT_XBOOL [label="[1...0]", fillcolor=lightblue, shape=oval];
- INT_CON [label="constants", fillcolor=lightblue, shape=oval];
- INT_CON0 [label="0", fillcolor=lightblue];
- INT_CON1 [label="1", fillcolor=lightblue];
- INT_BOOL [label="[0...1]", fillcolor=lightblue,shape=oval];
- INT_LO [label="[min<max]" , fillcolor=lightblue, shape=oval];
-
- TOP -> INT_TOP;
- INT_TOP -> INT_HI;
- INT_HI -> INT_XBOOL;
- INT_XBOOL -> INT_CON0;
- INT_XBOOL -> INT_CON1;
- INT_HI -> INT_CON;
- INT_CON0 -> INT_BOOL;
- INT_CON1 -> INT_BOOL;
- INT_BOOL -> INT_LO;
- INT_CON -> INT_LO;
- INT_LO -> INT_BOT;
- INT_BOT -> BOT;
-
- INT_CON0 -> INT_CON1 -> INT_CON [style=invis];
-
- // ----------------------
- // Floats
- FLT_TOP [label=<<B>⊤</B >:flt>, fillcolor=aqua, shape=oval];
- FLT_BOT [label=<<B>⊥</B>:flt>, fillcolor=aqua, shape=oval];
-
- FLT_CON64 [label="64b consts\n[excludes\n32b consts]" , fillcolor=aqua, shape=oval];
- FLT_CON32 [label="32b consts", fillcolor=aqua, shape=oval];
- FLT_CONPI [label="3.14159...", fillcolor=aqua, shape=oval];
- FLT_X32 [label="~f32", fillcolor=aqua];
- FLT_CON0 [label="0." , fillcolor=aqua];
- FLT_32 [label="f32" , fillcolor=aqua];
-
- TOP -> FLT_TOP;
- FLT_TOP -> FLT_CON64
- FLT_TOP -> FLT_CONPI
- FLT_CON64 -> FLT_BOT
- FLT_CONPI -> FLT_BOT
-
- FLT_TOP -> FLT_X32;
- FLT_X32 -> FLT_CON0;
- FLT_X32 -> FLT_CON32;
- FLT_CON0 -> FLT_32;
- FLT_CON32 -> FLT_32;
- FLT_32 -> FLT_BOT
-
- FLT_BOT -> BOT;
-
- FLT_CON0 -> FLT_CON32 -> FLT_CONPI -> FLT_CON64 [style=invis];
-
-
- // ----------------------
- // Pointers, just a boolean nil/not and a struct
- PTR [shape=none, fillcolor=none, label=<
- <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TR><TD BGCOLOR="green"><FONT color="white">*ptr</FONT></TD></TR>
- <TR><TD BGCOLOR="lightgreen">struct</TD></TR>
- </TABLE>
- >];
- PTR0 [shape=none, fillcolor=none, label=<
- <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TR><TD BGCOLOR="green"><FONT color="white">*ptr?</FONT></TD></TR>
- <TR><TD BGCOLOR="lightgreen">struct</TD></TR>
- </TABLE>
- >];
-
- TOP -> PTR;
- PTR -> PTR0;
- PTR0 -> BOT;
-
- // ----------------------
- // Structs
- STRUCT_TOP [label=<<B>⊤</B >:struct>, fillcolor=lightgreen, shape=oval];
- STRUCT_BOT [label=<<B>⊥</B>:struct>, fillcolor=lightgreen, shape=oval];
-
- STRUCT_S1 [shape=none, fillcolor=none, label=<
- <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
- </TABLE>
- >];
- STRUCT_S2 [shape=none, fillcolor=none, label=<
- <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
- <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
- </TABLE>
- >];
- STRUCT_SN [shape=none, fillcolor=none, label=<
- <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
- <TR><TD BGCOLOR="lightgreen">... </TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
- <TR><TD BGCOLOR="lightgreen">field</TD><TD BGCOLOR="lightblue:green" gradientangle="315">int/ptr</TD></TR>
- </TABLE>
- >];
-
-
- TOP -> STRUCT_TOP;
- STRUCT_TOP -> STRUCT_S1;
- STRUCT_TOP -> STRUCT_S2;
- STRUCT_TOP -> STRUCT_SN;
- STRUCT_S1 -> STRUCT_BOT;
- STRUCT_S2 -> STRUCT_BOT;
- STRUCT_SN -> STRUCT_BOT;
- STRUCT_BOT -> BOT;
-
- STRUCT_S1 -> STRUCT_S2 -> STRUCT_SN [style=invis];
-
- // ----------------------
- // Memory
- MEM_TOP [label=<<B>⊤</B >:mem>, fillcolor=blue, fontcolor=white, shape=oval];
- MEM_BOT [label=<<B>⊥</B>:mem>, fillcolor=blue, fontcolor=white, shape=oval];
- MEM_ALIAS1 [label="#1" , fillcolor=blue, fontcolor=white];
- MEM_ALIASDOTS [label="..." , fillcolor=blue, fontcolor=white, shape=oval];
- MEM_ALIASN [label="#n" , fillcolor=blue, fontcolor=white];
-
- TOP -> MEM_TOP;
- MEM_TOP -> MEM_ALIAS1;
- MEM_TOP -> MEM_ALIASDOTS;
- MEM_TOP -> MEM_ALIASN;
- MEM_ALIAS1 -> MEM_BOT;
- MEM_ALIASDOTS -> MEM_BOT;
- MEM_ALIASN -> MEM_BOT;
- MEM_BOT -> BOT;
-
- MEM_ALIAS1 -> MEM_ALIASDOTS -> MEM_ALIASN [style=invis];
-
- // ----------------------
- // Tuples
-
- TUPLE_TOP [label=<<B>⊤</B >:tuple>, fillcolor=pink, shape=oval];
- TUPLE_BOT [label=<<B>⊥</B>:tuple>, fillcolor=pink, shape=oval];
-
- TUPLE1 [shape=none, fillcolor=none, label=<
- <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TH><TD BGCOLOR="pink">Tuple</TD></TH>
- <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
- </TABLE>
- >];
- TUPLE2 [shape=none, fillcolor=none, label=<
- <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TH><TD BGCOLOR="pink">Tuple</TD></TH>
- <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
- <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
- </TABLE>
- >];
- TUPLEN [shape=none, fillcolor=none, label=<
- <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
- <TH><TD BGCOLOR="pink">Tuple</TD></TH>
- <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
- <TR><TD BGCOLOR="black" ><FONT color="white">...</FONT></TD></TR>
- <TR><TD BGCOLOR="black" ><FONT color="white">type</FONT></TD></TR>
- </TABLE>
- >];
-
-
- TOP -> TUPLE_TOP;
- TUPLE_TOP -> TUPLE1;
- TUPLE_TOP -> TUPLE2;
- TUPLE_TOP -> TUPLEN;
- TUPLE1 -> TUPLE_BOT;
- TUPLE2 -> TUPLE_BOT;
- TUPLEN -> TUPLE_BOT;
- TUPLE_BOT -> BOT;
-
- // ----------------------
- // Additional customization
- {rank=same; TOP;}
- {rank=same; INT_TOP; FLT_TOP; XCTRL; PTR; STRUCT_TOP; MEM_TOP; TUPLE_TOP}
- {rank=same; INT_CON; INT_CON0; INT_CON1;
- FLT_CON32; FLT_CON64; FLT_CON0; FLT_CONPI;
- STRUCT_S1; STRUCT_S2; STRUCT_SN;
- MEM_ALIAS1; MEM_ALIASDOTS; MEM_ALIASN;
- TUPLE1; TUPLE2; TUPLEN;
- }
- {rank=same; INT_BOT; FLT_BOT; CTRL; PTR0; STRUCT_BOT; MEM_BOT; TUPLE_BOT}
- {rank=same; BOT;}
-}
diff --git a/chapter28b/docs/lattice.svg b/chapter28b/docs/lattice.svg
deleted file mode 100644
index e72b473b..00000000
--- a/chapter28b/docs/lattice.svg
+++ /dev/null
@@ -1,691 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
- "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Generated by graphviz version 12.2.0 (20241103.1931)
- -->
-<!-- Title: Lattice Pages: 1 -->
-<svg width="1330pt" height="584pt"
- viewBox="0.00 0.00 1329.51 583.54" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 579.54)">
-<title>Lattice</title>
-<polygon fill="white" stroke="none" points="-4,4 -4,-579.54 1325.51,-579.54 1325.51,4 -4,4"/>
-<!-- TOP -->
-<g id="node1" class="node">
-<title>TOP</title>
-<ellipse fill="black" stroke="black" cx="641.51" cy="-554.42" rx="21.12" ry="21.12"/>
-<text text-anchor="start" x="633.63" y="-549.99" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊤</text>
-</g>
-<!-- XCTRL -->
-<g id="node3" class="node">
-<title>XCTRL</title>
-<ellipse fill="yellow" stroke="black" cx="36.51" cy="-492.79" rx="36.51" ry="18"/>
-<text text-anchor="start" x="17.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
-<text text-anchor="start" x="33.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:ctrl</text>
-</g>
-<!-- TOP&#45;&gt;XCTRL -->
-<g id="edge1" class="edge">
-<title>TOP&#45;&gt;XCTRL</title>
-<path fill="none" stroke="black" d="M620.33,-551.33C533.82,-542.8 206.01,-510.5 83.28,-498.4"/>
-<polygon fill="black" stroke="black" points="83.86,-494.94 73.56,-497.44 83.17,-501.91 83.86,-494.94"/>
-</g>
-<!-- INT_TOP -->
-<g id="node5" class="node">
-<title>INT_TOP</title>
-<ellipse fill="lightblue" stroke="black" cx="316.51" cy="-492.79" rx="33.95" ry="18"/>
-<text text-anchor="start" x="299.63" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
-<text text-anchor="start" x="315.38" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:int</text>
-</g>
-<!-- TOP&#45;&gt;INT_TOP -->
-<g id="edge4" class="edge">
-<title>TOP&#45;&gt;INT_TOP</title>
-<path fill="none" stroke="black" d="M620.68,-549.6C568.33,-539.99 430.36,-514.68 359.64,-501.71"/>
-<polygon fill="black" stroke="black" points="360.44,-498.29 349.97,-499.93 359.18,-505.18 360.44,-498.29"/>
-</g>
-<!-- FLT_TOP -->
-<g id="node14" class="node">
-<title>FLT_TOP</title>
-<ellipse fill="aqua" stroke="black" cx="494.51" cy="-492.79" rx="32.41" ry="18"/>
-<text text-anchor="start" x="478.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
-<text text-anchor="start" x="494.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:flt</text>
-</g>
-<!-- TOP&#45;&gt;FLT_TOP -->
-<g id="edge18" class="edge">
-<title>TOP&#45;&gt;FLT_TOP</title>
-<path fill="none" stroke="black" d="M621.99,-545.5C598.79,-536.09 559.6,-520.19 530.78,-508.5"/>
-<polygon fill="black" stroke="black" points="532.29,-505.34 521.7,-504.82 529.65,-511.82 532.29,-505.34"/>
-</g>
-<!-- PTR -->
-<g id="node22" class="node">
-<title>PTR</title>
-<polygon fill="none" stroke="none" points="668.51,-519.29 614.51,-519.29 614.51,-466.29 668.51,-466.29 668.51,-519.29"/>
-<polygon fill="green" stroke="none" points="623.51,-492.79 623.51,-515.29 659.51,-515.29 659.51,-492.79 623.51,-492.79"/>
-<polygon fill="none" stroke="black" points="623.51,-492.79 623.51,-515.29 659.51,-515.29 659.51,-492.79 623.51,-492.79"/>
-<text text-anchor="start" x="630.63" y="-498.99" font-family="Times New Roman,serif" font-size="14.00" fill="white">*ptr</text>
-<polygon fill="lightgreen" stroke="none" points="623.51,-470.29 623.51,-492.79 659.51,-492.79 659.51,-470.29 623.51,-470.29"/>
-<polygon fill="none" stroke="black" points="623.51,-470.29 623.51,-492.79 659.51,-492.79 659.51,-470.29 623.51,-470.29"/>
-<text text-anchor="start" x="626.51" y="-476.49" font-family="Times New Roman,serif" font-size="14.00">struct</text>
-</g>
-<!-- TOP&#45;&gt;PTR -->
-<g id="edge33" class="edge">
-<title>TOP&#45;&gt;PTR</title>
-<path fill="none" stroke="black" d="M641.51,-532.9C641.51,-532.06 641.51,-531.22 641.51,-530.37"/>
-<polygon fill="black" stroke="black" points="645.01,-530.61 641.51,-520.61 638.01,-530.61 645.01,-530.61"/>
-</g>
-<!-- STRUCT_TOP -->
-<g id="node24" class="node">
-<title>STRUCT_TOP</title>
-<ellipse fill="lightgreen" stroke="black" cx="746.51" cy="-492.79" rx="44.7" ry="18"/>
-<text text-anchor="start" x="721.76" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
-<text text-anchor="start" x="737.51" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:struct</text>
-</g>
-<!-- TOP&#45;&gt;STRUCT_TOP -->
-<g id="edge36" class="edge">
-<title>TOP&#45;&gt;STRUCT_TOP</title>
-<path fill="none" stroke="black" d="M659.8,-543.03C674.04,-534.94 694.24,-523.47 711.55,-513.64"/>
-<polygon fill="black" stroke="black" points="713.07,-516.81 720.03,-508.83 709.61,-510.72 713.07,-516.81"/>
-</g>
-<!-- MEM_TOP -->
-<g id="node29" class="node">
-<title>MEM_TOP</title>
-<ellipse fill="blue" stroke="black" cx="998.51" cy="-492.79" rx="43.67" ry="18"/>
-<text text-anchor="start" x="974.51" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊤</text>
-<text text-anchor="start" x="990.26" y="-488.74" font-family="Times New Roman,serif" font-size="14.00" fill="white">:mem</text>
-</g>
-<!-- TOP&#45;&gt;MEM_TOP -->
-<g id="edge46" class="edge">
-<title>TOP&#45;&gt;MEM_TOP</title>
-<path fill="none" stroke="black" d="M662.51,-549.91C717.64,-540.7 867.72,-515.64 947.33,-502.34"/>
-<polygon fill="black" stroke="black" points="947.77,-505.82 957.06,-500.72 946.62,-498.91 947.77,-505.82"/>
-</g>
-<!-- TUPLE_TOP -->
-<g id="node34" class="node">
-<title>TUPLE_TOP</title>
-<ellipse fill="pink" stroke="black" cx="1182.51" cy="-492.79" rx="42.65" ry="18"/>
-<text text-anchor="start" x="1159.26" y="-488.74" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊤</text>
-<text text-anchor="start" x="1175.01" y="-488.74" font-family="Times New Roman,serif" font-size="14.00">:tuple</text>
-</g>
-<!-- TOP&#45;&gt;TUPLE_TOP -->
-<g id="edge56" class="edge">
-<title>TOP&#45;&gt;TUPLE_TOP</title>
-<path fill="none" stroke="black" d="M662.72,-551.08C740.91,-542.46 1014.51,-512.31 1130.21,-499.56"/>
-<polygon fill="black" stroke="black" points="1130.5,-503.05 1140.05,-498.47 1129.73,-496.09 1130.5,-503.05"/>
-</g>
-<!-- BOT -->
-<g id="node2" class="node">
-<title>BOT</title>
-<ellipse fill="black" stroke="black" cx="641.51" cy="-21.12" rx="21.12" ry="21.12"/>
-<text text-anchor="start" x="633.63" y="-16.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊥</text>
-</g>
-<!-- CTRL -->
-<g id="node4" class="node">
-<title>CTRL</title>
-<ellipse fill="yellow" stroke="black" cx="49.51" cy="-82.75" rx="36.51" ry="18"/>
-<text text-anchor="start" x="30.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
-<text text-anchor="start" x="46.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:ctrl</text>
-</g>
-<!-- XCTRL&#45;&gt;CTRL -->
-<g id="edge2" class="edge">
-<title>XCTRL&#45;&gt;CTRL</title>
-<path fill="none" stroke="black" d="M37.09,-474.38C39.15,-409.6 46.05,-191.88 48.57,-112.25"/>
-<polygon fill="black" stroke="black" points="52.06,-112.73 48.88,-102.63 45.06,-112.51 52.06,-112.73"/>
-</g>
-<!-- CTRL&#45;&gt;BOT -->
-<g id="edge3" class="edge">
-<title>CTRL&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M85.21,-78.15C191.74,-67.42 506.36,-35.74 608.84,-25.41"/>
-<polygon fill="black" stroke="black" points="609.15,-28.9 618.75,-24.42 608.45,-21.94 609.15,-28.9"/>
-</g>
-<!-- INT_HI -->
-<g id="node7" class="node">
-<title>INT_HI</title>
-<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-434.29" rx="53.91" ry="18"/>
-<text text-anchor="middle" x="174.51" y="-429.24" font-family="Times New Roman,serif" font-size="14.00">[min&gt;max]</text>
-</g>
-<!-- INT_TOP&#45;&gt;INT_HI -->
-<g id="edge5" class="edge">
-<title>INT_TOP&#45;&gt;INT_HI</title>
-<path fill="none" stroke="black" d="M289.8,-481.17C269.89,-473.25 242.26,-462.25 219.03,-453.01"/>
-<polygon fill="black" stroke="black" points="220.37,-449.77 209.78,-449.33 217.78,-456.28 220.37,-449.77"/>
-</g>
-<!-- INT_BOT -->
-<g id="node6" class="node">
-<title>INT_BOT</title>
-<ellipse fill="lightblue" stroke="black" cx="329.51" cy="-82.75" rx="33.95" ry="18"/>
-<text text-anchor="start" x="312.63" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
-<text text-anchor="start" x="328.38" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:int</text>
-</g>
-<!-- INT_BOT&#45;&gt;BOT -->
-<g id="edge15" class="edge">
-<title>INT_BOT&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M361.14,-75.7C420.84,-64.3 549.47,-39.71 609.47,-28.25"/>
-<polygon fill="black" stroke="black" points="609.86,-31.73 619.03,-26.42 608.55,-24.86 609.86,-31.73"/>
-</g>
-<!-- INT_XBOOL -->
-<g id="node8" class="node">
-<title>INT_XBOOL</title>
-<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-373.97" rx="33.95" ry="18"/>
-<text text-anchor="middle" x="97.51" y="-368.92" font-family="Times New Roman,serif" font-size="14.00">[1...0]</text>
-</g>
-<!-- INT_HI&#45;&gt;INT_XBOOL -->
-<g id="edge6" class="edge">
-<title>INT_HI&#45;&gt;INT_XBOOL</title>
-<path fill="none" stroke="black" d="M153.47,-417.36C144.79,-410.78 134.6,-403.06 125.31,-396.03"/>
-<polygon fill="black" stroke="black" points="127.71,-393.46 117.63,-390.21 123.48,-399.04 127.71,-393.46"/>
-</g>
-<!-- INT_CON -->
-<g id="node9" class="node">
-<title>INT_CON</title>
-<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-282.65" rx="45.21" ry="18"/>
-<text text-anchor="middle" x="174.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">constants</text>
-</g>
-<!-- INT_HI&#45;&gt;INT_CON -->
-<g id="edge9" class="edge">
-<title>INT_HI&#45;&gt;INT_CON</title>
-<path fill="none" stroke="black" d="M174.51,-416C174.51,-390.27 174.51,-343.1 174.51,-312.31"/>
-<polygon fill="black" stroke="black" points="178.01,-312.52 174.51,-302.52 171.01,-312.52 178.01,-312.52"/>
-</g>
-<!-- INT_CON0 -->
-<g id="node10" class="node">
-<title>INT_CON0</title>
-<ellipse fill="lightblue" stroke="black" cx="47.51" cy="-282.65" rx="18" ry="18"/>
-<text text-anchor="middle" x="47.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">0</text>
-</g>
-<!-- INT_XBOOL&#45;&gt;INT_CON0 -->
-<g id="edge7" class="edge">
-<title>INT_XBOOL&#45;&gt;INT_CON0</title>
-<path fill="none" stroke="black" d="M88.34,-356.59C80.82,-343.17 70.07,-323.96 61.49,-308.62"/>
-<polygon fill="black" stroke="black" points="64.63,-307.08 56.69,-300.06 58.53,-310.5 64.63,-307.08"/>
-</g>
-<!-- INT_CON1 -->
-<g id="node11" class="node">
-<title>INT_CON1</title>
-<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-282.65" rx="18" ry="18"/>
-<text text-anchor="middle" x="97.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">1</text>
-</g>
-<!-- INT_XBOOL&#45;&gt;INT_CON1 -->
-<g id="edge8" class="edge">
-<title>INT_XBOOL&#45;&gt;INT_CON1</title>
-<path fill="none" stroke="black" d="M97.51,-355.75C97.51,-343.47 97.51,-326.68 97.51,-312.39"/>
-<polygon fill="black" stroke="black" points="101.01,-312.64 97.51,-302.64 94.01,-312.64 101.01,-312.64"/>
-</g>
-<!-- INT_LO -->
-<g id="node13" class="node">
-<title>INT_LO</title>
-<ellipse fill="lightblue" stroke="black" cx="174.51" cy="-141.25" rx="53.91" ry="18"/>
-<text text-anchor="middle" x="174.51" y="-136.2" font-family="Times New Roman,serif" font-size="14.00">[min&lt;max]</text>
-</g>
-<!-- INT_CON&#45;&gt;INT_LO -->
-<g id="edge13" class="edge">
-<title>INT_CON&#45;&gt;INT_LO</title>
-<path fill="none" stroke="black" d="M174.51,-264.17C174.51,-240.45 174.51,-199.04 174.51,-170.9"/>
-<polygon fill="black" stroke="black" points="178.01,-171.25 174.51,-161.25 171.01,-171.25 178.01,-171.25"/>
-</g>
-<!-- INT_CON0&#45;&gt;INT_CON1 -->
-<!-- INT_BOOL -->
-<g id="node12" class="node">
-<title>INT_BOOL</title>
-<ellipse fill="lightblue" stroke="black" cx="97.51" cy="-196.45" rx="33.95" ry="18"/>
-<text text-anchor="middle" x="97.51" y="-191.4" font-family="Times New Roman,serif" font-size="14.00">[0...1]</text>
-</g>
-<!-- INT_CON0&#45;&gt;INT_BOOL -->
-<g id="edge10" class="edge">
-<title>INT_CON0&#45;&gt;INT_BOOL</title>
-<path fill="none" stroke="black" d="M56.45,-266.6C63.5,-254.72 73.51,-237.87 81.88,-223.77"/>
-<polygon fill="black" stroke="black" points="84.75,-225.79 86.85,-215.41 78.73,-222.22 84.75,-225.79"/>
-</g>
-<!-- INT_CON1&#45;&gt;INT_CON -->
-<!-- INT_CON1&#45;&gt;INT_BOOL -->
-<g id="edge11" class="edge">
-<title>INT_CON1&#45;&gt;INT_BOOL</title>
-<path fill="none" stroke="black" d="M97.51,-264.21C97.51,-253.25 97.51,-238.87 97.51,-226.25"/>
-<polygon fill="black" stroke="black" points="101.01,-226.39 97.51,-216.39 94.01,-226.39 101.01,-226.39"/>
-</g>
-<!-- INT_BOOL&#45;&gt;INT_LO -->
-<g id="edge12" class="edge">
-<title>INT_BOOL&#45;&gt;INT_LO</title>
-<path fill="none" stroke="black" d="M117.34,-181.75C124.98,-176.47 133.91,-170.3 142.42,-164.42"/>
-<polygon fill="black" stroke="black" points="144.27,-167.4 150.5,-158.83 140.29,-161.64 144.27,-167.4"/>
-</g>
-<!-- INT_LO&#45;&gt;INT_BOT -->
-<g id="edge14" class="edge">
-<title>INT_LO&#45;&gt;INT_BOT</title>
-<path fill="none" stroke="black" d="M210.07,-127.29C234.52,-118.37 266.93,-106.56 291.82,-97.49"/>
-<polygon fill="black" stroke="black" points="292.71,-100.89 300.91,-94.17 290.31,-94.31 292.71,-100.89"/>
-</g>
-<!-- FLT_CON64 -->
-<g id="node16" class="node">
-<title>FLT_CON64</title>
-<ellipse fill="aqua" stroke="black" cx="564.51" cy="-282.65" rx="55.33" ry="40.66"/>
-<text text-anchor="middle" x="564.51" y="-294.1" font-family="Times New Roman,serif" font-size="14.00">64b consts</text>
-<text text-anchor="middle" x="564.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">[excludes</text>
-<text text-anchor="middle" x="564.51" y="-261.1" font-family="Times New Roman,serif" font-size="14.00">32b consts]</text>
-</g>
-<!-- FLT_TOP&#45;&gt;FLT_CON64 -->
-<g id="edge19" class="edge">
-<title>FLT_TOP&#45;&gt;FLT_CON64</title>
-<path fill="none" stroke="black" d="M500.48,-474.88C510.8,-443.88 532.35,-379.2 547.59,-333.44"/>
-<polygon fill="black" stroke="black" points="550.9,-334.57 550.74,-323.98 544.26,-332.36 550.9,-334.57"/>
-</g>
-<!-- FLT_CONPI -->
-<g id="node18" class="node">
-<title>FLT_CONPI</title>
-<ellipse fill="aqua" stroke="black" cx="446.51" cy="-282.65" rx="48.79" ry="18"/>
-<text text-anchor="middle" x="446.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">3.14159...</text>
-</g>
-<!-- FLT_TOP&#45;&gt;FLT_CONPI -->
-<g id="edge20" class="edge">
-<title>FLT_TOP&#45;&gt;FLT_CONPI</title>
-<path fill="none" stroke="black" d="M490.33,-474.51C482.02,-438.11 463.32,-356.27 453.25,-312.16"/>
-<polygon fill="black" stroke="black" points="456.69,-311.49 451.05,-302.52 449.86,-313.04 456.69,-311.49"/>
-</g>
-<!-- FLT_X32 -->
-<g id="node19" class="node">
-<title>FLT_X32</title>
-<ellipse fill="aqua" stroke="black" cx="333.51" cy="-373.97" rx="28.32" ry="28.32"/>
-<text text-anchor="middle" x="333.51" y="-368.92" font-family="Times New Roman,serif" font-size="14.00">~f32</text>
-</g>
-<!-- FLT_TOP&#45;&gt;FLT_X32 -->
-<g id="edge23" class="edge">
-<title>FLT_TOP&#45;&gt;FLT_X32</title>
-<path fill="none" stroke="black" d="M474.77,-478.22C447.56,-458.14 398.22,-421.73 365.65,-397.69"/>
-<polygon fill="black" stroke="black" points="367.88,-394.99 357.75,-391.86 363.72,-400.62 367.88,-394.99"/>
-</g>
-<!-- FLT_BOT -->
-<g id="node15" class="node">
-<title>FLT_BOT</title>
-<ellipse fill="aqua" stroke="black" cx="507.51" cy="-82.75" rx="32.41" ry="18"/>
-<text text-anchor="start" x="491.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
-<text text-anchor="start" x="507.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:flt</text>
-</g>
-<!-- FLT_BOT&#45;&gt;BOT -->
-<g id="edge29" class="edge">
-<title>FLT_BOT&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M532.4,-70.67C554.98,-60.63 588.29,-45.81 612.16,-35.19"/>
-<polygon fill="black" stroke="black" points="613.29,-38.51 621,-31.25 610.44,-32.12 613.29,-38.51"/>
-</g>
-<!-- FLT_CON64&#45;&gt;FLT_BOT -->
-<g id="edge21" class="edge">
-<title>FLT_CON64&#45;&gt;FLT_BOT</title>
-<path fill="none" stroke="black" d="M553.11,-242.68C542.05,-203.88 525.53,-145.95 515.71,-111.5"/>
-<polygon fill="black" stroke="black" points="519.17,-110.89 513.06,-102.23 512.44,-112.81 519.17,-110.89"/>
-</g>
-<!-- FLT_CON32 -->
-<g id="node17" class="node">
-<title>FLT_CON32</title>
-<ellipse fill="aqua" stroke="black" cx="333.51" cy="-282.65" rx="50.33" ry="18"/>
-<text text-anchor="middle" x="333.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">32b consts</text>
-</g>
-<!-- FLT_CON32&#45;&gt;FLT_CONPI -->
-<!-- FLT_32 -->
-<g id="node21" class="node">
-<title>FLT_32</title>
-<ellipse fill="aqua" stroke="black" cx="333.51" cy="-196.45" rx="23.2" ry="23.2"/>
-<text text-anchor="middle" x="333.51" y="-191.4" font-family="Times New Roman,serif" font-size="14.00">f32</text>
-</g>
-<!-- FLT_CON32&#45;&gt;FLT_32 -->
-<g id="edge27" class="edge">
-<title>FLT_CON32&#45;&gt;FLT_32</title>
-<path fill="none" stroke="black" d="M333.51,-264.21C333.51,-254.7 333.51,-242.59 333.51,-231.31"/>
-<polygon fill="black" stroke="black" points="337.01,-231.51 333.51,-221.51 330.01,-231.51 337.01,-231.51"/>
-</g>
-<!-- FLT_CONPI&#45;&gt;FLT_BOT -->
-<g id="edge22" class="edge">
-<title>FLT_CONPI&#45;&gt;FLT_BOT</title>
-<path fill="none" stroke="black" d="M452.03,-264.56C462.62,-229.86 485.86,-153.69 498.65,-111.79"/>
-<polygon fill="black" stroke="black" points="501.94,-112.98 501.51,-102.39 495.25,-110.93 501.94,-112.98"/>
-</g>
-<!-- FLT_CONPI&#45;&gt;FLT_CON64 -->
-<!-- FLT_X32&#45;&gt;FLT_CON32 -->
-<g id="edge25" class="edge">
-<title>FLT_X32&#45;&gt;FLT_CON32</title>
-<path fill="none" stroke="black" d="M333.51,-345.2C333.51,-334.71 333.51,-322.76 333.51,-312.19"/>
-<polygon fill="black" stroke="black" points="337.01,-312.52 333.51,-302.52 330.01,-312.53 337.01,-312.52"/>
-</g>
-<!-- FLT_CON0 -->
-<g id="node20" class="node">
-<title>FLT_CON0</title>
-<ellipse fill="aqua" stroke="black" cx="251.51" cy="-282.65" rx="18.08" ry="18.08"/>
-<text text-anchor="middle" x="251.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">0.</text>
-</g>
-<!-- FLT_X32&#45;&gt;FLT_CON0 -->
-<g id="edge24" class="edge">
-<title>FLT_X32&#45;&gt;FLT_CON0</title>
-<path fill="none" stroke="black" d="M314.9,-352.7C301.83,-338.47 284.38,-319.46 271.1,-304.99"/>
-<polygon fill="black" stroke="black" points="273.81,-302.77 264.47,-297.77 268.65,-307.5 273.81,-302.77"/>
-</g>
-<!-- FLT_CON0&#45;&gt;FLT_CON32 -->
-<!-- FLT_CON0&#45;&gt;FLT_32 -->
-<g id="edge26" class="edge">
-<title>FLT_CON0&#45;&gt;FLT_32</title>
-<path fill="none" stroke="black" d="M263.96,-268.86C276.06,-256.44 294.74,-237.26 309.69,-221.91"/>
-<polygon fill="black" stroke="black" points="312.03,-224.53 316.5,-214.92 307.01,-219.64 312.03,-224.53"/>
-</g>
-<!-- FLT_32&#45;&gt;FLT_BOT -->
-<g id="edge28" class="edge">
-<title>FLT_32&#45;&gt;FLT_BOT</title>
-<path fill="none" stroke="black" d="M353.13,-183.63C383.19,-163.99 440.9,-126.28 476.61,-102.94"/>
-<polygon fill="black" stroke="black" points="478.34,-105.99 484.79,-97.59 474.51,-100.13 478.34,-105.99"/>
-</g>
-<!-- PTR0 -->
-<g id="node23" class="node">
-<title>PTR0</title>
-<polygon fill="none" stroke="none" points="668.51,-109.25 614.51,-109.25 614.51,-56.25 668.51,-56.25 668.51,-109.25"/>
-<polygon fill="green" stroke="none" points="623.51,-82.75 623.51,-105.25 659.51,-105.25 659.51,-82.75 623.51,-82.75"/>
-<polygon fill="none" stroke="black" points="623.51,-82.75 623.51,-105.25 659.51,-105.25 659.51,-82.75 623.51,-82.75"/>
-<text text-anchor="start" x="627.63" y="-88.95" font-family="Times New Roman,serif" font-size="14.00" fill="white">*ptr?</text>
-<polygon fill="lightgreen" stroke="none" points="623.51,-60.25 623.51,-82.75 659.51,-82.75 659.51,-60.25 623.51,-60.25"/>
-<polygon fill="none" stroke="black" points="623.51,-60.25 623.51,-82.75 659.51,-82.75 659.51,-60.25 623.51,-60.25"/>
-<text text-anchor="start" x="626.51" y="-66.45" font-family="Times New Roman,serif" font-size="14.00">struct</text>
-</g>
-<!-- PTR&#45;&gt;PTR0 -->
-<g id="edge34" class="edge">
-<title>PTR&#45;&gt;PTR0</title>
-<path fill="none" stroke="black" d="M641.51,-466.52C641.51,-396.01 641.51,-202.44 641.51,-120.34"/>
-<polygon fill="black" stroke="black" points="645.01,-120.58 641.51,-110.58 638.01,-120.58 645.01,-120.58"/>
-</g>
-<!-- PTR0&#45;&gt;BOT -->
-<g id="edge35" class="edge">
-<title>PTR0&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M641.51,-56.46C641.51,-55.58 641.51,-54.7 641.51,-53.81"/>
-<polygon fill="black" stroke="black" points="645.01,-53.9 641.51,-43.9 638.01,-53.9 645.01,-53.9"/>
-</g>
-<!-- STRUCT_S1 -->
-<g id="node26" class="node">
-<title>STRUCT_S1</title>
-<polygon fill="none" stroke="none" points="741.38,-300.65 663.63,-300.65 663.63,-264.65 741.38,-264.65 741.38,-300.65"/>
-<polygon fill="lightgreen" stroke="none" points="671.63,-273.4 671.63,-291.9 698.38,-291.9 698.38,-273.4 671.63,-273.4"/>
-<polygon fill="none" stroke="black" points="671.63,-273.4 671.63,-291.9 698.38,-291.9 698.38,-273.4 671.63,-273.4"/>
-<text text-anchor="start" x="672.63" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">field</text>
-<defs>
-<linearGradient id="l_0" gradientUnits="userSpaceOnUse" x1="703.51" y1="-289.19" x2="728.26" y2="-276.11" >
-<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
-<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
-</linearGradient>
-</defs>
-<polygon fill="url(#l_0)" stroke="none" points="698.38,-273.4 698.38,-291.9 733.38,-291.9 733.38,-273.4 698.38,-273.4"/>
-<polygon fill="none" stroke="black" points="698.38,-273.4 698.38,-291.9 733.38,-291.9 733.38,-273.4 698.38,-273.4"/>
-<text text-anchor="start" x="699.38" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
-</g>
-<!-- STRUCT_TOP&#45;&gt;STRUCT_S1 -->
-<g id="edge37" class="edge">
-<title>STRUCT_TOP&#45;&gt;STRUCT_S1</title>
-<path fill="none" stroke="black" d="M742.68,-474.51C735.02,-437.92 717.75,-355.45 708.55,-311.5"/>
-<polygon fill="black" stroke="black" points="712.02,-311 706.54,-301.93 705.17,-312.43 712.02,-311"/>
-</g>
-<!-- STRUCT_S2 -->
-<g id="node27" class="node">
-<title>STRUCT_S2</title>
-<polygon fill="none" stroke="none" points="841.38,-309.15 755.63,-309.15 755.63,-256.15 841.38,-256.15 841.38,-309.15"/>
-<polygon fill="lightgreen" stroke="none" points="763.63,-282.65 763.63,-305.15 794.38,-305.15 794.38,-282.65 763.63,-282.65"/>
-<polygon fill="none" stroke="black" points="763.63,-282.65 763.63,-305.15 794.38,-305.15 794.38,-282.65 763.63,-282.65"/>
-<text text-anchor="start" x="766.63" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">field</text>
-<defs>
-<linearGradient id="l_1" gradientUnits="userSpaceOnUse" x1="800.1" y1="-301.86" x2="827.67" y2="-285.95" >
-<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
-<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
-</linearGradient>
-</defs>
-<polygon fill="url(#l_1)" stroke="none" points="794.38,-282.65 794.38,-305.15 833.38,-305.15 833.38,-282.65 794.38,-282.65"/>
-<polygon fill="none" stroke="black" points="794.38,-282.65 794.38,-305.15 833.38,-305.15 833.38,-282.65 794.38,-282.65"/>
-<text text-anchor="start" x="797.38" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
-<polygon fill="lightgreen" stroke="none" points="763.63,-260.15 763.63,-282.65 794.38,-282.65 794.38,-260.15 763.63,-260.15"/>
-<polygon fill="none" stroke="black" points="763.63,-260.15 763.63,-282.65 794.38,-282.65 794.38,-260.15 763.63,-260.15"/>
-<text text-anchor="start" x="766.63" y="-266.35" font-family="Times New Roman,serif" font-size="14.00">field</text>
-<defs>
-<linearGradient id="l_2" gradientUnits="userSpaceOnUse" x1="800.1" y1="-279.36" x2="827.67" y2="-263.45" >
-<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
-<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
-</linearGradient>
-</defs>
-<polygon fill="url(#l_2)" stroke="none" points="794.38,-260.15 794.38,-282.65 833.38,-282.65 833.38,-260.15 794.38,-260.15"/>
-<polygon fill="none" stroke="black" points="794.38,-260.15 794.38,-282.65 833.38,-282.65 833.38,-260.15 794.38,-260.15"/>
-<text text-anchor="start" x="797.38" y="-266.35" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
-</g>
-<!-- STRUCT_TOP&#45;&gt;STRUCT_S2 -->
-<g id="edge38" class="edge">
-<title>STRUCT_TOP&#45;&gt;STRUCT_S2</title>
-<path fill="none" stroke="black" d="M751.03,-474.51C759.53,-440.16 778.05,-365.34 789.28,-319.95"/>
-<polygon fill="black" stroke="black" points="792.67,-320.83 791.67,-310.28 785.87,-319.15 792.67,-320.83"/>
-</g>
-<!-- STRUCT_SN -->
-<g id="node28" class="node">
-<title>STRUCT_SN</title>
-<polygon fill="none" stroke="none" points="941.38,-320.4 855.63,-320.4 855.63,-244.9 941.38,-244.9 941.38,-320.4"/>
-<polygon fill="lightgreen" stroke="none" points="863.63,-293.9 863.63,-316.4 894.38,-316.4 894.38,-293.9 863.63,-293.9"/>
-<polygon fill="none" stroke="black" points="863.63,-293.9 863.63,-316.4 894.38,-316.4 894.38,-293.9 863.63,-293.9"/>
-<text text-anchor="start" x="866.63" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">field</text>
-<defs>
-<linearGradient id="l_3" gradientUnits="userSpaceOnUse" x1="900.1" y1="-313.11" x2="927.67" y2="-297.2" >
-<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
-<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
-</linearGradient>
-</defs>
-<polygon fill="url(#l_3)" stroke="none" points="894.38,-293.9 894.38,-316.4 933.38,-316.4 933.38,-293.9 894.38,-293.9"/>
-<polygon fill="none" stroke="black" points="894.38,-293.9 894.38,-316.4 933.38,-316.4 933.38,-293.9 894.38,-293.9"/>
-<text text-anchor="start" x="897.38" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
-<polygon fill="lightgreen" stroke="none" points="863.63,-271.4 863.63,-293.9 894.38,-293.9 894.38,-271.4 863.63,-271.4"/>
-<polygon fill="none" stroke="black" points="863.63,-271.4 863.63,-293.9 894.38,-293.9 894.38,-271.4 863.63,-271.4"/>
-<text text-anchor="start" x="869.63" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">... &#160;</text>
-<defs>
-<linearGradient id="l_4" gradientUnits="userSpaceOnUse" x1="900.1" y1="-290.61" x2="927.67" y2="-274.7" >
-<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
-<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
-</linearGradient>
-</defs>
-<polygon fill="url(#l_4)" stroke="none" points="894.38,-271.4 894.38,-293.9 933.38,-293.9 933.38,-271.4 894.38,-271.4"/>
-<polygon fill="none" stroke="black" points="894.38,-271.4 894.38,-293.9 933.38,-293.9 933.38,-271.4 894.38,-271.4"/>
-<text text-anchor="start" x="897.38" y="-277.6" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
-<polygon fill="lightgreen" stroke="none" points="863.63,-248.9 863.63,-271.4 894.38,-271.4 894.38,-248.9 863.63,-248.9"/>
-<polygon fill="none" stroke="black" points="863.63,-248.9 863.63,-271.4 894.38,-271.4 894.38,-248.9 863.63,-248.9"/>
-<text text-anchor="start" x="866.63" y="-255.1" font-family="Times New Roman,serif" font-size="14.00">field</text>
-<defs>
-<linearGradient id="l_5" gradientUnits="userSpaceOnUse" x1="900.1" y1="-268.11" x2="927.67" y2="-252.2" >
-<stop offset="0" style="stop-color:lightblue;stop-opacity:1.;"/>
-<stop offset="1" style="stop-color:green;stop-opacity:1.;"/>
-</linearGradient>
-</defs>
-<polygon fill="url(#l_5)" stroke="none" points="894.38,-248.9 894.38,-271.4 933.38,-271.4 933.38,-248.9 894.38,-248.9"/>
-<polygon fill="none" stroke="black" points="894.38,-248.9 894.38,-271.4 933.38,-271.4 933.38,-248.9 894.38,-248.9"/>
-<text text-anchor="start" x="897.38" y="-255.1" font-family="Times New Roman,serif" font-size="14.00">int/ptr</text>
-</g>
-<!-- STRUCT_TOP&#45;&gt;STRUCT_SN -->
-<g id="edge39" class="edge">
-<title>STRUCT_TOP&#45;&gt;STRUCT_SN</title>
-<path fill="none" stroke="black" d="M759.21,-475.24C782.17,-443.49 831.3,-375.56 864.63,-329.48"/>
-<polygon fill="black" stroke="black" points="867.37,-331.67 870.4,-321.52 861.7,-327.57 867.37,-331.67"/>
-</g>
-<!-- STRUCT_BOT -->
-<g id="node25" class="node">
-<title>STRUCT_BOT</title>
-<ellipse fill="lightgreen" stroke="black" cx="751.51" cy="-82.75" rx="44.7" ry="18"/>
-<text text-anchor="start" x="726.76" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
-<text text-anchor="start" x="742.51" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:struct</text>
-</g>
-<!-- STRUCT_BOT&#45;&gt;BOT -->
-<g id="edge43" class="edge">
-<title>STRUCT_BOT&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M725.72,-67.77C709,-58.7 687.2,-46.89 669.97,-37.55"/>
-<polygon fill="black" stroke="black" points="671.71,-34.51 661.25,-32.82 668.37,-40.67 671.71,-34.51"/>
-</g>
-<!-- STRUCT_S1&#45;&gt;STRUCT_BOT -->
-<g id="edge40" class="edge">
-<title>STRUCT_S1&#45;&gt;STRUCT_BOT</title>
-<path fill="none" stroke="black" d="M706.86,-264.91C715.3,-230.49 733.97,-154.31 744.29,-112.19"/>
-<polygon fill="black" stroke="black" points="747.69,-113.04 746.67,-102.5 740.89,-111.37 747.69,-113.04"/>
-</g>
-<!-- STRUCT_S1&#45;&gt;STRUCT_S2 -->
-<!-- STRUCT_S2&#45;&gt;STRUCT_BOT -->
-<g id="edge41" class="edge">
-<title>STRUCT_S2&#45;&gt;STRUCT_BOT</title>
-<path fill="none" stroke="black" d="M792.36,-256.52C783.62,-219.3 767.61,-151.25 758.45,-112.26"/>
-<polygon fill="black" stroke="black" points="761.88,-111.57 756.18,-102.64 755.07,-113.17 761.88,-111.57"/>
-</g>
-<!-- STRUCT_S2&#45;&gt;STRUCT_SN -->
-<!-- STRUCT_SN&#45;&gt;STRUCT_BOT -->
-<g id="edge42" class="edge">
-<title>STRUCT_SN&#45;&gt;STRUCT_BOT</title>
-<path fill="none" stroke="black" d="M870.86,-245.05C841.74,-205.46 796.64,-144.13 771.03,-109.3"/>
-<polygon fill="black" stroke="black" points="773.99,-107.42 765.25,-101.44 768.36,-111.57 773.99,-107.42"/>
-</g>
-<!-- MEM_ALIAS1 -->
-<g id="node31" class="node">
-<title>MEM_ALIAS1</title>
-<ellipse fill="blue" stroke="black" cx="975.51" cy="-282.65" rx="20.13" ry="20.13"/>
-<text text-anchor="middle" x="975.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">#1</text>
-</g>
-<!-- MEM_TOP&#45;&gt;MEM_ALIAS1 -->
-<g id="edge47" class="edge">
-<title>MEM_TOP&#45;&gt;MEM_ALIAS1</title>
-<path fill="none" stroke="black" d="M996.51,-474.51C992.58,-438.66 983.84,-358.73 978.96,-314.18"/>
-<polygon fill="black" stroke="black" points="982.45,-313.94 977.89,-304.38 975.5,-314.7 982.45,-313.94"/>
-</g>
-<!-- MEM_ALIASDOTS -->
-<g id="node32" class="node">
-<title>MEM_ALIASDOTS</title>
-<ellipse fill="blue" stroke="black" cx="1036.51" cy="-282.65" rx="27" ry="18"/>
-<text text-anchor="middle" x="1036.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">...</text>
-</g>
-<!-- MEM_TOP&#45;&gt;MEM_ALIASDOTS -->
-<g id="edge48" class="edge">
-<title>MEM_TOP&#45;&gt;MEM_ALIASDOTS</title>
-<path fill="none" stroke="black" d="M1001.81,-474.51C1008.41,-438.02 1023.27,-355.86 1031.23,-311.83"/>
-<polygon fill="black" stroke="black" points="1034.63,-312.69 1032.97,-302.23 1027.74,-311.45 1034.63,-312.69"/>
-</g>
-<!-- MEM_ALIASN -->
-<g id="node33" class="node">
-<title>MEM_ALIASN</title>
-<ellipse fill="blue" stroke="black" cx="1097.51" cy="-282.65" rx="20.13" ry="20.13"/>
-<text text-anchor="middle" x="1097.51" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">#n</text>
-</g>
-<!-- MEM_TOP&#45;&gt;MEM_ALIASN -->
-<g id="edge49" class="edge">
-<title>MEM_TOP&#45;&gt;MEM_ALIASN</title>
-<path fill="none" stroke="black" d="M1006.95,-474.88C1024.11,-438.46 1063.18,-355.52 1083.95,-311.44"/>
-<polygon fill="black" stroke="black" points="1087.07,-313.03 1088.16,-302.49 1080.73,-310.05 1087.07,-313.03"/>
-</g>
-<!-- MEM_BOT -->
-<g id="node30" class="node">
-<title>MEM_BOT</title>
-<ellipse fill="blue" stroke="black" cx="998.51" cy="-82.75" rx="43.67" ry="18"/>
-<text text-anchor="start" x="974.51" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00" fill="white">⊥</text>
-<text text-anchor="start" x="990.26" y="-78.7" font-family="Times New Roman,serif" font-size="14.00" fill="white">:mem</text>
-</g>
-<!-- MEM_BOT&#45;&gt;BOT -->
-<g id="edge53" class="edge">
-<title>MEM_BOT&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M958.6,-75.08C887.17,-63.15 738.86,-38.38 673.56,-27.48"/>
-<polygon fill="black" stroke="black" points="674.3,-24.05 663.86,-25.86 673.15,-30.96 674.3,-24.05"/>
-</g>
-<!-- MEM_ALIAS1&#45;&gt;MEM_BOT -->
-<g id="edge50" class="edge">
-<title>MEM_ALIAS1&#45;&gt;MEM_BOT</title>
-<path fill="none" stroke="black" d="M977.84,-262.38C981.92,-226.96 990.35,-153.62 995.09,-112.45"/>
-<polygon fill="black" stroke="black" points="998.57,-112.87 996.23,-102.53 991.61,-112.07 998.57,-112.87"/>
-</g>
-<!-- MEM_ALIAS1&#45;&gt;MEM_ALIASDOTS -->
-<!-- MEM_ALIASDOTS&#45;&gt;MEM_BOT -->
-<g id="edge51" class="edge">
-<title>MEM_ALIASDOTS&#45;&gt;MEM_BOT</title>
-<path fill="none" stroke="black" d="M1033.07,-264.56C1026.49,-229.93 1012.06,-154.01 1004.08,-112.06"/>
-<polygon fill="black" stroke="black" points="1007.56,-111.6 1002.25,-102.43 1000.68,-112.91 1007.56,-111.6"/>
-</g>
-<!-- MEM_ALIASDOTS&#45;&gt;MEM_ALIASN -->
-<!-- MEM_ALIASN&#45;&gt;MEM_BOT -->
-<g id="edge52" class="edge">
-<title>MEM_ALIASN&#45;&gt;MEM_BOT</title>
-<path fill="none" stroke="black" d="M1088.37,-264.2C1070.97,-229.07 1032.98,-152.35 1012.41,-110.82"/>
-<polygon fill="black" stroke="black" points="1015.58,-109.34 1008.01,-101.93 1009.31,-112.45 1015.58,-109.34"/>
-</g>
-<!-- TUPLE1 -->
-<g id="node36" class="node">
-<title>TUPLE1</title>
-<polygon fill="none" stroke="none" points="1185.51,-309.15 1131.51,-309.15 1131.51,-256.15 1185.51,-256.15 1185.51,-309.15"/>
-<polygon fill="pink" stroke="none" points="1139.76,-282.65 1139.76,-305.15 1177.26,-305.15 1177.26,-282.65 1139.76,-282.65"/>
-<polygon fill="none" stroke="black" points="1139.76,-282.65 1139.76,-305.15 1177.26,-305.15 1177.26,-282.65 1139.76,-282.65"/>
-<text text-anchor="start" x="1142.76" y="-288.85" font-family="Times New Roman,serif" font-size="14.00">Tuple</text>
-<polygon fill="black" stroke="none" points="1139.76,-260.15 1139.76,-282.65 1177.26,-282.65 1177.26,-260.15 1139.76,-260.15"/>
-<polygon fill="none" stroke="black" points="1139.76,-260.15 1139.76,-282.65 1177.26,-282.65 1177.26,-260.15 1139.76,-260.15"/>
-<text text-anchor="start" x="1146.88" y="-266.35" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
-</g>
-<!-- TUPLE_TOP&#45;&gt;TUPLE1 -->
-<g id="edge57" class="edge">
-<title>TUPLE_TOP&#45;&gt;TUPLE1</title>
-<path fill="none" stroke="black" d="M1180.42,-474.51C1176.51,-440.23 1167.99,-365.66 1162.8,-320.24"/>
-<polygon fill="black" stroke="black" points="1166.28,-319.86 1161.67,-310.32 1159.33,-320.65 1166.28,-319.86"/>
-</g>
-<!-- TUPLE2 -->
-<g id="node37" class="node">
-<title>TUPLE2</title>
-<polygon fill="none" stroke="none" points="1253.51,-320.4 1199.51,-320.4 1199.51,-244.9 1253.51,-244.9 1253.51,-320.4"/>
-<polygon fill="pink" stroke="none" points="1207.76,-293.9 1207.76,-316.4 1245.26,-316.4 1245.26,-293.9 1207.76,-293.9"/>
-<polygon fill="none" stroke="black" points="1207.76,-293.9 1207.76,-316.4 1245.26,-316.4 1245.26,-293.9 1207.76,-293.9"/>
-<text text-anchor="start" x="1210.76" y="-300.1" font-family="Times New Roman,serif" font-size="14.00">Tuple</text>
-<polygon fill="black" stroke="none" points="1207.76,-271.4 1207.76,-293.9 1245.26,-293.9 1245.26,-271.4 1207.76,-271.4"/>
-<polygon fill="none" stroke="black" points="1207.76,-271.4 1207.76,-293.9 1245.26,-293.9 1245.26,-271.4 1207.76,-271.4"/>
-<text text-anchor="start" x="1214.88" y="-277.6" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
-<polygon fill="black" stroke="none" points="1207.76,-248.9 1207.76,-271.4 1245.26,-271.4 1245.26,-248.9 1207.76,-248.9"/>
-<polygon fill="none" stroke="black" points="1207.76,-248.9 1207.76,-271.4 1245.26,-271.4 1245.26,-248.9 1207.76,-248.9"/>
-<text text-anchor="start" x="1214.88" y="-255.1" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
-</g>
-<!-- TUPLE_TOP&#45;&gt;TUPLE2 -->
-<g id="edge58" class="edge">
-<title>TUPLE_TOP&#45;&gt;TUPLE2</title>
-<path fill="none" stroke="black" d="M1186.34,-474.51C1192.95,-442.94 1206.72,-377.19 1216.28,-331.51"/>
-<polygon fill="black" stroke="black" points="1219.68,-332.35 1218.3,-321.85 1212.83,-330.92 1219.68,-332.35"/>
-</g>
-<!-- TUPLEN -->
-<g id="node38" class="node">
-<title>TUPLEN</title>
-<polygon fill="none" stroke="none" points="1321.51,-331.65 1267.51,-331.65 1267.51,-233.65 1321.51,-233.65 1321.51,-331.65"/>
-<polygon fill="pink" stroke="none" points="1275.76,-305.15 1275.76,-327.65 1313.26,-327.65 1313.26,-305.15 1275.76,-305.15"/>
-<polygon fill="none" stroke="black" points="1275.76,-305.15 1275.76,-327.65 1313.26,-327.65 1313.26,-305.15 1275.76,-305.15"/>
-<text text-anchor="start" x="1278.76" y="-311.35" font-family="Times New Roman,serif" font-size="14.00">Tuple</text>
-<polygon fill="black" stroke="none" points="1275.76,-282.65 1275.76,-305.15 1313.26,-305.15 1313.26,-282.65 1275.76,-282.65"/>
-<polygon fill="none" stroke="black" points="1275.76,-282.65 1275.76,-305.15 1313.26,-305.15 1313.26,-282.65 1275.76,-282.65"/>
-<text text-anchor="start" x="1282.88" y="-288.85" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
-<polygon fill="black" stroke="none" points="1275.76,-260.15 1275.76,-282.65 1313.26,-282.65 1313.26,-260.15 1275.76,-260.15"/>
-<polygon fill="none" stroke="black" points="1275.76,-260.15 1275.76,-282.65 1313.26,-282.65 1313.26,-260.15 1275.76,-260.15"/>
-<text text-anchor="start" x="1288.88" y="-266.35" font-family="Times New Roman,serif" font-size="14.00" fill="white">...</text>
-<polygon fill="black" stroke="none" points="1275.76,-237.65 1275.76,-260.15 1313.26,-260.15 1313.26,-237.65 1275.76,-237.65"/>
-<polygon fill="none" stroke="black" points="1275.76,-237.65 1275.76,-260.15 1313.26,-260.15 1313.26,-237.65 1275.76,-237.65"/>
-<text text-anchor="start" x="1282.88" y="-243.85" font-family="Times New Roman,serif" font-size="14.00" fill="white">type</text>
-</g>
-<!-- TUPLE_TOP&#45;&gt;TUPLEN -->
-<g id="edge59" class="edge">
-<title>TUPLE_TOP&#45;&gt;TUPLEN</title>
-<path fill="none" stroke="black" d="M1192.06,-474.88C1207.65,-445.62 1239.24,-386.36 1263.24,-341.32"/>
-<polygon fill="black" stroke="black" points="1266.22,-343.18 1267.83,-332.71 1260.04,-339.88 1266.22,-343.18"/>
-</g>
-<!-- TUPLE_BOT -->
-<g id="node35" class="node">
-<title>TUPLE_BOT</title>
-<ellipse fill="pink" stroke="black" cx="1192.51" cy="-82.75" rx="42.65" ry="18"/>
-<text text-anchor="start" x="1169.26" y="-78.7" font-family="Times New Roman,serif" font-weight="bold" font-size="14.00">⊥</text>
-<text text-anchor="start" x="1185.01" y="-78.7" font-family="Times New Roman,serif" font-size="14.00">:tuple</text>
-</g>
-<!-- TUPLE_BOT&#45;&gt;BOT -->
-<g id="edge63" class="edge">
-<title>TUPLE_BOT&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M1151.54,-77.32C1046.53,-65.95 769.32,-35.95 674.14,-25.65"/>
-<polygon fill="black" stroke="black" points="674.52,-22.18 664.2,-24.58 673.77,-29.14 674.52,-22.18"/>
-</g>
-<!-- TUPLE1&#45;&gt;TUPLE_BOT -->
-<g id="edge60" class="edge">
-<title>TUPLE1&#45;&gt;TUPLE_BOT</title>
-<path fill="none" stroke="black" d="M1162.95,-256.52C1169.28,-219.3 1180.86,-151.25 1187.49,-112.26"/>
-<polygon fill="black" stroke="black" points="1190.9,-113.1 1189.12,-102.66 1184,-111.93 1190.9,-113.1"/>
-</g>
-<!-- TUPLE2&#45;&gt;TUPLE_BOT -->
-<g id="edge61" class="edge">
-<title>TUPLE2&#45;&gt;TUPLE_BOT</title>
-<path fill="none" stroke="black" d="M1220.11,-245.05C1213.55,-206.44 1203.46,-147.16 1197.47,-111.94"/>
-<polygon fill="black" stroke="black" points="1200.99,-111.74 1195.86,-102.47 1194.09,-112.92 1200.99,-111.74"/>
-</g>
-<!-- TUPLEN&#45;&gt;TUPLE_BOT -->
-<g id="edge62" class="edge">
-<title>TUPLEN&#45;&gt;TUPLE_BOT</title>
-<path fill="none" stroke="black" d="M1269.56,-233.75C1249.85,-195.12 1223.04,-142.58 1206.8,-110.75"/>
-<polygon fill="black" stroke="black" points="1210.07,-109.46 1202.41,-102.15 1203.83,-112.65 1210.07,-109.46"/>
-</g>
-</g>
-</svg>
diff --git a/chapter28b/docs/lattice2.gv b/chapter28b/docs/lattice2.gv
deleted file mode 100644
index 6aa37fa0..00000000
--- a/chapter28b/docs/lattice2.gv
+++ /dev/null
@@ -1,85 +0,0 @@
-digraph Lattice {
- compound = true;
- // Define nodes
- splines=false;
- node [shape=oval, style=filled];
- nodesep=0.2;
- ranksep=0.4;
-
- TOP [label="~scalar\n+int+tmp+0", fillcolor=black, fontcolor=white, group=center];
-
- // ------------------------
- PTRHQ [label="~tmp\n+tmp+0", fillcolor=green, fontcolor=white, group=left];
- TOP -> PTRHQ
-
- VALH_ [label="~nscalar\n+int+tmp", fillcolor=white, group=center];
- TOP -> VALH_
-
- INTHQ [label="~int\n+int+0", fillcolor=lightblue, group=right];
- TOP -> INTHQ
-
- LEFTHI [label="junk", style=invis, group=junkleft]
-
-
- PTRHQ -> LEFTHI-> VALH_ -> INTHQ [style=invis];
- { rank=same; PTRHQ; LEFTHI; VALH_; INTHQ }
-
- // ------------------------
- PTRH_ [label="~ntmp\n+tmp", fillcolor=green, fontcolor=white, group=left];
- VALH_ -> PTRH_
- PTRHQ -> PTRH_
-
- INTH_ [label="~nint\n+int", fillcolor=lightblue, group=right];
- VALH_ -> INTH_
- INTHQ -> INTH_
- { rank=same; PTRH_; INTH_ }
-
- // ------------------------
- NILX_ [label="~nil\n[int,tmp]+0", fillcolor=aqua, group=junkleft];
- INTHQ -> NILX_
- PTRHQ -> NILX_
-
- NIL__ [label="nil\n[+int+tmp],0", fillcolor=pink];
- VALH_ -> NIL__;
-
- NILX_ -> NIL__ [style=invis];
- { rank=same; NIL__; NILX_ }
-
- // ------------------------
- PTRL_ [label="ntmp\ntmp", fillcolor=green, fontcolor=white, group=left];
- PTRH_ -> PTRL_
-
- INTL_ [label="nint\nint", fillcolor=lightblue, group=right];
- INTH_ -> INTL_
- { rank=same; PTRL_; INTL_ }
-
- // ------------------------
- PTRL0 [label="tmp\ntmp,0", fillcolor=green, fontcolor=white, group=left];
- PTRL_ -> PTRL0
- NIL__ -> PTRL0
-
- VALL_ [label="nscalar\nint,tmp", fillcolor=white, group=center];
- PTRL_ -> VALL_
- INTL_ -> VALL_
- NILX_ -> VALL_
-
- INTL0 [label="int\nint,0", fillcolor=lightblue, group=right];
- INTL_ -> INTL0
- NIL__ -> INTL0
-
- PTRL0 -> VALL_ -> INTL0 [style=invis];
- { rank=same; PTRL0; VALL_; INTL0 }
-
- // ------------------------
- BOT [label="scalar\nint,tmp,0", fillcolor=black, fontcolor=white, group=center];
- PTRL0 -> BOT
- INTL0 -> BOT
- VALL_ -> BOT
-
- // ------------------------
- // Verticle alignment
- TOP -> VALH_ -> VALL_ -> BOT [style=invis];
- PTRHQ -> PTRH_ -> PTRL_ -> PTRL0 [style=invis];
- INTHQ -> INTH_ -> INTL_ -> INTL0 [style=invis];
- LEFTHI -> NILX_ [style=invis]
-}
diff --git a/chapter28b/docs/lattice2.svg b/chapter28b/docs/lattice2.svg
deleted file mode 100644
index 8b682aa7..00000000
--- a/chapter28b/docs/lattice2.svg
+++ /dev/null
@@ -1,260 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
- "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Generated by graphviz version 12.2.0 (20241103.1931)
- -->
-<!-- Title: Lattice Pages: 1 -->
-<svg width="478pt" height="501pt"
- viewBox="0.00 0.00 477.96 500.90" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 496.9)">
-<title>Lattice</title>
-<polygon fill="white" stroke="none" points="-4,4 -4,-496.9 473.96,-496.9 473.96,4 -4,4"/>
-<!-- TOP -->
-<g id="node1" class="node">
-<title>TOP</title>
-<ellipse fill="black" stroke="black" cx="240.13" cy="-463.91" rx="59.04" ry="28.99"/>
-<text text-anchor="middle" x="240.13" y="-467.11" font-family="Times New Roman,serif" font-size="14.00" fill="white">~scalar</text>
-<text text-anchor="middle" x="240.13" y="-450.61" font-family="Times New Roman,serif" font-size="14.00" fill="white">+int+tmp+0</text>
-</g>
-<!-- PTRHQ -->
-<g id="node2" class="node">
-<title>PTRHQ</title>
-<ellipse fill="green" stroke="black" cx="43.13" cy="-376.92" rx="43.13" ry="28.99"/>
-<text text-anchor="middle" x="43.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00" fill="white">~tmp</text>
-<text text-anchor="middle" x="43.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00" fill="white">+tmp+0</text>
-</g>
-<!-- TOP&#45;&gt;PTRHQ -->
-<g id="edge1" class="edge">
-<title>TOP&#45;&gt;PTRHQ</title>
-<path fill="none" stroke="black" d="M196.41,-444.04C164.74,-430.38 121.99,-411.94 89.68,-398"/>
-<polygon fill="black" stroke="black" points="91.11,-394.81 80.55,-394.06 88.34,-401.24 91.11,-394.81"/>
-</g>
-<!-- VALH_ -->
-<g id="node3" class="node">
-<title>VALH_</title>
-<ellipse fill="white" stroke="black" cx="240.13" cy="-376.92" rx="48.44" ry="28.99"/>
-<text text-anchor="middle" x="240.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00">~nscalar</text>
-<text text-anchor="middle" x="240.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00">+int+tmp</text>
-</g>
-<!-- TOP&#45;&gt;VALH_ -->
-<g id="edge2" class="edge">
-<title>TOP&#45;&gt;VALH_</title>
-<path fill="none" stroke="black" d="M235.15,-434.61C234.93,-429.02 234.85,-423.06 234.9,-417.18"/>
-<polygon fill="black" stroke="black" points="238.39,-417.69 235.12,-407.61 231.39,-417.52 238.39,-417.69"/>
-</g>
-<!-- TOP&#45;&gt;VALH_ -->
-<!-- INTHQ -->
-<g id="node4" class="node">
-<title>INTHQ</title>
-<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-376.92" rx="37.83" ry="28.99"/>
-<text text-anchor="middle" x="432.13" y="-380.12" font-family="Times New Roman,serif" font-size="14.00">~int</text>
-<text text-anchor="middle" x="432.13" y="-363.62" font-family="Times New Roman,serif" font-size="14.00">+int+0</text>
-</g>
-<!-- TOP&#45;&gt;INTHQ -->
-<g id="edge3" class="edge">
-<title>TOP&#45;&gt;INTHQ</title>
-<path fill="none" stroke="black" d="M283.22,-443.83C314.91,-429.81 357.78,-410.83 389.39,-396.84"/>
-<polygon fill="black" stroke="black" points="390.53,-400.16 398.26,-392.92 387.7,-393.76 390.53,-400.16"/>
-</g>
-<!-- LEFTHI -->
-<!-- PTRHQ&#45;&gt;LEFTHI -->
-<!-- PTRH_ -->
-<g id="node6" class="node">
-<title>PTRH_</title>
-<ellipse fill="green" stroke="black" cx="43.13" cy="-289.94" rx="36.77" ry="28.99"/>
-<text text-anchor="middle" x="43.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00" fill="white">~ntmp</text>
-<text text-anchor="middle" x="43.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00" fill="white">+tmp</text>
-</g>
-<!-- PTRHQ&#45;&gt;PTRH_ -->
-<g id="edge8" class="edge">
-<title>PTRHQ&#45;&gt;PTRH_</title>
-<path fill="none" stroke="black" d="M38.15,-347.62C37.93,-342.03 37.85,-336.07 37.9,-330.2"/>
-<polygon fill="black" stroke="black" points="41.39,-330.71 38.12,-320.63 34.39,-330.54 41.39,-330.71"/>
-</g>
-<!-- PTRHQ&#45;&gt;PTRH_ -->
-<!-- NILX_ -->
-<g id="node8" class="node">
-<title>NILX_</title>
-<ellipse fill="aqua" stroke="black" cx="150.13" cy="-289.94" rx="56.39" ry="28.99"/>
-<text text-anchor="middle" x="150.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">~nil</text>
-<text text-anchor="middle" x="150.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">[int,tmp]+0</text>
-</g>
-<!-- PTRHQ&#45;&gt;NILX_ -->
-<g id="edge12" class="edge">
-<title>PTRHQ&#45;&gt;NILX_</title>
-<path fill="none" stroke="black" d="M70.41,-354.26C82.91,-344.33 97.94,-332.4 111.56,-321.58"/>
-<polygon fill="black" stroke="black" points="113.43,-324.56 119.09,-315.6 109.08,-319.08 113.43,-324.56"/>
-</g>
-<!-- VALH_&#45;&gt;INTHQ -->
-<!-- VALH_&#45;&gt;PTRH_ -->
-<g id="edge7" class="edge">
-<title>VALH_&#45;&gt;PTRH_</title>
-<path fill="none" stroke="black" d="M201.21,-359.13C167.78,-344.71 119.68,-323.96 85.33,-309.14"/>
-<polygon fill="black" stroke="black" points="87.1,-306.09 76.53,-305.35 84.33,-312.52 87.1,-306.09"/>
-</g>
-<!-- INTH_ -->
-<g id="node7" class="node">
-<title>INTH_</title>
-<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-289.94" rx="31.47" ry="28.99"/>
-<text text-anchor="middle" x="432.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">~nint</text>
-<text text-anchor="middle" x="432.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">+int</text>
-</g>
-<!-- VALH_&#45;&gt;INTH_ -->
-<g id="edge9" class="edge">
-<title>VALH_&#45;&gt;INTH_</title>
-<path fill="none" stroke="black" d="M278.53,-358.93C311.9,-344.16 360.02,-322.86 393.48,-308.05"/>
-<polygon fill="black" stroke="black" points="394.67,-311.35 402.4,-304.1 391.84,-304.95 394.67,-311.35"/>
-</g>
-<!-- NIL__ -->
-<g id="node9" class="node">
-<title>NIL__</title>
-<ellipse fill="pink" stroke="black" cx="324.13" cy="-289.94" rx="62.23" ry="28.99"/>
-<text text-anchor="middle" x="324.13" y="-293.14" font-family="Times New Roman,serif" font-size="14.00">nil</text>
-<text text-anchor="middle" x="324.13" y="-276.64" font-family="Times New Roman,serif" font-size="14.00">[+int+tmp],0</text>
-</g>
-<!-- VALH_&#45;&gt;NIL__ -->
-<g id="edge13" class="edge">
-<title>VALH_&#45;&gt;NIL__</title>
-<path fill="none" stroke="black" d="M264.43,-351.34C272.67,-343.01 282.01,-333.55 290.83,-324.64"/>
-<polygon fill="black" stroke="black" points="293.12,-327.3 297.66,-317.72 288.14,-322.38 293.12,-327.3"/>
-</g>
-<!-- VALL_ -->
-<g id="node13" class="node">
-<title>VALL_</title>
-<ellipse fill="white" stroke="black" cx="240.13" cy="-115.97" rx="39.42" ry="28.99"/>
-<text text-anchor="middle" x="240.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00">nscalar</text>
-<text text-anchor="middle" x="240.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00">int,tmp</text>
-</g>
-<!-- VALH_&#45;&gt;VALL_ -->
-<!-- INTHQ&#45;&gt;INTH_ -->
-<g id="edge10" class="edge">
-<title>INTHQ&#45;&gt;INTH_</title>
-<path fill="none" stroke="black" d="M427.17,-348.1C426.94,-342.37 426.85,-336.25 426.9,-330.21"/>
-<polygon fill="black" stroke="black" points="430.39,-330.42 427.13,-320.34 423.39,-330.26 430.39,-330.42"/>
-</g>
-<!-- INTHQ&#45;&gt;INTH_ -->
-<!-- INTHQ&#45;&gt;NILX_ -->
-<g id="edge11" class="edge">
-<title>INTHQ&#45;&gt;NILX_</title>
-<path fill="none" stroke="black" d="M396.99,-365.33C350,-351.17 266.03,-325.87 209.23,-308.75"/>
-<polygon fill="black" stroke="black" points="210.28,-305.41 199.69,-305.88 208.26,-312.11 210.28,-305.41"/>
-</g>
-<!-- LEFTHI&#45;&gt;VALH_ -->
-<!-- LEFTHI&#45;&gt;NILX_ -->
-<!-- PTRL_ -->
-<g id="node10" class="node">
-<title>PTRL_</title>
-<ellipse fill="green" stroke="black" cx="43.13" cy="-202.96" rx="31.47" ry="28.99"/>
-<text text-anchor="middle" x="43.13" y="-206.16" font-family="Times New Roman,serif" font-size="14.00" fill="white">ntmp</text>
-<text text-anchor="middle" x="43.13" y="-189.66" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp</text>
-</g>
-<!-- PTRH_&#45;&gt;PTRL_ -->
-<g id="edge15" class="edge">
-<title>PTRH_&#45;&gt;PTRL_</title>
-<path fill="none" stroke="black" d="M38.17,-261.11C37.94,-255.39 37.85,-249.26 37.9,-243.22"/>
-<polygon fill="black" stroke="black" points="41.39,-243.44 38.13,-233.36 34.39,-243.27 41.39,-243.44"/>
-</g>
-<!-- PTRH_&#45;&gt;PTRL_ -->
-<!-- INTL_ -->
-<g id="node11" class="node">
-<title>INTL_</title>
-<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-202.96" rx="27" ry="28.99"/>
-<text text-anchor="middle" x="432.13" y="-206.16" font-family="Times New Roman,serif" font-size="14.00">nint</text>
-<text text-anchor="middle" x="432.13" y="-189.66" font-family="Times New Roman,serif" font-size="14.00">int</text>
-</g>
-<!-- INTH_&#45;&gt;INTL_ -->
-<g id="edge16" class="edge">
-<title>INTH_&#45;&gt;INTL_</title>
-<path fill="none" stroke="black" d="M427.17,-261.11C426.94,-255.39 426.85,-249.26 426.9,-243.22"/>
-<polygon fill="black" stroke="black" points="430.39,-243.44 427.13,-233.36 423.39,-243.27 430.39,-243.44"/>
-</g>
-<!-- INTH_&#45;&gt;INTL_ -->
-<!-- NILX_&#45;&gt;NIL__ -->
-<!-- NILX_&#45;&gt;VALL_ -->
-<g id="edge21" class="edge">
-<title>NILX_&#45;&gt;VALL_</title>
-<path fill="none" stroke="black" d="M164.79,-261.6C180.03,-232.15 203.95,-185.91 220.74,-153.45"/>
-<polygon fill="black" stroke="black" points="223.74,-155.27 225.23,-144.78 217.53,-152.06 223.74,-155.27"/>
-</g>
-<!-- PTRL0 -->
-<g id="node12" class="node">
-<title>PTRL0</title>
-<ellipse fill="green" stroke="black" cx="43.13" cy="-115.97" rx="34.12" ry="28.99"/>
-<text text-anchor="middle" x="43.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp</text>
-<text text-anchor="middle" x="43.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00" fill="white">tmp,0</text>
-</g>
-<!-- NIL__&#45;&gt;PTRL0 -->
-<g id="edge18" class="edge">
-<title>NIL__&#45;&gt;PTRL0</title>
-<path fill="none" stroke="black" d="M286.24,-266.48C232.36,-233.13 134.24,-172.38 80.6,-139.17"/>
-<polygon fill="black" stroke="black" points="82.76,-136.39 72.41,-134.1 79.07,-142.34 82.76,-136.39"/>
-</g>
-<!-- INTL0 -->
-<g id="node14" class="node">
-<title>INTL0</title>
-<ellipse fill="lightblue" stroke="black" cx="432.13" cy="-115.97" rx="28.81" ry="28.99"/>
-<text text-anchor="middle" x="432.13" y="-119.17" font-family="Times New Roman,serif" font-size="14.00">int</text>
-<text text-anchor="middle" x="432.13" y="-102.67" font-family="Times New Roman,serif" font-size="14.00">int,0</text>
-</g>
-<!-- NIL__&#45;&gt;INTL0 -->
-<g id="edge23" class="edge">
-<title>NIL__&#45;&gt;INTL0</title>
-<path fill="none" stroke="black" d="M341.49,-261.99C360.34,-231.61 390.49,-183.05 410.79,-150.35"/>
-<polygon fill="black" stroke="black" points="413.65,-152.38 415.95,-142.04 407.7,-148.69 413.65,-152.38"/>
-</g>
-<!-- PTRL_&#45;&gt;PTRL0 -->
-<g id="edge17" class="edge">
-<title>PTRL_&#45;&gt;PTRL0</title>
-<path fill="none" stroke="black" d="M38.17,-174.13C37.94,-168.47 37.85,-162.41 37.9,-156.43"/>
-<polygon fill="black" stroke="black" points="41.39,-156.75 38.12,-146.67 34.39,-156.58 41.39,-156.75"/>
-</g>
-<!-- PTRL_&#45;&gt;PTRL0 -->
-<!-- PTRL_&#45;&gt;VALL_ -->
-<g id="edge19" class="edge">
-<title>PTRL_&#45;&gt;VALL_</title>
-<path fill="none" stroke="black" d="M71.36,-189.78C103.94,-175.73 157.92,-152.44 196.04,-135.99"/>
-<polygon fill="black" stroke="black" points="197.16,-139.32 204.96,-132.15 194.39,-132.9 197.16,-139.32"/>
-</g>
-<!-- INTL_&#45;&gt;VALL_ -->
-<g id="edge20" class="edge">
-<title>INTL_&#45;&gt;VALL_</title>
-<path fill="none" stroke="black" d="M407.43,-191.02C376.23,-177.21 322.01,-153.22 283.77,-136.29"/>
-<polygon fill="black" stroke="black" points="285.39,-133.18 274.83,-132.33 282.55,-139.58 285.39,-133.18"/>
-</g>
-<!-- INTL_&#45;&gt;INTL0 -->
-<g id="edge22" class="edge">
-<title>INTL_&#45;&gt;INTL0</title>
-<path fill="none" stroke="black" d="M427.17,-174.13C426.94,-168.41 426.85,-162.28 426.9,-156.24"/>
-<polygon fill="black" stroke="black" points="430.39,-156.46 427.13,-146.38 423.39,-156.29 430.39,-156.46"/>
-</g>
-<!-- INTL_&#45;&gt;INTL0 -->
-<!-- PTRL0&#45;&gt;VALL_ -->
-<!-- BOT -->
-<g id="node15" class="node">
-<title>BOT</title>
-<ellipse fill="black" stroke="black" cx="240.13" cy="-28.99" rx="46.85" ry="28.99"/>
-<text text-anchor="middle" x="240.13" y="-32.19" font-family="Times New Roman,serif" font-size="14.00" fill="white">scalar</text>
-<text text-anchor="middle" x="240.13" y="-15.69" font-family="Times New Roman,serif" font-size="14.00" fill="white">int,tmp,0</text>
-</g>
-<!-- PTRL0&#45;&gt;BOT -->
-<g id="edge26" class="edge">
-<title>PTRL0&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M73.48,-101.88C105.02,-88.28 154.83,-66.79 191.79,-50.85"/>
-<polygon fill="black" stroke="black" points="193.14,-54.08 200.94,-46.9 190.37,-47.65 193.14,-54.08"/>
-</g>
-<!-- VALL_&#45;&gt;INTL0 -->
-<!-- VALL_&#45;&gt;BOT -->
-<g id="edge28" class="edge">
-<title>VALL_&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M235.17,-87.15C234.94,-81.48 234.85,-75.42 234.9,-69.44"/>
-<polygon fill="black" stroke="black" points="238.39,-69.76 235.12,-59.68 231.39,-69.6 238.39,-69.76"/>
-</g>
-<!-- VALL_&#45;&gt;BOT -->
-<!-- INTL0&#45;&gt;BOT -->
-<g id="edge27" class="edge">
-<title>INTL0&#45;&gt;BOT</title>
-<path fill="none" stroke="black" d="M405.84,-103.34C375.52,-89.91 325.06,-67.58 287.78,-51.08"/>
-<polygon fill="black" stroke="black" points="289.59,-48.05 279.03,-47.21 286.75,-54.45 289.59,-48.05"/>
-</g>
-</g>
-</svg>
diff --git a/chapter28b/docs/pardoe.docx b/chapter28b/docs/pardoe.docx
deleted file mode 100644
index 24fde921..00000000
--- a/chapter28b/docs/pardoe.docx
+++ /dev/null
Binary files differ
diff --git a/chapter28b/docs/webtcp.min.js b/chapter28b/docs/webtcp.min.js
deleted file mode 100644
index bf23ee5a..00000000
--- a/chapter28b/docs/webtcp.min.js
+++ /dev/null
@@ -1,65 +0,0 @@
-function Http(j){this.cID=Math.random().toString(36).substr(2);this.webtcp=j;this.webtcp.httpClients[this.cID]=this;this.createPacket=function(){return{cID:this.cID,type:"http",host:this.remoteAddress,port:this.remotePort,method:null,data:null}};this.ondata=function(){};this.get=function(g,c){this.on("data",c);this.rpc("httpGet",[g])};this.post=function(g,c,j){this.on("data",j);this.rpc("httpPost",[g,c])}}Http.prototype=new WebTCPIO;function Socket(j,g,c,p){this.sID=Math.random().toString(36).substr(2);this.webtcp=j;this.closed=this.ready=!1;this.options=p;this.sockOpts={_updating:!1};this.remoteAddress=g;this.remotePort=c;this.webtcp.sockets[this.sID]=this;this.createPacket=function(){return{id:Math.random().toString(36).substr(2),sID:this.sID,type:"tcp",options:this.options,host:this.remoteAddress,port:this.remotePort,data:null}};this.onconnect=function(){this.webtcp.sockets[sID].ready=!0;this.webtcp.sockets[sID].closed=!1};
-this.ondata=function(){};this.onend=function(){};this.ontimeout=function(){};this.ondrain=function(){};this.onclose=function(){this.webtcp.sockets[sID].ready=!1;this.webtcp.sockets[sID].closed=!0};this.getSockOpts=function(){this.sockOpts._updating=!0;this.rpc("getSockOpts")};this.onSockOptsRcv=function(c){this.sockOpts=c;this.sockOpts._updating=!1}}Socket.prototype=new WebTCPIO;var JSON;JSON||(JSON={});
-(function(){function j(c,p){var q,t,n,x,i=k,e,l=p[c];l&&"object"==typeof l&&"function"==typeof l.toJSON&&(l=l.toJSON(c));"function"==typeof r&&(l=r.call(p,c,l));switch(typeof l){case "string":return g(l);case "number":return isFinite(l)?String(l):"null";case "boolean":case "null":return String(l);case "object":if(!l)return"null";k+=v;e=[];if("[object Array]"===Object.prototype.toString.apply(l)){x=l.length;for(q=0;q<x;q+=1)e[q]=j(q,l)||"null";n=0===e.length?"[]":k?"[\n"+k+e.join(",\n"+k)+"\n"+i+"]":
-"["+e.join(",")+"]";k=i;return n}if(r&&"object"==typeof r){x=r.length;for(q=0;q<x;q+=1)"string"==typeof r[q]&&(t=r[q],n=j(t,l),n&&e.push(g(t)+(k?": ":":")+n))}else for(t in l)Object.prototype.hasOwnProperty.call(l,t)&&(n=j(t,l),n&&e.push(g(t)+(k?": ":":")+n));n=0===e.length?"{}":k?"{\n"+k+e.join(",\n"+k)+"\n"+i+"}":"{"+e.join(",")+"}";k=i;return n}}function g(c){n.lastIndex=0;return n.test(c)?'"'+c.replace(n,function(c){var g=D[c];return"string"==typeof g?g:"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)})+
-'"':'"'+c+'"'}function c(c){return 10>c?"0"+c:c}"use strict";"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+c(this.getUTCMonth()+1)+"-"+c(this.getUTCDate())+"T"+c(this.getUTCHours())+":"+c(this.getUTCMinutes())+":"+c(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var p=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
-n=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,k,v,D={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},r;"function"!=typeof JSON.stringify&&(JSON.stringify=function(c,g,p){var n;v=k="";if("number"==typeof p)for(n=0;n<p;n+=1)v+=" ";else"string"==typeof p&&(v=p);r=g;if(!g||"function"==typeof g||"object"==typeof g&&"number"==typeof g.length)return j("",{"":c});throw Error("JSON.stringify");});"function"!=
-typeof JSON.parse&&(JSON.parse=function(c,g){function j(c,k){var i,e,l=c[k];if(l&&"object"==typeof l)for(i in l)Object.prototype.hasOwnProperty.call(l,i)&&(e=j(l,i),void 0!==e?l[i]=e:delete l[i]);return g.call(c,k,l)}var k,c=String(c);p.lastIndex=0;p.test(c)&&(c=c.replace(p,function(c){return"\\u"+("0000"+c.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(c.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
-"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return k=eval("("+c+")"),"function"==typeof g?j({"":k},""):k;throw new SyntaxError("JSON.parse");})})();
-SockJS=function(){var j=document,g=window,c={},p=function(){};p.prototype.addEventListener=function(a,b){this._listeners||(this._listeners={});a in this._listeners||(this._listeners[a]=[]);var f=this._listeners[a];-1===c.arrIndexOf(f,b)&&f.push(b)};p.prototype.removeEventListener=function(a,b){if(this._listeners&&a in this._listeners){var f=this._listeners[a],d=c.arrIndexOf(f,b);-1!==d&&(1<f.length?this._listeners[a]=f.slice(0,d).concat(f.slice(d+1)):delete this._listeners[a])}};p.prototype.dispatchEvent=
-function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d<this._listeners[b].length;d++)this._listeners[b][d].apply(this,c)};var n=function(a,b){this.type=a;if("undefined"!=typeof b)for(var c in b)b.hasOwnProperty(c)&&(this[c]=b[c])};n.prototype.toString=function(){var a=[],b;for(b in this)if(this.hasOwnProperty(b)){var c=this[b];"function"==typeof c&&(c="[function]");a.push(b+"="+c)}return"SimpleEvent("+
-a.join(", ")+")"};var k=function(a){this.events=a||[]};k.prototype.emit=function(a){var b=Array.prototype.slice.call(arguments,1);!this.nuked&&this["on"+a]&&this["on"+a].apply(this,b);-1===c.arrIndexOf(this.events,a)&&c.log("Event "+JSON.stringify(a)+" not listed "+JSON.stringify(this.events)+" in "+this)};k.prototype.nuke=function(){this.nuked=!0;for(var a=0;a<this.events.length;a++)delete this[this.events[a]]};c.random_string=function(a,b){var b=b||37,c,d=[];for(c=0;c<a;c++)d.push("abcdefghijklmnopqrstuvwxyz0123456789_".substr(Math.floor(Math.random()*
-b),1));return d.join("")};c.random_number=function(a){return Math.floor(Math.random()*a)};c.random_number_string=function(a){var b=(""+(a-1)).length;return(Array(b+1).join("0")+c.random_number(a)).slice(-b)};c.getOrigin=function(a){return(a+"/").split("/").slice(0,3).join("/")};c.isSameOriginUrl=function(a,b){return b||(b=g.location.href),a.split("/").slice(0,3).join("/")===b.split("/").slice(0,3).join("/")};c.getParentDomain=function(a){return/^[0-9.]*$/.test(a)||/^\[/.test(a)||!/[.]/.test(a)?a:
-a.split(".").slice(1).join(".")};c.objectExtend=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a};c.polluteGlobalNamespace=function(){"_jp"in g||(g._jp={})};c.closeFrame=function(a,b){return"c"+JSON.stringify([a,b])};c.userSetCode=function(a){return 1E3===a||3E3<=a&&4999>=a};c.countRTO=function(a){var b;return 100<a?b=3*a:b=a+200,b};c.log=function(){g.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)};c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b,
-arguments)}};c.flatUrl=function(a){return-1===a.indexOf("?")&&-1===a.indexOf("#")};c.amendUrl=function(a){var b=j.location;if(!a)throw Error("Wrong url for SockJS");if(!c.flatUrl(a))throw Error("Only basic urls are supported in SockJS");return 0===a.indexOf("//")&&(a=b.protocol+a),0===a.indexOf("/")&&(a=b.protocol+"//"+b.host+a),a=a.replace(/[/]+$/,""),a};c.arrIndexOf=function(a,b){for(var c=0;c<a.length;c++)if(a[c]===b)return c;return-1};c.arrSkip=function(a,b){var f=c.arrIndexOf(a,b);return-1===
-f?a.slice():a.slice(0,f).concat(a.slice(f+1))};c.isArray=Array.isArray||function(a){return 0<={}.toString.call(a).indexOf("Array")};c.delay=function(a,b){return"function"==typeof a&&(b=a,a=0),setTimeout(b,a)};var v=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,D={"\x00":"\\u0000","\u0001":"\\u0001","\u0002":"\\u0002","\u0003":"\\u0003","\u0004":"\\u0004","\u0005":"\\u0005","\u0006":"\\u0006","\u0007":"\\u0007","\b":"\\b",
-"\t":"\\t","\n":"\\n","\x0B":"\\u000b","\f":"\\f","\r":"\\r","\u000e":"\\u000e","\u000f":"\\u000f","\u0010":"\\u0010","\u0011":"\\u0011","\u0012":"\\u0012","\u0013":"\\u0013","\u0014":"\\u0014","\u0015":"\\u0015","\u0016":"\\u0016","\u0017":"\\u0017","\u0018":"\\u0018","\u0019":"\\u0019","\u001a":"\\u001a","\u001b":"\\u001b","\u001c":"\\u001c","\u001d":"\\u001d","\u001e":"\\u001e","\u001f":"\\u001f",'"':'\\"',"\\":"\\\\","\u007f":"\\u007f","\u0080":"\\u0080","\u0081":"\\u0081","\u0082":"\\u0082",
-"\u0083":"\\u0083","\u0084":"\\u0084","\u0085":"\\u0085","\u0086":"\\u0086","\u0087":"\\u0087","\u0088":"\\u0088","\u0089":"\\u0089","\u008a":"\\u008a","\u008b":"\\u008b","\u008c":"\\u008c","\u008d":"\\u008d","\u008e":"\\u008e","\u008f":"\\u008f","\u0090":"\\u0090","\u0091":"\\u0091","\u0092":"\\u0092","\u0093":"\\u0093","\u0094":"\\u0094","\u0095":"\\u0095","\u0096":"\\u0096","\u0097":"\\u0097","\u0098":"\\u0098","\u0099":"\\u0099","\u009a":"\\u009a","\u009b":"\\u009b","\u009c":"\\u009c","\u009d":"\\u009d",
-"\u009e":"\\u009e","\u009f":"\\u009f","\u00ad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063",
-"\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd",
-"\ufffe":"\\ufffe","\uffff":"\\uffff"},r=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,
-B,M=JSON&&JSON.stringify||function(a){return v.lastIndex=0,v.test(a)&&(a=a.replace(v,function(a){return D[a]})),'"'+a+'"'};c.quote=function(a){a=M(a);r.lastIndex=0;if(r.test(a)){if(!B){var b,c={},d=[];for(b=0;65536>b;b++)d.push(String.fromCharCode(b));B=(r.lastIndex=0,d.join("").replace(r,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),r.lastIndex=0,c)}a=a.replace(r,function(a){return B[a]})}return a};var q="websocket xdr-streaming xhr-streaming iframe-eventsource iframe-htmlfile xdr-polling xhr-polling iframe-xhr-polling jsonp-polling".split(" ");
-c.probeProtocols=function(){for(var a={},b=0;b<q.length;b++){var c=q[b];a[c]=e[c]&&e[c].enabled()}return a};c.detectProtocols=function(a,b,c){var d={},h=[];b||(b=q);for(var m=0;m<b.length;m++){var g=b[m];d[g]=a[g]}var e=function(a){var b=a.shift();d[b]?h.push(b):0<a.length&&e(a)};return!1!==c.websocket&&e(["websocket"]),d["xhr-streaming"]&&!c.null_origin?h.push("xhr-streaming"):d["xdr-streaming"]&&!c.cookie_needed&&!c.null_origin?h.push("xdr-streaming"):e(["iframe-eventsource","iframe-htmlfile"]),
-d["xhr-polling"]&&!c.null_origin?h.push("xhr-polling"):d["xdr-polling"]&&!c.cookie_needed&&!c.null_origin?h.push("xdr-polling"):e(["iframe-xhr-polling","jsonp-polling"]),h};c.createHook=function(){var a="a"+c.random_string(8);if(!("_sockjs_global"in g)){var b={};g._sockjs_global=function(a){return a in b||(b[a]={id:a,del:function(){delete b[a]}}),b[a]}}return g._sockjs_global(a)};c.attachMessage=function(a){c.attachEvent("message",a)};c.attachEvent=function(a,b){"undefined"!=typeof g.addEventListener?
-g.addEventListener(a,b,!1):(j.attachEvent("on"+a,b),g.attachEvent("on"+a,b))};c.detachMessage=function(a){c.detachEvent("message",a)};c.detachEvent=function(a,b){"undefined"!=typeof g.addEventListener?g.removeEventListener(a,b,!1):(j.detachEvent("on"+a,b),g.detachEvent("on"+a,b))};var t={},C=!1,x=function(){for(var a in t)t[a](),delete t[a]},i=function(){C||(C=!0,x())};c.attachEvent("beforeunload",i);c.attachEvent("unload",i);c.unload_add=function(a){var b=c.random_string(8);return t[b]=a,C&&c.delay(x),
-b};c.unload_del=function(a){a in t&&delete t[a]};c.createIframe=function(a,b){var f=j.createElement("iframe"),d,h,m=function(){clearTimeout(d);try{f.onload=null}catch(a){}f.onerror=null},g=function(){f&&(m(),setTimeout(function(){f&&f.parentNode.removeChild(f);f=null},0),c.unload_del(h))};return f.src=a,f.style.display="none",f.style.position="absolute",f.onerror=function(){f&&(g(),b("onerror"))},f.onload=function(){clearTimeout(d);d=setTimeout(function(){f&&(g(),b("onload timeout"))},2E3)},j.body.appendChild(f),
-d=setTimeout(function(){f&&(g(),b("timeout"))},15E3),h=c.unload_add(g),{post:function(a,b){try{f&&f.contentWindow&&f.contentWindow.postMessage(a,b)}catch(c){}},cleanup:g,loaded:m}};c.createHtmlfile=function(a,b){var f=new ActiveXObject("htmlfile"),d,h,m,e=function(){clearTimeout(d)},j=function(){f&&(e(),c.unload_del(h),m.parentNode.removeChild(m),m=f=null,CollectGarbage())};f.open();f.write('<html><script>document.domain="'+document.domain+'";<\/script></html>');f.close();f.parentWindow._jp=g._jp;
-var i=f.createElement("div");return f.body.appendChild(i),m=f.createElement("iframe"),i.appendChild(m),m.src=a,d=setTimeout(function(){f&&(j(),b("timeout"))},15E3),h=c.unload_add(j),{post:function(a,b){try{m&&m.contentWindow&&m.contentWindow.postMessage(a,b)}catch(c){}},cleanup:j,loaded:e}};i=function(){};i.prototype=new k(["chunk","finish"]);i.prototype._start=function(a,b,f,d){var h=this;try{h.xhr=new XMLHttpRequest}catch(m){}if(!h.xhr)try{h.xhr=new g.ActiveXObject("Microsoft.XMLHTTP")}catch(e){}if(g.ActiveXObject||
-g.XDomainRequest)b+=(-1===b.indexOf("?")?"?":"&")+"t="+ +new Date;h.unload_ref=c.unload_add(function(){h._cleanup(!0)});try{h.xhr.open(a,b,!0)}catch(j){h.emit("finish",0,"");h._cleanup();return}if(!d||!d.no_credentials)h.xhr.withCredentials="true";if(d&&d.headers)for(var i in d.headers)h.xhr.setRequestHeader(i,d.headers[i]);h.xhr.onreadystatechange=function(){if(h.xhr){var a=h.xhr;switch(a.readyState){case 3:try{var b=a.status,c=a.responseText}catch(d){}c&&0<c.length&&h.emit("chunk",b,c);break;case 4:h.emit("finish",
-a.status,a.responseText),h._cleanup(!1)}}};h.xhr.send(f)};i.prototype._cleanup=function(a){if(this.xhr){c.unload_del(this.unload_ref);this.xhr.onreadystatechange=function(){};if(a)try{this.xhr.abort()}catch(b){}this.unload_ref=this.xhr=null}};i.prototype.close=function(){this.nuke();this._cleanup(!0)};(c.XHRCorsObject=function(){var a=this,b=arguments;c.delay(function(){a._start.apply(a,b)})}).prototype=new i;(c.XHRLocalObject=function(a,b,f){var d=this;c.delay(function(){d._start(a,b,f,{no_credentials:!0})})}).prototype=
-new i;i=c.XDRObject=function(a,b,f){var d=this;c.delay(function(){d._start(a,b,f)})};i.prototype=new k(["chunk","finish"]);i.prototype._start=function(a,b,f){var d=this,h=new XDomainRequest,b=b+((-1===b.indexOf("?")?"?":"&")+"t="+ +new Date),g=h.ontimeout=h.onerror=function(){d.emit("finish",0,"");d._cleanup(!1)};h.onprogress=function(){d.emit("chunk",200,h.responseText)};h.onload=function(){d.emit("finish",200,h.responseText);d._cleanup(!1)};d.xdr=h;d.unload_ref=c.unload_add(function(){d._cleanup(!0)});
-try{d.xdr.open(a,b),d.xdr.send(f)}catch(e){g()}};i.prototype._cleanup=function(a){if(this.xdr){c.unload_del(this.unload_ref);this.xdr.ontimeout=this.xdr.onerror=this.xdr.onprogress=this.xdr.onload=null;if(a)try{this.xdr.abort()}catch(b){}this.unload_ref=this.xdr=null}};i.prototype.close=function(){this.nuke();this._cleanup(!0)};c.isXHRCorsCapable=function(){return g.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest?1:g.XDomainRequest&&j.domain?2:s.enabled()?3:4};var e=function(a,b,f){var d=this,
-h;d._options={devel:!1,debug:!1,protocols_whitelist:[],info:void 0,rtt:void 0};f&&c.objectExtend(d._options,f);d._base_url=c.amendUrl(a);d._server=d._options.server||c.random_number_string(1E3);d._options.protocols_whitelist&&d._options.protocols_whitelist.length?h=d._options.protocols_whitelist:("string"==typeof b&&0<b.length?h=[b]:c.isArray(b)?h=b:h=null,h&&d._debug('Deprecated API: Use "protocols_whitelist" option instead of supplying protocol list as a second parameter to SockJS constructor.'));
-d._protocols=[];d.protocol=null;d.readyState=e.CONNECTING;d._ir=N(d._base_url);d._ir.onfinish=function(a,b){d._ir=null;a?(d._options.info&&(a=c.objectExtend(a,d._options.info)),d._options.rtt&&(b=d._options.rtt),d._applyInfo(a,b,h),d._didClose()):d._didClose(1002,"Can't connect to server",!0)}};e.prototype=new p;e.version="0.3.2";e.CONNECTING=0;e.OPEN=1;e.CLOSING=2;e.CLOSED=3;e.prototype._debug=function(){this._options.debug&&c.log.apply(c,arguments)};e.prototype._dispatchOpen=function(){this.readyState===
-e.CONNECTING?(this._transport_tref&&(clearTimeout(this._transport_tref),this._transport_tref=null),this.readyState=e.OPEN,this.dispatchEvent(new n("open"))):this._didClose(1006,"Server lost session")};e.prototype._dispatchMessage=function(a){this.readyState===e.OPEN&&this.dispatchEvent(new n("message",{data:a}))};e.prototype._dispatchHeartbeat=function(){this.readyState===e.OPEN&&this.dispatchEvent(new n("heartbeat",{}))};e.prototype._didClose=function(a,b,f){var d=this;if(d.readyState!==e.CONNECTING&&
-d.readyState!==e.OPEN&&d.readyState!==e.CLOSING)throw Error("INVALID_STATE_ERR");d._ir&&(d._ir.nuke(),d._ir=null);d._transport&&(d._transport.doCleanup(),d._transport=null);var h=new n("close",{code:a,reason:b,wasClean:c.userSetCode(a)});if(!c.userSetCode(a)&&d.readyState===e.CONNECTING&&!f){if(d._try_next_protocol(h))return;h=new n("close",{code:2E3,reason:"All transports failed",wasClean:!1,last_event:h})}d.readyState=e.CLOSED;c.delay(function(){d.dispatchEvent(h)})};e.prototype._didMessage=function(a){switch(a.slice(0,
-1)){case "o":this._dispatchOpen();break;case "a":for(var a=JSON.parse(a.slice(1)||"[]"),b=0;b<a.length;b++)this._dispatchMessage(a[b]);break;case "m":a=JSON.parse(a.slice(1)||"null");this._dispatchMessage(a);break;case "c":a=JSON.parse(a.slice(1)||"[]");this._didClose(a[0],a[1]);break;case "h":this._dispatchHeartbeat()}};e.prototype._try_next_protocol=function(a){var b=this;b.protocol&&(b._debug("Closed transport:",b.protocol,""+a),b.protocol=null);for(b._transport_tref&&(clearTimeout(b._transport_tref),
-b._transport_tref=null);;){a=b.protocol=b._protocols.shift();if(!a)return!1;if(e[a]&&!0===e[a].need_body&&(!j.body||"undefined"!=typeof j.readyState&&"complete"!==j.readyState))return b._protocols.unshift(a),b.protocol="waiting-for-load",c.attachEvent("load",function(){b._try_next_protocol()}),!0;if(e[a]&&e[a].enabled(b._options)){b._transport_tref=c.delay((b._options.rto||0)*(e[a].roundTrips||1)||5E3,function(){b.readyState===e.CONNECTING&&b._didClose(2007,"Transport timeouted")});var f=c.random_string(8),
-f=b._base_url+"/"+b._server+"/"+f;return b._debug("Opening transport:",a," url:"+f," RTO:"+b._options.rto),b._transport=new e[a](b,f,b._base_url),!0}b._debug("Skipping transport:",a)}};e.prototype.close=function(a,b){if(a&&!c.userSetCode(a))throw Error("INVALID_ACCESS_ERR");return this.readyState!==e.CONNECTING&&this.readyState!==e.OPEN?!1:(this.readyState=e.CLOSING,this._didClose(a||1E3,b||"Normal closure"),!0)};e.prototype.send=function(a){if(this.readyState===e.CONNECTING)throw Error("INVALID_STATE_ERR");
-return this.readyState===e.OPEN&&this._transport.doSend(c.quote(""+a)),!0};e.prototype._applyInfo=function(a,b,f){this._options.info=a;this._options.rtt=b;this._options.rto=c.countRTO(b);this._options.info.null_origin=!j.domain;b=c.probeProtocols();this._protocols=c.detectProtocols(b,f,a)};i=e.websocket=function(a,b){var f=this,d=b+"/websocket";"https"===d.slice(0,5)?d="wss"+d.slice(5):d="ws"+d.slice(4);f.ri=a;f.url=d;f.ws=new (g.WebSocket||g.MozWebSocket)(f.url);f.ws.onmessage=function(a){f.ri._didMessage(a.data)};
-f.unload_ref=c.unload_add(function(){f.ws.close()});f.ws.onclose=function(){f.ri._didMessage(c.closeFrame(1006,"WebSocket connection broken"))}};i.prototype.doSend=function(a){this.ws.send("["+a+"]")};i.prototype.doCleanup=function(){var a=this.ws;a&&(a.onmessage=a.onclose=null,a.close(),c.unload_del(this.unload_ref),this.unload_ref=this.ri=this.ws=null)};i.enabled=function(){return!!g.WebSocket||!!g.MozWebSocket};i.roundTrips=2;var l=function(){};l.prototype.send_constructor=function(a){this.send_buffer=
-[];this.sender=a};l.prototype.doSend=function(a){this.send_buffer.push(a);this.send_stop||this.send_schedule()};l.prototype.send_schedule_wait=function(){var a=this,b;a.send_stop=function(){a.send_stop=null;clearTimeout(b)};b=c.delay(25,function(){a.send_stop=null;a.send_schedule()})};l.prototype.send_schedule=function(){var a=this;if(0<a.send_buffer.length){var b="["+a.send_buffer.join(",")+"]";a.send_stop=a.sender(a.trans_url,b,function(){a.send_stop=null;a.send_schedule_wait()});a.send_buffer=
-[]}};l.prototype.send_destructor=function(){this._send_stop&&this._send_stop();this._send_stop=null};var O=function(a,b,f){if(!("_send_form"in this)){var d=this._send_form=j.createElement("form"),h=this._send_area=j.createElement("textarea");h.name="d";d.style.display="none";d.style.position="absolute";d.method="POST";d.enctype="application/x-www-form-urlencoded";d.acceptCharset="UTF-8";d.appendChild(h);j.body.appendChild(d)}var d=this._send_form,h=this._send_area,g="a"+c.random_string(8);d.target=
-g;d.action=a+"/jsonp_send?i="+g;var e;try{e=j.createElement('<iframe name="'+g+'">')}catch(i){e=j.createElement("iframe"),e.name=g}e.id=g;d.appendChild(e);e.style.display="none";try{h.value=b}catch(k){c.log("Your browser is seriously broken. Go home! "+k.message)}d.submit();var l=function(){e.onerror&&(e.onreadystatechange=e.onerror=e.onload=null,c.delay(500,function(){e.parentNode.removeChild(e);e=null}),h.value="",f())};return e.onerror=e.onload=l,e.onreadystatechange=function(){"complete"==e.readyState&&
-l()},l},P=function(a){return function(b,c,d){return(new a("POST",b+"/xhr_send",c)).onfinish=function(a){d(a)},function(a){d(0,a)}}},i=e["jsonp-polling"]=function(a,b){c.polluteGlobalNamespace();this.ri=a;this.trans_url=b;this.send_constructor(O);this._schedule_recv()};i.prototype=new l;i.prototype._schedule_recv=function(){var a=this,b=a.trans_url+"/jsonp",f="a"+c.random_string(6),b=b+"?c="+escape("_jp."+f),d=function(b){delete g._jp[f];a._recv_stop=null;b&&(a._is_closing||a.ri._didMessage(b));a._is_closing||
-a._schedule_recv()},e,m=j.createElement("script"),i,k=function(a){i&&(i.parentNode.removeChild(i),i=null);m&&(clearTimeout(e),m.parentNode.removeChild(m),m.onreadystatechange=m.onerror=m.onload=m.onclick=null,m=null,d(a),d=null)},l=!1,p=null;m.id="a"+c.random_string(8);m.src=b;m.type="text/javascript";m.charset="UTF-8";m.onerror=function(){p||(p=setTimeout(function(){l||k(c.closeFrame(1006,"JSONP script loaded abnormally (onerror)"))},1E3))};m.onload=function(){k(c.closeFrame(1006,"JSONP script loaded abnormally (onload)"))};
-m.onreadystatechange=function(){if(/loaded|closed/.test(m.readyState)){if(m&&m.htmlFor&&m.onclick){l=!0;try{m.onclick()}catch(a){}}m&&k(c.closeFrame(1006,"JSONP script loaded abnormally (onreadystatechange)"))}};if("undefined"==typeof m.async&&j.attachEvent)if(/opera/i.test(navigator.userAgent))i=j.createElement("script"),i.text="try{var a = document.getElementById('"+m.id+"'); if(a)a.onerror();}catch(x){};",m.async=i.async=!1;else{try{m.htmlFor=m.id,m.event="onclick"}catch(n){}m.async=!0}"undefined"!=
-typeof m.async&&(m.async=!0);e=setTimeout(function(){k(c.closeFrame(1006,"JSONP script loaded abnormally (timeout)"))},35E3);b=j.getElementsByTagName("head")[0];b=(b.insertBefore(m,b.firstChild),i&&b.insertBefore(i,b.firstChild),k);g._jp[f]=b;a._recv_stop=function(){g._jp[f]&&g._jp[f](c.closeFrame(1E3,"JSONP user aborted read"))}};i.enabled=function(){return!0};i.need_body=!0;i.prototype.doCleanup=function(){this._is_closing=!0;this._recv_stop&&this._recv_stop();this.ri=this._recv_stop=null;this.send_destructor()};
-i=function(){};i.prototype=new l;i.prototype.run=function(a,b,c,d,e){this.ri=a;this.trans_url=b;this.send_constructor(P(e));this.poll=new E(a,d,b+c,e)};i.prototype.doCleanup=function(){this.poll&&(this.poll.abort(),this.poll=null)};var w=e["xhr-streaming"]=function(a,b){this.run(a,b,"/xhr_streaming",y,c.XHRCorsObject)};w.prototype=new i;w.enabled=function(){return g.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest&&!/opera/i.test(navigator.userAgent)};w.roundTrips=2;w.need_body=!0;l=e["xdr-streaming"]=
-function(a,b){this.run(a,b,"/xhr_streaming",y,c.XDRObject)};l.prototype=new i;l.enabled=function(){return!!g.XDomainRequest};l.roundTrips=2;var F=e["xhr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XHRCorsObject)};F.prototype=new i;F.enabled=w.enabled;F.roundTrips=2;w=e["xdr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XDRObject)};w.prototype=new i;w.enabled=l.enabled;w.roundTrips=2;var s=function(){};s.prototype.i_constructor=function(a,b,f){var d=this;d.ri=a;d.origin=c.getOrigin(f);d.base_url=
-f;d.trans_url=b;a=f+"/iframe.html";d.ri._options.devel&&(a+="?t="+ +new Date);d.window_id=c.random_string(8);a+="#"+d.window_id;d.iframeObj=c.createIframe(a,function(a){d.ri._didClose(1006,"Unable to load an iframe ("+a+")")});d.onmessage_cb=c.bind(d.onmessage,d);c.attachMessage(d.onmessage_cb)};s.prototype.doCleanup=function(){if(this.iframeObj){c.detachMessage(this.onmessage_cb);try{this.iframeObj.iframe.contentWindow&&this.postMessage("c")}catch(a){}this.iframeObj.cleanup();this.onmessage_cb=this.iframeObj=
-this.iframeObj=null}};s.prototype.onmessage=function(a){if(a.origin===this.origin){var b=a.data.slice(0,8),c=a.data.slice(8,9),a=a.data.slice(9);if(b===this.window_id)switch(c){case "s":this.iframeObj.loaded();this.postMessage("s",JSON.stringify([e.version,this.protocol,this.trans_url,this.base_url]));break;case "t":this.ri._didMessage(a)}}};s.prototype.postMessage=function(a,b){this.iframeObj.post(this.window_id+a+(b||""),this.origin)};s.prototype.doSend=function(a){this.postMessage("m",a)};s.enabled=
-function(){var a=navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Konqueror");return("function"==typeof g.postMessage||"object"==typeof g.postMessage)&&!a};var G,H=function(a,b){parent!==g?parent.postMessage(G+a+(b||""),"*"):c.log("Can't postMessage, no parent window.",a,b)},u=function(){};u.prototype._didClose=function(a,b){H("t",c.closeFrame(a,b))};u.prototype._didMessage=function(a){H("t",a)};u.prototype._doSend=function(a){this._transport.doSend(a)};u.prototype._doCleanup=function(){this._transport.doCleanup()};
-c.parent_origin=void 0;e.bootstrap_iframe=function(){var a;G=j.location.hash.slice(1);c.attachMessage(function(b){if(b.source===parent&&("undefined"==typeof c.parent_origin&&(c.parent_origin=b.origin),b.origin===c.parent_origin)){var f=b.data.slice(0,8),d=b.data.slice(8,9),b=b.data.slice(9);if(f===G)switch(d){case "s":var h=JSON.parse(b),f=h[0],d=h[1],b=h[2],h=h[3];f!==e.version&&c.log('Incompatibile SockJS! Main site uses: "'+f+'", the iframe: "'+e.version+'".');if(!c.flatUrl(b)||!c.flatUrl(h)){c.log("Only basic urls are supported in SockJS");
-break}if(!c.isSameOriginUrl(b)||!c.isSameOriginUrl(h)){c.log("Can't connect to different domain from within an iframe. ("+JSON.stringify([g.location.href,b,h])+")");break}a=new u;a._transport=new u[d](a,b,h);break;case "m":a._doSend(b);break;case "c":a&&a._doCleanup(),a=null}}});H("s")};var z=function(a,b){var f=this;c.delay(function(){f.doXhr(a,b)})};z.prototype=new k(["finish"]);z.prototype.doXhr=function(a,b){var f=this,d=(new Date).getTime(),e=new b("GET",a+"/info"),g=c.delay(8E3,function(){e.ontimeout()});
-e.onfinish=function(a,b){clearTimeout(g);g=null;if(200===a){var c=(new Date).getTime()-d,e=JSON.parse(b);"object"!=typeof e&&(e={});f.emit("finish",e,c)}else f.emit("finish")};e.ontimeout=function(){e.close();f.emit("finish")}};var K=function(a){var b=this,f=function(){var c=new s;c.protocol="w-iframe-info-receiver";var f=function(a){"string"==typeof a&&"m"===a.substr(0,1)?(a=JSON.parse(a.substr(1)),b.emit("finish",a[0],a[1])):b.emit("finish");c.doCleanup();c=null};c.i_constructor({_options:{},_didClose:f,
-_didMessage:f},a,a)};j.body?f():c.attachEvent("load",f)};K.prototype=new k(["finish"]);var L=function(){var a=this;c.delay(function(){a.emit("finish",{},2E3)})};L.prototype=new k(["finish"]);var N=function(a){if(c.isSameOriginUrl(a))return new z(a,c.XHRLocalObject);switch(c.isXHRCorsCapable()){case 1:return new z(a,c.XHRLocalObject);case 2:return new z(a,c.XDRObject);case 3:return new K(a);default:return new L}};(u["w-iframe-info-receiver"]=function(a,b,f){(new z(f,c.XHRLocalObject)).onfinish=function(b,
-c){a._didMessage("m"+JSON.stringify([b,c]));a._didClose()}}).prototype.doCleanup=function(){};k=e["iframe-eventsource"]=function(){this.protocol="w-iframe-eventsource";this.i_constructor.apply(this,arguments)};k.prototype=new s;k.enabled=function(){return"EventSource"in g&&s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-eventsource"]=function(a,b){this.run(a,b,"/eventsource",I,c.XHRLocalObject)}).prototype=new i;k=e["iframe-xhr-polling"]=function(){this.protocol="w-iframe-xhr-polling";this.i_constructor.apply(this,
-arguments)};k.prototype=new s;k.enabled=function(){return g.XMLHttpRequest&&s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-xhr-polling"]=function(a,b){this.run(a,b,"/xhr",y,c.XHRLocalObject)}).prototype=new i;k=e["iframe-htmlfile"]=function(){this.protocol="w-iframe-htmlfile";this.i_constructor.apply(this,arguments)};k.prototype=new s;k.enabled=function(){return s.enabled()};k.need_body=!0;k.roundTrips=3;(u["w-iframe-htmlfile"]=function(a,b){this.run(a,b,"/htmlfile",J,c.XHRLocalObject)}).prototype=
-new i;var E=function(a,b,c,d){this.ri=a;this.Receiver=b;this.recv_url=c;this.AjaxObject=d;this._scheduleRecv()};E.prototype._scheduleRecv=function(){var a=this,b=a.poll=new a.Receiver(a.recv_url,a.AjaxObject);b.onmessage=function(b){a.ri._didMessage(b.data)};b.onclose=function(c){a.poll=b=b.onmessage=b.onclose=null;a.poll_is_closing||("permanent"===c.reason?a.ri._didClose(1006,"Polling error ("+c.reason+")"):a._scheduleRecv())}};E.prototype.abort=function(){this.poll_is_closing=!0;this.poll&&this.poll.abort()};
-var I=function(a){var b=this,f=new EventSource(a);f.onmessage=function(a){b.dispatchEvent(new n("message",{data:unescape(a.data)}))};b.es_close=f.onerror=function(a,e){var g=e?"user":2!==f.readyState?"network":"permanent";b.es_close=f.onmessage=f.onerror=null;f.close();f=null;c.delay(200,function(){b.dispatchEvent(new n("close",{reason:g}))})}};I.prototype=new p;I.prototype.abort=function(){this.es_close&&this.es_close({},!0)};var A,J=function(a){var b=this;c.polluteGlobalNamespace();b.id="a"+c.random_string(6,
-26);a+=(-1===a.indexOf("?")?"?":"&")+"c="+escape("_jp."+b.id);if(void 0===A)if("ActiveXObject"in g)try{A=!!new ActiveXObject("htmlfile")}catch(f){}else A=!1;var d=A?c.createHtmlfile:c.createIframe,e;g._jp[b.id]={start:function(){e.loaded()},message:function(a){b.dispatchEvent(new n("message",{data:a}))},stop:function(){b.iframe_close({},"network")}};b.iframe_close=function(a,c){e.cleanup();b.iframe_close=e=null;delete g._jp[b.id];b.dispatchEvent(new n("close",{reason:c}))};e=d(a,function(){b.iframe_close({},
-"permanent")})};J.prototype=new p;J.prototype.abort=function(){this.iframe_close&&this.iframe_close({},"user")};var y=function(a,b){var c=this,d=0;c.xo=new b("POST",a,null);c.xo.onchunk=function(a,b){if(200===a)for(;;){var e=b.slice(d),g=e.indexOf("\n");if(-1===g)break;d+=g+1;e=e.slice(0,g);c.dispatchEvent(new n("message",{data:e}))}};c.xo.onfinish=function(a,b){c.xo.onchunk(a,b);c.xo=null;c.dispatchEvent(new n("close",{reason:200===a?"network":"permanent"}))}};return y.prototype=new p,y.prototype.abort=
-function(){this.xo&&(this.xo.close(),this.dispatchEvent(new n("close",{reason:"user"})),this.xo=null)},e.getUtils=function(){return c},e.getIframeTransport=function(){return s},e}();"_sockjs_onload"in window&&setTimeout(_sockjs_onload,1);"function"==typeof define&&define.amd&&define("sockjs",[],function(){return SockJS});function WebTCP(j,g){this.sockets={};this.httpClients={};this.sock=new SockJS("http://"+j+":"+g+"/bridge");this.ready=!1;this.outputBuffer=[];var c=this;this.sock.onopen=function(){c.ready=!0;c.processBuffer()};this.processBuffer=function(){if(0<this.outputBuffer.length&&this.ready)for(var c in this.outputBuffer)this.sock.send(this.outputBuffer[c])};this.sock.onmessage=function(g){g.data=JSON.parse(g.data);g.data.sID?(sID=g.data.sID,c.sockets[sID].onEvent(g.data.eventName,g.data.data)):g.data.cID&&
-(cID=g.data.cID,c.httpClients[cID].onEvent(g.data.eventName,g.data.data))};this.sock.onclose=function(){console.log("close")};this.createSocket=function(c,g,j){return new Socket(this,c,g,j)};this.createHTTPClient=function(){return new Http(this)}};function WebTCPIO(){this.onEvent=function(j,g){this["on"+j](g);if(this["on"+j+"Custom"])this["on"+j+"Custom"](g)};this.on=function(j,g){this["on"+j+"Custom"]=g};this.rpc=function(j,g){var c=this.createPacket();c.command=j;c.args=g;this.send(JSON.stringify(c));return c.id};this.write=function(j){var g=this.createPacket();g.data=j;this.send(JSON.stringify(g));return g.id};this.send=function(j){if(this.webtcp.ready)this.webtcp.sock.send(j);else{if(this.webtcp.outputBuffer>this.webtcp.BUFF_SIZE)throw"Output buffer is already full, but sockJS connection is not ready yet";
-this.webtcp.outputBuffer.push(j)}};this.onerror=function(j){throw j;}}; \ No newline at end of file
diff --git a/chapter28b/helloWorld.exe.stackdump b/chapter28b/helloWorld.exe.stackdump
deleted file mode 100644
index 369cf738..00000000
--- a/chapter28b/helloWorld.exe.stackdump
+++ /dev/null
@@ -1,16 +0,0 @@
-Exception: STATUS_ACCESS_VIOLATION at rip=001004010BB
-rax=0000000100405000 rbx=0000000000000001 rcx=00000001004010D0
-rdx=00000001004010A6 rsi=0000000000000015 rdi=0000000800049E60
-r8 =00000000FFFFC568 r9 =000000010040500C r10=0000000100000000
-r11=00000001004010A6 r12=0000000800051F10 r13=00000000FFFFCC40
-r14=00000000FFFFFFFF r15=00000000FFFFCC79
-rbp=0000000800051EF0 rsp=00000000FFFFCB50
-program=C:\Users\cliffc\Desktop\Simple\chapter25\build\objs\helloWorld.exe, pid 1066, thread main
-cs=0033 ds=002B es=002B fs=0053 gs=002B ss=002B
-Stack trace:
-Frame Function Args
-00800051EF0 001004010BB (0000000000D, 00000000000, 00000000015, 0010040181E)
-00800051EF0 00000000001 (00000000000, 00000000015, 0010040181E, 00000000015)
-00800051EF0 0010040500C (00000000015, 0010040181E, 00000000015, 00800049E60)
-00800051EF0 0000000000D (00000000015, 0010040181E, 00000000015, 00800049E60)
-End of stack trace
diff --git a/chapter28b/pom.xml b/chapter28b/pom.xml
index c60ec7a2..cad8a591 100644
--- a/chapter28b/pom.xml
+++ b/chapter28b/pom.xml
@@ -8,9 +8,9 @@
<artifactId>simple</artifactId>
<version>1.0</version>
</parent>
- <artifactId>chapter25</artifactId>
+ <artifactId>chapter28b</artifactId>
<packaging>jar</packaging>
- <name>Chapter 25</name>
+ <name>Separate Compilation</name>
<properties>
<simple.graph.directory>${project.basedir}/../graph</simple.graph.directory>
<simple.print.directory>${project.basedir}/../print</simple.print.directory>
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/Parser.java b/chapter28b/src/main/java/com/seaofnodes/simple/Parser.java
index 5ceda6a9..87a3570a 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -213,7 +213,6 @@ public class Parser {
}
-
/**
* Parses a function body, assuming the header is parsed.
*/
@@ -255,12 +254,14 @@ public class Parser {
}
private ReturnNode _parseFunctionBody( String funName, TypeFunPtr sig, Lexer loc, java.util.BitSet finals, String[] ids) {
+ Constructor outerCtor=_pendingCtor;
+ _pendingCtor=null;
boolean isInit = FunNode.isInit(funName);
boolean isCtor = isInit || isExplicitConstructorName(funName);
String typeName = isCtor ? ((TypeMemPtr)sig._sig[0])._obj._name : null;
// Stack parser state on the local Java stack, and unstack it later
- _scope.push(new Kind.Func(funName));
+ _scope.push(new Kind.Func(funName,isCtor && !funName.endsWith(".<clinit>")));
ScopeNode breakScope = _breakScope; _breakScope = null;
ScopeNode continueScope = _continueScope; _continueScope = null;
ScopeNode returnScope = _returnScope; _returnScope = null;
@@ -290,6 +291,20 @@ public class Parser {
_scope.define(ids[i], t, (i==0 && ids[0]=="self") || (finals!=null && finals.get(i)), new ParmNode(ids[i],i+2,t,fun,ConstantNode.seed(t).peephole()).peephole(), loc);
}
+ if( isCtor && !isInit ) {
+ int lex=_scope.klast()._lexSize;
+ TypeStruct shape=(TypeStruct)TYPES.get(typeName);
+ for( Field fld : shape._fields ) {
+ int idx=fieldVarIdxBefore(lex,fld._fname);
+ if( idx<0 ) continue;
+ Var v=_scope.var(idx);
+ if( v._fref || v._extern!=0 || v._uninit || _scope.in(v)._type.isConstant() ) continue;
+ Node value=convertExpr(
+ new LoadNode(loc,fld._fname,fld._alias,ctrl(),fun.parm(3),fun.parm(2),off(shape,fld._fname)).peephole(),v.type());
+ _scope.update(v,value);
+ }
+ }
+
// The initializer owns its run-once flag store. Parent initialization
// is a structural requirement, executed before this body by CLInit lowering.
if( fun.isClz() ) {
@@ -316,6 +331,9 @@ public class Parser {
nvar = updateSelfAsFieldsDiscovered( typeName, nvar );
}
+ if( isInit ) finishConstructor();
+ _pendingCtor=outerCtor;
+
ParmNode self = fun.parm(2);
// Last expression is the return
if( ctrl()._type==Type.CONTROL ) {
@@ -456,6 +474,10 @@ public class Parser {
Node val = _returnScope.in(vidx);
if( upgrade && v._uninit &&
!initialized(val._type) ) continue;
+ // A numeric merge can still be untyped while parsing. Retain its
+ // declared conversion at the helper boundary, as assignments do.
+ if( fld._t instanceof TypeInteger || fld._t instanceof TypeFloat )
+ val=convertExpr(val,fld._t);
// Store value into extended struct
Node prior = mmm.keep();
Node st = peep(new StoreNode(null, fld._fname, fld._alias, fld._t, null, prior, self, off(tself,fld._fname), val, fld._final));
@@ -597,6 +619,9 @@ public class Parser {
}
// Constructor declaration: new TypeName = { args -> body };
+ private record Constructor(String name, Lexer body) {}
+ private Constructor _pendingCtor;
+
private Node parseConstructorDeclaration() {
TypeStruct self = _scope.constructorSelf();
assert self != null;
@@ -605,6 +630,43 @@ public class Parser {
if( name != expect )
throw error("Constructor name '" + name + "' must match type name '" + expect + "'");
require("=");
+ if( _pendingCtor!=null ) throw error("Redefining constructor '"+name+"'");
+ _pendingCtor=new Constructor(name,loc());
+ // Parse the body after all field declarations are known, so self.x
+ // and x share a local slot even when x is declared below this body.
+ require("{");
+ int depth=1;
+ while( depth>0 ) {
+ _lexer.skipWhiteSpace();
+ if( _lexer.isEOF() ) throw error("Unclosed constructor body");
+ char ch=_lexer.nextChar();
+ if( ch=='{' ) depth++;
+ else if( ch=='}' ) depth--;
+ else if( ch=='"' || ch=='\'' ) {
+ char quote=ch;
+ while( !_lexer.isEOF() ) {
+ ch=_lexer.nextChar();
+ if( ch=='\\' ) _lexer.nextChar();
+ else if( ch==quote ) break;
+ }
+ }
+ }
+ require(";");
+ return _code.ZERO;
+ }
+
+ private void finishConstructor() {
+ if( _pendingCtor==null ) return;
+ Constructor ctor=_pendingCtor;
+ _pendingCtor=null;
+ Lexer after=_lexer;
+ _lexer=new Lexer(ctor.body());
+ defineConstructor(ctor.name());
+ _lexer=after;
+ }
+
+ private void defineConstructor(String name) {
+ TypeStruct self=_scope.constructorSelf();
Node ctor = constructorFunc(name);
TypeFunPtr sig = (TypeFunPtr)ctor._type;
String clzName = addClzPrefix(self._name);
@@ -614,7 +676,7 @@ public class Parser {
throw error("Redefining constructor '" + name + "'");
Field fld = Field.make("<ctor>",sig,_code.alias(_ref._cname),true);
TYPES.put(clzName,clz==null ? TypeStruct.make(clzName,true,fld) : clz.add(fld));
- return require(ctor,";");
+ require(ctor,";");
}
/**
@@ -1006,9 +1068,9 @@ public class Parser {
if( _scope.in(def._idx)._type!=Type.TOP && def._final &&
// Inside an allocation, final assign is OK, outside nope.
// The alloc() call added the allocation scope
- !(_scope.inConstructor() && def._idx >= _scope.klast()._lexSize) &&
- !inExplicitConstructor() )
+ !_scope.canInit(def) )
throw error("Cannot reassign final '"+name+"'");
+ checkSharedFunction(def);
// Parse assignment expression
Node expr = parseAsgn();
@@ -1082,6 +1144,15 @@ public class Parser {
};
}
+ private Type constructorFieldType(Type t) {
+ if( t instanceof TypeMemPtr ptr && ptr._obj.isConAry() ) {
+ TypeStruct declared=(TypeStruct)TYPES.get(ptr._obj._name);
+ TypeStruct shape=typeAry(declared._fields[1]._t,ptr._obj._fields[1]._final);
+ return TypeMemPtr.make((byte)2,shape,false).withAccess(ptr._ro);
+ }
+ return mutableInferredType(t);
+ }
+
// Seed an inferred return merge with the declared numeric family instead
// of a transient constant/range. Pointer nullability remains part of the
// inferred function result and is combined by the later Phi.
@@ -1163,6 +1234,11 @@ public class Parser {
throw error("a not-null/non-zero expression");
t = expr._type;
if( !xfinal ) t = mutableInferredType(t);
+ // Instance fields may change until the constructor finishes.
+ // Keep the declared family/access, not the default's constant
+ // value or string contents. Class constants remain static.
+ else if( _scope.klast() instanceof Kind.Func fun && FunNode.isInstance(fun._name) && !(t instanceof TypeFunPtr) )
+ t = constructorFieldType(t);
}
// expr is a constant function
@@ -1214,7 +1290,6 @@ public class Parser {
}
-
/**
* Parse a struct declaration, and return the following statement.
* Structs cannot be redefined, but can be nested.
@@ -1744,7 +1819,6 @@ public class Parser {
}
-
/**
* Parse an eq/ne expression
*/
@@ -1789,8 +1863,8 @@ public class Parser {
return cmp.unkeep();
}
- // rhs is keeped() and becomes lhs
- // cmp is NOT keeped() and is the last test
+ // Keep the middle operand separately from the boolean result.
+ // The next test compares adjacent operands, not that boolean.
Node ifNode = new IfNode(ctrl(), cmp.unkeep()).peephole();
Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole();
@@ -1806,9 +1880,9 @@ public class Parser {
throw error("Mixing relational directions in a chained relational test");
// True side parses next arm of test
ctrl(ifT.unkeep());
- lhs = rhs; // lhs is keeped() and is old RHS
+ lhs = rhs; // Reuse the previous RHS exactly once
rhs = parseShift().keep();
- cmp = makeCompBool(dir,lhs.unkeep(),rhs); // Convert to a bool
+ cmp = makeCompBool(dir0,lhs.unkeep(),rhs); // Convert to a bool
ifNode = new IfNode(ctrl(), cmp).peephole();
ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole();
@@ -1822,7 +1896,7 @@ public class Parser {
ctrl(ifT.unkeep());
RegionNode r = fail.mergeScopes(_scope, loc()).init();
_scope = fail;
- return new PhiNode("", r,_code.ZERO,con(1)).peephole();
+ return new PhiNode("", r,con(0),con(1)).peephole();
}
private int parseCompDir() {
@@ -1833,12 +1907,11 @@ public class Parser {
return 0;
}
- private Node makeCompBool( int dir0, Node lhs, Node rhs ) {
- // Convert to a bool
- if( Math.abs(dir0)==2 ) // Swap direction
- { Node tmp = lhs; lhs = rhs; rhs = tmp; }
- lhs = peep(dir0 < 0 ? new BoolNode.LE(lhs,rhs) : new BoolNode.LT(lhs,rhs));
- return peep(lhs);
+ private Node makeCompBool( int dir, Node lhs, Node rhs ) {
+ if( Math.abs(dir)==2 )
+ { Node tmp=lhs; lhs=rhs; rhs=tmp; }
+ Node cmp = dir < 0 ? new BoolNode.LE(lhs,rhs) : new BoolNode.LT(lhs,rhs);
+ return peep(cmp);
}
/**
@@ -1927,9 +2000,10 @@ public class Parser {
if( n != null && !(n.type() instanceof TypeMemPtr) ) {
if( n._extern!=0 )
return parsePostfixName(_scope.in(n),name,delta);
- if( n._final )
+ if( n._final && !_scope.canInit(n) )
throw error("Cannot reassign final '"+n._name+"'");
- Node expr = zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type());
+ checkSharedFunction(n);
+ Node expr = convertExpr(peep(new AddNode(_scope.in(n),con(delta))),n.type());
_scope.update(n,expr);
return expr;
}
@@ -2015,12 +2089,10 @@ public class Parser {
return parsePostfixName(rvalue,id,0);
if( var._fref )
return parsePostfix(rvalue);
- // Required constructor fields begin as their declared pointer type
- // widened to include null. Inside the explicit constructor, any read
- // before all paths replace that value observes a partially initialized
- // object. Ordinary methods run only after construction is complete;
- // their enclosing declaration scope still carries the same Var marker.
- if( inExplicitConstructor() && var._uninit && rvalue._type instanceof TypeNil tn && tn.nullable() )
+ // A constructor may read the unfinished object, but each field it
+ // reads must already be initialized on every path. Read the current
+ // local value; other fields may still be waiting for their first write.
+ if( inExplicitConstructor() && var._uninit && missingInit(rvalue,new BitSet()) )
throw error("Field '"+id+"' might not be initialized",var._loc);
// Check for an instance field load
@@ -2029,13 +2101,13 @@ public class Parser {
Kind kk = _scope._kinds.at(kx);
Kind fk = _scope._kinds.at(fx);
// Access instance field from 'self'
- if( kx+1 == fx && kk instanceof Kind.Func inst && FunNode.isInstance(inst._name) && fk instanceof Kind.Func method )
+ if( kx+1 == fx && kk instanceof Kind.Func inst && FunNode.isInstance(inst._name) && fk instanceof Kind.Func method && !inExplicitConstructor() )
return parsePostfixName(_scope.in(method._lexSize),id,0);
// Check for a function-escaping variable; these require true
// closures. Final constants are OK; final vars require a hidden var
// argument; not-final fields can just use an explicit struct arg.
- if( kx < fx && // Out of scope
+ if( kx < fx && !inExplicitConstructor() && // Out of scope
!(var._final && rvalue._type.isConstant()) ) { // Not a final constant
if( kx > 0 ) // Global scope is OK, only ever one of these (not one per function invoke)
throw error("Variable '"+var._name+"' is out of function scope and must be a final constant");
@@ -2062,8 +2134,9 @@ public class Parser {
return parsePostfixMethod(rvalue,_scope.in(_scope.update(_scope.var(selfx),null)));
}
// Assign-update direct into Scope
- if( var._final )
+ if( var._final && !_scope.canInit(var) )
throw error("Cannot reassign final '"+var._name+"'");
+ checkSharedFunction(var);
Node op = opAssign(ch,rvalue, var.type() );
_scope.update(var,op);
return postfix(ch) ? rvalue.unkeep() : op;
@@ -2261,6 +2334,58 @@ public class Parser {
return parsePostfixName(expr,name,0,site);
}
+ private boolean privateSelf(Node ptr) {
+ while( ptr instanceof ConvertNode || ptr instanceof ReadOnlyNode || ptr instanceof GuardNode ) ptr=ptr.in(1);
+ Node self=defaultSelf();
+ while( self instanceof ConvertNode || self instanceof ReadOnlyNode || self instanceof GuardNode ) self=self.in(1);
+ return ptr==self;
+ }
+
+ // An explicit self.x read sees the same current value as bare x. Loading
+ // the allocation here would miss updates still held in local SSA values.
+ private Node privateField(String name, int pre) {
+ int idx=fieldVarIdxBefore(_scope._kinds.at(_scope.enclosingFuncOrDecl())._lexSize,name);
+ if( idx<0 ) throw error("Cannot resolve field '"+name+"'");
+ Var v=_scope.var(idx);
+ if( pre==0 && matchOpx('=','=') ) {
+ checkSharedFunction(v);
+ Node value=parseAsgn().keep();
+ _scope.update(v,convertExpr(value,v.type()));
+ return value.unkeep();
+ }
+ Node value=_scope.in(_scope.update(v,null));
+ if( v._uninit && missingInit(value,new BitSet()) )
+ throw error("Field '"+name+"' might not be initialized",v._loc);
+ char ch=pre==0 ? _lexer.matchOperAssign() : (char)pre;
+ if( ch!=0 ) {
+ checkSharedFunction(v);
+ Node op=opAssign(ch,value,v.type());
+ _scope.update(v,op);
+ if( pre!=0 && postfix(ch) ) value.unkeep();
+ return pre==0 && postfix(ch) ? value.unkeep() : op;
+ }
+ return parsePostfixMethod(value,defaultSelf());
+ }
+
+ private void checkSharedFunction(Var v) {
+ if( _scope.canInit(v) && v._final && v.type() instanceof TypeFunPtr t && t.isConstant() )
+ throw error("Cannot reassign shared function '"+v._name+"'");
+ }
+
+ // A loop Phi is temporarily bottom before its backedge is parsed. That is
+ // not an unwritten field: follow its live inputs to the actual defaults.
+ private boolean missingInit(Node value, BitSet seen) {
+ if( value==null || seen.get(value._nid) ) return false;
+ seen.set(value._nid);
+ if( value instanceof PhiNode phi && !(value instanceof ParmNode) ) {
+ for( int i=1; i<phi.nIns(); i++ )
+ if( phi.region().in(i)!=null && phi.region().in(i)._type!=Type.XCONTROL && missingInit(phi.in(i),seen) )
+ return true;
+ return false;
+ }
+ return !initialized(value._type);
+ }
+
private Node defaultSelf() {
int selfx = _scope._kinds.at(_scope.enclosingFuncOrDecl())._lexSize;
return _scope.in(_scope.update(_scope.var(selfx),null));
@@ -2283,6 +2408,7 @@ public class Parser {
}
private Node parsePostfixName(Node expr, String name, int pre, Lexer site) {
+ if( inExplicitConstructor() && privateSelf(expr) ) return privateField(name,pre);
// Keep expr across possible updates
expr.keep();
@@ -2782,6 +2908,7 @@ public class Parser {
_position = pos;
}
+
public static void gather(Lexer loc, HashMap<String,Integer> strs) {
if( loc==null || loc._cu==null ) return;
Serialize.gather(strs,loc._cu._cname);
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index 47ec0112..8302ede6 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -318,7 +318,7 @@ public class CodeGen {
}
public String entryClinitName() {
- return Parser.addClzPrefix(_srcName==null ? "Test" : _srcName)+".<clinit>";
+ return Parser.addClzPrefix(_srcName==null ? "Test" : _srcName.replace('/','.'))+".<clinit>";
}
// Verbose printing during compilation
@@ -463,6 +463,8 @@ public class CodeGen {
// ---------------------------
// Parser object
+ public final Ary<CallNode> _calls = new Ary<>(CallNode.class);
+
public final Parser P;
// Parse ASCII text into Sea-of-Nodes IR
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java
index 128c4136..71126795 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CompUnit.java
@@ -57,7 +57,8 @@ public class CompUnit {
_obj = new File( CODE._buildDir + "/" + fname + ".o" );
boolean mustCompile = _smp.exists() && // Has source to compile
- ( !_obj.exists() || // No object file
+ ( fname.equals(CODE._srcName) || // Explicit input is a compilation request
+ !_obj.exists() || // No object file
_smp.lastModified() > _obj.lastModified() || // Out-of-date with source?
checkDependentObjs() ); // Out-of-date with other objs?
_src = mustCompile ? new String(Files.readAllBytes(_smp.toPath())) : null;
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Opto.java b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Opto.java
index ef222f60..3b3f9f19 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Opto.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Opto.java
@@ -145,8 +145,7 @@ abstract public class Opto {
private static void linkStart( CodeGen code, FunNode fun ) {
assert !fun.isDead();
StartCUNode start = fun._compunit._start;
- if( fun.nIns() < 2 ) {
- assert fun.nIns() < 2 || fun.in(1) != start;
+ if( !fun.unknownCallers() ) {
assert !start.isDead();
code.keepInCompUnit(fun);
// Function is reachable by any *remote* caller who gets the pointer!
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java
index 7ce727fd..c55f9a90 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java
@@ -81,11 +81,16 @@ public abstract class ParseAll {
}
}
+ private static void enqueue(CompUnit cu) {
+ if( cu._par!=null && (cu._par._src!=null || (cu._par._obj!=null && cu._par._obj.exists())) ) enqueue(cu._par);
+ if( cu._start==null && WORK.find(cu)==-1 ) WORK.add(cu);
+ }
+
private static void parseAll(CodeGen code, CompUnit cunit) {
WORK.clear(); // Cleanup from prior tests
NEEDS_LOAD.clear();
// Prime the pump with the first CompUnit
- WORK.add(cunit);
+ enqueue(cunit);
// Work through all unparsed compilation units and parse
while( !WORK.isEmpty() ) {
CompUnit cu = WORK.remove(0);
@@ -245,7 +250,7 @@ public abstract class ParseAll {
if( sub==null ) sub=makeCUnit(code,null,fname,depObj(code,cunit,fname));
// If not seen before or out of date, parse the new nested cunit
if( sub._clz == null && WORK.find(sub)==-1 && NEEDS_LOAD.find(sub)==-1 )
- WORK.add(sub);
+ enqueue(sub);
}
} catch( IOException ioe ) {
throw new RuntimeException(ioe);
@@ -370,7 +375,7 @@ public abstract class ParseAll {
CompUnit sub = makeCUnit(code,cunit,symbol);
// If not seen before or out of date, parse the new nested cunit
if( sub._clz == null && WORK.find(sub) == -1 )
- WORK.add(sub);
+ enqueue(sub);
return sub;
}
@@ -405,7 +410,7 @@ public abstract class ParseAll {
Parser.TYPES.put(clzName,clz = TypeStruct.make(clzName,true));
cunit._clz = clz;
if( WORK.find(cunit) == -1 && NEEDS_LOAD.find(cunit) == -1 )
- WORK.add(cunit);
+ enqueue(cunit);
return cunit;
}
}
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
index 6a23252d..5a74ded2 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
@@ -110,9 +110,12 @@ abstract public class Serialize {
stopType.gather(types);
startType.gather(types);
// A defined class remains importable even when its initializer body dies.
+ // Nested structs also need their class declaration: <new> and <ctor>
+ // can be absent from graph types even while their function bodies live.
+ // Keep the nearest file owner, so an imported child is not re-exported
+ // merely because its parent was compiled here.
for( TypeStruct ts : pubTypes )
- if( Parser.startsClzPrefix(ts._name) &&
- Arrays.binarySearch(units,ts._name.substring(Parser.CLZ.length()).replace('.','/'))>=0 )
+ if( ownedClass(ts,units) )
ts.gather(types);
// Only record canonical declarations already reachable from this
@@ -262,6 +265,18 @@ abstract public class Serialize {
}
// Returned array of *public* strings only.
+ private static boolean ownedClass(TypeStruct ts, String[] units) {
+ if( !Parser.startsClzPrefix(ts._name) ) return false;
+ String name=ts._name.substring(Parser.CLZ.length()).replace('.','/');
+ while( true ) {
+ if( CodeGen.CODE._compunits.containsKey(name) )
+ return Arrays.binarySearch(units,name)>=0;
+ int slash=name.lastIndexOf('/');
+ if( slash<0 ) return false;
+ name=name.substring(0,slash);
+ }
+ }
+
static void read_public_strs(ElfReader elf) {
BAOS bais = elf._bais;
Type.TAGOFFS();
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/chapter28b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index 36a012e1..fc6effdc 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -127,7 +127,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
// A linked function for every concrete function
TypeTuple state = hasExtern
- ? TypeTuple.make(Type.CONTROL,TypeMem.BOT,tfp.ret())
+ ? TypeTuple.make(Type.CONTROL,call.exposed(this),tfp.ret())
: TypeTuple.STATE.dual();
for( int i=1; i<nIns(); i++ )
state = (TypeTuple)state.meet(in(i)._type);
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter28b/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 71f6d146..bcb756fa 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -22,7 +22,7 @@ public class CallNode extends CFGNode {
// Source location for late reported errors
public final Parser.Lexer _loc;
- public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc=loc; }
+ public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc=loc; CodeGen.CODE._calls.add(this); }
public CallNode(CallNode call) { super(call); _loc = call._loc; }
@Override public Tag serialTag() { return Tag.Call; }
public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
@@ -67,7 +67,25 @@ public class CallNode extends CFGNode {
public TypeFunPtr tfp() { return (TypeFunPtr)fptr()._type; }
// Call is to an externally supplied code
- public boolean external() { return false; }
+ public boolean external() {
+ if( !(fptr()._type instanceof TypeFunPtr tfp) ) return false;
+ if( XInt.isHigh(tfp.fidxs()) ) return true;
+ for( int fidx=XInt.next(tfp.fidxs(),-1); fidx>=0; fidx=XInt.next(tfp.fidxs(),fidx) )
+ if( CodeGen.CODE.externFunc(fidx)!=null ) return true;
+ return false;
+ }
+
+ // As in the escape chapter, native code may retain an argument even if
+ // this path never returns. StopCU reads this independently of CallEnd.
+ TypeMem exposed(Node dep) {
+ dep.addDepForwards(this);
+ dep.addDepForwards(fptr());
+ if( _type!=Type.CONTROL || !external() ) return TypeMem.START;
+ Type input=dep.addDepForwards(mem())._type;
+ TypeMem escaped=input instanceof TypeMem m ? m : input.isHigh() ? TypeMem.TOP : TypeMem.BOT;
+ for( int i=2; i<nIns()-1; i++ ) escaped=escaped.escapes(dep.addDepForwards(arg(i))._type);
+ return TypeMem.make(1,Type.BOTTOM,false,false,false,escaped._escFs,escaped._escAs);
+ }
// Find the Call End from the Call
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter28b/src/main/java/com/seaofnodes/simple/node/Node.java
index 77b43ba8..8718f351 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -715,6 +715,7 @@ public abstract class Node extends BaseNode<Node> implements Cloneable {
n._outputs = new Ary<>(Node.class);
n._deps = null;
n._hash = 0;
+ if( n instanceof CallNode call ) CODE._calls.add(call);
return n;
}
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter28b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index 21d60d53..694d5dcb 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -39,7 +39,12 @@ public class ScopeNode extends MemMergeNode {
public final TypeStruct _self;
}
// Function scope
- public static class Func extends Kind { public Func(String name) { _name=name;} public final String _name; }
+ public static class Func extends Kind {
+ public final String _name;
+ public final boolean _constructor;
+ public Func(String name) { this(name,false); }
+ public Func(String name, boolean constructor) { _name=name; _constructor=constructor; }
+ }
public int _lexSize; // Number of Vars in this scope
}
@@ -184,10 +189,7 @@ public class ScopeNode extends MemMergeNode {
}
// Which lexical scope contains this Var?
- public Kind kind( Var v ) {
- int kx = kindx(v);
- return kx == -1 ? null : _kinds.at(kx);
- }
+
public int kindFcnx( Var v ) {
for( int i=depth()-1; i>=0; i-- )
@@ -202,6 +204,21 @@ public class ScopeNode extends MemMergeNode {
return kx == -1 ? null : _kinds.at(kx);
}
+ // Final fields are writable local values inside their own initializer or
+ // constructor. Final parameters, local variables, and captured bindings
+ // remain final. Use the same rule when deciding which loop Phis to build.
+ public boolean canInit(Var v) {
+ int fx=enclosingFuncOrDecl();
+ Kind.Func fun=(Kind.Func)_kinds.at(fx);
+ if( !fun._constructor || v._extern!=0 ) return false;
+ int dx=FunNode.isInstance(fun._name) ? fx : fx-1;
+ return dx>=0 && kindx(v)==dx && v._idx>=_kinds.at(dx)._lexSize+2;
+ }
+
+ private boolean loopVar(Var v) {
+ return !v._final || (canInit(v) && !v.type().isHighOrConst());
+ }
+
// Find name in reverse, return an index into _vars or -1. Linear scan
// instead of hashtable, but probably doesn't matter until the scan
// typically hits many dozens of variables.
@@ -346,7 +363,7 @@ public class ScopeNode extends MemMergeNode {
for( int i=2; i<nIns(); i++ )
// For lazy phis on loops we use a sentinel
// that will trigger phi creation on update
- dup.addDef(loop && !var(i)._final ? this : in(i));
+ dup.addDef(loop && loopVar(var(i)) ? this : in(i));
return dup;
}
@@ -431,7 +448,7 @@ public class ScopeNode extends MemMergeNode {
// Fill in the backedge of any inserted Phis
void _endLoop( ScopeNode scope, Node back, Node exit ) {
for( int i=1; i<nIns(); i++ ) {
- if( var(i)._final ) continue; // Final vars did not get modified in the loop
+ if( !loopVar(var(i)) ) continue;
Node n = in(i);
if( var(i).type().isHighOrConst() && !(n instanceof PhiNode phi && phi.inProgress()) ) { // Cannot lift higher than a constant, so no Phi
Node con = ConstantNode.make(var(i).type()).init();
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/node/StopCUNode.java b/chapter28b/src/main/java/com/seaofnodes/simple/node/StopCUNode.java
index d3a356ed..43acf631 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/node/StopCUNode.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/node/StopCUNode.java
@@ -79,6 +79,11 @@ public class StopCUNode extends StopNode {
// here as its effects are not visible to the outside world.
addDepForwards(start); // Check again if function escapes
if( pub || start.escapedFIDX(sig.fidx()) ) {
+ // External callers already hold their object arguments, so
+ // writes through those arguments expose addresses too.
+ for( Node use : fun._outputs )
+ if( use instanceof ParmNode p && p._idx>=2 )
+ xmem=publicBottom(xmem.escapes(addDepForwards(p)._type));
if( tret.ctl().isHigh() ) continue; // Return not reachable
// Capture (precisely?) all escaping pointer aliases and fidxs.
// Escaped fidxs means the linked world can call that function;
@@ -88,6 +93,10 @@ public class StopCUNode extends StopNode {
xmem = (TypeMem)xmem.meet(publicBottom(tret.mem(),xmem));
}
}
+ // Native retention is shared by the whole program, including calls
+ // on paths with no Return. A class boundary must not hide it.
+ for( CallNode call : CodeGen.CODE._calls )
+ if( !call.isDead() ) xmem=publicBottom(xmem.escapes(call.exposed(this)));
assert xmem._alias==1 && !xmem._one;
return TypeTuple.make(Type.CONTROL,xmem,Type.BOTTOM);
}
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter28b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index cc958348..5fa0b99f 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -184,8 +184,12 @@ public class StoreNode extends MemOpNode {
return this;
}
+ // Private constructor memory describes the value that the next helper
+ // reads. Keep its conversion, even if a machine store could truncate it.
+ // This is the same rule as in the construction and escape chapters.
+ boolean priv = mem()._type instanceof TypeMem tm && tm._one;
// Value is automatically truncated by narrow store
- if( _size!=0 && val() instanceof AndNode and && and.in(2)._type.isConstant() ) {
+ if( !priv && _size!=0 && val() instanceof AndNode and && and.in(2)._type.isConstant() ) {
int log = log_size();
if( log<3 ) { // And-mask vs narrow store
long mask = ((TypeInteger)and.in(2)._type).value();
@@ -198,7 +202,7 @@ public class StoreNode extends MemOpNode {
}
// Store will chop high order bits off; math to change those bits can be dropped.
- if( _size!=0 && val() instanceof SarNode shr &&
+ if( !priv && _size!=0 && val() instanceof SarNode shr &&
shr.in(1) instanceof ShlNode shl &&
shr.in(2)._type.isConstant() &&
shl.in(2)._type.isConstant() ) {
@@ -253,7 +257,7 @@ public class StoreNode extends MemOpNode {
return switch( use ) {
case MemOpNode mem -> mem._alias;
case MemPhiNode phi -> phi._alias;
- case EscapeNode esc -> throw Utils.TODO("Untested"); // esc.pub()==this ? esc.fld()._alias : 0;
+ case EscapeNode esc -> esc.pub()==this ? esc.fld()._alias : 0;
default -> 0;
};
}
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/BoundaryTest.java b/chapter28b/src/test/java/com/seaofnodes/simple/BoundaryTest.java
new file mode 100644
index 00000000..b736ccb3
--- /dev/null
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/BoundaryTest.java
@@ -0,0 +1,86 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+// The new module boundary must preserve the preceding chapters' examples.
+public class BoundaryTest {
+ @Test public void testLocalConstruction() throws java.io.IOException {
+ String src="struct Counter { int x; int y; int z; new Counter={int n -> while(x<n) { int old=x; y=x+1; x=y+1; z=old; }}; }; val c=new Counter(arg); return c.x*100+c.z;";
+ CodeGen code=new CodeGen(src).driver(CodeGen.Phase.LocalSched);
+ assertEquals("2018",Eval2.eval(code,19));
+ assertEquals("0",Eval2.eval(code,0));
+ code._stop.walk(n -> {
+ if( n instanceof StoreNode st ) assertEquals(0,st.cfg0().loopDepth());
+ return null;
+ });
+ code=new CodeGen("struct Byte { new Byte={int n -> self.x=n; self.x+=3;}; u8 x; }; return new Byte(255).x;").driver(CodeGen.Phase.TypeCheck);
+ assertEquals("2",Eval2.eval(code,0));
+ // data is still unset when the constructor reads and updates x.
+ code=new CodeGen("struct Box { int x=7; int[] data; new Box={ -> self.x++; int n=self.x; data=new int[n]; }; }; return new Box().data#;")
+ .driver(CodeGen.Phase.TypeCheck);
+ assertEquals("8",Eval2.eval(code,0));
+ for( String field : new String[]{"data","self.data"} ) {
+ try {
+ new CodeGen("struct Box { int[] data; new Box={ -> int n="+field+"#; data=new int[n]; }; }; return new Box();")
+ .driver(CodeGen.Phase.TypeCheck);
+ fail("An uninitialized field must not be read");
+ } catch( Parser.ParseException e ) {
+ assertTrue(e.getMessage(),e.getMessage().contains("might not be initialized"));
+ }
+ }
+ java.nio.file.Path cfile=java.nio.file.Path.of("build/objs/constructionBoundary.c");
+ java.nio.file.Files.createDirectories(cfile.getParent());
+ java.nio.file.Files.writeString(cfile,"""
+ __attribute__((CALL_CONV)) extern long long count(long long);
+ int main(void) { return count(19)!=2018 || count(0)!=0; }
+ """);
+ TestC.run("val count={int arg ->"+src+"};","constructionBoundary",null,TestC.CALL_CONVENTION,
+ TestC.CALL_CONVENTION.equals("win64") ? "ms_abi" : "sysv_abi",cfile.toString(),"",-1);
+ }
+
+ @Test public void testRecursiveCallback() {
+ String src="""
+ val run={int n ->
+ val power={int base,int exponent -> if(exponent<=0) return 1; return base*power(base,exponent-1);};
+ val sumPowers={int n,{int int -> int} f -> if(n<=0) return 0; return f(2,n)+sumPowers(n-1,f);};
+ return sumPowers(n,power);
+ };
+ return run(arg);
+ """;
+ for( long seed : new long[]{2,13,123} ) {
+ CodeGen code=new CodeGen(src,seed,true).driver(CodeGen.Phase.Opto);
+ ParmNode base=code._start.walk(n -> n instanceof ParmNode p && p._label.equals("base") ? p : null);
+ assertNotNull(base);
+ assertSame(TypeInteger.constant(2),base._type);
+ code.driver(CodeGen.Phase.TypeCheck);
+ assertEquals("14",Eval2.eval(code,3));
+ }
+ }
+
+ @Test public void testNativeExposure() {
+ for( String tail : new String[]{
+ "retain(power); return power(2,n);",
+ "if(n) { retain(power); while(true) {} } return power(2,n);"
+ } ) {
+ String src="val run={int n -> val power={int base,int exponent -> if(exponent<=0) return 1; return base*power(base,exponent-1);}; { {int int -> int} -> int } retain=\"C\";"+tail+"}; return run(arg);";
+ CodeGen code=new CodeGen(src).driver(CodeGen.Phase.Opto);
+ ParmNode base=code._start.walk(n -> n instanceof ParmNode p && p._label.equals("base") ? p : null);
+ assertNotNull(base);
+ assertSame(TypeInteger.BOT,base._type);
+ code.driver(CodeGen.Phase.TypeCheck);
+ }
+ }
+
+ @Test public void testLazyTables() {
+ for( long seed : new long[]{1,123,987654321} ) {
+ CodeGen code=new CodeGen("src/test/smp/lazy","build/objs",null,"App/Main",null,seed,true,TypeInteger.BOT)
+ .driver(CodeGen.Phase.TypeCheck);
+ assertEquals("0",Eval2.eval(code,0));
+ assertEquals("125",Eval2.eval(code,1));
+ }
+ }
+}
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/ChainedComparisonTest.java b/chapter28b/src/test/java/com/seaofnodes/simple/ChainedComparisonTest.java
new file mode 100644
index 00000000..8db9c024
--- /dev/null
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/ChainedComparisonTest.java
@@ -0,0 +1,37 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.type.TypeInteger;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class ChainedComparisonTest {
+ @Test public void testAdjacentOperandsAndOperators() {
+ String[] expressions = {"10 < arg < 20", "0 < arg <= 3", "3 >= arg > 0",
+ "0 <= arg < 3", "0 < arg <= 3 < 4"};
+ String[][] expected = {{"0","0","1","0"}, {"0","1","0","0"},
+ {"0","1","0","0"}, {"1","0","0","0"},
+ {"0","1","0","0"}};
+ int[] args = {0,3,15,30};
+ for(int i=0;i<expressions.length;i++) {
+ CodeGen code = new CodeGen("return "+expressions[i]+";").driver(CodeGen.Phase.TypeCheck);
+ for(int j=0;j<args.length;j++) assertEquals(expressions[i],expected[i][j],Eval2.eval(code,args[j]));
+ }
+ }
+
+ @Test public void testSingleEvaluationAndShortCircuit() {
+ for(int seed:new int[]{2,13,123}) {
+ CodeGen code = new CodeGen("int x=arg; int y=0; val b=0 < x++ <= (y+=3) < (y+=1); return x*100+y*10+b;",seed,true)
+ .driver(CodeGen.Phase.TypeCheck);
+ assertEquals("100",Eval2.eval(code,0));
+ assertEquals("441",Eval2.eval(code,3));
+ assertEquals("530",Eval2.eval(code,4));
+ }
+ try {
+ new CodeGen("return 0 < arg > 1;").parse();
+ fail("mixed directions");
+ } catch(RuntimeException e) {
+ assertEquals("Mixing relational directions in a chained relational test",e.getMessage());
+ }
+ }
+}
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index 08029c4b..4019fd38 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -300,7 +300,7 @@ A x = new A;
for( String fields : new String[] {
"!B !b; new A = { -> if (arg) b = new B; };",
"!B !b; new A = { -> if (arg) return 0; b = new B; };",
- "!B !b; new A = { !B b -> b = new B; };", // Argument shadows field
+ "!B !b; new A = { !B !b -> b = new B; };", // Argument shadows field
"new A = { -> }; !B !b;", // Field declared later
} ) {
CodeGen code = new CodeGen("struct B {}; struct A { "+fields+" }; return 0;");
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index ae1dca5f..632b9539 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -436,7 +436,7 @@ return s.x;
catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*Test.S'",e.getMessage()); }
}
- @Test @Ignore
+ @Test
public void testErr5() {
CodeGen code = new CodeGen(
"""
@@ -450,8 +450,8 @@ return g();
}
- // Mutual recursion. Fails without SCCP to lift the recursive return types.
- @Ignore @Test
+ // Mutual recursion: check results, independent of how much gets inlined.
+ @Test
public void testFcnMutRec() {
CodeGen code = new CodeGen(
"""
@@ -460,7 +460,6 @@ val is_odd = { int x -> x ? is_even(x-1) : false; };
return is_even(arg);
""");
code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("Stop[ return #2; return Phi(Region,0,1,#2); ]", code.print());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
assertEquals("1", Eval2.eval(code, 2));
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 0458941e..cdfd16a7 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -202,7 +202,6 @@ flt farg = arg; return _test_sqrt_noInline(farg) + _test_sqrt_noInline(farg+2.0)
}
-
@Test
public void testArray1() {
String src =
@@ -257,9 +256,9 @@ val _hashCodeString = { !String self ->
return hash;
};
""";
- testCPU(src,"x86_64_v2", "SystemV",21,null);
- testCPU(src,"riscv" , "SystemV",8,null);
- testCPU(src,"arm" , "SystemV",8,null);
+ testCPU(src,"x86_64_v2", "SystemV",12,null);
+ testCPU(src,"riscv" , "SystemV",6,null);
+ testCPU(src,"arm" , "SystemV",6,null);
}
@Test
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 07ae42eb..2fcf65e9 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -383,9 +383,9 @@ val _hashCodeString = { !String self ->
return hash;
};
""";
- testCPU(src,"x86_64_v2", "SystemV",21,null);
- testCPU(src,"riscv" , "SystemV",8,null);
- testCPU(src,"arm" , "SystemV",8,null);
+ testCPU(src,"x86_64_v2", "SystemV",12,null);
+ testCPU(src,"riscv" , "SystemV",6,null);
+ testCPU(src,"arm" , "SystemV",6,null);
}
@Test public void testStringExport() throws IOException {
@@ -422,7 +422,7 @@ val _hashCodeString = { !String self ->
return hash;
};
""";
- TestC.runC(src, "stringHash", "",21);
+ TestC.runC(src, "stringHash", "",12);
}
@Test public void testLoop2() {
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java
index 275be96a..31a616b5 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Chapter25Test.java
@@ -412,7 +412,7 @@ public class Chapter25Test {
@Test
public void testHelloWorldDriver() throws Exception {
String out = "build/objs/helloWorldDriver/";
- Simple.main(new String[]{"-L",SYS_BLDDIR,"-o",out,"--norun","docs/examples/A_helloWorld.smp"});
+ Simple.main(new String[]{"-L",SYS_BLDDIR,"-o",out,"--norun","src/test/smp/examples/A_helloWorld.smp"});
assertTrue(Files.isRegularFile(Path.of(out,"A_helloWorld.o")));
}
@@ -423,7 +423,7 @@ public class Chapter25Test {
try( PrintStream capture = new PrintStream(output,true,StandardCharsets.UTF_8) ) {
System.setOut(capture);
Simple.main(new String[]{"-L",SYS_FILE.toString(),"-o","build/objs/helloWorldDriverLibFile/",
- "docs/examples/A_helloWorld.smp"});
+ "src/test/smp/examples/A_helloWorld.smp"});
} finally {
System.setOut(saved);
}
@@ -471,7 +471,7 @@ return rez < buf# ? 0 : sys.libc._exit(-2);
@Test
public void testBubbles() throws IOException {
- String src = Files.readString( Path.of("docs/examples/BubbleSort.smp"));
+ String src = Files.readString( Path.of("src/test/smp/examples/BubbleSort.smp"));
String exe = TestC.compile(src,"BubbleSort",new Ary<>(new String[]{SYS_BLDDIR}),
TestC.CALL_CONVENTION,null,null,-1);
assertEquals("[-17, 2, 3, 999]\n",
@@ -492,7 +492,7 @@ return rez < buf# ? 0 : sys.libc._exit(-2);
@Test
public void testCapitalize() throws IOException {
- String src = Files.readString(Path.of("docs/examples/Capitalize.smp"));
+ String src = Files.readString(Path.of("src/test/smp/examples/Capitalize.smp"));
String exe = TestC.compile(src,"Capitalize",new Ary<>(new String[]{SYS_BLDDIR}),
TestC.CALL_CONVENTION,null,null,-1);
assertEquals("Hello world\n",TestC.exec(exe,"hello world"));
@@ -505,7 +505,7 @@ return rez < buf# ? 0 : sys.libc._exit(-2);
@Test
public void testDijkstra() throws IOException {
- String src = Files.readString(Path.of("docs/examples/Dijkstra.smp"));
+ String src = Files.readString(Path.of("src/test/smp/examples/Dijkstra.smp"));
String matrix = "[0, 2, 0, 6, 0, 2, 0, 3, 8, 5, 0, 3, 0, 0, 7, 6, 8, 0, 0, 9, 0, 5, 7, 9, 0]";
String exe = TestC.compile(src,"Dijkstra",new Ary<>(new String[]{SYS_BLDDIR}),
TestC.CALL_CONVENTION,null,null,-1);
@@ -520,7 +520,7 @@ return rez < buf# ? 0 : sys.libc._exit(-2);
@Test
public void testFileIO() throws IOException {
- String src = Files.readString(Path.of("docs/examples/FileIO.smp"));
+ String src = Files.readString(Path.of("src/test/smp/examples/FileIO.smp"));
String exe = TestC.compile(src,"FileIO",new Ary<>(new String[]{SYS_BLDDIR}),
TestC.CALL_CONVENTION,null,null,-1);
assertEquals("File I/O succeeded\n",TestC.exec(exe));
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/FinalConstructionTest.java b/chapter28b/src/test/java/com/seaofnodes/simple/FinalConstructionTest.java
new file mode 100644
index 00000000..f583e2d2
--- /dev/null
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/FinalConstructionTest.java
@@ -0,0 +1,65 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.StoreNode;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class FinalConstructionTest {
+ private static final String LOOP = """
+ val step={int x -> x*2+1;};
+ struct C {
+ val x=1;
+ new C={int n ->
+ x=0; x=1;
+ for(int i=0; i<n; i++) x=step(x);
+ };
+ val get={->x;};
+ };
+ return new C(arg).get();
+ """;
+
+ @Test public void testFinalConstructorLocals() throws java.io.IOException {
+ var code=new CodeGen(LOOP).driver(CodeGen.Phase.LocalSched);
+ assertEquals("1",Eval2.eval(code,0));
+ assertEquals("15",Eval2.eval(code,3));
+ assertEquals("63",Eval2.eval(code,5));
+ code._stop.walk(n -> {
+ if( n instanceof StoreNode st && st._name.equals("x") )
+ assertEquals("Final field writes belong after the loop",0,st.cfg0().loopDepth());
+ return null;
+ });
+ String[] sources={
+ "struct C { val x=1; new C={->x=1; x=2;}; }; return new C().x;",
+ "struct C { int ~x; new C={int n -> self.x=1; for(int i=0;i<n;i++) self.x*=2;}; }; return new C(arg).x;",
+ "struct C { val x=1; new C={int n -> for(int i=0;i<n;i++) ++x;}; }; return new C(arg).x;",
+ "struct C { val x=1.5; new C={->x=2.5;}; }; return new C().x;",
+ "struct C { val x=\"a\"; new C={int n -> for(int i=0;i<n;i++) x=\"b\";}; }; return new C(arg).x[0];",
+ "struct C { val x=new int[1]; new C={->x=new int[2]; x=new int[3];}; }; return new C().x#;"
+ };
+ String[] results={"2","8","4","2.5","98","3"};
+ for( int i=0; i<sources.length; i++ )
+ assertEquals(results[i],Eval2.eval(new CodeGen(sources[i]).driver(CodeGen.Phase.TypeCheck),3));
+
+ String[] rejected={
+ "struct C { val x=1; }; val c=new C(); c.x=2; return c.x;",
+ "struct C { new C={->val x=1; x=2;}; }; return new C();",
+ "struct C { val f={->1;}; new C={->f={->2;};}; }; return new C().f();"
+ };
+ for( String src : rejected ) {
+ try { new CodeGen(src).driver(CodeGen.Phase.TypeCheck); fail(src); }
+ catch( Parser.ParseException e ) { assertTrue(e.getMessage(),e.getMessage().startsWith("Cannot")); }
+ }
+
+ var dir=java.nio.file.Path.of("build/objs");
+ java.nio.file.Files.createDirectories(dir);
+ java.nio.file.Files.writeString(dir.resolve("finalConstruction.smp"),"val buildFinal={int arg ->"+LOOP+"};");
+ java.nio.file.Files.writeString(dir.resolve("finalConstruction.c"),"""
+ extern long long buildFinal(long long);
+ int main(void) { return buildFinal(0)!=1 || buildFinal(3)!=15 || buildFinal(5)!=63; }
+ """);
+ TestC.run("val buildFinal={int arg ->"+LOOP+"};","finalConstruction",null,TestC.CALL_CONVENTION,
+ TestC.CALL_CONVENTION.equals("win64") ? "ms_abi" : "sysv_abi",
+ dir.resolve("finalConstruction.c").toString(),"",-1);
+ }
+}
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/SeparateCompilationTest.java b/chapter28b/src/test/java/com/seaofnodes/simple/SeparateCompilationTest.java
new file mode 100644
index 00000000..4576bc94
--- /dev/null
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/SeparateCompilationTest.java
@@ -0,0 +1,90 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.util.Ary;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class SeparateCompilationTest {
+ @Test public void testImportedNativeExposure() throws Exception {
+ Path build=Path.of("build/objs/separate");
+ Files.createDirectories(build);
+ Path lib=Files.createTempDirectory(build,"callbacks-");
+ String src="""
+ val run={int n ->
+ val power={int base,int exponent ->
+ if(exponent<=0) return 1;
+ return base*power(base,exponent-1);
+ };
+ { {int int -> int} -> int } retain="C";
+ retain(power);
+ return power(2,n);
+ };
+ """;
+ new CodeGen(null,lib.toString(),null,"Callbacks",src,123,true,TypeInteger.BOT)
+ .driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
+ CodeGen code=new CodeGen(null,null,new Ary<>(new String[]{lib.toString()}),
+ "Client","return Callbacks.run(arg);",123,true,TypeInteger.BOT)
+ .driver(CodeGen.Phase.Opto);
+ assertNull(code._compunits.get("Callbacks")._src);
+ ParmNode base=code._start.walk(n -> n instanceof ParmNode p && p._label.equals("base") ? p : null);
+ assertNotNull(base);
+ assertSame("Native code may call the saved callback with any base",TypeInteger.BOT,base._type);
+ code.driver(CodeGen.Phase.TypeCheck);
+ }
+
+ // The client root contains no library source. The saved constructor must
+ // keep local loop values, and retained calls must use the library's storage.
+ @Test public void testImportedConstructorAndState() throws Exception {
+ Path fixtures=Path.of("src/test/smp/separate");
+ Path build=Path.of("build/objs/separate");
+ Files.createDirectories(build);
+ Path lib=Files.createTempDirectory(build,"library-");
+ Path client=Files.createTempDirectory(build,"client-");
+ new CodeGen(fixtures.resolve("library").toString(),lib.toString(),null,
+ "Counters",null,123,true,TypeInteger.BOT)
+ .driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
+ assertTrue(Files.isRegularFile(lib.resolve("Counters.o")));
+ assertFalse(Files.exists(fixtures.resolve("client/Counters.smp")));
+ var paths=new Ary<>(new String[]{lib.toString()});
+ for( long seed : new long[]{2,13,123} ) {
+ CodeGen code=new CodeGen(fixtures.resolve("client").toString(),client.toString(),paths,
+ "Main",null,seed,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
+ CompUnit imported=code._compunits.get("Counters");
+ assertNotNull(imported);
+ assertNull("Library source must not be parsed",imported._src);
+ assertEquals(lib.resolve("Counters.o").toRealPath(),imported._obj.toPath().toRealPath());
+ assertEquals("0",Eval2.eval(code,0));
+ assertEquals("107",Eval2.eval(code,3));
+ assertEquals("131",Eval2.eval(code,5));
+ }
+ new CodeGen(fixtures.resolve("client").toString(),client.toString(),paths,
+ "Main",null,123,true,TypeInteger.BOT)
+ .driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
+ Path driver=client.resolve("driver.c");
+ Files.writeString(driver,"""
+ #include <stdio.h>
+ __attribute__((CALL_CONV)) extern long long run(long long);
+ int main(void) {
+ long long args[]={0,3,5,0,3}, expected[]={0,107,231,0,307};
+ for(int i=0;i<5;i++) {
+ long long actual=run(args[i]);
+ if(actual!=expected[i]) {
+ printf("call %d: got %lld, expected %lld\\n",i,actual,expected[i]);
+ return 1;
+ }
+ }
+ return 0;
+ }
+ """);
+ String exe=client.resolve(TestC.OS.startsWith("Windows")?"test.exe":"test").toString();
+ TestC.linkExe(client.resolve("Main.o").toString(),
+ TestC.CALL_CONVENTION.equals("win64")?"ms_abi":"sysv_abi",driver.toString(),
+ new Ary<>(new String[]{lib.resolve("Counters.o").toString()}),exe);
+ assertEquals("",TestC.exec(TestC.TEST_TIMEOUT_SECONDS,exe));
+ }
+}
diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/Simple.java b/chapter28b/src/test/java/com/seaofnodes/simple/Simple.java
index 2d688bbe..7cfdf57d 100644
--- a/chapter28b/src/test/java/com/seaofnodes/simple/Simple.java
+++ b/chapter28b/src/test/java/com/seaofnodes/simple/Simple.java
@@ -13,7 +13,6 @@ import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.stream.Collectors;
public class Simple {
static void usage() {
@@ -83,7 +82,11 @@ Options:
static String getInput() {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
- return reader.lines().collect(Collectors.joining("\n"));
+ StringBuilder text=new StringBuilder();
+ try {
+ for( String line; (line=reader.readLine())!=null; ) text.append(line).append('\n');
+ } catch( IOException e ) { throw new RuntimeException(e); }
+ return text.toString();
}
public static void main(String[] args) throws Exception {
@@ -140,6 +143,9 @@ Options:
case "-o": if (out != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
out = args[++i]; do_codegen=true;
break;
+ case "--root": if( root!=null || i+1>=args.length ) bad_usage();
+ root=args[++i];
+ break;
case "-L": if (i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
libPaths.add(args[++i]);
break;
@@ -175,7 +181,8 @@ Options:
if( modPath == null )
modPath = Path.of(".");
} else {
- throw Utils.TODO();
+ modPath=Path.of(root);
+ inPath=modPath.resolve(inPath);
}
// Compute the module-relative paths for all sources
@@ -184,7 +191,8 @@ Options:
Path relPath = n==1 ? null : srcRelPath.subpath(0,n-1);
// Compute source/class name
String srcNameExt = srcRelPath.getName(n-1).toString();
- String srcName = srcNameExt.substring(0,srcNameExt.length()-4);
+ String srcName = srcRelPath.toString().replace('\\','/');
+ srcName=srcName.substring(0,srcName.length()-4);
// Compute the build path.
Path outPath;
@@ -219,15 +227,14 @@ Options:
if( simpleHome != null && !simpleHome.isEmpty() )
libPaths.add(Path.of(simpleHome,cpu+"_"+abi).toString());
- // Read input file
- try {
- src = input_filename.isEmpty() ? getInput() : Files.readString(Path.of(input_filename));
- } catch( IOException e ) { throw bad("Cannot read input file: "+input_filename); }
+ // Keep file-backed units so discovery sees parents and siblings.
+ src = input_filename.isEmpty() ? getInput() : null;
// Compilation pipeline
Ary<String> externPaths = libPaths.isEmpty() ? null : new Ary<>(libPaths.toArray(new String[0]));
CodeGen code = new CodeGen(modPath.toString(), outPath.toString(), externPaths, srcName, src, 456, true, TypeInteger.BOT );
- code.driver(Phase.LastPhase,cpu,abi, false, emit_main || do_run || do_eval, dump);
+ code.driver(do_eval && !do_codegen ? Phase.TypeCheck : Phase.LastPhase,
+ cpu,abi, false, emit_main || do_run || do_eval, dump);
if( do_codegen && print_asm )
System.out.println(code.asm());
diff --git a/chapter28b/docs/examples/A_helloWorld.smp b/chapter28b/src/test/smp/examples/A_helloWorld.smp
index 4ed6abdf..4ed6abdf 100644
--- a/chapter28b/docs/examples/A_helloWorld.smp
+++ b/chapter28b/src/test/smp/examples/A_helloWorld.smp
diff --git a/chapter28b/docs/examples/BubbleSort.smp b/chapter28b/src/test/smp/examples/BubbleSort.smp
index ca6906b5..ca6906b5 100644
--- a/chapter28b/docs/examples/BubbleSort.smp
+++ b/chapter28b/src/test/smp/examples/BubbleSort.smp
diff --git a/chapter28b/docs/examples/Capitalize.smp b/chapter28b/src/test/smp/examples/Capitalize.smp
index 8e31c18f..8e31c18f 100644
--- a/chapter28b/docs/examples/Capitalize.smp
+++ b/chapter28b/src/test/smp/examples/Capitalize.smp
diff --git a/chapter28b/docs/examples/Dijkstra.smp b/chapter28b/src/test/smp/examples/Dijkstra.smp
index fd595eb5..fd595eb5 100644
--- a/chapter28b/docs/examples/Dijkstra.smp
+++ b/chapter28b/src/test/smp/examples/Dijkstra.smp
diff --git a/chapter28b/docs/examples/FileIO.smp b/chapter28b/src/test/smp/examples/FileIO.smp
index e48d435b..e48d435b 100644
--- a/chapter28b/docs/examples/FileIO.smp
+++ b/chapter28b/src/test/smp/examples/FileIO.smp
diff --git a/chapter28b/src/test/smp/lazy/App.smp b/chapter28b/src/test/smp/lazy/App.smp
new file mode 100644
index 00000000..caf7b0c1
--- /dev/null
+++ b/chapter28b/src/test/smp/lazy/App.smp
@@ -0,0 +1,3 @@
+val Main=Main;
+val Tables=Tables;
+val Stats=Stats;
diff --git a/chapter28b/src/test/smp/lazy/App/Main.smp b/chapter28b/src/test/smp/lazy/App/Main.smp
new file mode 100644
index 00000000..2444f21b
--- /dev/null
+++ b/chapter28b/src/test/smp/lazy/App/Main.smp
@@ -0,0 +1 @@
+return arg ? Tables.square(3)+Tables.square(4)+100*Stats.builds : Stats.builds;
diff --git a/chapter28b/src/test/smp/lazy/App/Stats.smp b/chapter28b/src/test/smp/lazy/App/Stats.smp
new file mode 100644
index 00000000..e2d9e02e
--- /dev/null
+++ b/chapter28b/src/test/smp/lazy/App/Stats.smp
@@ -0,0 +1 @@
+int builds=0;
diff --git a/chapter28b/src/test/smp/lazy/App/Tables.smp b/chapter28b/src/test/smp/lazy/App/Tables.smp
new file mode 100644
index 00000000..0f32a118
--- /dev/null
+++ b/chapter28b/src/test/smp/lazy/App/Tables.smp
@@ -0,0 +1,4 @@
+Stats.builds++;
+int[] _squares=new int[256];
+for(int i=0; i<256; i++) _squares[i]=i*i;
+val square={u8 n -> _squares[n];};
diff --git a/chapter28b/src/test/smp/separate/client/Main.smp b/chapter28b/src/test/smp/separate/client/Main.smp
new file mode 100644
index 00000000..077f6575
--- /dev/null
+++ b/chapter28b/src/test/smp/separate/client/Main.smp
@@ -0,0 +1,6 @@
+val run={int n ->
+ if(!n) return 0;
+ val c=new Counters.Counter(n);
+ return c.get()+100*Counters.next_noInline();
+};
+return run(arg);
diff --git a/chapter28b/src/test/smp/separate/library/Counters.smp b/chapter28b/src/test/smp/separate/library/Counters.smp
new file mode 100644
index 00000000..a3e36562
--- /dev/null
+++ b/chapter28b/src/test/smp/separate/library/Counters.smp
@@ -0,0 +1,12 @@
+// A library can keep shared state and publish instance constructors.
+int calls=0;
+val next_noInline={ -> calls++; return calls; };
+
+struct Counter {
+ val value=1;
+ new Counter={int n ->
+ value=0;
+ for(int i=0; i<n; i++) value=value*2+1;
+ };
+ val get={ -> value; };
+};