//! JoinIR routing logic for loop lowering
use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
use super::trace;
impl MirBuilder {
/// Phase 49: Try JoinIR Frontend for mainline integration
///
/// Returns `Ok(Some(value))` if the loop is successfully lowered via JoinIR,
/// `Ok(None)` if no JoinIR pattern matched (unsupported loop structure).
/// Phase 187-2: Legacy LoopBuilder removed - all loops must use JoinIR.
///
/// # Phase 49-4: Multi-target support
///
/// Targets are enabled via separate dev flags:
/// - `HAKO_JOINIR_PRINT_TOKENS_MAIN=1`: JsonTokenizer.print_tokens/0
/// - `HAKO_JOINIR_ARRAY_FILTER_MAIN=1`: ArrayExtBox.filter/2
///
/// Note: Arity in function names does NOT include implicit `me` receiver.
/// - Instance method `print_tokens()` → `/0` (no explicit params)
/// - Static method `filter(arr, pred)` → `/2` (two params)
pub(in crate::mir::builder) fn try_cf_loop_joinir(
&mut self,
condition: &ASTNode,
body: &[ASTNode],
) -> Result, String> {
// Get current function name
let func_name = self
.current_function
.as_ref()
.map(|f| f.signature.name.clone())
.unwrap_or_default();
// Phase 195: Use unified trace
trace::trace().routing("router", &func_name, "try_cf_loop_joinir called");
// Phase 170-4: Structure-based routing option
// When NYASH_JOINIR_STRUCTURE_ONLY=1, skip function name whitelist
// and route purely based on loop structure analysis
// Phase 196: Default to structure-first routing now that LoopBuilder is removed.
// - Default: ON (structure_only = true) to allow JoinIR patterns to run for all loops.
// - To revert to the previous whitelist-only behavior, set NYASH_JOINIR_STRUCTURE_ONLY=0.
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY")
.ok()
.as_deref()
{
Some("0") | Some("off") => false,
_ => true,
};
if structure_only {
trace::trace().routing("router", &func_name, "Structure-only mode enabled, skipping whitelist");
} else {
// Phase 49-4 + Phase 80: Multi-target routing (legacy whitelist)
// - JoinIR は常時 ON。legacy LoopBuilder は削除済み。
// - 代表2本(print_tokens / ArrayExt.filter)も常に JoinIR で試行する。
// Note: Arity does NOT include implicit `me` receiver
// Phase 188: Add "main" routing for loop pattern expansion
// Phase 170: Add JsonParserBox methods for selfhost validation
let is_target = match func_name.as_str() {
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
"JsonTokenizer.print_tokens/0" => true,
"ArrayExtBox.filter/2" => true,
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
"JsonParserBox._trim/1" => true,
"JsonParserBox._skip_whitespace/2" => true,
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
"JsonParserBox._parse_string/2" => true,
"JsonParserBox._parse_array/2" => true,
"JsonParserBox._parse_object/2" => true,
// Phase 182: Add simple loop methods
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
"JsonParserBox._atoi/1" => true, // P2 Break (s)
// Phase 170-A-1: Test methods (simplified versions)
"TrimTest.trim/1" => true,
"Main.trim/1" => true, // Phase 171-fix: Main box variant
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
// Phase 173: JsonParser P5 expansion test
"JsonParserTest._skip_whitespace/3" => true,
"JsonParserTest.main/0" => true,
// Phase 174: JsonParser complex loop P5B extension test
"JsonParserStringTest.parse_string_min/0" => true,
"JsonParserStringTest.main/0" => true,
// Phase 175: P5 multi-carrier support (2 carriers: pos + result)
"JsonParserStringTest2.parse_string_min2/0" => true,
"JsonParserStringTest2.main/0" => true,
_ => false,
};
if !is_target {
return Ok(None);
}
}
// Debug log when routing through JoinIR Frontend
// Phase 195: Check trace flags directly from JoinLoopTrace
let debug = trace::trace().is_loopform_enabled() || trace::trace().is_mainline_enabled();
trace::trace().routing("router", &func_name, "Routing through JoinIR Frontend mainline");
// Phase 49-3: Implement JoinIR Frontend integration
self.cf_loop_joinir_impl(condition, body, &func_name, debug)
}
/// Phase 49-3: JoinIR Frontend integration implementation
///
/// Routes loop compilation through either:
/// 1. Pattern-based router (Phase 194+) - preferred for new patterns
/// 2. Legacy binding path (Phase 49-3) - for whitelisted functions only
pub(in crate::mir::builder) fn cf_loop_joinir_impl(
&mut self,
condition: &ASTNode,
body: &[ASTNode],
func_name: &str,
debug: bool,
) -> Result , String> {
// Phase 194: Use table-driven router instead of if/else chain
use super::patterns::{route_loop_pattern, LoopPatternContext};
// Phase 200-C: Pass fn_body_ast to LoopPatternContext if available
// Clone fn_body_ast to avoid borrow checker issues
let fn_body_clone = self.fn_body_ast.clone();
eprintln!("[routing] fn_body_ast is {} for '{}'", if fn_body_clone.is_some() { "SOME" } else { "NONE" }, func_name);
let ctx = if let Some(ref fn_body) = fn_body_clone {
eprintln!("[routing] Creating ctx with fn_body ({} nodes)", fn_body.len());
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
} else {
LoopPatternContext::new(condition, body, &func_name, debug)
};
if let Some(result) = route_loop_pattern(self, &ctx)? {
trace::trace().routing("router", func_name, "Pattern router succeeded");
return Ok(Some(result));
}
// Phase 187-2: Pattern router failed, try legacy whitelist
trace::trace().routing("router", func_name, "Pattern router found no match, trying legacy whitelist");
// Delegate to legacy binding path (routing_legacy_binding.rs)
self.cf_loop_joinir_legacy_binding(condition, body, func_name, debug)
}
}