macro(if/match): stabilize PeekExpr→If normalization via internal-child; default runner off; propagate child stderr; fix JsonBuilder local_decl; add scope-hints doc; extend PHI smoke; golden for match literal passes

This commit is contained in:
Selfhosting Dev
2025-09-20 05:00:31 +09:00
parent 8cb93b9f1f
commit 166c374eec
14 changed files with 362 additions and 47 deletions

View File

@ -1,5 +1,67 @@
use serde_json::Value;
fn map_expr_to_stmt(e: nyash_rust::ASTNode) -> nyash_rust::ASTNode { e }
fn transform_peek_to_if_expr(peek: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span};
if let A::PeekExpr { scrutinee, arms, else_expr, .. } = peek {
// only support literal-only arms conservatively
let mut conds_bodies: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new();
for (lit, body) in arms {
conds_bodies.push((lit.clone(), (*body).clone()));
}
let mut current: A = *(*else_expr).clone();
for (lit, body) in conds_bodies.into_iter().rev() {
let rhs = A::Literal { value: lit, span: Span::unknown() };
let cond = A::BinaryOp { operator: BinaryOperator::Equal, left: scrutinee.clone(), right: Box::new(rhs), span: Span::unknown() };
let then_body = vec![map_expr_to_stmt(body)];
let else_body = Some(vec![map_expr_to_stmt(current)]);
current = A::If { condition: Box::new(cond), then_body, else_body, span: Span::unknown() };
}
Some(current)
} else { None }
}
fn transform_peek_match_literal_local_init(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
use nyash_rust::ast::ASTNode as A;
match ast.clone() {
A::Program { statements, span } => {
A::Program { statements: statements.into_iter().map(|n| transform_peek_match_literal_local_init(&n)).collect(), span }
}
A::If { condition, then_body, else_body, span } => {
A::If {
condition: Box::new(transform_peek_match_literal_local_init(&condition)),
then_body: then_body.into_iter().map(|n| transform_peek_match_literal_local_init(&n)).collect(),
else_body: else_body.map(|v| v.into_iter().map(|n| transform_peek_match_literal_local_init(&n)).collect()),
span,
}
}
A::Loop { condition, body, span } => {
A::Loop {
condition: Box::new(transform_peek_match_literal_local_init(&condition)),
body: body.into_iter().map(|n| transform_peek_match_literal_local_init(&n)).collect(),
span,
}
}
A::Local { variables, initial_values, span } => {
let mut new_inits: Vec<Option<Box<A>>> = Vec::with_capacity(initial_values.len());
for opt in initial_values {
if let Some(v) = opt {
if let Some(ifexpr) = transform_peek_to_if_expr(&v) {
new_inits.push(Some(Box::new(ifexpr)));
} else {
new_inits.push(Some(Box::new(transform_peek_match_literal_local_init(&v))));
}
} else {
new_inits.push(None);
}
}
A::Local { variables, initial_values: new_inits, span }
}
other => other,
}
}
fn transform_array_prepend_zero(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
use nyash_rust::ast::{ASTNode as A, LiteralValue, Span};
match ast {
@ -99,6 +161,9 @@ pub fn run_macro_child(macro_file: &str) {
// MVP: identity (future: normalize Loop into carrier-based form)
ast.clone()
}
crate::r#macro::macro_box_ny::MacroBehavior::IfMatchNormalize => {
transform_peek_match_literal_local_init(&ast)
}
};
let out_json = crate::r#macro::ast_json::ast_to_json(&out_ast);
println!("{}", out_json.to_string());