feat(joinir): Phase 27.1 - FuncScanner.trim JoinIR変換実装完了

目的:
- minimal_ssa_skip_ws に続き、FuncScannerBox.trim/1 ループを JoinIR 変換対象に追加
- Phase 27.0 の実用化拡張(より簡単なループで動作確認)

実装内容:
1. lower_funcscanner_trim_to_joinir() 関数追加 (src/mir/join_ir.rs:515-770)
   - trim_main + loop_step の2関数生成
   - 固定 ValueId 割り当て (5000-5018, 6000-6018)
   - OR 条件の chain 処理 (ch == " " || "\t" || "\n" || "\r")

2. BinOpKind 拡張 (src/mir/join_ir.rs:147-154)
   - Or/And variant 追加(論理演算対応)
   - Phase 27.1 実験的拡張として最小限の変更

3. テストインフラ追加 (src/tests/mir_joinir_funcscanner_trim.rs)
   - auto_lowering テスト: #[ignore] + NYASH_JOINIR_EXPERIMENT=1 トグル
   - type_sanity テスト: 常時実行の軽量テスト

4. ドキュメント完備 (docs/private/roadmap2/phases/phase-27.1-joinir/)
   - IMPLEMENTATION_LOG.md: 技術メモ + BinOpKind 拡張決定の記録
   - TASKS.md: 実装ステップ進捗管理

検証結果:
-  ビルド成功 (リリースビルド 55.75s)
-  type_sanity テスト PASS
-  既存 JoinIR テスト全て PASS (mir_joinir_min, mir_joinir_skip_ws)
-  トグル OFF で本線影響なし確認済み

トグル制御:
- NYASH_JOINIR_EXPERIMENT=1 で JoinIR 変換有効化
- デフォルトは従来の MIR/LoopForm 維持

次のステップ:
- C-2: トグル ON での動作確認
- D-1: ベースライン緑度確認
- E-1: README 更新

🤖 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:29:22 +09:00
parent a103fbdb34
commit 96086f485d
3 changed files with 361 additions and 1 deletions

View File

@ -0,0 +1,100 @@
// mir_joinir_funcscanner_trim.rs
// Phase 27.1: FuncScannerBox.trim JoinIR変換テスト
//
// 目的:
// - FuncScannerBox.trim/1 の MIR → JoinIR 自動変換の動作確認
// - trailing whitespace 除去ループの JoinIR 表現検証
// - Phase 27.0 skip_ws に続く実用ループ変換(より簡単なケース)
//
// 実行条件:
// - デフォルトでは #[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_funcscanner_trim_auto_lowering() {
// Phase 27.1: FuncScannerBox.trim の MIR → JoinIR 自動変換
// 環境変数トグルチェック
if std::env::var("NYASH_JOINIR_EXPERIMENT").ok().as_deref() != Some("1") {
eprintln!("[joinir/trim] NYASH_JOINIR_EXPERIMENT=1 not set, skipping auto-lowering test");
return;
}
// Stage-3 parser を有効化local キーワード対応)
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("HAKO_PARSER_STAGE3", "1");
std::env::set_var("NYASH_ENABLE_USING", "1");
std::env::set_var("HAKO_ENABLE_USING", "1");
// Step 1: MIR までコンパイル
// FuncScanner 本体と最小テストを結合
let test_file = "lang/src/compiler/tests/funcscanner_trim_min.hako";
let func_scanner_src = include_str!("../../lang/src/compiler/entry/func_scanner.hako");
let test_src = std::fs::read_to_string(test_file)
.unwrap_or_else(|_| panic!("Failed to read {}", test_file));
let src = format!("{func_scanner_src}\n\n{test_src}");
let ast: ASTNode = NyashParser::parse_from_string(&src)
.expect("trim: parse failed");
let mut mc = MirCompiler::with_options(false);
let compiled = mc.compile(ast).expect("trim: MIR compile failed");
eprintln!(
"[joinir/trim] MIR module compiled, {} functions",
compiled.module.functions.len()
);
// Step 2: MIR → JoinIR 自動変換
let join_module = lower_funcscanner_trim_to_joinir(&compiled.module)
.expect("lower_funcscanner_trim_to_joinir failed");
eprintln!("[joinir/trim] JoinIR module generated:");
eprintln!("{:#?}", join_module);
// Step 3: 妥当性検証
assert_eq!(join_module.functions.len(), 2, "Expected 2 functions (trim_main + loop_step)");
let trim_main_id = JoinFuncId::new(0);
let loop_step_id = JoinFuncId::new(1);
// trim_main 関数の検証
let trim_main_func = join_module.functions.get(&trim_main_id)
.expect("trim_main function not found");
assert_eq!(trim_main_func.name, "trim_main");
assert_eq!(trim_main_func.params.len(), 1, "trim_main has 1 parameter (s)");
assert!(trim_main_func.body.len() >= 5, "trim_main should have at least 5 instructions");
// 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 (str, b, e)");
assert!(loop_step_func.body.len() >= 10, "loop_step should have multiple instructions");
eprintln!("[joinir/trim] ✅ 自動変換成功Phase 27.1");
}
#[test]
fn mir_joinir_funcscanner_trim_type_sanity() {
// Phase 27.1: 型定義の基本的なサニティチェック(常時実行)
// trim 用の JoinFunction が作成できることを確認
let trim_main_id = JoinFuncId::new(10);
let trim_main_func = JoinFunction::new(
trim_main_id,
"trim_main_test".to_string(),
vec![ValueId(1)],
);
assert_eq!(trim_main_func.id, trim_main_id);
assert_eq!(trim_main_func.name, "trim_main_test");
assert_eq!(trim_main_func.params.len(), 1);
assert_eq!(trim_main_func.body.len(), 0);
}

View File

@ -12,7 +12,8 @@ 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_joinir_skip_ws; // Phase 27.0: minimal_ssa_skip_ws JoinIR変換
pub mod mir_joinir_funcscanner_trim; // Phase 27.1: FuncScannerBox.trim JoinIR変換
pub mod mir_locals_ssa;
pub mod mir_loopform_conditional_reassign;
pub mod mir_loopform_exit_phi;