feat(joinir): Phase 49-4 multi-target routing with graceful fallback

- Add ArrayExtBox.filter/2 as second JoinIR mainline target
- Fix function name arity: print_tokens is /0 (no implicit me in arity)
- Construct proper JSON v0 format with defs array for JoinIR Frontend
- Add catch_unwind for graceful fallback on unsupported patterns
- Add 3 array_filter tests (smoke, fallback, A/B comparison)
- All 6 Phase 49 tests passing

Dev flags:
- HAKO_JOINIR_PRINT_TOKENS_MAIN=1: JsonTokenizer.print_tokens/0
- HAKO_JOINIR_ARRAY_FILTER_MAIN=1: ArrayExtBox.filter/2

Note: Currently all loops fall back to legacy LoopBuilder due to
JoinIR Frontend expecting hardcoded variable names (i, acc, n).
Full JoinIR integration pending variable scope support in Phase 50+.

🤖 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-28 20:12:39 +09:00
parent 5819423e25
commit 1e1b2183b2
2 changed files with 310 additions and 31 deletions

View File

@ -1,4 +1,4 @@
// Phase 49-3.2: JoinIR Frontend Mainline Integration Test
// Phase 49: JoinIR Frontend Mainline Integration Test
//
// このテストは cf_loop の JoinIR Frontend mainline route が
// 正常に動作することを確認する。
@ -7,8 +7,13 @@
// - merge_joinir_mir_blocks() によるブロックマージ
// - A/B 比較テストRoute A vs Route B
//
// Phase 49-4 実装済み:
// - ArrayExtBox.filter/2 対応
// - HAKO_JOINIR_ARRAY_FILTER_MAIN=1 dev フラグ追加
//
// テスト方法:
// HAKO_JOINIR_PRINT_TOKENS_MAIN=1 cargo test --release joinir_mainline_phase49
// HAKO_JOINIR_ARRAY_FILTER_MAIN=1 cargo test --release phase49_joinir_array_filter
use crate::ast::ASTNode;
use crate::mir::MirCompiler;
@ -222,3 +227,201 @@ static box Main {
std::env::remove_var("HAKO_PARSER_STAGE3");
std::env::remove_var("NYASH_DISABLE_PLUGINS");
}
// =============================================================================
// Phase 49-4: ArrayExtBox.filter/2 Tests
// =============================================================================
/// Phase 49-4: JoinIR Frontend mainline パイプラインが
/// ArrayExtBox.filter 関数のコンパイル時にクラッシュしないことを確認
#[test]
fn phase49_joinir_array_filter_smoke() {
// Phase 49-4 mainline route は dev フラグで制御
std::env::set_var("HAKO_JOINIR_ARRAY_FILTER_MAIN", "1");
std::env::set_var("NYASH_JOINIR_MAINLINE_DEBUG", "1");
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("HAKO_PARSER_STAGE3", "1");
std::env::set_var("NYASH_DISABLE_PLUGINS", "1");
// ArrayExtBox.filter 簡易実装if-in-loop パターン)
// fn パラメータは pred に変更(予約語競合回避)
let src = r#"
static box ArrayExtBox {
filter(arr, pred) {
local out = new ArrayBox()
local i = 0
local n = arr.size()
loop(i < n) {
local v = arr.get(i)
if pred(v) {
out.push(v)
}
i = i + 1
}
return out
}
}
static box Main {
main() {
return 0
}
}
"#;
let ast: ASTNode = NyashParser::parse_from_string(src)
.expect("phase49-4: parse failed");
let mut mc = MirCompiler::with_options(false);
let result = mc.compile(ast);
// パイプラインがクラッシュしないことを確認
assert!(
result.is_ok(),
"phase49-4 array_filter mainline compile should not crash: {:?}",
result.err()
);
// クリーンアップ
std::env::remove_var("HAKO_JOINIR_ARRAY_FILTER_MAIN");
std::env::remove_var("NYASH_JOINIR_MAINLINE_DEBUG");
std::env::remove_var("NYASH_PARSER_STAGE3");
std::env::remove_var("HAKO_PARSER_STAGE3");
std::env::remove_var("NYASH_DISABLE_PLUGINS");
}
/// Phase 49-4: dev フラグ OFF 時は従来経路を使用することを確認
#[test]
fn phase49_joinir_array_filter_fallback() {
// dev フラグ OFF
std::env::remove_var("HAKO_JOINIR_ARRAY_FILTER_MAIN");
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("HAKO_PARSER_STAGE3", "1");
std::env::set_var("NYASH_DISABLE_PLUGINS", "1");
let src = r#"
static box ArrayExtBox {
filter(arr, pred) {
local out = new ArrayBox()
local i = 0
local n = arr.size()
loop(i < n) {
local v = arr.get(i)
if pred(v) {
out.push(v)
}
i = i + 1
}
return out
}
}
static box Main {
main() {
return 0
}
}
"#;
let ast: ASTNode = NyashParser::parse_from_string(src)
.expect("phase49-4 fallback: parse failed");
let mut mc = MirCompiler::with_options(false);
let result = mc.compile(ast);
assert!(
result.is_ok(),
"phase49-4 fallback compile should succeed: {:?}",
result.err()
);
// クリーンアップ
std::env::remove_var("NYASH_PARSER_STAGE3");
std::env::remove_var("HAKO_PARSER_STAGE3");
std::env::remove_var("NYASH_DISABLE_PLUGINS");
}
/// Phase 49-4: A/B 比較テスト - Route A (legacy) vs Route B (JoinIR)
/// ArrayExtBox.filter版
#[test]
fn phase49_joinir_array_filter_ab_comparison() {
let src = r#"
static box ArrayExtBox {
filter(arr, pred) {
local out = new ArrayBox()
local i = 0
local n = arr.size()
loop(i < n) {
local v = arr.get(i)
if pred(v) {
out.push(v)
}
i = i + 1
}
return out
}
}
static box Main {
main() {
return 0
}
}
"#;
// Route A: Legacy path (flag OFF)
std::env::remove_var("HAKO_JOINIR_ARRAY_FILTER_MAIN");
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("HAKO_PARSER_STAGE3", "1");
std::env::set_var("NYASH_DISABLE_PLUGINS", "1");
let ast_a: ASTNode = NyashParser::parse_from_string(src)
.expect("phase49-4 A/B: parse failed (Route A)");
let mut mc_a = MirCompiler::with_options(false);
let result_a = mc_a.compile(ast_a);
assert!(
result_a.is_ok(),
"Route A compile should succeed: {:?}",
result_a.err()
);
let module_a = result_a.unwrap().module;
let blocks_a: usize = module_a
.functions
.values()
.map(|f| f.blocks.len())
.sum();
// Route B: JoinIR Frontend path (flag ON)
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("HAKO_PARSER_STAGE3", "1");
std::env::set_var("NYASH_DISABLE_PLUGINS", "1");
std::env::set_var("HAKO_JOINIR_ARRAY_FILTER_MAIN", "1");
let ast_b: ASTNode = NyashParser::parse_from_string(src)
.expect("phase49-4 A/B: parse failed (Route B)");
let mut mc_b = MirCompiler::with_options(false);
let result_b = mc_b.compile(ast_b);
assert!(
result_b.is_ok(),
"Route B compile should succeed: {:?}",
result_b.err()
);
let module_b = result_b.unwrap().module;
let blocks_b: usize = module_b
.functions
.values()
.map(|f| f.blocks.len())
.sum();
// Log block counts for debugging
eprintln!(
"[phase49-4 A/B filter] Route A: {} total blocks, Route B: {} total blocks",
blocks_a, blocks_b
);
// クリーンアップ
std::env::remove_var("HAKO_JOINIR_ARRAY_FILTER_MAIN");
std::env::remove_var("NYASH_PARSER_STAGE3");
std::env::remove_var("HAKO_PARSER_STAGE3");
std::env::remove_var("NYASH_DISABLE_PLUGINS");
}