Files
hakorune/src/mir/join_ir/frontend/ast_lowerer/analysis.rs
nyash-codex 447bbec998 refactor(joinir): Split ast_lowerer and join_ir_vm_bridge into modules
ast_lowerer.rs → ast_lowerer/ (10 files):
- mod.rs: public surface + entry dispatch
- context.rs: ExtractCtx helpers
- expr.rs: expression-to-JoinIR extraction
- if_return.rs: simple if→Select lowering
- loop_patterns.rs: loop variants (simple/break/continue)
- read_quoted.rs: read_quoted_from lowering (Phase 45-46)
- nested_if.rs: NestedIfMerge lowering
- analysis.rs: loop if-var analysis + metadata helpers
- tests.rs: frontend lowering tests
- README.md: module documentation

join_ir_vm_bridge.rs → join_ir_vm_bridge/ (5 files):
- mod.rs: public surface + shared helpers
- convert.rs: JoinIR→MIR lowering
- runner.rs: VM execution entry (run_joinir_via_vm)
- meta.rs: experimental metadata-aware hooks
- tests.rs: bridge-specific unit tests
- README.md: module documentation

Benefits:
- Clear separation of concerns per pattern
- Easier navigation and maintenance
- Each file has single responsibility
- README documents module boundaries

Co-authored-by: ChatGPT <noreply@openai.com>

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 17:42:19 +09:00

343 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use super::{AstToJoinIrLowerer, HashSet, JoinModule};
use crate::mir::join_ir::frontend::func_meta::{JoinFuncMeta, JoinFuncMetaMap};
impl AstToJoinIrLowerer {
/// Phase 40-1で実装予定: ループ内if文の変数追跡
///
/// # Purpose
///
/// array_ext.filter等のif-in-loopパターンで、ループ内で修正される変数を
/// 追跡し、ループ出口PHI生成に使用する。
///
/// # Implementation Plan (Phase 40-1)
///
/// ## Input
/// - `loop_body`: ループ本体ASTJSON v0形式
/// - `loop_vars`: ループで使用される変数Header PHIで定義
///
/// ## Output
/// - `HashSet<String>`: if分岐内で修正された変数名セット
///
/// ## Logic
/// 1. Recursive AST walk (helper: `extract_assigned_vars_from_body`)
/// 2. Detect assignments in if branches only
/// 3. Filter for variables in `loop_vars` (loop-carried variables)
/// 4. Return set of modified variable names
///
/// ## Integration Point
/// - Call from: `lower_loop_case_a_simple()` or similar loop lowering
/// - Use output for: Loop exit PHI generation in `create_exit_function()`
///
/// # Example
///
/// ```nyash,ignore
/// local out = new ArrayBox() // loop_vars = {out, i}
/// local i = 0
/// loop(i < n) {
/// if fn(arr[i]) { // ← この中の代入を検出
/// out.push(arr[i]) // ← out修正検出
/// }
/// i = i + 1
/// }
/// // Result: extract_if_in_loop_modified_vars() = {out}
/// // → Loop exit PHI: phi out_exit = (out_header, out_loop_modified)
/// ```
///
/// # Replaces (Phase 40-1削除対象)
///
/// - `if_phi::collect_assigned_vars()` (32 lines)
/// - Current callsites: loop_builder.rs:1069, 1075
/// - この関数実装でcallsites削除可能
///
/// # See Also
///
/// - Design: `docs/.../phase-39-if-phi-level2/joinir_extension_design.md`
/// - A/B test: array_ext.filter (Primary representative function)
///
/// # TODO(Phase 40-1)
///
/// ```rust,ignore
/// fn extract_if_in_loop_modified_vars(
/// &mut self,
/// loop_body: &serde_json::Value,
/// loop_vars: &HashSet<String>,
/// ) -> HashSet<String> {
/// // Step 1: Recursive AST walk
/// let all_assigned = self.extract_assigned_vars_from_body(loop_body);
///
/// // Step 2: Filter for if-statement assignments only
/// let if_assigned = all_assigned.iter()
/// .filter(|var| self.is_assigned_in_if_branch(loop_body, var))
/// .cloned()
/// .collect::<HashSet<_>>();
///
/// // Step 3: Filter for loop-carried variables
/// if_assigned.intersection(loop_vars).cloned().collect()
/// }
/// ```
#[allow(dead_code)]
pub fn extract_if_in_loop_modified_vars(
&mut self,
loop_body: &serde_json::Value,
loop_vars: &HashSet<String>,
) -> HashSet<String> {
// Step 1: Recursive AST walk to collect all assigned variables
let all_assigned = self.extract_assigned_vars_from_body(loop_body);
// Step 2: Filter for if-statement assignments only
let if_assigned = self.extract_if_assigned_vars(loop_body);
// Step 3: Filter for loop-carried variables
// Return intersection of (if_assigned ∩ loop_vars)
if_assigned
.intersection(loop_vars)
.filter(|var| all_assigned.contains(*var))
.cloned()
.collect()
}
/// Phase 40-1: if文内での代入変数抽出
///
/// # Purpose
///
/// loop body内のif文でのみ代入される変数を抽出する。
/// これはloop exit PHI生成に必要。
pub fn extract_if_assigned_vars(
&mut self,
body: &serde_json::Value,
) -> std::collections::HashSet<String> {
use std::collections::HashSet;
let mut if_assigned = HashSet::new();
// Handle array of statements
if let Some(stmts) = body.as_array() {
for stmt in stmts {
if stmt.get("type").and_then(|t| t.as_str()) == Some("If") {
// Extract assignments from then/else branches
if let Some(then_body) = stmt.get("then") {
if_assigned.extend(self.extract_assigned_vars_from_body(then_body));
}
if let Some(else_body) = stmt.get("else") {
if_assigned.extend(self.extract_assigned_vars_from_body(else_body));
}
}
// Recursive: nested loops
else if stmt.get("type").and_then(|t| t.as_str()) == Some("Loop") {
if let Some(loop_body) = stmt.get("body") {
if_assigned.extend(self.extract_if_assigned_vars(loop_body));
}
}
}
}
// Handle Block node
else if let Some(stmts) = body.get("body").and_then(|b| b.as_array()) {
for stmt in stmts {
if stmt.get("type").and_then(|t| t.as_str()) == Some("If") {
if let Some(then_body) = stmt.get("then") {
if_assigned.extend(self.extract_assigned_vars_from_body(then_body));
}
if let Some(else_body) = stmt.get("else") {
if_assigned.extend(self.extract_assigned_vars_from_body(else_body));
}
} else if stmt.get("type").and_then(|t| t.as_str()) == Some("Loop") {
if let Some(loop_body) = stmt.get("body") {
if_assigned.extend(self.extract_if_assigned_vars(loop_body));
}
}
}
}
if_assigned
}
/// Phase 40-1で実装予定: 再帰的AST走査代入検出
///
/// # Purpose
///
/// AST bodyを再帰的に走査し、代入文を検出する。
///
/// # Implementation Plan
///
/// ## Recursive Descent
/// - Handle: "Local" assignments (`local x = ...` or `x = ...`)
/// - Handle: Nested blocks (`{ ... }`)
/// - Handle: If/Loop bodies (recursive call)
///
/// ## Return
/// - `HashSet<String>`: 代入された変数名全て
///
/// # Example
///
/// ```json
/// {
/// "type": "Block",
/// "body": [
/// {"type": "Local", "name": "x", "expr": ...}, // x assigned
/// {"type": "If", "cond": ..., "then": [
/// {"type": "Local", "name": "y", "expr": ...} // y assigned
/// ]}
/// ]
/// }
/// ```
/// Result: {x, y}
///
#[allow(dead_code)]
pub fn extract_assigned_vars_from_body(
&mut self,
body: &serde_json::Value,
) -> std::collections::HashSet<String> {
use std::collections::HashSet;
let mut assigned = HashSet::new();
// Handle array of statements
if let Some(stmts) = body.as_array() {
for stmt in stmts {
self.extract_assigned_vars_from_stmt(stmt, &mut assigned);
}
}
// Handle single statement (Block node)
else if let Some(stmts) = body.get("body").and_then(|b| b.as_array()) {
for stmt in stmts {
self.extract_assigned_vars_from_stmt(stmt, &mut assigned);
}
}
assigned
}
/// Phase 40-1: 再帰的AST走査ヘルパー単一文処理
///
/// # Purpose
///
/// 単一のAST文を処理し、代入された変数を収集する。
pub(crate) fn extract_assigned_vars_from_stmt(
&mut self,
stmt: &serde_json::Value,
assigned: &mut std::collections::HashSet<String>,
) {
match stmt.get("type").and_then(|t| t.as_str()) {
Some("Local") => {
// local x = ... または x = ...
if let Some(name) = stmt.get("name").and_then(|n| n.as_str()) {
assigned.insert(name.to_string());
}
}
Some("If") => {
// if 文の then/else 分岐を再帰処理
if let Some(then_body) = stmt.get("then") {
assigned.extend(self.extract_assigned_vars_from_body(then_body));
}
if let Some(else_body) = stmt.get("else") {
assigned.extend(self.extract_assigned_vars_from_body(else_body));
}
}
Some("Loop") => {
// loop 文の body を再帰処理
if let Some(loop_body) = stmt.get("body") {
assigned.extend(self.extract_assigned_vars_from_body(loop_body));
}
}
Some("Block") => {
// { ... } ブロックの body を再帰処理
if let Some(block_body) = stmt.get("body") {
assigned.extend(self.extract_assigned_vars_from_body(block_body));
}
}
_ => {
// その他の文は無視Return, Call, etc.
}
}
}
/// Phase 40-1実験用: array_ext.filter パターン専用lowering
///
/// 通常の lower_loop_case_a_simple() に加えて、
/// if-in-loop modified varsをJoinFuncMetaMapとして返す。
///
/// # Returns
/// - `JoinModule`: 通常のJoinIR module
/// - `JoinFuncMetaMap`: loop_step関数のif_modified_vars情報
///
/// # Phase 40-1専用
/// この関数はPhase 40-1 A/Bテスト専用。
/// 本番パスでは使わない従来のlower_loop_case_a_simple()を使う)。
#[allow(dead_code)]
pub fn lower_loop_with_if_meta(
&mut self,
program_json: &serde_json::Value,
) -> (JoinModule, JoinFuncMetaMap) {
// 1. 通常のJoinModule生成既存ロジック流用
let module = self.lower_loop_case_a_simple(program_json);
// 2. loop body ASTからif-in-loop modified varsを抽出
let loop_body = self.extract_loop_body_from_program(program_json);
let loop_vars = self.get_loop_carried_vars_from_program(program_json);
let if_modified = self.extract_if_in_loop_modified_vars(&loop_body, &loop_vars);
// 3. JoinFuncMetaMap組み立て
// loop_step関数のIDを特定通常は2番目の関数、名前に"loop_step"を含む)
let mut meta_map = JoinFuncMetaMap::new();
if let Some((loop_step_id, _)) = module
.functions
.iter()
.find(|(_, func)| func.name.contains("loop_step"))
{
meta_map.insert(
*loop_step_id,
JoinFuncMeta {
if_modified_vars: if if_modified.is_empty() {
None
} else {
Some(if_modified)
},
..Default::default()
},
);
}
(module, meta_map)
}
/// loop body ASTを抽出するヘルパー
///
/// # Purpose
/// Program → defs[0] → body → Loop statement → body のパスでloop bodyを取得
pub fn extract_loop_body_from_program(
&self,
program_json: &serde_json::Value,
) -> serde_json::Value {
// Program → defs[0] → body → Loop statement → body
program_json["defs"][0]["body"]
.as_array()
.and_then(|stmts| stmts.iter().find(|s| s["type"] == "Loop"))
.and_then(|loop_stmt| loop_stmt.get("body"))
.cloned()
.unwrap_or(serde_json::Value::Null)
}
/// loop-carried varsを抽出するヘルパー
///
/// # Purpose
/// loop前に宣言された変数を収集簡易実装
/// より正確な実装はPhase 40-2で拡張
pub fn get_loop_carried_vars_from_program(
&self,
program_json: &serde_json::Value,
) -> HashSet<String> {
let mut vars = HashSet::new();
if let Some(body) = program_json["defs"][0]["body"].as_array() {
for stmt in body {
if stmt["type"] == "Local" {
if let Some(name) = stmt["name"].as_str() {
vars.insert(name.to_string());
}
}
if stmt["type"] == "Loop" {
break; // loop以降は無視
}
}
}
vars
}
}