2025-11-28 17:42:19 +09:00
|
|
|
|
//! AST/CFG → JoinIR Lowering
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! このモジュールは AST/CFG ノードを JoinIR 命令に変換する。
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## 責務
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - **If 文→Select/IfMerge 変換**: 条件分岐を JoinIR の継続渡しスタイルに変換
|
|
|
|
|
|
//! - **Loop 文→loop_step/k_exit 変換**: ループを関数呼び出しと継続に正規化
|
|
|
|
|
|
//! - **Break/Continue/Return→k_* 変換**: 制御フローを継続 ID として表現
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## Phase 34-2 での実装スコープ
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! 最初は `IfSelectTest.*` 相当の tiny ケースのみ対応:
|
|
|
|
|
|
//! - Simple pattern: `if cond { return 1 } else { return 2 }`
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## 設計原則
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - **JoinIR = PHI 生成器**: 既存 PHI の変換器にはしない(Phase 33-10 原則)
|
|
|
|
|
|
//! - **段階的移行**: 既存 MIR Builder 経路は保持、新経路はデフォルト OFF
|
|
|
|
|
|
//! - **A/B テスト可能**: 既存経路と新経路の両方で実行して比較検証
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) use crate::mir::join_ir::{
|
|
|
|
|
|
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MergePair,
|
|
|
|
|
|
VarId,
|
|
|
|
|
|
};
|
|
|
|
|
|
pub(crate) use std::collections::{BTreeMap, HashSet};
|
|
|
|
|
|
|
|
|
|
|
|
mod analysis;
|
|
|
|
|
|
mod context;
|
|
|
|
|
|
mod expr;
|
2025-11-29 07:20:56 +09:00
|
|
|
|
mod if_in_loop;
|
2025-11-28 17:42:19 +09:00
|
|
|
|
mod if_return;
|
2025-11-29 09:04:18 +09:00
|
|
|
|
mod loop_frontend_binding;
|
2025-11-28 17:42:19 +09:00
|
|
|
|
mod loop_patterns;
|
2025-11-29 09:04:18 +09:00
|
|
|
|
mod loop_patterns_old;
|
2025-11-28 17:42:19 +09:00
|
|
|
|
mod nested_if;
|
|
|
|
|
|
mod read_quoted;
|
2025-11-29 04:42:16 +09:00
|
|
|
|
mod stmt_handlers;
|
2025-11-28 17:42:19 +09:00
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests;
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) use context::ExtractCtx;
|
2025-11-29 07:20:56 +09:00
|
|
|
|
pub(crate) use stmt_handlers::StatementEffect;
|
2025-11-28 17:42:19 +09:00
|
|
|
|
|
2025-12-11 20:54:33 +09:00
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
|
enum FunctionRoute {
|
|
|
|
|
|
IfReturn,
|
|
|
|
|
|
LoopFrontend,
|
|
|
|
|
|
NestedIf,
|
|
|
|
|
|
ReadQuoted,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn resolve_function_route(func_name: &str) -> Result<FunctionRoute, String> {
|
2025-12-12 03:15:45 +09:00
|
|
|
|
const TABLE: &[(&str, FunctionRoute)] = &[
|
|
|
|
|
|
("test", FunctionRoute::IfReturn),
|
|
|
|
|
|
("local", FunctionRoute::IfReturn),
|
|
|
|
|
|
("_read_value_from_pair", FunctionRoute::IfReturn),
|
|
|
|
|
|
("simple", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("filter", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("print_tokens", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("map", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("reduce", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("fold", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("jsonparser_skip_ws_mini", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("jsonparser_skip_ws_real", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("jsonparser_atoi_mini", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("jsonparser_atoi_real", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("jsonparser_parse_number_real", FunctionRoute::LoopFrontend),
|
2025-12-12 07:13:34 +09:00
|
|
|
|
("pattern3_if_sum_multi_min", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("jsonparser_if_sum_min", FunctionRoute::LoopFrontend),
|
2025-12-12 16:40:20 +09:00
|
|
|
|
("selfhost_token_scan_p2", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("selfhost_token_scan_p2_accum", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("selfhost_args_parse_p2", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("selfhost_if_sum_p3", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("selfhost_if_sum_p3_ext", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("selfhost_stmt_count_p3", FunctionRoute::LoopFrontend),
|
2025-12-12 17:12:58 +09:00
|
|
|
|
// Phase 54: selfhost P2/P3 shape growth
|
|
|
|
|
|
("selfhost_verify_schema_p2", FunctionRoute::LoopFrontend),
|
|
|
|
|
|
("selfhost_detect_format_p3", FunctionRoute::LoopFrontend),
|
2025-12-12 06:31:13 +09:00
|
|
|
|
// Phase 48-A: Pattern4 continue minimal
|
|
|
|
|
|
("pattern4_continue_minimal", FunctionRoute::LoopFrontend),
|
2025-12-12 16:40:20 +09:00
|
|
|
|
// Phase 48-B: JsonParser continue skip_ws fixtures
|
|
|
|
|
|
(
|
|
|
|
|
|
"jsonparser_parse_array_continue_skip_ws",
|
|
|
|
|
|
FunctionRoute::LoopFrontend,
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"jsonparser_parse_object_continue_skip_ws",
|
|
|
|
|
|
FunctionRoute::LoopFrontend,
|
|
|
|
|
|
),
|
2025-12-11 22:12:46 +09:00
|
|
|
|
];
|
2025-12-11 20:54:33 +09:00
|
|
|
|
|
2025-12-12 03:15:45 +09:00
|
|
|
|
if let Some((_, route)) = TABLE.iter().find(|(name, _)| *name == func_name) {
|
|
|
|
|
|
return Ok(*route);
|
2025-12-11 20:54:33 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if func_name == "parse_loop" {
|
|
|
|
|
|
if crate::config::env::joinir_dev_enabled()
|
|
|
|
|
|
&& std::env::var("HAKO_JOINIR_NESTED_IF").ok().as_deref() == Some("1")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Ok(FunctionRoute::NestedIf);
|
|
|
|
|
|
}
|
|
|
|
|
|
return Err(
|
|
|
|
|
|
"[joinir/frontend] 'parse_loop' requires HAKO_JOINIR_NESTED_IF=1 (dev only)"
|
|
|
|
|
|
.to_string(),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if func_name == "read_quoted_from" {
|
|
|
|
|
|
if crate::config::env::joinir_dev_enabled()
|
|
|
|
|
|
&& std::env::var("HAKO_JOINIR_READ_QUOTED").ok().as_deref() == Some("1")
|
|
|
|
|
|
{
|
|
|
|
|
|
return Ok(FunctionRoute::ReadQuoted);
|
|
|
|
|
|
}
|
|
|
|
|
|
return Err(
|
|
|
|
|
|
"[joinir/frontend] 'read_quoted_from' requires HAKO_JOINIR_READ_QUOTED=1 (dev only)"
|
|
|
|
|
|
.to_string(),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Err(format!(
|
|
|
|
|
|
"[joinir/frontend] unsupported function '{}' (dev fixture not registered)",
|
|
|
|
|
|
func_name
|
|
|
|
|
|
))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-28 17:42:19 +09:00
|
|
|
|
/// AST/CFG → JoinIR 変換器
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Phase 34-2: Program(JSON v0) から tiny IfSelect ケースを JoinIR に変換
|
|
|
|
|
|
pub struct AstToJoinIrLowerer {
|
|
|
|
|
|
pub(crate) next_func_id: u32,
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
|
pub(crate) next_var_id: u32,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl AstToJoinIrLowerer {
|
|
|
|
|
|
/// 新しい lowerer を作成
|
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
next_func_id: 0,
|
|
|
|
|
|
next_var_id: 0,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Program(JSON v0) → JoinModule
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Phase 34-2/34-3/34-4: simple/local/json_shape pattern に対応
|
|
|
|
|
|
/// Phase 34-5: extract_value 統一化(Int/Var/Method 構造まで)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Panics
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - パターンに合わない Program(JSON) が来た場合(Phase 34 は tiny テスト専用)
|
|
|
|
|
|
/// - ループ・複数変数・副作用付き if(Phase 34-6 以降で対応予定)
|
|
|
|
|
|
pub fn lower_program_json(&mut self, program_json: &serde_json::Value) -> JoinModule {
|
|
|
|
|
|
// 1. Program(JSON) から defs を取得
|
|
|
|
|
|
let defs = program_json["defs"]
|
|
|
|
|
|
.as_array()
|
|
|
|
|
|
.expect("Program(JSON v0) must have 'defs' array");
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 最初の関数定義を取得
|
|
|
|
|
|
let func_def = defs
|
|
|
|
|
|
.get(0)
|
|
|
|
|
|
.expect("At least one function definition required");
|
|
|
|
|
|
|
|
|
|
|
|
let func_name = func_def["name"]
|
|
|
|
|
|
.as_str()
|
|
|
|
|
|
.expect("Function must have 'name'");
|
|
|
|
|
|
|
2025-12-11 20:54:33 +09:00
|
|
|
|
let route = resolve_function_route(func_name)
|
|
|
|
|
|
.unwrap_or_else(|msg| panic!("{msg}"));
|
|
|
|
|
|
|
|
|
|
|
|
match route {
|
|
|
|
|
|
FunctionRoute::IfReturn => self.lower_if_return_pattern(program_json),
|
|
|
|
|
|
FunctionRoute::LoopFrontend => loop_frontend_binding::lower_loop_by_function_name(
|
|
|
|
|
|
self,
|
|
|
|
|
|
program_json,
|
|
|
|
|
|
),
|
|
|
|
|
|
FunctionRoute::NestedIf => self.lower_nested_if_pattern(program_json),
|
|
|
|
|
|
FunctionRoute::ReadQuoted => self.lower_read_quoted_pattern(program_json),
|
2025-11-28 17:42:19 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 次の関数 ID を生成
|
|
|
|
|
|
pub(crate) fn next_func_id(&mut self) -> JoinFuncId {
|
|
|
|
|
|
let id = JoinFuncId::new(self.next_func_id);
|
|
|
|
|
|
self.next_func_id += 1;
|
|
|
|
|
|
id
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Default for AstToJoinIrLowerer {
|
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
|
Self::new()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|