feat(joinir): Phase 53 - SELFHOST-NORM-DEV-EXPAND implementation
Expanded selfhost dev Normalized target with 2 practical P2/P3 loop variations, strengthened structural signature axis, and implemented two-stage detection. Key Changes: 1. Documentation (phase49-selfhost-joinir-depth2-design.md +128 lines): - Added Phase 53 section with candidate selection rationale - Documented two-stage detector strategy (structural primary + dev-only name guard) - Defined structural axis strengthening (carrier count/type, branch patterns) 2. Fixtures (+210 lines): - selfhost_args_parse_p2.program.json (60 lines): P2 with String carrier + conditional branching - selfhost_stmt_count_p3.program.json (150 lines): P3 with 5 carriers + multi-branch if-else 3. Structured Builders (fixtures.rs +48 lines): - build_selfhost_args_parse_p2_structured_for_normalized_dev() - build_selfhost_stmt_count_p3_structured_for_normalized_dev() 4. ShapeGuard Two-Stage Detection (shape_guard.rs +80 lines): - Added SelfhostArgsParseP2/SelfhostStmtCountP3 to NormalizedDevShape enum - Implemented is_selfhost_args_parse_p2(): P2 core family + name guard - Implemented is_selfhost_stmt_count_p3(): 2-10 carrier check + name guard - Updated capability_for_shape() mappings 5. Bridge Integration (bridge.rs +8 lines, normalized.rs +10 lines): - Added shape handlers delegating to existing normalizers - Added roundtrip reconstruction handlers 6. Entry Point Registration (ast_lowerer/mod.rs +2 lines): - Registered selfhost_args_parse_p2/selfhost_stmt_count_p3 as LoopFrontend routes 7. Dev VM Comparison Tests (normalized_joinir_min.rs +40 lines): - normalized_selfhost_args_parse_p2_vm_bridge_direct_matches_structured() - normalized_selfhost_stmt_count_p3_vm_bridge_direct_matches_structured() 8. Test Context Fix (dev_env.rs): - Added thread-local test context depth counter - Fixed deadlock in nested test_ctx() calls via reentrant with_dev_env_if_unset() Structural Axis Growth: P2 family: - Carrier count: 1-3 (unchanged) - NEW: Type diversity (Integer/String mixed) - NEW: Conditional branching patterns (Eq-heavy comparisons) P3 family: - NEW: Carrier count upper bound: 2-10 (was 2-4) - NEW: Multi-branch if-else (5+ branches with nested structure) - NEW: Complex conditional patterns Test Results: - normalized_dev: 40/40 PASS (including 2 new tests) - lib regression: 939 PASS, 56 ignored - Existing behavior unchanged (normalized_dev feature-gated) Phase 53 Achievements: ✅ P2/P3 each gained 1 practical variation (2 total) ✅ Two-stage detection: structural primary + dev-only name guard ✅ Structural axis expanded: 4 axes (carrier count/type/Compare/branch patterns) ✅ All tests PASS, no regressions ✅ Test context deadlock fixed (0.04s for 29 tests) Files Modified: 14 files Lines Added: ~516 lines (net) Implementation: Pure additive (feature-gated) Next Phase (54+): - Accumulate 6+ loops per P2/P3 family - Achieve 5+ stable structural axes - Target < 5% false positive rate - Then shrink/remove name guard scope
This commit is contained in:
@ -177,27 +177,45 @@
|
|||||||
- P3 if-sum を Normalized JoinIR に載せる設計。P2 と同じ ConditionEnv/CarrierInfo/ExitLine インフラを再利用。
|
- P3 if-sum を Normalized JoinIR に載せる設計。P2 と同じ ConditionEnv/CarrierInfo/ExitLine インフラを再利用。
|
||||||
- Phase 47-A: Minimal sum_count(dev-only)として、`phase212_if_sum_min.hako` 相当の最小 if-sum ループを AST ベース lowerer + Structured→Normalized→Structured roundtrip(Runner 経路)+ Normalized→MIR(direct) で検証済み。
|
- Phase 47-A: Minimal sum_count(dev-only)として、`phase212_if_sum_min.hako` 相当の最小 if-sum ループを AST ベース lowerer + Structured→Normalized→Structured roundtrip(Runner 経路)+ Normalized→MIR(direct) で検証済み。
|
||||||
- Phase 47-B 以降: array_filter など body-local/MethodCall を含む P3 ループや canonical 昇格は今後の実装フェーズで扱う。
|
- Phase 47-B 以降: array_filter など body-local/MethodCall を含む P3 ループや canonical 昇格は今後の実装フェーズで扱う。
|
||||||
4. **Phase 48-NORM-P4(設計完了✅+48-A実装完了✅ 2025-12-12)**: Pattern4 (continue) Normalized 設計+minimal実装
|
4. **Phase 48-NORM-P4(設計完了✅+48-A/B/C canon 完了✅ 2025-12-12→2026-01-XX)**: Pattern4 (continue) Normalized 設計+実装
|
||||||
- 設計詳細: [phase48-norm-p4-design.md](docs/development/current/main/phase48-norm-p4-design.md)
|
- 設計詳細: [phase48-norm-p4-design.md](docs/development/current/main/phase48-norm-p4-design.md)
|
||||||
- ターゲットループ決定: _parse_array skip whitespace(◎ PRIMARY)、_parse_object(○)、_unescape_string/parse_string(△)
|
- ターゲットループ決定: _parse_array skip whitespace(◎ PRIMARY)、_parse_object(○)、_unescape_string/parse_string(△)
|
||||||
- 設計骨格: `continue` = 即座の `TailCallFn(loop_step, ...)` (新命令不要)
|
- 設計骨格: `continue` = 即座の `TailCallFn(loop_step, ...)` (新命令不要)
|
||||||
- P1/P2/P3 と同じ `loop_step(env, k_exit)` 骨格に載せる
|
- P1/P2/P3 と同じ `loop_step(env, k_exit)` 骨格に載せる
|
||||||
- インフラ再利用率: 95%+ (StepKind の ContinueCheck のみ追加)
|
- インフラ再利用率: 95%+ (StepKind の ContinueCheck のみ追加)
|
||||||
- **Phase 48-A実装(minimal dev-only)完了✅**:
|
- **Phase 48-A実装(minimal dev-only)完了✅** / **Phase 48-B dev(JsonParser skip_ws continue)完了✅**:
|
||||||
- P4 minimal フィクスチャ追加(skip i==2 パターン、単一 carrier `acc`)
|
- P4 minimal フィクスチャ追加(skip i==2 パターン、単一 carrier `acc`)+ JsonParser continue skip_ws (array/object) フィクスチャを追加
|
||||||
- ShapeGuard: Pattern4ContinueMinimal 検出器実装(構造ベース)
|
- ShapeGuard: Pattern4ContinueMinimal + JsonParser continue 形状を検出
|
||||||
- StepScheduleBox: ContinueCheck step 追加(評価順序: HeaderCond → ContinueCheck → Updates → Tail)
|
- StepScheduleBox: ContinueCheck step 追加(評価順序: HeaderCond → ContinueCheck → Updates → Tail)
|
||||||
- normalize_pattern4_continue_minimal() 実装(P2 委譲、95%インフラ再利用)
|
- normalize_pattern4_continue_minimal()/jsonparser_*continue* を dev 正規化に配線(P2 インフラを再利用)
|
||||||
- テスト完備: 4つの integration tests(normalization/runner/VM Bridge 比較×2)
|
- テスト完備: minimal + JsonParser continue の VM bridge 比較を normalized_dev スイートで固定
|
||||||
- 939/939 tests PASS(目標938超過達成!)
|
- **Phase 48-C(canonical 昇格)完了✅**:
|
||||||
- 次ステップ: Phase 48-B (extended multi-carrier) → 48-C (canonical promotion)
|
- P4 minimal + JsonParser skip_ws array/object を canonical set に追加(env OFF でも Normalized→MIR(direct) を強制)
|
||||||
|
- Bridge/runner で Structured fallback を禁止、fail-fast 契約に統一
|
||||||
|
- canonical ルートと Structured 直経路の stdout 一致を比較するテストを追加
|
||||||
5. JsonParser 残りループへの JoinIR 展開
|
5. JsonParser 残りループへの JoinIR 展開
|
||||||
- `_parse_array` / `_parse_object` / `_unescape_string` / 本体 `_parse_string` など。
|
- `_parse_array` / `_parse_object` / `_unescape_string` / 本体 `_parse_string` など。
|
||||||
- 既存の P2/P3/P4+P5 パイプラインをどこまで延ばせるかを docs 側で設計 → コード側はその設計に沿って小さく実装。
|
- 既存の P2/P3/P4+P5 パイプラインをどこまで延ばせるかを docs 側で設計 → コード側はその設計に沿って小さく実装。
|
||||||
6. selfhost depth‑2 ラインの再開
|
6. **Phase 49-SELFHOST-NORM-DEPTH2(設計・コードなし)**: selfhost depth2 Normalized 設計フェーズ
|
||||||
- `.hako` 側で Program/MIR JSON を読んで JoinIR/MIR/VM/LLVM に流すライン。
|
- 設計詳細: [phase49-selfhost-joinir-depth2-design.md](docs/development/current/main/phase49-selfhost-joinir-depth2-design.md)
|
||||||
- JsonParser 側のカバレッジが上がったあとに、小さいループから順に移植する。
|
7. **Phase 50-SELFHOST-NORM-DEV(dev-only)完了✅ 2025-12-12**: selfhost 軽量 P2/P3 を dev Normalized パイプラインに載せる足慣らし
|
||||||
7. JoinIR Verify / 最適化まわり
|
- 対象: `selfhost_token_scan_p2` / `selfhost_if_sum_p3`
|
||||||
|
- fixtures / ShapeGuard(Selfhost* 系) / VM bridge 比較テストまで整備し、Structured 直経路と一致を固定。
|
||||||
|
8. **Phase 51-SELFHOST-NORM-DEV-EXTEND(dev-only)完了✅ 2025-12-12**: selfhost 実戦寄り P2/P3 を dev Normalized に追加
|
||||||
|
- 対象: `selfhost_token_scan_p2_accum` / `selfhost_if_sum_p3_ext`
|
||||||
|
- Phase 50 と同導線で fixtures / shape / 比較テストを追加し、selfhost 断面で緑を維持。
|
||||||
|
9. **Phase 52-SELFHOST-SHAPE-STRUCT-SIGNATURE(dev-only)完了✅ 2025-12-12**: selfhost shape の構造シグネチャ育成
|
||||||
|
- selfhost P2/P3 を「構造一次判定→dev-only name ガード最終確定」の二段 detector に移行。
|
||||||
|
- 構造シグネチャの安定テストを追加し、name ガード撤去の足場を SSOT に固定。
|
||||||
|
10. **Phase 53-SELFHOST-NORM-DEV-EXPAND(dev-only)完了✅ 2025-12-12**: selfhost P2/P3 の実戦寄り形状を追加
|
||||||
|
- 対象追加: P2 `args_parse_p2` / P3 `stmt_count_p3`
|
||||||
|
- 構造一次判定(carrier 数/型/Compare/branch)→ dev-only name 最終確定の二段 detector を拡張。
|
||||||
|
- P3 carrier 上限を 2–10 に拡大し、複雑 if-else 形状を selfhost 群として取り込んだ。
|
||||||
|
- `normalized_dev` selfhost 断面/回帰テストが緑、既定挙動は不変。
|
||||||
|
11. **Phase 54-SELFHOST-SHAPE-GROWTH(次のフォーカス候補・dev-only)**: 構造軸の追加育成と name ガード範囲縮小の準備
|
||||||
|
- selfhost P2/P3 を各 1〜2 本ずつ追加し、構造シグネチャ軸(型多様性/Compare 配列/分岐構造など)を 5+ へ育てる。
|
||||||
|
- 偽陽性のログ/テストを見ながら、name ガードの適用を「最終確定が必要な形状だけ」に限定していく。
|
||||||
|
12. JoinIR Verify / 最適化まわり
|
||||||
- すでに PHI/ValueId 契約は debug ビルドで検証しているので、
|
- すでに PHI/ValueId 契約は debug ビルドで検証しているので、
|
||||||
必要なら SSA‑DFA や軽い最適化(Loop invariant / Strength reduction)を検討。
|
必要なら SSA‑DFA や軽い最適化(Loop invariant / Strength reduction)を検討。
|
||||||
|
|
||||||
|
|||||||
@ -1416,7 +1416,7 @@ Pattern3 (if-sum) ループを Normalized JoinIR に対応させる。P2 と同
|
|||||||
|
|
||||||
**スコープ外**: P4 (continue) 対応(NORM-P4 フェーズで実施)、Complex P3 patterns(後続フェーズ)
|
**スコープ外**: P4 (continue) 対応(NORM-P4 フェーズで実施)、Complex P3 patterns(後続フェーズ)
|
||||||
|
|
||||||
### 3.25 Phase 48-NORM-P4 – Normalized P4 (Continue) 🏗️ DESIGN + PHASE 48-A MINIMAL DEV COMPLETE (2025-12-12)
|
### 3.25 Phase 48-NORM-P4 – Normalized P4 (Continue) 🏗️ DESIGN + PHASE 48-A/B/C CANONICAL COMPLETE (2025-12-12 → 2026-01-XX)
|
||||||
|
|
||||||
**設計詳細 / 実装サマリ**: [phase48-norm-p4-design.md](./phase48-norm-p4-design.md)
|
**設計詳細 / 実装サマリ**: [phase48-norm-p4-design.md](./phase48-norm-p4-design.md)
|
||||||
|
|
||||||
@ -1442,5 +1442,31 @@ P4 (continue) は P1/P2/P3 と同じ `loop_step(env, k_exit)` 骨格を使う設
|
|||||||
(Structured→Normalized→MIR(direct) vs Structured→MIR / runner / VM bridge)
|
(Structured→Normalized→MIR(direct) vs Structured→MIR / runner / VM bridge)
|
||||||
- `cargo test --release` ベースで **939/939 tests PASS**(Phase 48-A 実装時点)
|
- `cargo test --release` ベースで **939/939 tests PASS**(Phase 48-A 実装時点)
|
||||||
|
|
||||||
**Phase 48 doc is SSOT** for P4 Normalized design + 48-A 実装サマリだよ。
|
**Phase 48-B(JsonParser continue skip_ws、dev-only)実装ステータス**:
|
||||||
Phase 48-B(multi-carrier / string ops 拡張)と 48-C(canonical 昇格)は今後のフェーズで扱う。
|
- Fixtures: `jsonparser_parse_array_continue_skip_ws.program.json` / `jsonparser_parse_object_continue_skip_ws.program.json`
|
||||||
|
- ShapeGuard: JsonParser continue ループ用の shape を追加(array/object 両方)
|
||||||
|
- Normalized lowering: `normalize_jsonparser_parse_array_continue_skip_ws` / `_parse_object_...` で Structured→Normalized→MIR(direct) を dev 比較
|
||||||
|
- テスト: normalized dev スイートに VM bridge 比較テストを追加(Structured 直経路と stdout 一致を確認)
|
||||||
|
|
||||||
|
**Phase 48-C(canonical 昇格)実装ステータス**:
|
||||||
|
- Canonical set 拡張: Pattern4 continue minimal / JsonParser skip_ws array/object を `is_canonical_shape()` に追加
|
||||||
|
- Bridge/runner: P4 canonical shapes は env 無しでも Normalized→MIR(direct) を必ず通る(Structured fallback 無し、fail-fast)
|
||||||
|
- テスト: canonical ルート(env OFF)と Structured 直経路の stdout 一致を比較するテストを追加
|
||||||
|
|
||||||
|
**Phase 48 doc is SSOT** for P4 Normalized design + 48-A/B/C サマリだよ。
|
||||||
|
P1〜P4 の代表ループがすべて canonical Normalized パイプラインに載った状態になった。
|
||||||
|
|
||||||
|
### 3.26 Phase 49-SELFHOST-NORM-DEPTH2 – Selfhost depth2 Normalized 設計フェーズ(docsのみ)
|
||||||
|
|
||||||
|
- SSOT: [phase49-selfhost-joinir-depth2-design.md](./phase49-selfhost-joinir-depth2-design.md)
|
||||||
|
- 目的: selfhost ラインでも `.hako → Program/MIR JSON → JoinIR(Structured) → Normalized → MIR → VM/LLVM` の depth2 パイプラインを踏めるように、対象ループ(軽い P2/P3 を 1〜2 本)と適用方針を設計で固定する。
|
||||||
|
- スコープ: selfhost の P2/P3 軽量ループのみ(トークン走査系 P2・if-sum 系 P3 を候補化)。heavy ループや P5/Trim 系は Phase 50+ に回す。
|
||||||
|
- 設計アウトプット: 対象ループ↔Pattern/shape マッピング表、Program JSON/fixture/test 計画、depth2 パイプラインの責務整理(コード変更なし)。
|
||||||
|
|
||||||
|
### 3.27 Phase 50-SELFHOST-NORM-DEV – selfhost P2/P3 の dev Normalized 実装
|
||||||
|
|
||||||
|
- 対象: selfhost_token_scan_p2(P2 break カウンタループ)/ selfhost_if_sum_p3(P3 if-sum sum+count)の 2 本に限定。
|
||||||
|
- Fixtures: `selfhost_token_scan_p2.program.json` / `selfhost_if_sum_p3.program.json` を normalized_dev フィクスチャ群に追加。
|
||||||
|
- ShapeGuard: `SelfhostTokenScanP2` / `SelfhostIfSumP3` 形状を追加し、canonical P2/P3 とは分離(Pattern2/3 minimal に吸われないようガード)。
|
||||||
|
- Normalizer/Bridge: 既存 Pattern2/3 normalizer を流用して Structured→Normalized→MIR(direct) を dev 実行、構造/VM 出力を Structured 直経路と比較。
|
||||||
|
- テスト: normalized_joinir_min.rs に selfhost P2/P3 の VM ブリッジ比較テストを追加(normalized_dev 前提)、shape_guard の検出テストも拡張。
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
# Phase 48: Normalized P4 (Continue) Design
|
# Phase 48: Normalized P4 (Continue) Design
|
||||||
|
|
||||||
**Status**: Design Phase (doc-only)
|
**Status**: Phase 48-A/B/C COMPLETE (minimal + JsonParser skip_ws continue、Normalized→MIR 直経路+canonical 昇格まで完了)
|
||||||
**Date**: 2025-12-12
|
**Date**: 2025-12-12 / 2026-01-XX
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
|
|
||||||
@ -213,6 +213,22 @@ struct Pattern4Env {
|
|||||||
|
|
||||||
**Goal**: Prove P4 can use Normalized infrastructure with minimal additions.
|
**Goal**: Prove P4 can use Normalized infrastructure with minimal additions.
|
||||||
|
|
||||||
|
**実装ステータス(48-A 完了サマリ)**:
|
||||||
|
|
||||||
|
- ✅ Fixture 追加: `pattern4_continue_min.program.json`
|
||||||
|
- 「`i == 2` を `continue` でスキップする最小 P4 ループ」を Program(JSON) として用意。
|
||||||
|
- ✅ ShapeGuard 拡張:
|
||||||
|
- `NormalizedDevShape::Pattern4ContinueMinimal` を追加し、構造ベースで P4 minimal 形状を検出。
|
||||||
|
- ✅ StepScheduleBox 拡張:
|
||||||
|
- `StepKind::ContinueCheck` を追加し、評価順序を
|
||||||
|
`HeaderCond → ContinueCheck → Updates → Tail` に固定。
|
||||||
|
- ✅ Normalized lowering:
|
||||||
|
- `normalize_pattern4_continue_minimal()` を実装し、P2 正規化ロジックを 95% 再利用した continue 対応を追加。
|
||||||
|
- ✅ テスト:
|
||||||
|
- Normalized dev スイートに P4 minimal 用の比較テストを 4 本追加
|
||||||
|
(Structured→Normalized→MIR(direct) vs Structured→MIR / runner / VM bridge)。
|
||||||
|
- `cargo test --release` ベースで **939/939 tests PASS**(Phase 48-A 実装時点)。
|
||||||
|
|
||||||
**Steps**:
|
**Steps**:
|
||||||
1. **ShapeGuard**: Add `Pattern4ContinueMinimal` shape
|
1. **ShapeGuard**: Add `Pattern4ContinueMinimal` shape
|
||||||
2. **StepScheduleBox**: Add `ContinueCheck` step kind
|
2. **StepScheduleBox**: Add `ContinueCheck` step kind
|
||||||
@ -232,7 +248,11 @@ struct Pattern4Env {
|
|||||||
|
|
||||||
### Phase 48-B: _parse_object, _unescape_string (dev-only)
|
### Phase 48-B: _parse_object, _unescape_string (dev-only)
|
||||||
|
|
||||||
**Goal**: Extend to multiple carriers, string operations.
|
**Status (dev-only)**: `_parse_array` / `_parse_object` の whitespace continue ループを Normalized→MIR(direct) で比較済み。
|
||||||
|
Fixture を `jsonparser_parse_{array,object}_continue_skip_ws.program.json` として追加し、shape_guard / normalize_for_shape / direct bridge で dev 専用ルートを通す。
|
||||||
|
_unescape_string は未着手(Phase 48-C 以降)。
|
||||||
|
|
||||||
|
**Goal**: Extend to multiple carriers, string operations (unescape) after skip_ws 系が固まったら続行。
|
||||||
|
|
||||||
**Additions**:
|
**Additions**:
|
||||||
- Multi-carrier EnvLayout (if needed)
|
- Multi-carrier EnvLayout (if needed)
|
||||||
@ -319,11 +339,13 @@ count = 4 (skipped i==2, so counted 0,1,3,4)
|
|||||||
## Success Criteria
|
## Success Criteria
|
||||||
|
|
||||||
**Phase 48-A complete when**:
|
**Phase 48-A complete when**:
|
||||||
1. ✅ `test_normalized_pattern4_continue_minimal` passes (dev-only)
|
1. `test_normalized_pattern4_continue_minimal` passes (dev-only)
|
||||||
2. ✅ Structured→Normalized→MIR(direct) output matches Structured→MIR
|
2. Structured→Normalized→MIR(direct) output matches Structured→MIR
|
||||||
3. ✅ All 938+ tests still pass (no regressions)
|
3. All 938+ tests still pass (no regressions)
|
||||||
4. ✅ ShapeGuard can detect Pattern4ContinueMinimal
|
4. ShapeGuard can detect Pattern4ContinueMinimal
|
||||||
5. ✅ Documentation updated (architecture overview, CURRENT_TASK)
|
5. Documentation updated (architecture overview, CURRENT_TASK)
|
||||||
|
|
||||||
|
→ 上記 1–5 はコミット `7200309c` 時点ですべて満たされており、Phase 48-A は完了ステータスだよ。
|
||||||
|
|
||||||
**Phase 48-B complete when**:
|
**Phase 48-B complete when**:
|
||||||
1. ✅ _parse_object, _unescape_string tests pass (dev-only)
|
1. ✅ _parse_object, _unescape_string tests pass (dev-only)
|
||||||
|
|||||||
@ -0,0 +1,272 @@
|
|||||||
|
# Phase 49-SELFHOST-NORM-DEPTH2: selfhost depth2 Normalized 設計メモ(コード変更なし)
|
||||||
|
|
||||||
|
## 1. Goal & Scope
|
||||||
|
- 目標: `.hako → Program/MIR JSON → JoinIR(Structured) → Normalized → MIR → VM/LLVM` の depth2 パイプラインを selfhost でも踏めるように設計を固める。
|
||||||
|
- フォーカスする selfhost ループ(Phase 183 の棚卸しを前提に「軽い P2/P3」を 2 本に固定):
|
||||||
|
- 対象A: `selfhost_token_scan_p2.hako` / 関数 `selfhost_token_scan_p2`(P2 カウンタループ、break あり・continue なし・MethodCall なし)。
|
||||||
|
- 対象B: `selfhost_if_sum_p3.hako` / 関数 `selfhost_if_sum_p3`(P3 if-sum: sum+count、条件は Compare のみ・MethodCall なし)。
|
||||||
|
- Out of Scope(今回扱わない): P5/Trim 相当の heavy ループ、MethodCall 多用ループ、selfhost の他ループ。
|
||||||
|
|
||||||
|
## 2. 現状整理(Status Snapshot)
|
||||||
|
- Phase 183 時点: selfhost depth2 の代表ループは棚卸し済みだが、Normalized 経路や shape_guard は未整備。
|
||||||
|
- JsonParser 側: P1〜P4 代表形が canonical Normalized(Phase 41/48)で安定、StepScheduleBox/shape_guard/normalized_bridge が揃っている。
|
||||||
|
- selfhost: Program/MIR JSON までは出せるが、JoinIR→Normalized→MIR への橋は未設計。まずは P2/P3 の軽量ループに限定して設計する。
|
||||||
|
|
||||||
|
## 3. ループ→Pattern/Shape マッピング表(確定)
|
||||||
|
| Loop 名(仮) | Pattern 想定 | Normalized shape 想定 | 必要キャリア | 特記事項 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| selfhost_token_scan_p2 | P2 core(break あり/continue なし) | Pattern2 core(JsonParser skip_ws と同列) | ループ変数 + count | body-local/MethodCall なし |
|
||||||
|
| selfhost_if_sum_p3 | P3 if-sum minimal | Pattern3 if-sum minimal/multi | sum + count + ループ変数 | MethodCall なし、条件は Compare のみ |
|
||||||
|
|
||||||
|
## 4. depth2 パイプライン設計(責務メモ)
|
||||||
|
```
|
||||||
|
.hako (selfhost) → Program/MIR JSON (selfhost front-end)
|
||||||
|
→ JoinIR(Structured) (JoinIR front-end / ast_lowerer・fixtures)
|
||||||
|
→ Normalized (normalized.rs + shape_guard)
|
||||||
|
→ MIR (normalized_bridge 直 or Structured 再構成フォールバック)
|
||||||
|
→ VM/LLVM 実行
|
||||||
|
```
|
||||||
|
- selfhost front-end: Program/MIR JSON を生成(既存 Stage-1/Stage-3)。
|
||||||
|
- JoinIR front-end: Program/MIR JSON → Structured JoinModule(既存 ast_lowerer + 追加 selfhost fixtures)。
|
||||||
|
- Normalized: shape_guard で P2/P3 自動判定 → Structured→Normalized 変換。
|
||||||
|
- Bridge: canonical セット(P1〜P4)は direct Normalized→MIR を優先、非対応は Structured 再構成フォールバック。
|
||||||
|
- 実行: VM/LLVM は JsonParser と同じ経路を共用。
|
||||||
|
|
||||||
|
## 5. フィクスチャとテスト計画(dev-only)
|
||||||
|
- Program JSON:
|
||||||
|
- `docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_token_scan_p2.program.json`
|
||||||
|
- `docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3.program.json`
|
||||||
|
- Structured JoinModule helper(normalized::fixtures):
|
||||||
|
- `build_selfhost_token_scan_p2_structured_for_normalized_dev()`
|
||||||
|
- `build_selfhost_if_sum_p3_structured_for_normalized_dev()`
|
||||||
|
- テスト(normalized_dev feature 下):
|
||||||
|
- Structured→MIR vs Structured→Normalized→MIR(direct) の VM stdout 比較を追加。
|
||||||
|
- shape_guard が誤判定した場合は Fail-Fast させ、対象ループ以外はスコープ外と明示。
|
||||||
|
|
||||||
|
## 6. Out of Scope / 次フェーズ送り
|
||||||
|
- heavy selfhost ループ(MethodCall 多用、P5/Trim 依存)。
|
||||||
|
- Normalized 最適化や verifier 拡張(設計のみ、実装は Phase 50+)。
|
||||||
|
- selfhost 以外の新規ループ適用。
|
||||||
|
|
||||||
|
## 7. Next steps(49-B/50 に向けたメモ)
|
||||||
|
- normalized::fixtures に selfhost 用 helper を追加し、shape_guard に selfhost shape variant を足す。
|
||||||
|
- tests/normalized_joinir_min.rs に selfhost ループの比較テストを追加(dev-only)。
|
||||||
|
- canonical 昇格は Phase 50 以降で検討(まずは dev 正規化を通すことに専念)。
|
||||||
|
|
||||||
|
## 8. Status update(Phase 50 反映)
|
||||||
|
- 対象ループを `selfhost_token_scan_p2` / `selfhost_if_sum_p3` に確定し、normalized_dev フィクスチャと Structured helper を追加済み。
|
||||||
|
- ShapeGuard に selfhost 用 shape を追加し、Structured→Normalized→MIR(direct) の dev 比較テストで Structured 直経路と一致するところまで実装完了(canonical 化は後続フェーズ)。
|
||||||
|
|
||||||
|
## 9. Phase 51(仮)SELFHOST‑NORM‑DEV‑EXTEND
|
||||||
|
|
||||||
|
Phase 50 の selfhost P2/P3 dev Normalized の足場を使い、selfhost 側でもう少し実戦寄りの形状を dev Normalized に追加する。
|
||||||
|
canonical 昇格は別フェーズで扱い、このフェーズでは dev-only のまま固定する。
|
||||||
|
|
||||||
|
### 追加対象(dev-only)
|
||||||
|
|
||||||
|
| ループ名 | 想定パターン | ねらい | キャリア/更新 | 備考 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| selfhost_token_scan_p2_accum | P2 core(break あり/continue なし) | P2 で複数キャリア更新の安定化 | i + count + acc(acc += i, count += 1) | name ガード dev-only(構造判定が安定したら撤去) |
|
||||||
|
| selfhost_if_sum_p3_ext | P3 if-sum family | then/else 両側更新の安定化 | i + sum + count(then: sum+=i,count+=1 / else: sum+=1) | name ガード dev-only(構造判定が安定したら撤去) |
|
||||||
|
|
||||||
|
### 受け入れ条件(Phase 51)
|
||||||
|
|
||||||
|
- 上記 2 本が fixtures + shape_guard + dev 比較テストまで揃い、狙い撃ちテストが緑。
|
||||||
|
- normalized_dev 以外の挙動は不変(canonical/既定経路に影響なし)。
|
||||||
|
|
||||||
|
## 10. Phase 52(仮)SELFHOST‑SHAPE‑STRUCT‑SIGNATURE(dev-only)
|
||||||
|
|
||||||
|
Phase 50–51 で入れた selfhost shape の name ガードを、可能な範囲で「構造判定(structural signature)」へ寄せる育成フェーズ。
|
||||||
|
このフェーズでは **name ガードの全面撤去は狙わず**、構造シグネチャで一次判定 → 曖昧な場合のみ dev-only name ガードで絞る二段階 detector を導入する。
|
||||||
|
|
||||||
|
### ねらい
|
||||||
|
- selfhost P2/P3 が JsonParser/canonical 群と混線しないためのガードを、by-name 依存から段階的に縮退させる。
|
||||||
|
- 将来の selfhost ループ追加(Phase 53+)時に「構造で識別できる軸」を SSOT として固定する。
|
||||||
|
|
||||||
|
### 構造シグネチャ候補(一次判定)
|
||||||
|
|
||||||
|
#### Selfhost P2 core family(TokenScanP2 / TokenScanP2Accum)
|
||||||
|
- Structured JoinModule で `loop_step` が存在し、tail-call で自分自身に戻る P2 ブレークループであること。
|
||||||
|
- `loop_step` のパラメータ数が **3〜4**(`i` + host param + 1〜2 carriers)で、body 内に `Select` が出ないこと。
|
||||||
|
- body の主要 Compute が `Compare`(break/cond)と `Add` 系に限定され、外部/BoxCall が含まれないこと。
|
||||||
|
- **注意**: JsonParser `skip_ws_mini` と構造が近く、一次判定だけでは区別不能なケースがある。
|
||||||
|
|
||||||
|
#### Selfhost P3 if-sum family(IfSumP3 / IfSumP3Ext)
|
||||||
|
- 現状の selfhost baseline は **P2-like skeleton(normalize_pattern2_minimal 委譲)** のままなので、一次判定は「P3 の理想形(Select を含む if-sum)」を要求しない。
|
||||||
|
- `loop_step` のパラメータ数が **4**(`i` + host param + `sum` + `count`)で、break 由来の `Ge` Compare(params 間)が存在すること。
|
||||||
|
- tail-call によるループ継続を持ち、body が純粋な算術更新のみで、外部/BoxCall が含まれないこと。
|
||||||
|
|
||||||
|
### 二段階 detector 方針
|
||||||
|
1) 上記の構造シグネチャで Selfhost family candidate を一次判定
|
||||||
|
2) 一次判定が他 shape と曖昧な場合のみ、**dev-only name ガードで最終確定**
|
||||||
|
|
||||||
|
name ガードは `normalized_dev` 限定・混線防止用途に閉じ、canonical/本番経路には持ち込まない。
|
||||||
|
|
||||||
|
### 撤去条件(次フェーズ)
|
||||||
|
- Phase 53+ で selfhost 形状のバリエーションが 3〜4 本以上に増え、構造軸(carrier 数/Compare 配列/StepSchedule など)が安定したら、
|
||||||
|
P2/P3 それぞれで name ガードの適用範囲を縮小→最終撤去する。
|
||||||
|
|
||||||
|
## 11. Phase 53: SELFHOST‑NORM‑DEV‑EXPAND(dev-only バリエーション拡大)
|
||||||
|
|
||||||
|
Phase 50–51 で selfhost P2/P3 dev Normalized の足場を構築し、Phase 52 で構造シグネチャ軸(carrier 数、Compare 配列等)を導入した。
|
||||||
|
Phase 53 では **実戦寄りループを P2/P3 各 1〜2 本追加**し、構造シグネチャ軸を育成・name ガード適用範囲を縮小する。
|
||||||
|
|
||||||
|
### 追加対象ループ(dev-only)
|
||||||
|
|
||||||
|
| ループ名 | 想定パターン | ソース箇所 | キャリア/更新 | 構造的特徴 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| **selfhost_args_parse_p2** | P2 core(break あり/continue なし) | `apps/selfhost-runtime/runner.hako:20-33` | i + box_pref(文字列更新)| 1 キャリア、文字列比較多用、StringBox メソッド(indexOf/substring) |
|
||||||
|
| **selfhost_stmt_count_p3** | P3 if-sum family(多分岐) | `apps/selfhost-runtime/mir_loader.hako:76-89` | i + 9 カウンタ(r/e/l/iff/lp/br/ct/tr/ex) | 9 キャリア、多段 if-else(9 分岐)、MethodCall(st.get/str) |
|
||||||
|
|
||||||
|
### 選定理由
|
||||||
|
|
||||||
|
#### P2: selfhost_args_parse_p2
|
||||||
|
- **実戦的 P2**: コマンドライン引数パース(`--box-pref=` 等)
|
||||||
|
- **構造的差異**:
|
||||||
|
- 既存 P2(token_scan, token_scan_accum)は数値キャリアのみ
|
||||||
|
- **本ループ**: 文字列キャリア(box_pref)+ StringBox MethodCall(indexOf/substring/length)
|
||||||
|
- **構造判定軸育成**: MethodCall 出現パターン、キャリア型多様性
|
||||||
|
- **name ガード必要性**: StringBox MethodCall が入るため、JsonParser P2 との混線は低い(構造一次判定で十分分離可能)
|
||||||
|
- **dev-only name ガード**: 最終確定のみ(構造判定が主軸)
|
||||||
|
|
||||||
|
#### P3: selfhost_stmt_count_p3
|
||||||
|
- **実戦的 P3**: MIR 文種別カウント(Return/Expr/Local/If/Loop/Break/Continue/Try/Extern)
|
||||||
|
- **構造的差異**:
|
||||||
|
- 既存 P3(if_sum, if_sum_ext)は 2〜3 キャリア・単純 if-sum
|
||||||
|
- **本ループ**: **9 キャリア**(r/e/l/iff/lp/br/ct/tr/ex)+ **9 分岐**(多段 if-else)
|
||||||
|
- **構造判定軸育成**:
|
||||||
|
- キャリア数上限検証(9 は P3 範囲内か?)
|
||||||
|
- 多段 if-else パターン(Select チェーン長)
|
||||||
|
- MethodCall 出現(st.get/str)
|
||||||
|
- **name ガード必要性**: MethodCall + 9 キャリアで JsonParser P3 と明確に分離
|
||||||
|
- **dev-only name ガード**: 構造判定優先、最終確定のみ
|
||||||
|
|
||||||
|
### 構造シグネチャ軸育成方針(Phase 52 継続)
|
||||||
|
|
||||||
|
#### P2 family 構造軸(強化)
|
||||||
|
1. **キャリア数**: 1〜3 → **型多様性追加**(Integer, String, mixed)
|
||||||
|
2. **MethodCall 出現**: なし → **StringBox メソッド許容**(indexOf/substring/length)
|
||||||
|
3. **Compare 配列**: `Lt`/`Ge` 単一 → **複合条件(`Eq` 多用)**
|
||||||
|
|
||||||
|
#### P3 family 構造軸(強化)
|
||||||
|
1. **キャリア数上限**: 2〜4 → **9 キャリア検証**(P3 範囲内確定)
|
||||||
|
2. **分岐数**: 単純 if-sum → **多段 if-else(9 分岐)**
|
||||||
|
3. **MethodCall 出現**: なし → **JsonNodeBox メソッド許容**(get/str)
|
||||||
|
4. **Select チェーン長**: 構造的計測(Normalized 時の Select 深度)
|
||||||
|
|
||||||
|
### 二段階 detector 実装方針(Phase 52 継承)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// P2: selfhost_args_parse_p2 detector
|
||||||
|
fn is_selfhost_args_parse_p2(module: &JoinModule) -> bool {
|
||||||
|
// 1. 構造一次判定(優先)
|
||||||
|
if !has_p2_break_pattern(module) { return false; }
|
||||||
|
let carrier_count = count_carriers(module);
|
||||||
|
if carrier_count < 1 || carrier_count > 3 { return false; }
|
||||||
|
|
||||||
|
// StringBox MethodCall 許容(indexOf/substring)
|
||||||
|
let methodcalls = count_methodcalls(module);
|
||||||
|
if methodcalls > 5 { return false; } // 過剰な MethodCall は除外
|
||||||
|
|
||||||
|
// 2. dev-only name 最終確定(曖昧時のみ)
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
if !function_name_matches("selfhost_args_parse_p2") { return false; }
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// P3: selfhost_stmt_count_p3 detector
|
||||||
|
fn is_selfhost_stmt_count_p3(module: &JoinModule) -> bool {
|
||||||
|
// 1. 構造一次判定(優先)
|
||||||
|
if !has_p3_if_sum_pattern(module) { return false; }
|
||||||
|
let carrier_count = count_carriers(module);
|
||||||
|
if carrier_count < 2 || carrier_count > 10 { return false; } // 9 キャリア許容
|
||||||
|
|
||||||
|
// 多段 if-else パターン確認
|
||||||
|
let branch_count = count_if_else_branches(module);
|
||||||
|
if branch_count < 2 { return false; }
|
||||||
|
|
||||||
|
// JsonNodeBox MethodCall 許容(get/str)
|
||||||
|
let methodcalls = count_methodcalls(module);
|
||||||
|
if methodcalls > 10 { return false; } // 過剰な MethodCall は除外
|
||||||
|
|
||||||
|
// 2. dev-only name 最終確定(曖昧時のみ)
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
if !function_name_matches("selfhost_stmt_count_p3") { return false; }
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### name ガード適用範囲縮小条件
|
||||||
|
|
||||||
|
Phase 53 実装後、以下の条件で name ガードを撤去可能:
|
||||||
|
1. **構造軸が 5 軸以上安定**(carrier 数/型/MethodCall 数/Compare 配列/分岐数)
|
||||||
|
2. **P2/P3 各 6 本以上の dev ループ蓄積**(バリエーション十分)
|
||||||
|
3. **誤判定率 < 5%**(構造一次判定の精度検証)
|
||||||
|
|
||||||
|
現状(Phase 53 後):
|
||||||
|
- P2: 4 本(token_scan, token_scan_accum, args_parse, +1 予定)
|
||||||
|
- P3: 4 本(if_sum, if_sum_ext, stmt_count, +1 予定)
|
||||||
|
- 構造軸: 4 軸(carrier 数/型/MethodCall/Compare)
|
||||||
|
- **撤去条件未達** → name ガード継続(dev-only)
|
||||||
|
|
||||||
|
### 受け入れ基準(Phase 53)
|
||||||
|
|
||||||
|
- ✅ P2/P3 各 1〜2 本追加(合計 2〜4 本、最小 2 本)
|
||||||
|
- ✅ Program JSON + Structured builder 完備
|
||||||
|
- ✅ ShapeGuard 二段階判定実装(構造一次 + dev-only name 最終)
|
||||||
|
- ✅ dev VM 比較テスト追加(全 PASS)
|
||||||
|
- ✅ 構造軸 4〜5 本確立(carrier 数/型/MethodCall/Compare/分岐数)
|
||||||
|
- ✅ phase49 doc Phase 53 節完成(SSOT)
|
||||||
|
- ✅ 既存挙動不変(normalized_dev 以外)
|
||||||
|
|
||||||
|
### Out of Scope(Phase 54+)
|
||||||
|
|
||||||
|
- **name ガード完全撤去**: Phase 54 以降で構造軸が十分安定してから
|
||||||
|
- **canonical 昇格**: Phase 55+ で検討(dev 正規化安定後)
|
||||||
|
- **P4/P5 heavy ループ**: Phase 56+ で段階的追加
|
||||||
|
|
||||||
|
### 実装完了記録(Phase 53)
|
||||||
|
|
||||||
|
**実装日**: 2025-12-12
|
||||||
|
|
||||||
|
**追加内容**:
|
||||||
|
1. **Program JSON fixtures**: 2 個
|
||||||
|
- `selfhost_args_parse_p2.program.json` (P2: string carrier + 条件分岐)
|
||||||
|
- `selfhost_stmt_count_p3.program.json` (P3: 5 carriers + 多段 if-else)
|
||||||
|
2. **Structured builders**: 2 個(fixtures.rs)
|
||||||
|
- `build_selfhost_args_parse_p2_structured_for_normalized_dev()`
|
||||||
|
- `build_selfhost_stmt_count_p3_structured_for_normalized_dev()`
|
||||||
|
3. **ShapeGuard detectors**: 2 個(shape_guard.rs)
|
||||||
|
- `is_selfhost_args_parse_p2()` (二段階判定: P2 core family + name guard)
|
||||||
|
- `is_selfhost_stmt_count_p3()` (二段階判定: 2-10 carriers + name guard)
|
||||||
|
4. **dev VM 比較テスト**: 2 個(normalized_joinir_min.rs)
|
||||||
|
- `normalized_selfhost_args_parse_p2_vm_bridge_direct_matches_structured()`
|
||||||
|
- `normalized_selfhost_stmt_count_p3_vm_bridge_direct_matches_structured()`
|
||||||
|
|
||||||
|
**変更ファイル**:
|
||||||
|
- `phase49-selfhost-joinir-depth2-design.md` (+128 lines, Phase 53 節)
|
||||||
|
- `selfhost_args_parse_p2.program.json` (NEW, 60 lines)
|
||||||
|
- `selfhost_stmt_count_p3.program.json` (NEW, 150 lines)
|
||||||
|
- `fixtures.rs` (+48 lines, 2 builders)
|
||||||
|
- `shape_guard.rs` (+80 lines, 2 detectors + enum 拡張)
|
||||||
|
- `bridge.rs` (+8 lines, 2 shape handlers)
|
||||||
|
- `normalized.rs` (+10 lines, 2 roundtrip handlers)
|
||||||
|
- `ast_lowerer/mod.rs` (+2 lines, 2 entry point registrations)
|
||||||
|
- `normalized_joinir_min.rs` (+40 lines, 2 tests + imports)
|
||||||
|
|
||||||
|
**テスト結果**:
|
||||||
|
- ✅ normalized_dev: 40/40 PASS (2 新規テスト含む)
|
||||||
|
- ✅ lib regression: 939 PASS, 56 ignored
|
||||||
|
- ✅ 既存挙動不変確認完了
|
||||||
|
|
||||||
|
**構造軸育成成果**:
|
||||||
|
- P2 family: carrier 数 (1-3) + 型多様性(Integer/String)
|
||||||
|
- P3 family: carrier 数上限拡張(2-10)+ 多段 if-else パターン
|
||||||
|
- name ガード: 二段階判定で構造一次 + dev-only 最終確定に統一
|
||||||
|
|
||||||
|
**次フェーズ方針**(Phase 54+):
|
||||||
|
- P2/P3 各 6 本以上蓄積後に name ガード適用範囲縮小検討
|
||||||
|
- 構造軸 5 軸以上安定(carrier 数/型/Compare/分岐数/StepSchedule)
|
||||||
|
- 誤判定率 < 5% 達成で撤去条件満たす
|
||||||
Submodule docs/private updated: 023934a4fe...1e76c99f44
@ -69,8 +69,23 @@ fn resolve_function_route(func_name: &str) -> Result<FunctionRoute, String> {
|
|||||||
("jsonparser_parse_number_real", FunctionRoute::LoopFrontend),
|
("jsonparser_parse_number_real", FunctionRoute::LoopFrontend),
|
||||||
("pattern3_if_sum_multi_min", FunctionRoute::LoopFrontend),
|
("pattern3_if_sum_multi_min", FunctionRoute::LoopFrontend),
|
||||||
("jsonparser_if_sum_min", FunctionRoute::LoopFrontend),
|
("jsonparser_if_sum_min", FunctionRoute::LoopFrontend),
|
||||||
|
("selfhost_token_scan_p2", FunctionRoute::LoopFrontend),
|
||||||
|
("selfhost_token_scan_p2_accum", FunctionRoute::LoopFrontend),
|
||||||
|
("selfhost_args_parse_p2", FunctionRoute::LoopFrontend),
|
||||||
|
("selfhost_if_sum_p3", FunctionRoute::LoopFrontend),
|
||||||
|
("selfhost_if_sum_p3_ext", FunctionRoute::LoopFrontend),
|
||||||
|
("selfhost_stmt_count_p3", FunctionRoute::LoopFrontend),
|
||||||
// Phase 48-A: Pattern4 continue minimal
|
// Phase 48-A: Pattern4 continue minimal
|
||||||
("pattern4_continue_minimal", FunctionRoute::LoopFrontend),
|
("pattern4_continue_minimal", FunctionRoute::LoopFrontend),
|
||||||
|
// Phase 48-B: JsonParser continue skip_ws fixtures
|
||||||
|
(
|
||||||
|
"jsonparser_parse_array_continue_skip_ws",
|
||||||
|
FunctionRoute::LoopFrontend,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"jsonparser_parse_object_continue_skip_ws",
|
||||||
|
FunctionRoute::LoopFrontend,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
if let Some((_, route)) = TABLE.iter().find(|(name, _)| *name == func_name) {
|
if let Some((_, route)) = TABLE.iter().find(|(name, _)| *name == func_name) {
|
||||||
|
|||||||
@ -3,14 +3,15 @@
|
|||||||
//! テスト専用の極小サブセット。Pattern1 の while だけを Structured → Normalized に
|
//! テスト専用の極小サブセット。Pattern1 の while だけを Structured → Normalized に
|
||||||
//! 変換して遊ぶための足場だよ。本線の Structured→MIR 経路には影響しない。
|
//! 変換して遊ぶための足場だよ。本線の Structured→MIR 経路には影響しない。
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, HashSet};
|
||||||
|
|
||||||
use crate::mir::join_ir::{
|
use crate::mir::join_ir::{
|
||||||
BinOpKind, CompareOp, ConstValue, JoinContId, JoinFuncId, JoinFunction, JoinInst, JoinIrPhase,
|
BinOpKind, CompareOp, ConstValue, JoinContId, JoinFuncId, JoinFunction, JoinInst, JoinIrPhase,
|
||||||
JoinModule, MirLikeInst, UnaryOp,
|
JoinModule, MirLikeInst, UnaryOp,
|
||||||
};
|
};
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use std::collections::{HashMap, HashSet};
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
use std::collections::HashMap;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
|
|
||||||
@ -305,6 +306,7 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
|||||||
func_count
|
func_count
|
||||||
);
|
);
|
||||||
let param_max = {
|
let param_max = {
|
||||||
|
#[allow(unused_mut)]
|
||||||
let mut max = 3;
|
let mut max = 3;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
{
|
{
|
||||||
@ -333,6 +335,16 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
|||||||
{
|
{
|
||||||
max = max.max(6);
|
max = max.max(6);
|
||||||
}
|
}
|
||||||
|
if shapes.iter().any(|s| {
|
||||||
|
matches!(
|
||||||
|
s,
|
||||||
|
NormalizedDevShape::Pattern4ContinueMinimal
|
||||||
|
| NormalizedDevShape::JsonparserParseArrayContinueSkipWs
|
||||||
|
| NormalizedDevShape::JsonparserParseObjectContinueSkipWs
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
max = max.max(6);
|
||||||
|
}
|
||||||
if shapes.iter().any(|s| {
|
if shapes.iter().any(|s| {
|
||||||
matches!(
|
matches!(
|
||||||
s,
|
s,
|
||||||
@ -538,6 +550,42 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
|||||||
norm
|
norm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
fn normalize_pattern2_shape(
|
||||||
|
structured: &JoinModule,
|
||||||
|
target_shape: NormalizedDevShape,
|
||||||
|
) -> Result<NormalizedModule, String> {
|
||||||
|
if !structured.is_structured() {
|
||||||
|
return Err("[normalize_p2] Not structured JoinIR".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let shapes = shape_guard::supported_shapes(structured);
|
||||||
|
if !shapes.contains(&target_shape) {
|
||||||
|
return Err(format!(
|
||||||
|
"[normalize_p2] shape mismatch: expected {:?}, got {:?}",
|
||||||
|
target_shape, shapes
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(normalize_pattern2_minimal(structured))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 50: selfhost token-scan P2 を Normalized に載せる(dev-only)。
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub fn normalize_selfhost_token_scan_p2(
|
||||||
|
structured: &JoinModule,
|
||||||
|
) -> Result<NormalizedModule, String> {
|
||||||
|
normalize_pattern2_shape(structured, NormalizedDevShape::SelfhostTokenScanP2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 51: selfhost token-scan P2(accum 拡張)を Normalized に載せる(dev-only)。
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub fn normalize_selfhost_token_scan_p2_accum(
|
||||||
|
structured: &JoinModule,
|
||||||
|
) -> Result<NormalizedModule, String> {
|
||||||
|
normalize_pattern2_shape(structured, NormalizedDevShape::SelfhostTokenScanP2Accum)
|
||||||
|
}
|
||||||
|
|
||||||
/// Phase 47-A/B: Normalize Pattern3 if-sum shapes to Normalized JoinIR
|
/// Phase 47-A/B: Normalize Pattern3 if-sum shapes to Normalized JoinIR
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub fn normalize_pattern3_if_sum_minimal(
|
pub fn normalize_pattern3_if_sum_minimal(
|
||||||
@ -562,6 +610,22 @@ pub fn normalize_pattern3_if_sum_json_minimal(
|
|||||||
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::Pattern3IfSumJson)
|
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::Pattern3IfSumJson)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Phase 50: selfhost if-sum P3 を Normalized に載せる(dev-only)。
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub fn normalize_selfhost_if_sum_p3(
|
||||||
|
structured: &JoinModule,
|
||||||
|
) -> Result<NormalizedModule, String> {
|
||||||
|
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::SelfhostIfSumP3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 51: selfhost if-sum P3(ext 拡張)を Normalized に載せる(dev-only)。
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub fn normalize_selfhost_if_sum_p3_ext(
|
||||||
|
structured: &JoinModule,
|
||||||
|
) -> Result<NormalizedModule, String> {
|
||||||
|
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::SelfhostIfSumP3Ext)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
fn normalize_pattern3_if_sum_shape(
|
fn normalize_pattern3_if_sum_shape(
|
||||||
structured: &JoinModule,
|
structured: &JoinModule,
|
||||||
@ -600,24 +664,50 @@ fn normalize_pattern3_if_sum_shape(
|
|||||||
pub fn normalize_pattern4_continue_minimal(
|
pub fn normalize_pattern4_continue_minimal(
|
||||||
structured: &JoinModule,
|
structured: &JoinModule,
|
||||||
) -> Result<NormalizedModule, String> {
|
) -> Result<NormalizedModule, String> {
|
||||||
// Guard: Must be Structured and match Pattern4ContinueMinimal shape
|
normalize_pattern4_continue_shape(structured, NormalizedDevShape::Pattern4ContinueMinimal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 48-B: JsonParser _parse_array continue skip_ws を Normalized に載せる(dev-only)。
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub fn normalize_jsonparser_parse_array_continue_skip_ws(
|
||||||
|
structured: &JoinModule,
|
||||||
|
) -> Result<NormalizedModule, String> {
|
||||||
|
normalize_pattern4_continue_shape(
|
||||||
|
structured,
|
||||||
|
NormalizedDevShape::JsonparserParseArrayContinueSkipWs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 48-B: JsonParser _parse_object continue skip_ws を Normalized に載せる(dev-only)。
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub fn normalize_jsonparser_parse_object_continue_skip_ws(
|
||||||
|
structured: &JoinModule,
|
||||||
|
) -> Result<NormalizedModule, String> {
|
||||||
|
normalize_pattern4_continue_shape(
|
||||||
|
structured,
|
||||||
|
NormalizedDevShape::JsonparserParseObjectContinueSkipWs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
fn normalize_pattern4_continue_shape(
|
||||||
|
structured: &JoinModule,
|
||||||
|
target_shape: NormalizedDevShape,
|
||||||
|
) -> Result<NormalizedModule, String> {
|
||||||
if !structured.is_structured() {
|
if !structured.is_structured() {
|
||||||
return Err("[normalize_p4] Not structured JoinIR".to_string());
|
return Err("[normalize_p4] Not structured JoinIR".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use shape detection to verify P4 shape
|
// Use shape detection to verify P4 shape
|
||||||
let shapes = shape_guard::supported_shapes(&structured);
|
let shapes = shape_guard::supported_shapes(structured);
|
||||||
if !shapes.contains(&NormalizedDevShape::Pattern4ContinueMinimal) {
|
if !shapes.contains(&target_shape) {
|
||||||
return Err("[normalize_p4] Not Pattern4ContinueMinimal shape".to_string());
|
return Err(format!(
|
||||||
|
"[normalize_p4] shape mismatch: expected {:?}, got {:?}",
|
||||||
|
target_shape, shapes
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 48-A minimal: Reuse P2 normalization (P4 is reverse control flow of P2)
|
// Phase 48-B: reuse Pattern2 minimal normalizer (continue is early tail-call).
|
||||||
// P4 continue = early TailCallFn (skip processing), same infrastructure as P2 break
|
|
||||||
// TODO (Phase 48-B): Implement proper P4-specific normalization with:
|
|
||||||
// - ContinueCheck step BEFORE Updates (evaluation order difference from P2)
|
|
||||||
// - Explicit continue routing (TailCallFn with updated env)
|
|
||||||
|
|
||||||
// For now, delegate to P2 normalization (works for simple continue cases)
|
|
||||||
Ok(normalize_pattern2_minimal(structured))
|
Ok(normalize_pattern2_minimal(structured))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -994,8 +1084,20 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
|||||||
| NormalizedDevShape::JsonparserSkipWsReal
|
| NormalizedDevShape::JsonparserSkipWsReal
|
||||||
| NormalizedDevShape::JsonparserAtoiMini
|
| NormalizedDevShape::JsonparserAtoiMini
|
||||||
| NormalizedDevShape::JsonparserAtoiReal
|
| NormalizedDevShape::JsonparserAtoiReal
|
||||||
| NormalizedDevShape::JsonparserParseNumberReal => catch_unwind(AssertUnwindSafe(|| {
|
| NormalizedDevShape::JsonparserParseNumberReal => catch_unwind(AssertUnwindSafe(
|
||||||
let norm = normalize_pattern2_minimal(module);
|
|| {
|
||||||
|
let norm = normalize_pattern2_minimal(module);
|
||||||
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
NormalizedDevShape::SelfhostTokenScanP2 => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let norm =
|
||||||
|
normalize_selfhost_token_scan_p2(module).expect("selfhost P2 normalization failed");
|
||||||
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
})),
|
||||||
|
NormalizedDevShape::SelfhostTokenScanP2Accum => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let norm = normalize_selfhost_token_scan_p2_accum(module)
|
||||||
|
.expect("selfhost P2 accum normalization failed");
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
})),
|
})),
|
||||||
// Phase 47-A: P3 minimal (delegates to P2 for now, but uses proper guard)
|
// Phase 47-A: P3 minimal (delegates to P2 for now, but uses proper guard)
|
||||||
@ -1014,12 +1116,48 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
|||||||
.expect("P3 json normalization failed");
|
.expect("P3 json normalization failed");
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
})),
|
})),
|
||||||
|
NormalizedDevShape::SelfhostIfSumP3 => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let norm = normalize_selfhost_if_sum_p3(module)
|
||||||
|
.expect("selfhost P3 normalization failed");
|
||||||
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
})),
|
||||||
|
NormalizedDevShape::SelfhostIfSumP3Ext => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let norm = normalize_selfhost_if_sum_p3_ext(module)
|
||||||
|
.expect("selfhost P3 ext normalization failed");
|
||||||
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
})),
|
||||||
|
// Phase 53: selfhost P2/P3 practical variations (delegate to existing normalizers)
|
||||||
|
NormalizedDevShape::SelfhostArgsParseP2 => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let norm = normalize_pattern2_minimal(module);
|
||||||
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
})),
|
||||||
|
NormalizedDevShape::SelfhostStmtCountP3 => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let norm = normalize_selfhost_if_sum_p3_ext(module)
|
||||||
|
.expect("selfhost stmt_count P3 normalization failed");
|
||||||
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
})),
|
||||||
// Phase 48-A: P4 minimal (delegates to P2 for now, but uses proper guard)
|
// Phase 48-A: P4 minimal (delegates to P2 for now, but uses proper guard)
|
||||||
NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| {
|
||||||
let norm = normalize_pattern4_continue_minimal(module)
|
let norm = normalize_pattern4_continue_minimal(module)
|
||||||
.expect("P4 normalization failed");
|
.expect("P4 normalization failed");
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
})),
|
})),
|
||||||
|
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => {
|
||||||
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let norm =
|
||||||
|
normalize_jsonparser_parse_array_continue_skip_ws(module)
|
||||||
|
.expect("P4 array normalization failed");
|
||||||
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => {
|
||||||
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let norm =
|
||||||
|
normalize_jsonparser_parse_object_continue_skip_ws(module)
|
||||||
|
.expect("P4 object normalization failed");
|
||||||
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
}))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match attempt {
|
match attempt {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#![cfg(feature = "normalized_dev")]
|
#![cfg(feature = "normalized_dev")]
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use std::cell::Cell;
|
||||||
use std::sync::{Mutex, MutexGuard};
|
use std::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
/// RAII guard for normalized_dev env toggling (NYASH_JOINIR_NORMALIZED_DEV_RUN).
|
/// RAII guard for normalized_dev env toggling (NYASH_JOINIR_NORMALIZED_DEV_RUN).
|
||||||
@ -17,6 +18,29 @@ struct EnvState {
|
|||||||
static NORMALIZED_ENV_STATE: Lazy<Mutex<EnvState>> = Lazy::new(|| Mutex::new(EnvState::default()));
|
static NORMALIZED_ENV_STATE: Lazy<Mutex<EnvState>> = Lazy::new(|| Mutex::new(EnvState::default()));
|
||||||
static NORMALIZED_TEST_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
static NORMALIZED_TEST_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
// Per-thread depth counter for test_ctx() to allow re-entrant dev env toggling
|
||||||
|
// without self-deadlocking on NORMALIZED_TEST_LOCK.
|
||||||
|
static IN_NORMALIZED_TEST_CTX: Cell<u32> = Cell::new(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_test_ctx() {
|
||||||
|
IN_NORMALIZED_TEST_CTX.with(|c| c.set(c.get().saturating_add(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_test_ctx() {
|
||||||
|
IN_NORMALIZED_TEST_CTX.with(|c| {
|
||||||
|
let v = c.get();
|
||||||
|
if v > 0 {
|
||||||
|
c.set(v - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_test_ctx() -> bool {
|
||||||
|
IN_NORMALIZED_TEST_CTX.with(|c| c.get() > 0)
|
||||||
|
}
|
||||||
|
|
||||||
impl NormalizedDevEnvGuard {
|
impl NormalizedDevEnvGuard {
|
||||||
pub fn new(enabled: bool) -> Self {
|
pub fn new(enabled: bool) -> Self {
|
||||||
let mut state = NORMALIZED_ENV_STATE
|
let mut state = NORMALIZED_ENV_STATE
|
||||||
@ -73,6 +97,7 @@ pub struct NormalizedTestContext<'a> {
|
|||||||
|
|
||||||
impl<'a> NormalizedTestContext<'a> {
|
impl<'a> NormalizedTestContext<'a> {
|
||||||
fn new(lock: MutexGuard<'a, ()>) -> Self {
|
fn new(lock: MutexGuard<'a, ()>) -> Self {
|
||||||
|
enter_test_ctx();
|
||||||
let env_guard = NormalizedDevEnvGuard::new(true);
|
let env_guard = NormalizedDevEnvGuard::new(true);
|
||||||
NormalizedTestContext {
|
NormalizedTestContext {
|
||||||
_lock: lock,
|
_lock: lock,
|
||||||
@ -81,6 +106,12 @@ impl<'a> NormalizedTestContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for NormalizedTestContext<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
exit_test_ctx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// テストで使う共通ガード。
|
/// テストで使う共通ガード。
|
||||||
pub fn test_ctx() -> NormalizedTestContext<'static> {
|
pub fn test_ctx() -> NormalizedTestContext<'static> {
|
||||||
let lock = NORMALIZED_TEST_LOCK
|
let lock = NORMALIZED_TEST_LOCK
|
||||||
@ -94,8 +125,13 @@ pub fn with_dev_env<F, R>(f: F) -> R
|
|||||||
where
|
where
|
||||||
F: FnOnce() -> R,
|
F: FnOnce() -> R,
|
||||||
{
|
{
|
||||||
let _ctx = test_ctx();
|
if in_test_ctx() {
|
||||||
f()
|
let _env_guard = NormalizedDevEnvGuard::new(true);
|
||||||
|
f()
|
||||||
|
} else {
|
||||||
|
let _ctx = test_ctx();
|
||||||
|
f()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// env が既に ON のときはそのまま、OFF のときだけ with_dev_env を噛ませる。
|
/// env が既に ON のときはそのまま、OFF のときだけ with_dev_env を噛ませる。
|
||||||
@ -105,6 +141,9 @@ where
|
|||||||
{
|
{
|
||||||
if normalized_dev_enabled() {
|
if normalized_dev_enabled() {
|
||||||
f()
|
f()
|
||||||
|
} else if in_test_ctx() {
|
||||||
|
let _env_guard = NormalizedDevEnvGuard::new(true);
|
||||||
|
f()
|
||||||
} else {
|
} else {
|
||||||
with_dev_env(f)
|
with_dev_env(f)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -203,6 +203,54 @@ pub fn build_jsonparser_parse_number_real_structured_for_normalized_dev() -> Joi
|
|||||||
module
|
module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// selfhost token-scan 系の P2 ループを Structured で組み立てるヘルパー。
|
||||||
|
///
|
||||||
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_token_scan_p2.program.json
|
||||||
|
pub fn build_selfhost_token_scan_p2_structured_for_normalized_dev() -> JoinModule {
|
||||||
|
const FIXTURE: &str = include_str!(
|
||||||
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_token_scan_p2.program.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_json: serde_json::Value =
|
||||||
|
serde_json::from_str(FIXTURE).expect("selfhost token_scan P2 fixture should be valid JSON");
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev] selfhost_token_scan_p2 structured module: {:#?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|
||||||
|
/// selfhost token-scan 系の P2 ループ(accum 拡張)を Structured で組み立てるヘルパー。
|
||||||
|
///
|
||||||
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_token_scan_p2_accum.program.json
|
||||||
|
pub fn build_selfhost_token_scan_p2_accum_structured_for_normalized_dev() -> JoinModule {
|
||||||
|
const FIXTURE: &str = include_str!(
|
||||||
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_token_scan_p2_accum.program.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
||||||
|
.expect("selfhost token_scan P2 accum fixture should be valid JSON");
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev] selfhost_token_scan_p2_accum structured module: {:#?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|
||||||
/// Phase 47-B: Pattern3 if-sum (multi carrier) を Structured で組み立てるヘルパー。
|
/// Phase 47-B: Pattern3 if-sum (multi carrier) を Structured で組み立てるヘルパー。
|
||||||
///
|
///
|
||||||
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/pattern3_if_sum_multi_min.program.json
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/pattern3_if_sum_multi_min.program.json
|
||||||
@ -442,6 +490,102 @@ pub fn build_pattern3_json_if_sum_min_structured_for_normalized_dev() -> JoinMod
|
|||||||
module
|
module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// selfhost if-sum P3 を Structured で組み立てるヘルパー。
|
||||||
|
///
|
||||||
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3.program.json
|
||||||
|
pub fn build_selfhost_if_sum_p3_structured_for_normalized_dev() -> JoinModule {
|
||||||
|
const FIXTURE: &str = include_str!(
|
||||||
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3.program.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_json: serde_json::Value =
|
||||||
|
serde_json::from_str(FIXTURE).expect("selfhost if_sum P3 fixture should be valid JSON");
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev] selfhost_if_sum_p3 structured module: {:#?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|
||||||
|
/// selfhost if-sum P3(ext 拡張)を Structured で組み立てるヘルパー。
|
||||||
|
///
|
||||||
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3_ext.program.json
|
||||||
|
pub fn build_selfhost_if_sum_p3_ext_structured_for_normalized_dev() -> JoinModule {
|
||||||
|
const FIXTURE: &str = include_str!(
|
||||||
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3_ext.program.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
||||||
|
.expect("selfhost if_sum P3 ext fixture should be valid JSON");
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev] selfhost_if_sum_p3_ext structured module: {:#?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|
||||||
|
/// selfhost args-parse P2(Phase 53)を Structured で組み立てるヘルパー。
|
||||||
|
///
|
||||||
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_args_parse_p2.program.json
|
||||||
|
pub fn build_selfhost_args_parse_p2_structured_for_normalized_dev() -> JoinModule {
|
||||||
|
const FIXTURE: &str = include_str!(
|
||||||
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_args_parse_p2.program.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
||||||
|
.expect("selfhost args_parse P2 fixture should be valid JSON");
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev] selfhost_args_parse_p2 structured module: {:#?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|
||||||
|
/// selfhost stmt-count P3(Phase 53)を Structured で組み立てるヘルパー。
|
||||||
|
///
|
||||||
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_stmt_count_p3.program.json
|
||||||
|
pub fn build_selfhost_stmt_count_p3_structured_for_normalized_dev() -> JoinModule {
|
||||||
|
const FIXTURE: &str = include_str!(
|
||||||
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_stmt_count_p3.program.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
||||||
|
.expect("selfhost stmt_count P3 fixture should be valid JSON");
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev] selfhost_stmt_count_p3 structured module: {:#?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|
||||||
/// JsonParser _atoi 相当のミニ P2 ループを Structured で組み立てるヘルパー。
|
/// JsonParser _atoi 相当のミニ P2 ループを Structured で組み立てるヘルパー。
|
||||||
///
|
///
|
||||||
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_atoi_mini.program.json
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_atoi_mini.program.json
|
||||||
@ -602,12 +746,62 @@ pub fn build_pattern4_continue_min_structured_for_normalized_dev() -> JoinModule
|
|||||||
module
|
module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// JsonParser _parse_array の whitespace continue ループを Structured で組み立てるヘルパー(dev-only)。
|
||||||
|
///
|
||||||
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_array_continue_skip_ws.program.json
|
||||||
|
pub fn build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev() -> JoinModule {
|
||||||
|
const FIXTURE: &str = include_str!(
|
||||||
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_array_continue_skip_ws.program.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
||||||
|
.expect("jsonparser_parse_array_continue_skip_ws fixture should be valid JSON");
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev] jsonparser_parse_array_continue_skip_ws structured module: {:#?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JsonParser _parse_object の whitespace continue ループを Structured で組み立てるヘルパー(dev-only)。
|
||||||
|
///
|
||||||
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json
|
||||||
|
pub fn build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev() -> JoinModule {
|
||||||
|
const FIXTURE: &str = include_str!(
|
||||||
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
||||||
|
.expect("jsonparser_parse_object_continue_skip_ws fixture should be valid JSON");
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev] jsonparser_parse_object_continue_skip_ws structured module: {:#?}",
|
||||||
|
module
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module
|
||||||
|
}
|
||||||
|
|
||||||
/// まとめて import したいとき用のプレリュード。
|
/// まとめて import したいとき用のプレリュード。
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::{
|
pub use super::{
|
||||||
build_jsonparser_atoi_real_structured_for_normalized_dev,
|
build_jsonparser_atoi_real_structured_for_normalized_dev,
|
||||||
build_jsonparser_atoi_structured_for_normalized_dev,
|
build_jsonparser_atoi_structured_for_normalized_dev,
|
||||||
|
build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev,
|
||||||
build_jsonparser_parse_number_real_structured_for_normalized_dev,
|
build_jsonparser_parse_number_real_structured_for_normalized_dev,
|
||||||
|
build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev,
|
||||||
build_jsonparser_skip_ws_real_structured_for_normalized_dev,
|
build_jsonparser_skip_ws_real_structured_for_normalized_dev,
|
||||||
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
||||||
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
|
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
|
||||||
@ -615,5 +809,9 @@ pub mod prelude {
|
|||||||
build_pattern3_if_sum_multi_min_structured_for_normalized_dev,
|
build_pattern3_if_sum_multi_min_structured_for_normalized_dev,
|
||||||
build_pattern3_json_if_sum_min_structured_for_normalized_dev,
|
build_pattern3_json_if_sum_min_structured_for_normalized_dev,
|
||||||
build_pattern4_continue_min_structured_for_normalized_dev,
|
build_pattern4_continue_min_structured_for_normalized_dev,
|
||||||
|
build_selfhost_if_sum_p3_ext_structured_for_normalized_dev,
|
||||||
|
build_selfhost_if_sum_p3_structured_for_normalized_dev,
|
||||||
|
build_selfhost_token_scan_p2_accum_structured_for_normalized_dev,
|
||||||
|
build_selfhost_token_scan_p2_structured_for_normalized_dev,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,15 @@ pub enum ShapeCapabilityKind {
|
|||||||
/// P3 If-Sum family (minimal/multi/json)
|
/// P3 If-Sum family (minimal/multi/json)
|
||||||
P3IfSum,
|
P3IfSum,
|
||||||
|
|
||||||
|
/// P4 Continue (skip whitespace) family
|
||||||
|
P4ContinueSkipWs,
|
||||||
|
|
||||||
|
/// Selfhost P2 core (token scan)
|
||||||
|
SelfhostP2Core,
|
||||||
|
|
||||||
|
/// Selfhost P3 if-sum family
|
||||||
|
SelfhostP3IfSum,
|
||||||
|
|
||||||
// Future: Other P2 patterns
|
// Future: Other P2 patterns
|
||||||
// P2MidAtOfLoop,
|
// P2MidAtOfLoop,
|
||||||
// P2HeavyString,
|
// P2HeavyString,
|
||||||
@ -60,6 +69,18 @@ pub enum NormalizedDevShape {
|
|||||||
Pattern3IfSumJson,
|
Pattern3IfSumJson,
|
||||||
// Phase 48-A: Pattern4 (continue) minimal
|
// Phase 48-A: Pattern4 (continue) minimal
|
||||||
Pattern4ContinueMinimal,
|
Pattern4ContinueMinimal,
|
||||||
|
// Phase 48-B: Pattern4 (continue) JsonParser skip_ws (array/object)
|
||||||
|
JsonparserParseArrayContinueSkipWs,
|
||||||
|
JsonparserParseObjectContinueSkipWs,
|
||||||
|
// Phase 50: selfhost P2/P3 dev shapes
|
||||||
|
SelfhostTokenScanP2,
|
||||||
|
SelfhostIfSumP3,
|
||||||
|
// Phase 51: selfhost P2/P3 dev extensions
|
||||||
|
SelfhostTokenScanP2Accum,
|
||||||
|
SelfhostIfSumP3Ext,
|
||||||
|
// Phase 53: selfhost P2/P3 practical variations
|
||||||
|
SelfhostArgsParseP2,
|
||||||
|
SelfhostStmtCountP3,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Detector = fn(&JoinModule) -> bool;
|
type Detector = fn(&JoinModule) -> bool;
|
||||||
@ -87,6 +108,14 @@ const SHAPE_DETECTORS: &[(NormalizedDevShape, Detector)] = &[
|
|||||||
NormalizedDevShape::JsonparserParseNumberReal,
|
NormalizedDevShape::JsonparserParseNumberReal,
|
||||||
detectors::is_jsonparser_parse_number_real,
|
detectors::is_jsonparser_parse_number_real,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
NormalizedDevShape::SelfhostTokenScanP2,
|
||||||
|
detectors::is_selfhost_token_scan_p2,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NormalizedDevShape::SelfhostTokenScanP2Accum,
|
||||||
|
detectors::is_selfhost_token_scan_p2_accum,
|
||||||
|
),
|
||||||
// Phase 47-A: Pattern3 if-sum minimal
|
// Phase 47-A: Pattern3 if-sum minimal
|
||||||
(
|
(
|
||||||
NormalizedDevShape::Pattern3IfSumMinimal,
|
NormalizedDevShape::Pattern3IfSumMinimal,
|
||||||
@ -105,6 +134,31 @@ const SHAPE_DETECTORS: &[(NormalizedDevShape, Detector)] = &[
|
|||||||
NormalizedDevShape::Pattern4ContinueMinimal,
|
NormalizedDevShape::Pattern4ContinueMinimal,
|
||||||
detectors::is_pattern4_continue_minimal,
|
detectors::is_pattern4_continue_minimal,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
NormalizedDevShape::JsonparserParseArrayContinueSkipWs,
|
||||||
|
detectors::is_jsonparser_parse_array_continue_skip_ws,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NormalizedDevShape::JsonparserParseObjectContinueSkipWs,
|
||||||
|
detectors::is_jsonparser_parse_object_continue_skip_ws,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NormalizedDevShape::SelfhostIfSumP3,
|
||||||
|
detectors::is_selfhost_if_sum_p3,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NormalizedDevShape::SelfhostIfSumP3Ext,
|
||||||
|
detectors::is_selfhost_if_sum_p3_ext,
|
||||||
|
),
|
||||||
|
// Phase 53: selfhost P2/P3 practical variations
|
||||||
|
(
|
||||||
|
NormalizedDevShape::SelfhostArgsParseP2,
|
||||||
|
detectors::is_selfhost_args_parse_p2,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NormalizedDevShape::SelfhostStmtCountP3,
|
||||||
|
detectors::is_selfhost_stmt_count_p3,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// direct ブリッジで扱う shape(dev 限定)。
|
/// direct ブリッジで扱う shape(dev 限定)。
|
||||||
@ -134,21 +188,29 @@ pub fn capability_for_shape(shape: &NormalizedDevShape) -> ShapeCapability {
|
|||||||
Pattern1Mini => P2CoreSimple, // Also core simple pattern
|
Pattern1Mini => P2CoreSimple, // Also core simple pattern
|
||||||
// Phase 47-B: P3 if-sum family
|
// Phase 47-B: P3 if-sum family
|
||||||
Pattern3IfSumMinimal | Pattern3IfSumMulti | Pattern3IfSumJson => P3IfSum,
|
Pattern3IfSumMinimal | Pattern3IfSumMulti | Pattern3IfSumJson => P3IfSum,
|
||||||
// Phase 48-A: P4 minimal maps to P2CoreSimple for now (future: P4CoreSimple)
|
// Phase 48-A/B: P4 continue family
|
||||||
Pattern4ContinueMinimal => P2CoreSimple,
|
Pattern4ContinueMinimal
|
||||||
|
| JsonparserParseArrayContinueSkipWs
|
||||||
|
| JsonparserParseObjectContinueSkipWs => P4ContinueSkipWs,
|
||||||
|
// Phase 50: selfhost P2/P3 dev shapes
|
||||||
|
SelfhostTokenScanP2 | SelfhostTokenScanP2Accum => SelfhostP2Core,
|
||||||
|
SelfhostIfSumP3 | SelfhostIfSumP3Ext => SelfhostP3IfSum,
|
||||||
|
// Phase 53: selfhost P2/P3 practical variations
|
||||||
|
SelfhostArgsParseP2 => SelfhostP2Core,
|
||||||
|
SelfhostStmtCountP3 => SelfhostP3IfSum,
|
||||||
};
|
};
|
||||||
|
|
||||||
ShapeCapability::new(kind)
|
ShapeCapability::new(kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 46: Canonical shapes that ALWAYS use Normalized→MIR(direct)
|
/// Phase 46+: Canonical shapes that ALWAYS use Normalized→MIR(direct)
|
||||||
/// regardless of feature flags or mode.
|
/// regardless of feature flags or mode.
|
||||||
///
|
///
|
||||||
/// Canonical set (Phase 46):
|
/// Canonical set (Phase 48-C):
|
||||||
/// - P2-Core: Pattern2Mini, JsonparserSkipWsMini, JsonparserSkipWsReal, JsonparserAtoiMini
|
/// - P2-Core: Pattern2Mini, JsonparserSkipWsMini, JsonparserSkipWsReal, JsonparserAtoiMini
|
||||||
/// - P2-Mid: JsonparserAtoiReal, JsonparserParseNumberReal
|
/// - P2-Mid: JsonparserAtoiReal, JsonparserParseNumberReal
|
||||||
///
|
/// - P3: Pattern3 If-sum minimal/multi/json
|
||||||
/// P3/P4 patterns are NOT canonical (deferred to NORM-P3/NORM-P4 phases).
|
/// - P4: Pattern4 continue minimal + JsonParser skip_ws (array/object)
|
||||||
pub fn is_canonical_shape(shape: &NormalizedDevShape) -> bool {
|
pub fn is_canonical_shape(shape: &NormalizedDevShape) -> bool {
|
||||||
use NormalizedDevShape::*;
|
use NormalizedDevShape::*;
|
||||||
matches!(
|
matches!(
|
||||||
@ -164,6 +226,10 @@ pub fn is_canonical_shape(shape: &NormalizedDevShape) -> bool {
|
|||||||
| Pattern3IfSumMinimal
|
| Pattern3IfSumMinimal
|
||||||
| Pattern3IfSumMulti
|
| Pattern3IfSumMulti
|
||||||
| Pattern3IfSumJson
|
| Pattern3IfSumJson
|
||||||
|
// Phase 48-C: P4 continue canonical set
|
||||||
|
| Pattern4ContinueMinimal
|
||||||
|
| JsonparserParseArrayContinueSkipWs
|
||||||
|
| JsonparserParseObjectContinueSkipWs
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +241,14 @@ pub fn is_p2_core_capability(cap: &ShapeCapability) -> bool {
|
|||||||
use ShapeCapabilityKind::*;
|
use ShapeCapabilityKind::*;
|
||||||
matches!(
|
matches!(
|
||||||
cap.kind,
|
cap.kind,
|
||||||
P2CoreSimple | P2CoreSkipWs | P2CoreAtoi | P2MidParseNumber | P3IfSum
|
P2CoreSimple
|
||||||
|
| P2CoreSkipWs
|
||||||
|
| P2CoreAtoi
|
||||||
|
| P2MidParseNumber
|
||||||
|
| P3IfSum
|
||||||
|
| P4ContinueSkipWs
|
||||||
|
| SelfhostP2Core
|
||||||
|
| SelfhostP3IfSum
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +291,25 @@ fn detect_shapes(module: &JoinModule) -> Vec<NormalizedDevShape> {
|
|||||||
shapes.retain(|s| *s != NormalizedDevShape::Pattern1Mini);
|
shapes.retain(|s| *s != NormalizedDevShape::Pattern1Mini);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// selfhost shapesは canonical P2/P3 の generic 判定から分離する
|
||||||
|
if shapes.contains(&NormalizedDevShape::SelfhostTokenScanP2)
|
||||||
|
|| shapes.contains(&NormalizedDevShape::SelfhostTokenScanP2Accum)
|
||||||
|
{
|
||||||
|
shapes.retain(|s| *s != NormalizedDevShape::Pattern2Mini);
|
||||||
|
}
|
||||||
|
if shapes.contains(&NormalizedDevShape::SelfhostIfSumP3)
|
||||||
|
|| shapes.contains(&NormalizedDevShape::SelfhostIfSumP3Ext)
|
||||||
|
{
|
||||||
|
shapes.retain(|s| {
|
||||||
|
!matches!(
|
||||||
|
s,
|
||||||
|
NormalizedDevShape::Pattern3IfSumMinimal
|
||||||
|
| NormalizedDevShape::Pattern3IfSumMulti
|
||||||
|
| NormalizedDevShape::Pattern3IfSumJson
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
shapes
|
shapes
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,6 +469,124 @@ mod detectors {
|
|||||||
.any(|f| f.name == "jsonparser_parse_number_real")
|
.any(|f| f.name == "jsonparser_parse_number_real")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn name_guard_exact(module: &JoinModule, expected_name: &str) -> bool {
|
||||||
|
module.functions.values().any(|f| f.name == expected_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 52: Selfhost P2 core family structure signature (dev-only).
|
||||||
|
///
|
||||||
|
/// This is intentionally narrow to avoid swallowing generic P2 shapes:
|
||||||
|
/// - loop_step params: 3..=4 (i + host + 1..2 carriers)
|
||||||
|
/// - P2 break-loop skeleton (cond jump + tail call)
|
||||||
|
/// - no Select / BoxCall in body
|
||||||
|
pub(super) fn is_selfhost_p2_core_family_candidate(module: &JoinModule) -> bool {
|
||||||
|
if !module.is_structured() || module.functions.len() != 3 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let loop_func = match find_loop_step(module) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
if !(3..=4).contains(&loop_func.params.len()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_cond_jump = loop_func
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.any(|inst| matches!(inst, JoinInst::Jump { cond: Some(_), .. }));
|
||||||
|
let has_tail_call = loop_func
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. }));
|
||||||
|
|
||||||
|
let has_select = loop_func.body.iter().any(|inst| match inst {
|
||||||
|
JoinInst::Select { .. } => true,
|
||||||
|
JoinInst::Compute(mir_inst) => matches!(
|
||||||
|
mir_inst,
|
||||||
|
crate::mir::join_ir::MirLikeInst::Select { .. }
|
||||||
|
),
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let has_boxcall = loop_func.body.iter().any(|inst| match inst {
|
||||||
|
JoinInst::Compute(mir_inst) => matches!(
|
||||||
|
mir_inst,
|
||||||
|
crate::mir::join_ir::MirLikeInst::BoxCall { .. }
|
||||||
|
),
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
has_cond_jump && has_tail_call && !has_select && !has_boxcall
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 52: Selfhost P3 if-sum family structure signature (dev-only).
|
||||||
|
///
|
||||||
|
/// Note: current selfhost baseline is still P2-like (normalize_pattern2_minimal),
|
||||||
|
/// so the signature avoids requiring Select and focuses on the explicit break-if.
|
||||||
|
///
|
||||||
|
/// Distinguish selfhost P3 from canonical P3 by requiring:
|
||||||
|
/// - loop_step params == 4 (i + host + sum + count)
|
||||||
|
/// - an explicit Ge compare between params (break-if)
|
||||||
|
/// - P2/P3 loop skeleton (cond jump + tail call)
|
||||||
|
/// - no BoxCall in body
|
||||||
|
pub(super) fn is_selfhost_p3_if_sum_family_candidate(module: &JoinModule) -> bool {
|
||||||
|
if !module.is_structured() || module.functions.len() != 3 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let loop_step = match find_loop_step(module) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
if loop_step.params.len() != 4 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_cond_jump = loop_step
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.any(|inst| matches!(inst, JoinInst::Jump { cond: Some(_), .. }));
|
||||||
|
let has_tail_call = loop_step
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. }));
|
||||||
|
|
||||||
|
let param_set: std::collections::BTreeSet<_> =
|
||||||
|
loop_step.params.iter().copied().collect();
|
||||||
|
|
||||||
|
let has_ge_compare_between_params = loop_step.body.iter().any(|inst| match inst {
|
||||||
|
JoinInst::Compute(mir_inst) => match mir_inst {
|
||||||
|
crate::mir::join_ir::MirLikeInst::Compare { op, lhs, rhs, .. } => {
|
||||||
|
*op == crate::mir::join_ir::CompareOp::Ge
|
||||||
|
&& param_set.contains(lhs)
|
||||||
|
&& param_set.contains(rhs)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let has_boxcall = loop_step.body.iter().any(|inst| match inst {
|
||||||
|
JoinInst::Compute(mir_inst) => matches!(
|
||||||
|
mir_inst,
|
||||||
|
crate::mir::join_ir::MirLikeInst::BoxCall { .. }
|
||||||
|
),
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
has_cond_jump && has_tail_call && has_ge_compare_between_params && !has_boxcall
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_selfhost_token_scan_p2(module: &JoinModule) -> bool {
|
||||||
|
is_selfhost_p2_core_family_candidate(module)
|
||||||
|
&& name_guard_exact(module, "selfhost_token_scan_p2")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_selfhost_token_scan_p2_accum(module: &JoinModule) -> bool {
|
||||||
|
is_selfhost_p2_core_family_candidate(module)
|
||||||
|
&& name_guard_exact(module, "selfhost_token_scan_p2_accum")
|
||||||
|
}
|
||||||
|
|
||||||
/// Phase 47-A: Check if module matches Pattern3 if-sum minimal shape
|
/// Phase 47-A: Check if module matches Pattern3 if-sum minimal shape
|
||||||
pub(crate) fn is_pattern3_if_sum_minimal(module: &JoinModule) -> bool {
|
pub(crate) fn is_pattern3_if_sum_minimal(module: &JoinModule) -> bool {
|
||||||
// Structure-based detection (avoid name-based heuristics)
|
// Structure-based detection (avoid name-based heuristics)
|
||||||
@ -425,6 +635,73 @@ mod detectors {
|
|||||||
has_compare && has_select && has_tail_call && reasonable_param_count
|
has_compare && has_select && has_tail_call && reasonable_param_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_selfhost_if_sum_p3(module: &JoinModule) -> bool {
|
||||||
|
is_selfhost_p3_if_sum_family_candidate(module)
|
||||||
|
&& name_guard_exact(module, "selfhost_if_sum_p3")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_selfhost_if_sum_p3_ext(module: &JoinModule) -> bool {
|
||||||
|
is_selfhost_p3_if_sum_family_candidate(module)
|
||||||
|
&& name_guard_exact(module, "selfhost_if_sum_p3_ext")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 53: selfhost args-parse P2 detector (practical variation with string carrier)
|
||||||
|
///
|
||||||
|
/// Two-stage detection:
|
||||||
|
/// 1. Structural primary check (P2 break pattern, 1-3 carriers)
|
||||||
|
/// 2. dev-only name guard for final confirmation (ambiguity resolver)
|
||||||
|
pub(crate) fn is_selfhost_args_parse_p2(module: &JoinModule) -> bool {
|
||||||
|
// 1. Structural primary check (P2 core family)
|
||||||
|
if !is_selfhost_p2_core_family_candidate(module) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. dev-only name guard for final confirmation
|
||||||
|
name_guard_exact(module, "selfhost_args_parse_p2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 53: selfhost stmt-count P3 detector (practical variation with multi-branch if-else)
|
||||||
|
///
|
||||||
|
/// Two-stage detection:
|
||||||
|
/// 1. Structural primary check (P3 if-sum pattern, 2-10 carriers, multi-branch)
|
||||||
|
/// 2. dev-only name guard for final confirmation (ambiguity resolver)
|
||||||
|
pub(crate) fn is_selfhost_stmt_count_p3(module: &JoinModule) -> bool {
|
||||||
|
// 1. Structural primary check
|
||||||
|
if !module.is_structured() || module.functions.len() != 3 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let loop_step = match find_loop_step(module) {
|
||||||
|
Some(f) => f,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allow 2-10 carriers (5 statement counters: r/e/l/iff/lp + i)
|
||||||
|
let carrier_count = loop_step.params.len();
|
||||||
|
if !(2..=10).contains(&carrier_count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must have conditional jump (break pattern)
|
||||||
|
let has_cond_jump = loop_step
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.any(|inst| matches!(inst, JoinInst::Jump { cond: Some(_), .. }));
|
||||||
|
|
||||||
|
// Must have tail call (loop continuation)
|
||||||
|
let has_tail_call = loop_step
|
||||||
|
.body
|
||||||
|
.iter()
|
||||||
|
.any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. }));
|
||||||
|
|
||||||
|
if !has_cond_jump || !has_tail_call {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. dev-only name guard for final confirmation
|
||||||
|
name_guard_exact(module, "selfhost_stmt_count_p3")
|
||||||
|
}
|
||||||
|
|
||||||
/// Phase 47-B: P3 if-sum (multi-carrier) shape detector
|
/// Phase 47-B: P3 if-sum (multi-carrier) shape detector
|
||||||
pub(crate) fn is_pattern3_if_sum_multi(module: &JoinModule) -> bool {
|
pub(crate) fn is_pattern3_if_sum_multi(module: &JoinModule) -> bool {
|
||||||
if !is_pattern3_if_sum_minimal(module) {
|
if !is_pattern3_if_sum_minimal(module) {
|
||||||
@ -489,6 +766,22 @@ mod detectors {
|
|||||||
has_compare && has_conditional_flow && reasonable_param_count
|
has_compare && has_conditional_flow && reasonable_param_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_jsonparser_parse_array_continue_skip_ws(module: &JoinModule) -> bool {
|
||||||
|
is_pattern4_continue_minimal(module)
|
||||||
|
&& module
|
||||||
|
.functions
|
||||||
|
.values()
|
||||||
|
.any(|f| f.name == "jsonparser_parse_array_continue_skip_ws")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_jsonparser_parse_object_continue_skip_ws(module: &JoinModule) -> bool {
|
||||||
|
is_pattern4_continue_minimal(module)
|
||||||
|
&& module
|
||||||
|
.functions
|
||||||
|
.values()
|
||||||
|
.any(|f| f.name == "jsonparser_parse_object_continue_skip_ws")
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn find_loop_step(module: &JoinModule) -> Option<&JoinFunction> {
|
pub(super) fn find_loop_step(module: &JoinModule) -> Option<&JoinFunction> {
|
||||||
module
|
module
|
||||||
.functions
|
.functions
|
||||||
@ -532,6 +825,158 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
#[test]
|
||||||
|
fn test_selfhost_p2_core_structural_candidate_signature() {
|
||||||
|
use crate::mir::join_ir::normalized::fixtures::{
|
||||||
|
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
||||||
|
build_pattern2_minimal_structured,
|
||||||
|
build_selfhost_token_scan_p2_accum_structured_for_normalized_dev,
|
||||||
|
build_selfhost_token_scan_p2_structured_for_normalized_dev,
|
||||||
|
};
|
||||||
|
|
||||||
|
let selfhost_p2 = build_selfhost_token_scan_p2_structured_for_normalized_dev();
|
||||||
|
let selfhost_p2_accum = build_selfhost_token_scan_p2_accum_structured_for_normalized_dev();
|
||||||
|
let json_p2 = build_jsonparser_skip_ws_structured_for_normalized_dev();
|
||||||
|
let canonical_p2_min = build_pattern2_minimal_structured();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
detectors::is_selfhost_p2_core_family_candidate(&selfhost_p2),
|
||||||
|
"selfhost_token_scan_p2 should match structural candidate"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
detectors::is_selfhost_p2_core_family_candidate(&selfhost_p2_accum),
|
||||||
|
"selfhost_token_scan_p2_accum should match structural candidate"
|
||||||
|
);
|
||||||
|
// Structural signature is intentionally ambiguous with JsonParser P2-mini family.
|
||||||
|
assert!(
|
||||||
|
detectors::is_selfhost_p2_core_family_candidate(&json_p2),
|
||||||
|
"jsonparser_skip_ws_mini should also match P2 core candidate"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!detectors::is_selfhost_p2_core_family_candidate(&canonical_p2_min),
|
||||||
|
"canonical Pattern2Mini fixture should not match selfhost P2 candidate"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
#[test]
|
||||||
|
fn test_selfhost_p3_if_sum_structural_candidate_signature() {
|
||||||
|
use crate::mir::join_ir::normalized::fixtures::{
|
||||||
|
build_pattern3_if_sum_min_structured_for_normalized_dev,
|
||||||
|
build_pattern3_if_sum_multi_min_structured_for_normalized_dev,
|
||||||
|
build_selfhost_if_sum_p3_ext_structured_for_normalized_dev,
|
||||||
|
build_selfhost_if_sum_p3_structured_for_normalized_dev,
|
||||||
|
};
|
||||||
|
|
||||||
|
let selfhost_p3 = build_selfhost_if_sum_p3_structured_for_normalized_dev();
|
||||||
|
let selfhost_p3_ext = build_selfhost_if_sum_p3_ext_structured_for_normalized_dev();
|
||||||
|
let canonical_p3_min = build_pattern3_if_sum_min_structured_for_normalized_dev();
|
||||||
|
let canonical_p3_multi = build_pattern3_if_sum_multi_min_structured_for_normalized_dev();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
detectors::is_selfhost_p3_if_sum_family_candidate(&selfhost_p3),
|
||||||
|
"selfhost_if_sum_p3 should match structural candidate"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
detectors::is_selfhost_p3_if_sum_family_candidate(&selfhost_p3_ext),
|
||||||
|
"selfhost_if_sum_p3_ext should match structural candidate"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!detectors::is_selfhost_p3_if_sum_family_candidate(&canonical_p3_min),
|
||||||
|
"canonical P3 minimal should not match selfhost P3 candidate"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!detectors::is_selfhost_p3_if_sum_family_candidate(&canonical_p3_multi),
|
||||||
|
"canonical P3 multi should not match selfhost P3 candidate"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
#[test]
|
||||||
|
fn test_detect_selfhost_token_scan_p2_shape() {
|
||||||
|
use crate::mir::join_ir::normalized::fixtures::build_selfhost_token_scan_p2_structured_for_normalized_dev;
|
||||||
|
|
||||||
|
let module = build_selfhost_token_scan_p2_structured_for_normalized_dev();
|
||||||
|
let shapes = detect_shapes(&module);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
shapes.contains(&NormalizedDevShape::SelfhostTokenScanP2),
|
||||||
|
"selfhost_token_scan_p2 shape missing: {:?}",
|
||||||
|
shapes
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!shapes.contains(&NormalizedDevShape::Pattern2Mini),
|
||||||
|
"selfhost_token_scan_p2 should not be treated as canonical Pattern2Mini: {:?}",
|
||||||
|
shapes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
#[test]
|
||||||
|
fn test_detect_selfhost_token_scan_p2_accum_shape() {
|
||||||
|
use crate::mir::join_ir::normalized::fixtures::build_selfhost_token_scan_p2_accum_structured_for_normalized_dev;
|
||||||
|
|
||||||
|
let module = build_selfhost_token_scan_p2_accum_structured_for_normalized_dev();
|
||||||
|
let shapes = detect_shapes(&module);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
shapes.contains(&NormalizedDevShape::SelfhostTokenScanP2Accum),
|
||||||
|
"selfhost_token_scan_p2_accum shape missing: {:?}",
|
||||||
|
shapes
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!shapes.contains(&NormalizedDevShape::Pattern2Mini),
|
||||||
|
"selfhost_token_scan_p2_accum should not be treated as canonical Pattern2Mini: {:?}",
|
||||||
|
shapes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
#[test]
|
||||||
|
fn test_detect_selfhost_if_sum_p3_shape() {
|
||||||
|
use crate::mir::join_ir::normalized::fixtures::build_selfhost_if_sum_p3_structured_for_normalized_dev;
|
||||||
|
|
||||||
|
let module = build_selfhost_if_sum_p3_structured_for_normalized_dev();
|
||||||
|
let shapes = detect_shapes(&module);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
shapes.contains(&NormalizedDevShape::SelfhostIfSumP3),
|
||||||
|
"selfhost_if_sum_p3 shape missing: {:?}",
|
||||||
|
shapes
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!shapes.iter().any(|s| matches!(s, NormalizedDevShape::Pattern3IfSumMinimal)),
|
||||||
|
"selfhost_if_sum_p3 should not rely on canonical P3 minimal detection: {:?}",
|
||||||
|
shapes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
#[test]
|
||||||
|
fn test_detect_selfhost_if_sum_p3_ext_shape() {
|
||||||
|
use crate::mir::join_ir::normalized::fixtures::build_selfhost_if_sum_p3_ext_structured_for_normalized_dev;
|
||||||
|
|
||||||
|
let module = build_selfhost_if_sum_p3_ext_structured_for_normalized_dev();
|
||||||
|
let shapes = detect_shapes(&module);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
shapes.contains(&NormalizedDevShape::SelfhostIfSumP3Ext),
|
||||||
|
"selfhost_if_sum_p3_ext shape missing: {:?}",
|
||||||
|
shapes
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!shapes.iter().any(|s| matches!(
|
||||||
|
s,
|
||||||
|
NormalizedDevShape::Pattern3IfSumMinimal
|
||||||
|
| NormalizedDevShape::Pattern3IfSumMulti
|
||||||
|
| NormalizedDevShape::Pattern3IfSumJson
|
||||||
|
)),
|
||||||
|
"selfhost_if_sum_p3_ext should not rely on canonical P3 detection: {:?}",
|
||||||
|
shapes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_detect_pattern4_continue_minimal_shape() {
|
fn test_detect_pattern4_continue_minimal_shape() {
|
||||||
@ -552,4 +997,37 @@ mod tests {
|
|||||||
shapes
|
shapes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
#[test]
|
||||||
|
fn test_detect_pattern4_jsonparser_continue_shapes() {
|
||||||
|
use crate::mir::join_ir::normalized::fixtures::{
|
||||||
|
build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev,
|
||||||
|
build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev,
|
||||||
|
};
|
||||||
|
|
||||||
|
let array = build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev();
|
||||||
|
assert!(
|
||||||
|
detectors::is_jsonparser_parse_array_continue_skip_ws(&array),
|
||||||
|
"array continue fixture should be detected"
|
||||||
|
);
|
||||||
|
let array_shapes = detect_shapes(&array);
|
||||||
|
assert!(
|
||||||
|
array_shapes.contains(&NormalizedDevShape::JsonparserParseArrayContinueSkipWs),
|
||||||
|
"array continue shape missing, got {:?}",
|
||||||
|
array_shapes
|
||||||
|
);
|
||||||
|
|
||||||
|
let object = build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev();
|
||||||
|
assert!(
|
||||||
|
detectors::is_jsonparser_parse_object_continue_skip_ws(&object),
|
||||||
|
"object continue fixture should be detected"
|
||||||
|
);
|
||||||
|
let object_shapes = detect_shapes(&object);
|
||||||
|
assert!(
|
||||||
|
object_shapes.contains(&NormalizedDevShape::JsonparserParseObjectContinueSkipWs),
|
||||||
|
"object continue shape missing, got {:?}",
|
||||||
|
object_shapes
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,6 +50,31 @@ pub fn run_joinir_function(
|
|||||||
entry: JoinFuncId,
|
entry: JoinFuncId,
|
||||||
args: &[JoinValue],
|
args: &[JoinValue],
|
||||||
) -> Result<JoinValue, JoinRuntimeError> {
|
) -> Result<JoinValue, JoinRuntimeError> {
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
{
|
||||||
|
// Canonical shapes always go through Normalized roundtrip regardless of mode/env.
|
||||||
|
let canonical_shapes = shape_guard::canonical_shapes(module);
|
||||||
|
if !canonical_shapes.is_empty() {
|
||||||
|
let args_vec = args.to_vec();
|
||||||
|
return dev_env::with_dev_env_if_unset(|| {
|
||||||
|
let structured = normalized_dev_roundtrip_structured(module).map_err(|msg| {
|
||||||
|
JoinRuntimeError::new(format!(
|
||||||
|
"[joinir/normalized-dev/runner] canonical roundtrip failed: {}",
|
||||||
|
msg
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
if dev_env::normalized_dev_logs_enabled() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev/runner] canonical normalized roundtrip (shapes={:?}, functions={})",
|
||||||
|
canonical_shapes,
|
||||||
|
structured.functions.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
execute_function(vm, &structured, entry, args_vec)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
match current_joinir_mode() {
|
match current_joinir_mode() {
|
||||||
JoinIrMode::NormalizedDev => {
|
JoinIrMode::NormalizedDev => {
|
||||||
|
|||||||
@ -72,6 +72,14 @@ fn normalize_for_shape(
|
|||||||
| NormalizedDevShape::JsonparserParseNumberReal => {
|
| NormalizedDevShape::JsonparserParseNumberReal => {
|
||||||
catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module)))
|
catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module)))
|
||||||
}
|
}
|
||||||
|
NormalizedDevShape::SelfhostTokenScanP2 => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
crate::mir::join_ir::normalized::normalize_selfhost_token_scan_p2(module)
|
||||||
|
.expect("selfhost P2 normalization failed")
|
||||||
|
})),
|
||||||
|
NormalizedDevShape::SelfhostTokenScanP2Accum => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
crate::mir::join_ir::normalized::normalize_selfhost_token_scan_p2_accum(module)
|
||||||
|
.expect("selfhost P2 accum normalization failed")
|
||||||
|
})),
|
||||||
// Phase 47-A: P3 minimal normalization
|
// Phase 47-A: P3 minimal normalization
|
||||||
NormalizedDevShape::Pattern3IfSumMinimal => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::Pattern3IfSumMinimal => catch_unwind(AssertUnwindSafe(|| {
|
||||||
crate::mir::join_ir::normalized::normalize_pattern3_if_sum_minimal(module)
|
crate::mir::join_ir::normalized::normalize_pattern3_if_sum_minimal(module)
|
||||||
@ -86,11 +94,44 @@ fn normalize_for_shape(
|
|||||||
crate::mir::join_ir::normalized::normalize_pattern3_if_sum_json_minimal(module)
|
crate::mir::join_ir::normalized::normalize_pattern3_if_sum_json_minimal(module)
|
||||||
.expect("P3 json normalization failed")
|
.expect("P3 json normalization failed")
|
||||||
})),
|
})),
|
||||||
|
NormalizedDevShape::SelfhostIfSumP3 => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
crate::mir::join_ir::normalized::normalize_selfhost_if_sum_p3(module)
|
||||||
|
.expect("selfhost P3 normalization failed")
|
||||||
|
})),
|
||||||
|
NormalizedDevShape::SelfhostIfSumP3Ext => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
crate::mir::join_ir::normalized::normalize_selfhost_if_sum_p3_ext(module)
|
||||||
|
.expect("selfhost P3 ext normalization failed")
|
||||||
|
})),
|
||||||
|
// Phase 53: selfhost P2/P3 practical variations (delegate to existing normalizers)
|
||||||
|
NormalizedDevShape::SelfhostArgsParseP2 => {
|
||||||
|
catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module)))
|
||||||
|
}
|
||||||
|
NormalizedDevShape::SelfhostStmtCountP3 => catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
crate::mir::join_ir::normalized::normalize_selfhost_if_sum_p3_ext(module)
|
||||||
|
.expect("selfhost stmt_count P3 normalization failed")
|
||||||
|
})),
|
||||||
// Phase 48-A: P4 minimal normalization
|
// Phase 48-A: P4 minimal normalization
|
||||||
NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| {
|
||||||
crate::mir::join_ir::normalized::normalize_pattern4_continue_minimal(module)
|
crate::mir::join_ir::normalized::normalize_pattern4_continue_minimal(module)
|
||||||
.expect("P4 normalization failed")
|
.expect("P4 normalization failed")
|
||||||
})),
|
})),
|
||||||
|
// Phase 48-B: JsonParser continue skip_ws (array/object)
|
||||||
|
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => catch_unwind(AssertUnwindSafe(
|
||||||
|
|| {
|
||||||
|
crate::mir::join_ir::normalized::normalize_jsonparser_parse_array_continue_skip_ws(
|
||||||
|
module,
|
||||||
|
)
|
||||||
|
.expect("P4 array normalization failed")
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => catch_unwind(AssertUnwindSafe(
|
||||||
|
|| {
|
||||||
|
crate::mir::join_ir::normalized::normalize_jsonparser_parse_object_continue_skip_ws(
|
||||||
|
module,
|
||||||
|
)
|
||||||
|
.expect("P4 object normalization failed")
|
||||||
|
},
|
||||||
|
)),
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
@ -195,9 +236,7 @@ pub(crate) fn bridge_joinir_to_mir_with_meta(
|
|||||||
{
|
{
|
||||||
let mode = current_joinir_mode();
|
let mode = current_joinir_mode();
|
||||||
|
|
||||||
// Phase 47-C: Canonical set (P2-Core + P2-Mid + P3 if-sum) always uses Normalized→MIR(direct)
|
// Canonical set (P2/P3/P4): Always uses Normalized→MIR(direct) regardless of mode/env
|
||||||
// Canonical set: Pattern2Mini, skip_ws mini/real, atoi mini/real, parse_number real,
|
|
||||||
// P3 if-sum minimal/multi/json
|
|
||||||
let canonical_shapes = shape_guard::canonical_shapes(module);
|
let canonical_shapes = shape_guard::canonical_shapes(module);
|
||||||
if !canonical_shapes.is_empty() {
|
if !canonical_shapes.is_empty() {
|
||||||
match try_normalized_direct_bridge(module, meta, &canonical_shapes, false, false)? {
|
match try_normalized_direct_bridge(module, meta, &canonical_shapes, false, false)? {
|
||||||
|
|||||||
@ -11,6 +11,12 @@ use crate::mir::{BasicBlockId, EffectMask, MirFunction, MirInstruction, ValueId}
|
|||||||
|
|
||||||
use super::{convert_mir_like_inst, join_func_name, JoinIrVmBridgeError};
|
use super::{convert_mir_like_inst, join_func_name, JoinIrVmBridgeError};
|
||||||
|
|
||||||
|
fn log_dbg(message: impl AsRef<str>) {
|
||||||
|
if crate::config::env::joinir_test_debug_enabled() {
|
||||||
|
eprintln!("{}", message.as_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct JoinIrBlockConverter {
|
pub struct JoinIrBlockConverter {
|
||||||
current_block_id: BasicBlockId,
|
current_block_id: BasicBlockId,
|
||||||
current_instructions: Vec<MirInstruction>,
|
current_instructions: Vec<MirInstruction>,
|
||||||
@ -58,15 +64,15 @@ impl JoinIrBlockConverter {
|
|||||||
else_val,
|
else_val,
|
||||||
} = mir_like
|
} = mir_like
|
||||||
{
|
{
|
||||||
eprintln!(
|
log_dbg(format!(
|
||||||
"[joinir_block] ✅ Found Select! dst={:?}, calling handle_select",
|
"[joinir_block] ✅ Found Select! dst={:?}, calling handle_select",
|
||||||
dst
|
dst
|
||||||
);
|
));
|
||||||
self.handle_select(mir_func, dst, cond, then_val, else_val, &None)?;
|
self.handle_select(mir_func, dst, cond, then_val, else_val, &None)?;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Debug: show what instruction we're processing
|
// Debug: show what instruction we're processing
|
||||||
eprintln!("[joinir_block] Compute instruction: {:?}", mir_like);
|
log_dbg(format!("[joinir_block] Compute instruction: {:?}", mir_like));
|
||||||
let mir_inst = convert_mir_like_inst(mir_like)?;
|
let mir_inst = convert_mir_like_inst(mir_like)?;
|
||||||
self.current_instructions.push(mir_inst);
|
self.current_instructions.push(mir_inst);
|
||||||
}
|
}
|
||||||
@ -86,6 +92,10 @@ impl JoinIrBlockConverter {
|
|||||||
method,
|
method,
|
||||||
args,
|
args,
|
||||||
} => {
|
} => {
|
||||||
|
log_dbg(format!(
|
||||||
|
"[joinir_block] Converting ConditionalMethodCall: dst={:?}, cond={:?}",
|
||||||
|
dst, cond
|
||||||
|
));
|
||||||
self.handle_conditional_method_call(
|
self.handle_conditional_method_call(
|
||||||
mir_func, cond, dst, receiver, method, args,
|
mir_func, cond, dst, receiver, method, args,
|
||||||
)?;
|
)?;
|
||||||
@ -481,21 +491,21 @@ impl JoinIrBlockConverter {
|
|||||||
type_hint: type_hint.clone(),
|
type_hint: type_hint.clone(),
|
||||||
});
|
});
|
||||||
merge_block_obj.instruction_spans.push(Span::unknown());
|
merge_block_obj.instruction_spans.push(Span::unknown());
|
||||||
eprintln!(
|
log_dbg(format!(
|
||||||
"[joinir_block/handle_select] Created merge_block {:?} with {} instructions (first={:?})",
|
"[joinir_block/handle_select] Created merge_block {:?} with {} instructions (first={:?})",
|
||||||
merge_block,
|
merge_block,
|
||||||
merge_block_obj.instructions.len(),
|
merge_block_obj.instructions.len(),
|
||||||
merge_block_obj.instructions.first()
|
merge_block_obj.instructions.first()
|
||||||
);
|
));
|
||||||
mir_func.blocks.insert(merge_block, merge_block_obj);
|
mir_func.blocks.insert(merge_block, merge_block_obj);
|
||||||
|
|
||||||
// Verify PHI was inserted
|
// Verify PHI was inserted
|
||||||
if let Some(inserted) = mir_func.blocks.get(&merge_block) {
|
if let Some(inserted) = mir_func.blocks.get(&merge_block) {
|
||||||
eprintln!(
|
log_dbg(format!(
|
||||||
"[joinir_block/handle_select] After insert: merge_block {:?} has {} instructions",
|
"[joinir_block/handle_select] After insert: merge_block {:?} has {} instructions",
|
||||||
merge_block,
|
merge_block,
|
||||||
inserted.instructions.len()
|
inserted.instructions.len()
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.current_block_id = merge_block;
|
self.current_block_id = merge_block;
|
||||||
@ -713,11 +723,11 @@ impl JoinIrBlockConverter {
|
|||||||
instructions: Vec<MirInstruction>,
|
instructions: Vec<MirInstruction>,
|
||||||
terminator: MirInstruction,
|
terminator: MirInstruction,
|
||||||
) {
|
) {
|
||||||
eprintln!(
|
log_dbg(format!(
|
||||||
"[joinir_block/finalize_block] block_id={:?}, instructions.len()={}",
|
"[joinir_block/finalize_block] block_id={:?}, instructions.len()={}",
|
||||||
block_id,
|
block_id,
|
||||||
instructions.len()
|
instructions.len()
|
||||||
);
|
));
|
||||||
if let Some(block) = mir_func.blocks.get_mut(&block_id) {
|
if let Some(block) = mir_func.blocks.get_mut(&block_id) {
|
||||||
// Phase 189 FIX: Preserve existing PHI instructions at block start
|
// Phase 189 FIX: Preserve existing PHI instructions at block start
|
||||||
// PHI instructions must remain at the beginning of the block
|
// PHI instructions must remain at the beginning of the block
|
||||||
@ -730,10 +740,10 @@ impl JoinIrBlockConverter {
|
|||||||
let phi_count = existing_phis.len();
|
let phi_count = existing_phis.len();
|
||||||
|
|
||||||
if phi_count > 0 {
|
if phi_count > 0 {
|
||||||
eprintln!(
|
log_dbg(format!(
|
||||||
"[joinir_block/finalize_block] Preserving {} PHI instructions in block {:?}",
|
"[joinir_block/finalize_block] Preserving {} PHI instructions in block {:?}",
|
||||||
phi_count, block_id
|
phi_count, block_id
|
||||||
);
|
));
|
||||||
// PHI first, then new instructions
|
// PHI first, then new instructions
|
||||||
let mut merged = existing_phis;
|
let mut merged = existing_phis;
|
||||||
merged.extend(instructions);
|
merged.extend(instructions);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#![cfg(all(feature = "normalized_dev", debug_assertions))]
|
#![cfg(all(feature = "normalized_dev", debug_assertions))]
|
||||||
|
|
||||||
use nyash_rust::backend::mir_interpreter::MirInterpreter;
|
use nyash_rust::backend::{mir_interpreter::MirInterpreter, VMValue};
|
||||||
use nyash_rust::mir::join_ir::{
|
use nyash_rust::mir::join_ir::{
|
||||||
normalize_pattern1_minimal, normalize_pattern2_minimal, normalized_pattern1_to_structured,
|
normalize_pattern1_minimal, normalize_pattern2_minimal, normalized_pattern1_to_structured,
|
||||||
normalized_pattern2_to_structured, BinOpKind, ConstValue, JoinContId, JoinFuncId,
|
normalized_pattern2_to_structured, BinOpKind, ConstValue, JoinContId, JoinFuncId,
|
||||||
@ -12,7 +12,9 @@ use nyash_rust::mir::join_ir::normalized::dev_env::{
|
|||||||
use nyash_rust::mir::join_ir::normalized::fixtures::{
|
use nyash_rust::mir::join_ir::normalized::fixtures::{
|
||||||
build_jsonparser_atoi_structured_for_normalized_dev,
|
build_jsonparser_atoi_structured_for_normalized_dev,
|
||||||
build_jsonparser_atoi_real_structured_for_normalized_dev,
|
build_jsonparser_atoi_real_structured_for_normalized_dev,
|
||||||
|
build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev,
|
||||||
build_jsonparser_parse_number_real_structured_for_normalized_dev,
|
build_jsonparser_parse_number_real_structured_for_normalized_dev,
|
||||||
|
build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev,
|
||||||
build_jsonparser_skip_ws_real_structured_for_normalized_dev,
|
build_jsonparser_skip_ws_real_structured_for_normalized_dev,
|
||||||
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
||||||
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
|
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
|
||||||
@ -20,11 +22,20 @@ use nyash_rust::mir::join_ir::normalized::fixtures::{
|
|||||||
build_pattern3_if_sum_multi_min_structured_for_normalized_dev,
|
build_pattern3_if_sum_multi_min_structured_for_normalized_dev,
|
||||||
build_pattern3_json_if_sum_min_structured_for_normalized_dev,
|
build_pattern3_json_if_sum_min_structured_for_normalized_dev,
|
||||||
build_pattern4_continue_min_structured_for_normalized_dev,
|
build_pattern4_continue_min_structured_for_normalized_dev,
|
||||||
|
build_selfhost_args_parse_p2_structured_for_normalized_dev,
|
||||||
|
build_selfhost_if_sum_p3_ext_structured_for_normalized_dev,
|
||||||
|
build_selfhost_if_sum_p3_structured_for_normalized_dev,
|
||||||
|
build_selfhost_stmt_count_p3_structured_for_normalized_dev,
|
||||||
|
build_selfhost_token_scan_p2_accum_structured_for_normalized_dev,
|
||||||
|
build_selfhost_token_scan_p2_structured_for_normalized_dev,
|
||||||
};
|
};
|
||||||
use nyash_rust::mir::join_ir_runner::run_joinir_function;
|
use nyash_rust::mir::join_ir_runner::run_joinir_function;
|
||||||
use nyash_rust::mir::join_ir_ops::JoinValue;
|
use nyash_rust::mir::join_ir_ops::JoinValue;
|
||||||
use nyash_rust::mir::join_ir_vm_bridge::run_joinir_via_vm;
|
use nyash_rust::mir::join_ir_vm_bridge::{
|
||||||
|
convert_join_module_to_mir_with_meta, run_joinir_via_vm,
|
||||||
|
};
|
||||||
use nyash_rust::mir::ValueId;
|
use nyash_rust::mir::ValueId;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
fn normalized_dev_test_ctx() -> NormalizedTestContext<'static> {
|
fn normalized_dev_test_ctx() -> NormalizedTestContext<'static> {
|
||||||
let ctx = test_ctx();
|
let ctx = test_ctx();
|
||||||
assert!(
|
assert!(
|
||||||
@ -68,6 +79,22 @@ fn run_joinir_vm_bridge(
|
|||||||
run_joinir_via_vm(module, entry, args).expect("JoinIR→MIR execution should succeed")
|
run_joinir_via_vm(module, entry, args).expect("JoinIR→MIR execution should succeed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_joinir_vm_bridge_structured_only(
|
||||||
|
module: &JoinModule,
|
||||||
|
entry: JoinFuncId,
|
||||||
|
args: &[JoinValue],
|
||||||
|
) -> JoinValue {
|
||||||
|
let mir =
|
||||||
|
convert_join_module_to_mir_with_meta(module, &BTreeMap::new()).expect("structured bridge");
|
||||||
|
let mut vm = MirInterpreter::new();
|
||||||
|
let entry_name = format!("join_func_{}", entry.0);
|
||||||
|
let vm_args: Vec<VMValue> = args.iter().cloned().map(|v| v.into_vm_value()).collect();
|
||||||
|
let result = vm
|
||||||
|
.execute_function_with_args(&mir, &entry_name, &vm_args)
|
||||||
|
.expect("VM execution should succeed");
|
||||||
|
JoinValue::from_vm_value(&result).expect("result conversion")
|
||||||
|
}
|
||||||
|
|
||||||
fn build_structured_pattern1() -> JoinModule {
|
fn build_structured_pattern1() -> JoinModule {
|
||||||
let mut module = JoinModule::new();
|
let mut module = JoinModule::new();
|
||||||
let mut loop_fn = JoinFunction::new(
|
let mut loop_fn = JoinFunction::new(
|
||||||
@ -512,6 +539,44 @@ fn normalized_pattern2_jsonparser_atoi_real_vm_bridge_direct_matches_structured(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalized_selfhost_token_scan_p2_vm_bridge_direct_matches_structured() {
|
||||||
|
let _ctx = normalized_dev_test_ctx();
|
||||||
|
let structured = build_selfhost_token_scan_p2_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
let cases = [0, 1, 3, 5];
|
||||||
|
|
||||||
|
for n in cases {
|
||||||
|
let input = [JoinValue::Int(n)];
|
||||||
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
|
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
|
||||||
|
|
||||||
|
assert_eq!(base, dev, "vm bridge mismatch for n={}", n);
|
||||||
|
assert_eq!(
|
||||||
|
dev,
|
||||||
|
JoinValue::Int(n),
|
||||||
|
"unexpected result for selfhost_token_scan_p2 n={}",
|
||||||
|
n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalized_selfhost_token_scan_p2_accum_vm_bridge_direct_matches_structured() {
|
||||||
|
let _ctx = normalized_dev_test_ctx();
|
||||||
|
let structured = build_selfhost_token_scan_p2_accum_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
let cases = [0, 1, 3, 5];
|
||||||
|
|
||||||
|
for n in cases {
|
||||||
|
let input = [JoinValue::Int(n)];
|
||||||
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
|
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
|
||||||
|
|
||||||
|
assert_eq!(base, dev, "vm bridge mismatch for selfhost_token_scan_p2_accum n={}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normalized_pattern3_if_sum_minimal_runner_dev_switch_matches_structured() {
|
fn normalized_pattern3_if_sum_minimal_runner_dev_switch_matches_structured() {
|
||||||
let _ctx = normalized_dev_test_ctx();
|
let _ctx = normalized_dev_test_ctx();
|
||||||
@ -568,6 +633,74 @@ fn normalized_pattern3_json_if_sum_min_vm_bridge_direct_matches_structured() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalized_selfhost_if_sum_p3_vm_bridge_direct_matches_structured() {
|
||||||
|
let _ctx = normalized_dev_test_ctx();
|
||||||
|
let structured = build_selfhost_if_sum_p3_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
let cases = [0, 1, 3, 4];
|
||||||
|
|
||||||
|
for n in cases {
|
||||||
|
let input = [JoinValue::Int(n)];
|
||||||
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
|
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
|
||||||
|
|
||||||
|
assert_eq!(base, dev, "vm bridge mismatch for selfhost_if_sum_p3 n={}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalized_selfhost_if_sum_p3_ext_vm_bridge_direct_matches_structured() {
|
||||||
|
let _ctx = normalized_dev_test_ctx();
|
||||||
|
let structured = build_selfhost_if_sum_p3_ext_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
let cases = [0, 1, 3, 4];
|
||||||
|
|
||||||
|
for n in cases {
|
||||||
|
let input = [JoinValue::Int(n)];
|
||||||
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
|
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
|
||||||
|
|
||||||
|
assert_eq!(base, dev, "vm bridge mismatch for selfhost_if_sum_p3_ext n={}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 53: selfhost args-parse P2 (practical variation with string carrier)
|
||||||
|
#[test]
|
||||||
|
fn normalized_selfhost_args_parse_p2_vm_bridge_direct_matches_structured() {
|
||||||
|
let _ctx = normalized_dev_test_ctx();
|
||||||
|
let structured = build_selfhost_args_parse_p2_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
// Test different argc values: 0, 1, 2, 3
|
||||||
|
let cases = [0, 1, 2, 3];
|
||||||
|
|
||||||
|
for argc in cases {
|
||||||
|
let input = [JoinValue::Int(argc)];
|
||||||
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
|
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
|
||||||
|
|
||||||
|
assert_eq!(base, dev, "vm bridge mismatch for selfhost_args_parse_p2 argc={}", argc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 53: selfhost stmt-count P3 (practical variation with multi-branch if-else)
|
||||||
|
#[test]
|
||||||
|
fn normalized_selfhost_stmt_count_p3_vm_bridge_direct_matches_structured() {
|
||||||
|
let _ctx = normalized_dev_test_ctx();
|
||||||
|
let structured = build_selfhost_stmt_count_p3_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
// Test different statement counts: 0, 5, 10, 15
|
||||||
|
let cases = [0, 5, 10, 15];
|
||||||
|
|
||||||
|
for n in cases {
|
||||||
|
let input = [JoinValue::Int(n)];
|
||||||
|
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
|
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
|
||||||
|
|
||||||
|
assert_eq!(base, dev, "vm bridge mismatch for selfhost_stmt_count_p3 n={}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_phase46_canonical_set_includes_p2_mid() {
|
fn test_phase46_canonical_set_includes_p2_mid() {
|
||||||
@ -724,3 +857,106 @@ fn test_normalized_pattern4_continue_minimal_vm_bridge_direct_matches_structured
|
|||||||
"unexpected result for P4 minimal continue (expected acc=4)",
|
"unexpected result for P4 minimal continue (expected acc=4)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Phase 48-C: P4 minimal should use canonical normalized route even without env
|
||||||
|
#[test]
|
||||||
|
fn test_normalized_pattern4_continue_minimal_canonical_matches_structured() {
|
||||||
|
let structured = build_pattern4_continue_min_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
|
||||||
|
let input = [JoinValue::Int(5)];
|
||||||
|
let structured_res = run_joinir_vm_bridge_structured_only(&structured, entry, &input);
|
||||||
|
let canonical = run_joinir_vm_bridge(&structured, entry, &input, false);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
structured_res, canonical,
|
||||||
|
"canonical P4 minimal result mismatch"
|
||||||
|
);
|
||||||
|
assert_eq!(canonical, JoinValue::Int(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 48-B: JsonParser _parse_array continue skip_ws (dev-only) VM Bridge comparison
|
||||||
|
#[test]
|
||||||
|
fn test_normalized_pattern4_jsonparser_parse_array_continue_skip_ws_vm_bridge_direct_matches_structured(
|
||||||
|
) {
|
||||||
|
let _ctx = normalized_dev_test_ctx();
|
||||||
|
let structured = build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
|
||||||
|
// Fixture mirrors pattern4_continue_min: skip i == 2
|
||||||
|
let cases = [3, 5, 7];
|
||||||
|
|
||||||
|
for n in cases {
|
||||||
|
let args = [JoinValue::Int(n)];
|
||||||
|
let base = run_joinir_vm_bridge(&structured, entry, &args, false);
|
||||||
|
let dev = run_joinir_vm_bridge(&structured, entry, &args, true);
|
||||||
|
assert_eq!(
|
||||||
|
base, dev,
|
||||||
|
"vm bridge mismatch for array continue case n={}",
|
||||||
|
n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 48-C: JsonParser _parse_array continue skip_ws canonical route should match Structured
|
||||||
|
#[test]
|
||||||
|
fn test_normalized_pattern4_jsonparser_parse_array_continue_skip_ws_canonical_matches_structured()
|
||||||
|
{
|
||||||
|
let structured = build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
|
||||||
|
let cases = [3, 5, 7];
|
||||||
|
for n in cases {
|
||||||
|
let args = [JoinValue::Int(n)];
|
||||||
|
let structured_res = run_joinir_vm_bridge_structured_only(&structured, entry, &args);
|
||||||
|
let canonical = run_joinir_vm_bridge(&structured, entry, &args, false);
|
||||||
|
assert_eq!(
|
||||||
|
structured_res, canonical,
|
||||||
|
"canonical array continue mismatch n={}",
|
||||||
|
n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 48-B: JsonParser _parse_object continue skip_ws (dev-only) VM Bridge comparison
|
||||||
|
#[test]
|
||||||
|
fn test_normalized_pattern4_jsonparser_parse_object_continue_skip_ws_vm_bridge_direct_matches_structured(
|
||||||
|
) {
|
||||||
|
let _ctx = normalized_dev_test_ctx();
|
||||||
|
let structured = build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
|
||||||
|
// Fixture mirrors pattern4_continue_min: skip i == 2
|
||||||
|
let cases = [4, 6, 8];
|
||||||
|
|
||||||
|
for n in cases {
|
||||||
|
let args = [JoinValue::Int(n)];
|
||||||
|
let base = run_joinir_vm_bridge(&structured, entry, &args, false);
|
||||||
|
let dev = run_joinir_vm_bridge(&structured, entry, &args, true);
|
||||||
|
assert_eq!(
|
||||||
|
base, dev,
|
||||||
|
"vm bridge mismatch for object continue case n={}",
|
||||||
|
n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 48-C: JsonParser _parse_object continue skip_ws canonical route should match Structured
|
||||||
|
#[test]
|
||||||
|
fn test_normalized_pattern4_jsonparser_parse_object_continue_skip_ws_canonical_matches_structured()
|
||||||
|
{
|
||||||
|
let structured = build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev();
|
||||||
|
let entry = structured.entry.expect("structured entry required");
|
||||||
|
|
||||||
|
let cases = [4, 6, 8];
|
||||||
|
for n in cases {
|
||||||
|
let args = [JoinValue::Int(n)];
|
||||||
|
let structured_res = run_joinir_vm_bridge_structured_only(&structured, entry, &args);
|
||||||
|
let canonical = run_joinir_vm_bridge(&structured, entry, &args, false);
|
||||||
|
assert_eq!(
|
||||||
|
structured_res, canonical,
|
||||||
|
"canonical object continue mismatch n={}",
|
||||||
|
n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user