feat(joinir): Phase 27.1 - minimal_ssa_skip_ws JoinIR変換実装

目的:
- Phase 26-H で確立した JoinIR 型システムを実用ループに適用
- minimal_ssa_skip_ws.hako (ネストif + loop(1==1) + break) の変換実装
- トグル制御 (NYASH_JOINIR_EXPERIMENT=1) で実験的に有効化

実装内容:
- src/mir/join_ir.rs: lower_skip_ws_to_joinir() 関数追加 (187行)
  - skip 関数: i_init=0, n=s.length(), loop_step 呼び出し
  - loop_step 関数: ネストif処理 + 2つのbreak経路 + 再帰呼び出し
  - 固定ValueId割り当て (3000-3002: skip, 4000-4011: loop_step)

- src/tests/mir_joinir_skip_ws.rs: テストインフラ追加
  - mir_joinir_skip_ws_auto_lowering: トグル制御実験用 (#[ignore])
  - mir_joinir_skip_ws_type_sanity: 常時実行の型妥当性チェック
  - Stage-3 parser 有効化 (local キーワード対応)

- src/tests/mod.rs: テストモジュール登録

検証結果:
 トグル ON: JoinIR 変換成功、2関数生成確認
 トグル OFF: 既存テスト影響なし (1 passed; 1 ignored)
 本線テスト: 367 passed (既存失敗は Phase 27.1 と無関係)

Phase 26-H との違い:
- 複雑度: 簡単 (joinir_min) → 中程度 (skip_ws)
- break 箇所: 1箇所 → 2箇所 (ネストif内)
- ループ条件: i < 3 → 1 == 1 (定数true)
- 変数: i のみ → s, i, n, ch (4変数)

次フェーズ:
- Phase 27.2+: MIR 解析による自動検出 (現在は固定実装)
- Phase 28: 一般化された変換器実装

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-23 06:06:16 +09:00
parent 15568b63f9
commit cf60e7cbbc
3 changed files with 284 additions and 0 deletions

View File

@ -0,0 +1,95 @@
// mir_joinir_skip_ws.rs
// Phase 27.1: minimal_ssa_skip_ws JoinIR変換テスト
//
// 目的:
// - minimal_ssa_skip_ws.hako の MIR → JoinIR 自動変換の動作確認
// - ネストしたif + loop(1 == 1) + break パターンの変換検証
// - Phase 26-H の拡張版(複雑度: 中程度)
//
// 実行条件:
// - デフォルトでは #[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 27.1 実験段階)
fn mir_joinir_skip_ws_auto_lowering() {
// Phase 27.1: minimal_ssa_skip_ws の MIR → JoinIR 自動変換
// 環境変数トグルチェック
if std::env::var("NYASH_JOINIR_EXPERIMENT").ok().as_deref() != Some("1") {
eprintln!("[joinir/skip_ws] NYASH_JOINIR_EXPERIMENT=1 not set, skipping auto-lowering test");
return;
}
// Step 1: MIR までコンパイル
// Stage-3 parser を有効化local キーワード対応)
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("HAKO_PARSER_STAGE3", "1");
let test_file = "apps/tests/minimal_ssa_skip_ws.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("skip_ws: parse failed");
let mut mc = MirCompiler::with_options(false);
let compiled = mc.compile(ast).expect("skip_ws: MIR compile failed");
eprintln!(
"[joinir/skip_ws] MIR module compiled, {} functions",
compiled.module.functions.len()
);
// Step 2: MIR → JoinIR 自動変換
let join_module = lower_skip_ws_to_joinir(&compiled.module)
.expect("lower_skip_ws_to_joinir failed");
eprintln!("[joinir/skip_ws] JoinIR module generated:");
eprintln!("{:#?}", join_module);
// Step 3: 妥当性検証
assert_eq!(join_module.functions.len(), 2, "Expected 2 functions (skip + loop_step)");
let skip_id = JoinFuncId::new(0);
let loop_step_id = JoinFuncId::new(1);
// skip 関数の検証
let skip_func = join_module.functions.get(&skip_id)
.expect("skip function not found");
assert_eq!(skip_func.name, "skip");
assert_eq!(skip_func.params.len(), 1, "skip has 1 parameter (s)");
assert!(skip_func.body.len() >= 3, "skip should have at least 3 instructions (const 0, length call, loop_step call)");
// loop_step 関数の検証
let loop_step_func = join_module.functions.get(&loop_step_id)
.expect("loop_step function not found");
assert_eq!(loop_step_func.name, "loop_step");
assert_eq!(loop_step_func.params.len(), 3, "loop_step has 3 parameters (s, i, n)");
assert!(loop_step_func.body.len() >= 8, "loop_step should have multiple instructions (comparisons, substring, recursive call, etc.)");
eprintln!("[joinir/skip_ws] ✅ 自動変換成功Phase 27.1");
}
#[test]
fn mir_joinir_skip_ws_type_sanity() {
// Phase 27.1: 型定義の基本的なサニティチェック(常時実行)
// skip_ws 用の JoinFunction が作成できることを確認
let skip_id = JoinFuncId::new(10);
let skip_func = JoinFunction::new(
skip_id,
"skip_ws_test".to_string(),
vec![ValueId(1), ValueId(2), ValueId(3)],
);
assert_eq!(skip_func.id, skip_id);
assert_eq!(skip_func.name, "skip_ws_test");
assert_eq!(skip_func.params.len(), 3);
assert_eq!(skip_func.body.len(), 0);
}

View File

@ -12,6 +12,7 @@ pub mod mir_funcscanner_parse_params_trim_min;
pub mod mir_funcscanner_trim_min;
pub mod mir_funcscanner_ssa;
pub mod mir_joinir_min; // Phase 26-H: JoinIR型定義妥当性確認
pub mod mir_joinir_skip_ws; // Phase 27.1: minimal_ssa_skip_ws JoinIR変換
pub mod mir_locals_ssa;
pub mod mir_loopform_conditional_reassign;
pub mod mir_loopform_exit_phi;