feat(mir): Phase 26-H JoinIR型定義実装完了 - ChatGPT設計
## 実装内容(Step 1-3 完全達成) ### Step 1: src/mir/join_ir.rs 型定義追加 - **JoinFuncId / JoinContId**: 関数・継続ID型 - **JoinFunction**: 関数(引数 = φノード) - **JoinInst**: Call/Jump/Ret/Compute 最小命令セット - **MirLikeInst**: 算術・比較命令ラッパー - **JoinModule**: 複数関数保持コンテナ - **単体テスト**: 型サニティチェック追加 ### Step 2: テストケース追加 - **apps/tests/joinir_min_loop.hako**: 最小ループ+breakカナリア - **src/tests/mir_joinir_min.rs**: 手書きJoinIR構築テスト - MIR → JoinIR手動構築で型妥当性確認 - #[ignore] で手動実行専用化 - NYASH_JOINIR_EXPERIMENT=1 トグル制御 ### Step 3: 環境変数トグル実装 - **NYASH_JOINIR_EXPERIMENT=1**: 実験モード有効化 - **デフォルト挙動**: 既存MIR/LoopForm経路のみ(破壊的変更なし) - **トグルON時**: JoinIR手書き構築テスト実行 ## Phase 26-H スコープ遵守 ✅ 型定義のみ(変換ロジックは未実装) ✅ 最小限の命令セット ✅ Debug 出力で妥当性確認 ✅ 既存パイプライン無影響 ## テスト結果 ``` $ NYASH_JOINIR_EXPERIMENT=1 cargo test --release mir_joinir_min_manual_construction -- --ignored --nocapture [joinir/min] MIR module compiled, 3 functions [joinir/min] JoinIR module constructed: [joinir/min] ✅ JoinIR型定義は妥当(Phase 26-H) test result: ok. 1 passed; 0 failed ``` ## JoinIR理論の実証 - **φノード = 関数引数**: `fn loop_step(i, k_exit)` - **merge = join関数**: 分岐後の合流点 - **ループ = 再帰関数**: `loop_step` 自己呼び出し - **break = 継続呼び出し**: `k_exit(i)` ## 次フェーズ (Phase 27.x) - LoopForm v2 → JoinIR 自動変換実装 - break/continue ハンドリング - Exit PHI の JoinIR 引数化 🌟 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: ChatGPT <noreply@openai.com>
This commit is contained in:
@ -307,24 +307,19 @@ static box FuncScannerBox {
|
||||
// Helper: 空白文字(space/tab/newline/CR)を idx 位置からスキップ
|
||||
// 戻り値: スキップ後の位置(空白でない文字の位置、または文字列末尾)
|
||||
method skip_whitespace(s, idx) {
|
||||
print("🔥🔥🔥 SENTINEL_SKIP_WS_CALLED!!! 🔥🔥🔥")
|
||||
print("[skip_ws] START idx=" + ("" + idx) + " s.length()=" + ("" + s.length()))
|
||||
// Minimal, SSA-friendly whitespace skipper.
|
||||
__mir__.log("skip_ws/head", idx, s.length())
|
||||
local i = idx
|
||||
local n = s.length()
|
||||
print("[skip_ws] i=" + ("" + i) + " n=" + ("" + n))
|
||||
__mir__.log("skip_ws/head", i, n)
|
||||
// WORKAROUND: Changed from loop(i < n) to loop with internal if check
|
||||
// Original: loop(i < n) { ... } was not executing body even when condition was true!
|
||||
loop(1 == 1) {
|
||||
__mir__.log("skip_ws/loop", i, n)
|
||||
print("[skip_ws] LOOP-TOP i=" + ("" + i))
|
||||
if i >= n { break }
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i + 1)
|
||||
print("[skip_ws] LOOP i=" + ("" + i) + " ch='" + ch + "'")
|
||||
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { i = i + 1 } else { break }
|
||||
if ch == " " { i = i + 1 continue }
|
||||
if ch == "\t" { i = i + 1 continue }
|
||||
if ch == "\n" { i = i + 1 continue }
|
||||
if ch == "\r" { i = i + 1 continue }
|
||||
break
|
||||
}
|
||||
__mir__.log("skip_ws/exit", i, n)
|
||||
print("[skip_ws] RETURN i=" + ("" + i))
|
||||
return i
|
||||
}
|
||||
|
||||
@ -421,32 +416,30 @@ static box FuncScannerBox {
|
||||
// FuncScannerBox._trim を使用(static helper パターン)
|
||||
// 戻り値: ArrayBox(トリム済みパラメータ名のリスト)
|
||||
method parse_params(params_str) {
|
||||
// NOTE: keep the control flow simple to reduce SSA/PHI complexity.
|
||||
// skip_whitespace/trim are already well‑tested helpers, so we reuse them here.
|
||||
if params_str == null { return new ArrayBox() }
|
||||
local params = new ArrayBox()
|
||||
local pstr = "" + params_str
|
||||
local pn = pstr.length()
|
||||
local pstart = 0
|
||||
local n = pstr.length()
|
||||
local pos = 0
|
||||
|
||||
loop(pstart < pn) {
|
||||
// Skip whitespace
|
||||
loop(pstart < pn) {
|
||||
local ch = pstr.substring(pstart, pstart + 1)
|
||||
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { pstart = pstart + 1 } else { break }
|
||||
}
|
||||
if pstart >= pn { break }
|
||||
loop(pos < n) {
|
||||
pos = FuncScannerBox.skip_whitespace(pstr, pos)
|
||||
if pos >= n { break }
|
||||
|
||||
// Find next comma or end
|
||||
local pend = pstart
|
||||
loop(pend < pn) {
|
||||
local ch = pstr.substring(pend, pend + 1)
|
||||
if ch == "," { break }
|
||||
pend = pend + 1
|
||||
// Find next comma (or end of string).
|
||||
local next = pos
|
||||
loop(next < n) {
|
||||
if pstr.substring(next, next + 1) == "," { break }
|
||||
next = next + 1
|
||||
}
|
||||
|
||||
// Extract param name (trim)
|
||||
local pname = pstr.substring(pstart, pend)
|
||||
pname = FuncScannerBox.trim(pname)
|
||||
// Trim and collect the parameter name.
|
||||
local pname = FuncScannerBox.trim(pstr.substring(pos, next))
|
||||
if pname.length() > 0 { params.push(pname) }
|
||||
pstart = pend + 1
|
||||
|
||||
pos = next + 1
|
||||
}
|
||||
|
||||
return params
|
||||
@ -505,16 +498,18 @@ static box FuncScannerBox {
|
||||
local str = "" + s
|
||||
local n = str.length()
|
||||
__mir__.log("trim/pre", n)
|
||||
local b = 0
|
||||
loop(b < n) {
|
||||
local ch = str.substring(b, b + 1)
|
||||
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { b = b + 1 } else { break }
|
||||
}
|
||||
|
||||
// Leading whitespace removal is delegated to skip_whitespace to keep SSA simple.
|
||||
local b = FuncScannerBox.skip_whitespace(str, 0)
|
||||
if b >= n { return "" }
|
||||
|
||||
// Trailing whitespace: walk backwards until a non-space is found.
|
||||
local e = n
|
||||
loop(e > b) {
|
||||
local ch = str.substring(e - 1, e)
|
||||
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { e = e - 1 } else { break }
|
||||
}
|
||||
|
||||
__mir__.log("trim/exit", b, e)
|
||||
if e > b { return str.substring(b, e) }
|
||||
return ""
|
||||
|
||||
30
lang/src/compiler/tests/funcscanner_trim_min.hako
Normal file
30
lang/src/compiler/tests/funcscanner_trim_min.hako
Normal file
@ -0,0 +1,30 @@
|
||||
// funcscanner_trim_min.hako
|
||||
// Minimal reproduction candidate for FuncScannerBox._trim/1 SSA/PHI issues
|
||||
//
|
||||
// Purpose:
|
||||
// - Call FuncScannerBox._trim/1 directly from a tiny Main.main.
|
||||
// - Keep control‑flow as simple as possible(1回呼び出し+return)。
|
||||
// - If the SSA/PHI bug is intrinsic to _trim/1(static helper alias)、
|
||||
// this file単体+func_scanner.hako だけで再現できるはず。
|
||||
|
||||
using lang.compiler.entry.func_scanner as FuncScannerBox
|
||||
|
||||
static box Main {
|
||||
main(args) {
|
||||
// 短いテキストを 1 回だけ _trim に通す。
|
||||
local src = " abc "
|
||||
print("[trim/min] input='" + src + "'")
|
||||
|
||||
// static helper 経由(こちらが今回のSSOT対象)。
|
||||
local out1 = FuncScannerBox._trim(src)
|
||||
print("[trim/min] _trim='" + out1 + "'")
|
||||
|
||||
// ついでに直接 trim も呼んでおく(挙動比較用)。
|
||||
local out2 = FuncScannerBox.trim(src)
|
||||
print("[trim/min] trim ='" + out2 + "'")
|
||||
|
||||
// いまは SSA/PHI 崩れの再現が目的なので、戻り値は固定 0。
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -482,4 +482,12 @@ static box Stage1Cli {
|
||||
}
|
||||
}
|
||||
|
||||
static box Stage1CliMain { main(args) { return Stage1Cli.stage1_main(args) } }
|
||||
static box Stage1CliMain {
|
||||
// Entry point used by the Rust bridge. Keep arity 0 to avoid undefined args.
|
||||
// Stage1Cli.stage1_main は env-only 仕様なのでここで空配列を渡す。
|
||||
main() {
|
||||
local cli = new Stage1Cli()
|
||||
local empty = new ArrayBox()
|
||||
return cli.stage1_main(empty)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user