Files
hakorune/src/runner/modes/macro_child/transforms/lift.rs
nyash-codex f9d100ce01 chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更
Phase 25.1 完了成果:
-  LoopForm v2 テスト・ドキュメント・コメント完備
  - 4ケース(A/B/C/D)完全テストカバレッジ
  - 最小再現ケース作成(SSAバグ調査用)
  - SSOT文書作成(loopform_ssot.md)
  - 全ソースに [LoopForm] コメントタグ追加

-  Stage-1 CLI デバッグ環境構築
  - stage1_cli.hako 実装
  - stage1_bridge.rs ブリッジ実装
  - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh)
  - アーキテクチャ改善提案文書

-  環境変数削減計画策定
  - 25変数の完全調査・分類
  - 6段階削減ロードマップ(25→5、80%削減)
  - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG)

Phase 26-D からの累積変更:
- PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等)
- MIRビルダーリファクタリング
- 型伝播・最適化パス改善
- その他約300ファイルの累積変更

🎯 技術的成果:
- SSAバグ根本原因特定(条件分岐内loop変数変更)
- Region+next_iパターン適用完了(UsingCollectorBox等)
- LoopFormパターン文書化・テスト化完了
- セルフホスティング基盤強化

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Task Assistant <task@anthropic.com>
2025-11-21 06:25:17 +09:00

436 lines
14 KiB
Rust

pub(super) fn transform_lift_nested_functions(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
use nyash_rust::ast::ASTNode as A;
use std::sync::atomic::{AtomicUsize, Ordering};
static COUNTER: AtomicUsize = AtomicUsize::new(0);
fn gensym(base: &str) -> String {
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
format!("__ny_lifted_{}_{}", base, n)
}
fn collect_locals(n: &A, set: &mut std::collections::HashSet<String>) {
match n {
A::Local { variables, .. } => {
for v in variables {
set.insert(v.clone());
}
}
A::Program { statements, .. } => {
for s in statements {
collect_locals(s, set);
}
}
A::FunctionDeclaration { body, .. } => {
for s in body {
collect_locals(s, set);
}
}
A::If {
then_body,
else_body,
..
} => {
for s in then_body {
collect_locals(s, set);
}
if let Some(b) = else_body {
for s in b {
collect_locals(s, set);
}
}
}
_ => {}
}
}
fn collect_vars(n: &A, set: &mut std::collections::HashSet<String>) {
match n {
A::Variable { name, .. } => {
set.insert(name.clone());
}
A::Program { statements, .. } => {
for s in statements {
collect_vars(s, set);
}
}
A::FunctionDeclaration { body, .. } => {
for s in body {
collect_vars(s, set);
}
}
A::If {
condition,
then_body,
else_body,
..
} => {
collect_vars(condition, set);
for s in then_body {
collect_vars(s, set);
}
if let Some(b) = else_body {
for s in b {
collect_vars(s, set);
}
}
}
A::Assignment { target, value, .. } => {
collect_vars(target, set);
collect_vars(value, set);
}
A::Return { value, .. } => {
if let Some(v) = value {
collect_vars(v, set);
}
}
A::Print { expression, .. } => collect_vars(expression, set),
A::BinaryOp { left, right, .. } => {
collect_vars(left, set);
collect_vars(right, set);
}
A::UnaryOp { operand, .. } => collect_vars(operand, set),
A::MethodCall {
object, arguments, ..
} => {
collect_vars(object, set);
for a in arguments {
collect_vars(a, set);
}
}
A::FunctionCall { arguments, .. } => {
for a in arguments {
collect_vars(a, set);
}
}
A::ArrayLiteral { elements, .. } => {
for e in elements {
collect_vars(e, set);
}
}
A::MapLiteral { entries, .. } => {
for (_, v) in entries {
collect_vars(v, set);
}
}
_ => {}
}
}
fn rename_calls(n: &A, mapping: &std::collections::HashMap<String, String>) -> A {
use nyash_rust::ast::ASTNode as A;
match n.clone() {
A::FunctionCall {
name,
arguments,
span,
} => {
let new_name = mapping.get(&name).cloned().unwrap_or(name);
A::FunctionCall {
name: new_name,
arguments: arguments
.into_iter()
.map(|a| rename_calls(&a, mapping))
.collect(),
span,
}
}
A::Program { statements, span } => A::Program {
statements: statements
.into_iter()
.map(|s| rename_calls(&s, mapping))
.collect(),
span,
},
A::FunctionDeclaration {
name,
params,
body,
is_static,
is_override,
span,
} => A::FunctionDeclaration {
name,
params,
body: body
.into_iter()
.map(|s| rename_calls(&s, mapping))
.collect(),
is_static,
is_override,
span,
},
A::If {
condition,
then_body,
else_body,
span,
} => A::If {
condition: Box::new(rename_calls(&condition, mapping)),
then_body: then_body
.into_iter()
.map(|s| rename_calls(&s, mapping))
.collect(),
else_body: else_body
.map(|v| v.into_iter().map(|s| rename_calls(&s, mapping)).collect()),
span,
},
A::Assignment {
target,
value,
span,
} => A::Assignment {
target: Box::new(rename_calls(&target, mapping)),
value: Box::new(rename_calls(&value, mapping)),
span,
},
A::Return { value, span } => A::Return {
value: value.as_ref().map(|v| Box::new(rename_calls(v, mapping))),
span,
},
A::Print { expression, span } => A::Print {
expression: Box::new(rename_calls(&expression, mapping)),
span,
},
A::BinaryOp {
operator,
left,
right,
span,
} => A::BinaryOp {
operator,
left: Box::new(rename_calls(&left, mapping)),
right: Box::new(rename_calls(&right, mapping)),
span,
},
A::UnaryOp {
operator,
operand,
span,
} => A::UnaryOp {
operator,
operand: Box::new(rename_calls(&operand, mapping)),
span,
},
A::MethodCall {
object,
method,
arguments,
span,
} => A::MethodCall {
object: Box::new(rename_calls(&object, mapping)),
method,
arguments: arguments
.into_iter()
.map(|a| rename_calls(&a, mapping))
.collect(),
span,
},
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
elements: elements
.into_iter()
.map(|e| rename_calls(&e, mapping))
.collect(),
span,
},
A::MapLiteral { entries, span } => A::MapLiteral {
entries: entries
.into_iter()
.map(|(k, v)| (k, rename_calls(&v, mapping)))
.collect(),
span,
},
other => other,
}
}
fn lift_in_body(
body: Vec<A>,
hoisted: &mut Vec<A>,
mapping: &mut std::collections::HashMap<String, String>,
) -> Vec<A> {
use std::collections::HashSet;
let mut out: Vec<A> = Vec::new();
for st in body.into_iter() {
match st.clone() {
A::FunctionDeclaration {
name,
params,
body,
is_static,
is_override,
span,
} => {
let mut locals: HashSet<String> = HashSet::new();
collect_locals(
&A::FunctionDeclaration {
name: name.clone(),
params: params.clone(),
body: body.clone(),
is_static,
is_override,
span,
},
&mut locals,
);
let mut used: HashSet<String> = HashSet::new();
collect_vars(
&A::FunctionDeclaration {
name: name.clone(),
params: params.clone(),
body: body.clone(),
is_static,
is_override,
span,
},
&mut used,
);
let params_set: HashSet<String> = params.iter().cloned().collect();
let mut extra: HashSet<String> = used.drain().collect();
extra.retain(|v| !params_set.contains(v) && !locals.contains(v));
if extra.is_empty() {
let new_name = gensym(&name);
let lifted = A::FunctionDeclaration {
name: new_name.clone(),
params,
body,
is_static: true,
is_override,
span,
};
hoisted.push(lifted);
mapping.insert(name, new_name);
continue;
} else {
out.push(st);
}
}
other => out.push(other),
}
}
out.into_iter().map(|n| rename_calls(&n, mapping)).collect()
}
fn walk(n: &A, hoisted: &mut Vec<A>) -> A {
use nyash_rust::ast::ASTNode as A;
match n.clone() {
A::Program { statements, span } => {
let mut mapping = std::collections::HashMap::new();
let stmts2 = lift_in_body(
statements.into_iter().map(|s| walk(&s, hoisted)).collect(),
hoisted,
&mut mapping,
);
A::Program {
statements: stmts2,
span,
}
}
A::FunctionDeclaration {
name,
params,
body,
is_static,
is_override,
span,
} => {
let mut mapping = std::collections::HashMap::new();
let body2: Vec<A> = body.into_iter().map(|s| walk(&s, hoisted)).collect();
let body3 = lift_in_body(body2, hoisted, &mut mapping);
A::FunctionDeclaration {
name,
params,
body: body3,
is_static,
is_override,
span,
}
}
A::If {
condition,
then_body,
else_body,
span,
} => A::If {
condition: Box::new(walk(&condition, hoisted)),
then_body: then_body.into_iter().map(|s| walk(&s, hoisted)).collect(),
else_body: else_body.map(|v| v.into_iter().map(|s| walk(&s, hoisted)).collect()),
span,
},
A::Assignment {
target,
value,
span,
} => A::Assignment {
target: Box::new(walk(&target, hoisted)),
value: Box::new(walk(&value, hoisted)),
span,
},
A::Return { value, span } => A::Return {
value: value.as_ref().map(|v| Box::new(walk(v, hoisted))),
span,
},
A::Print { expression, span } => A::Print {
expression: Box::new(walk(&expression, hoisted)),
span,
},
A::BinaryOp {
operator,
left,
right,
span,
} => A::BinaryOp {
operator,
left: Box::new(walk(&left, hoisted)),
right: Box::new(walk(&right, hoisted)),
span,
},
A::UnaryOp {
operator,
operand,
span,
} => A::UnaryOp {
operator,
operand: Box::new(walk(&operand, hoisted)),
span,
},
A::MethodCall {
object,
method,
arguments,
span,
} => A::MethodCall {
object: Box::new(walk(&object, hoisted)),
method,
arguments: arguments.into_iter().map(|a| walk(&a, hoisted)).collect(),
span,
},
A::FunctionCall {
name,
arguments,
span,
} => A::FunctionCall {
name,
arguments: arguments.into_iter().map(|a| walk(&a, hoisted)).collect(),
span,
},
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
elements: elements.into_iter().map(|e| walk(&e, hoisted)).collect(),
span,
},
A::MapLiteral { entries, span } => A::MapLiteral {
entries: entries
.into_iter()
.map(|(k, v)| (k, walk(&v, hoisted)))
.collect(),
span,
},
other => other,
}
}
let mut hoisted: Vec<A> = Vec::new();
let mut out = walk(ast, &mut hoisted);
if let A::Program { statements, span } = out.clone() {
let mut ss = statements;
ss.extend(hoisted.into_iter());
out = A::Program {
statements: ss,
span,
};
}
out
}