## 実装内容(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>
140 lines
4.8 KiB
Rust
140 lines
4.8 KiB
Rust
// mir_joinir_min.rs
|
||
// Phase 26-H: JoinIR型定義妥当性確認テスト(最小ループ)
|
||
//
|
||
// 目的:
|
||
// - JoinFunction/JoinInst の型が破綻していないか確認
|
||
// - 手書きで JoinIR を組み立ててみて、設計の妥当性をチェック
|
||
// - まだ LoopForm → JoinIR 自動変換は書かない(Phase 27以降)
|
||
//
|
||
// 実行条件:
|
||
// - デフォルトでは #[ignore] にしておいて手動実行用にする
|
||
// - 環境変数 NYASH_JOINIR_EXPERIMENT=1 で実験モード有効化
|
||
|
||
use crate::ast::ASTNode;
|
||
use crate::mir::join_ir::*;
|
||
use crate::mir::{MirCompiler, ValueId};
|
||
use crate::parser::NyashParser;
|
||
|
||
#[test]
|
||
#[ignore] // 手動実行用(Phase 26-H 実験段階)
|
||
fn mir_joinir_min_manual_construction() {
|
||
// Phase 26-H スコープ: 型定義の妥当性確認のみ
|
||
// LoopForm からの自動変換は Phase 27 以降で実装
|
||
|
||
// 環境変数トグルチェック
|
||
if std::env::var("NYASH_JOINIR_EXPERIMENT").ok().as_deref() != Some("1") {
|
||
eprintln!("[joinir/min] NYASH_JOINIR_EXPERIMENT=1 not set, skipping manual construction test");
|
||
return;
|
||
}
|
||
|
||
// Step 1: MIR までコンパイル(既存パイプラインで)
|
||
// Stage-3 環境変数を設定(local キーワード対応)
|
||
std::env::set_var("NYASH_PARSER_STAGE3", "1");
|
||
std::env::set_var("HAKO_PARSER_STAGE3", "1");
|
||
|
||
let test_file = "apps/tests/joinir_min_loop.hako";
|
||
let src = std::fs::read_to_string(test_file)
|
||
.unwrap_or_else(|_| panic!("Failed to read {}", test_file));
|
||
|
||
let ast: ASTNode = NyashParser::parse_from_string(&src)
|
||
.expect("joinir_min: parse failed");
|
||
|
||
let mut mc = MirCompiler::with_options(false);
|
||
let compiled = mc.compile(ast).expect("joinir_min: MIR compile failed");
|
||
|
||
eprintln!(
|
||
"[joinir/min] MIR module compiled, {} functions",
|
||
compiled.module.functions.len()
|
||
);
|
||
|
||
// Step 2: 手書きで JoinIR を構築(設計の妥当性チェック)
|
||
let mut join_module = JoinModule::new();
|
||
|
||
// fn main(k_exit) { loop_step(0, k_exit) }
|
||
let main_id = JoinFuncId::new(0);
|
||
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]);
|
||
|
||
// 引数: i_init = 0 (ValueId(100) とする)
|
||
let i_init = ValueId(100);
|
||
main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||
dst: i_init,
|
||
value: ConstValue::Integer(0),
|
||
}));
|
||
|
||
// loop_step(i_init, k_exit)
|
||
let loop_step_id = JoinFuncId::new(1);
|
||
let k_exit_id = JoinContId::new(0);
|
||
main_func.body.push(JoinInst::Call {
|
||
func: loop_step_id,
|
||
args: vec![i_init],
|
||
k_next: Some(k_exit_id),
|
||
});
|
||
|
||
join_module.add_function(main_func);
|
||
|
||
// fn loop_step(i, k_exit) { if i >= 2 { k_exit(i) } else { loop_step(i+1, k_exit) } }
|
||
let mut loop_step_func = JoinFunction::new(
|
||
loop_step_id,
|
||
"loop_step".to_string(),
|
||
vec![ValueId(200)], // i の引数
|
||
);
|
||
|
||
let i_param = ValueId(200);
|
||
let cmp_result = ValueId(201);
|
||
let i_plus_1 = ValueId(202);
|
||
|
||
// cmp_result = (i >= 2)
|
||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||
dst: cmp_result,
|
||
op: CompareOp::Ge,
|
||
lhs: i_param,
|
||
rhs: ValueId(203), // const 2
|
||
}));
|
||
|
||
// const 2
|
||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||
dst: ValueId(203),
|
||
value: ConstValue::Integer(2),
|
||
}));
|
||
|
||
// if cmp_result { k_exit(i) } else { loop_step(i+1, k_exit) }
|
||
// ここでは簡略化して Jump 命令だけ書く(実際は分岐制御が必要だが Phase 26-H では型チェックのみ)
|
||
loop_step_func.body.push(JoinInst::Jump {
|
||
cont: k_exit_id,
|
||
args: vec![i_param],
|
||
});
|
||
|
||
// i_plus_1 = i + 1
|
||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||
dst: i_plus_1,
|
||
op: BinOpKind::Add,
|
||
lhs: i_param,
|
||
rhs: ValueId(204), // const 1
|
||
}));
|
||
|
||
join_module.add_function(loop_step_func);
|
||
|
||
// Step 3: Debug 出力で妥当性確認
|
||
eprintln!("[joinir/min] JoinIR module constructed:");
|
||
eprintln!("{:#?}", join_module);
|
||
|
||
// アサーション(型定義が使えることを確認)
|
||
assert_eq!(join_module.functions.len(), 2);
|
||
assert!(join_module.functions.contains_key(&main_id));
|
||
assert!(join_module.functions.contains_key(&loop_step_id));
|
||
|
||
eprintln!("[joinir/min] ✅ JoinIR型定義は妥当(Phase 26-H)");
|
||
}
|
||
|
||
#[test]
|
||
fn mir_joinir_min_type_sanity() {
|
||
// Phase 26-H: 型定義の基本的なサニティチェック(常時実行)
|
||
let func_id = JoinFuncId::new(0);
|
||
let func = JoinFunction::new(func_id, "test".to_string(), vec![ValueId(1)]);
|
||
|
||
assert_eq!(func.id, func_id);
|
||
assert_eq!(func.name, "test");
|
||
assert_eq!(func.params.len(), 1);
|
||
assert_eq!(func.body.len(), 0);
|
||
}
|