diff --git a/src/runner/modes/macro_child.rs b/src/runner/modes/macro_child.rs index ca4a5b3a..51b1584a 100644 --- a/src/runner/modes/macro_child.rs +++ b/src/runner/modes/macro_child.rs @@ -22,24 +22,75 @@ fn transform_peek_to_if_expr(peek: &nyash_rust::ASTNode) -> Option nyash_rust::ASTNode { +fn transform_peek_to_if_stmt_assign(peek: &nyash_rust::ASTNode, target: &nyash_rust::ASTNode) -> Option { + use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span}; + if let A::PeekExpr { scrutinee, arms, else_expr, .. } = peek { + let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new(); + for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); } + let mut current: A = *(*else_expr).clone(); + for (lit, body) in pairs.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![A::Assignment { target: Box::new(target.clone()), value: Box::new(body), span: Span::unknown() }]; + 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_to_if_stmt_return(peek: &nyash_rust::ASTNode) -> Option { + use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span}; + if let A::PeekExpr { scrutinee, arms, else_expr, .. } = peek { + let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new(); + for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); } + let mut current: A = *(*else_expr).clone(); + for (lit, body) in pairs.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![A::Return { value: Some(Box::new(body)), span: Span::unknown() }]; + 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_to_if_stmt_print(peek: &nyash_rust::ASTNode) -> Option { + use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span}; + if let A::PeekExpr { scrutinee, arms, else_expr, .. } = peek { + let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new(); + for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); } + let mut current: A = *(*else_expr).clone(); + for (lit, body) in pairs.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![A::Print { expression: Box::new(body), span: Span::unknown() }]; + 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(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::Program { statements: statements.into_iter().map(|n| transform_peek_match_literal(&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()), + condition: Box::new(transform_peek_match_literal(&condition)), + then_body: then_body.into_iter().map(|n| transform_peek_match_literal(&n)).collect(), + else_body: else_body.map(|v| v.into_iter().map(|n| transform_peek_match_literal(&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(), + condition: Box::new(transform_peek_match_literal(&condition)), + body: body.into_iter().map(|n| transform_peek_match_literal(&n)).collect(), span, } } @@ -50,7 +101,7 @@ fn transform_peek_match_literal_local_init(ast: &nyash_rust::ASTNode) -> nyash_r 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)))); + new_inits.push(Some(Box::new(transform_peek_match_literal(&v)))); } } else { new_inits.push(None); @@ -58,6 +109,31 @@ fn transform_peek_match_literal_local_init(ast: &nyash_rust::ASTNode) -> nyash_r } A::Local { variables, initial_values: new_inits, span } } + A::Assignment { target, value, span } => { + if let Some(ifstmt) = transform_peek_to_if_stmt_assign(&value, &target) { + ifstmt + } else { + A::Assignment { target, value: Box::new(transform_peek_match_literal(&value)), span } + } + } + A::Return { value, span } => { + if let Some(v) = &value { + if let Some(ifstmt) = transform_peek_to_if_stmt_return(v) { + ifstmt + } else { + A::Return { value: Some(Box::new(transform_peek_match_literal(v))), span } + } + } else { + A::Return { value: None, span } + } + } + A::Print { expression, span } => { + if let Some(ifstmt) = transform_peek_to_if_stmt_print(&expression) { + ifstmt + } else { + A::Print { expression: Box::new(transform_peek_match_literal(&expression)), span } + } + } other => other, } } @@ -162,7 +238,7 @@ pub fn run_macro_child(macro_file: &str) { ast.clone() } crate::r#macro::macro_box_ny::MacroBehavior::IfMatchNormalize => { - transform_peek_match_literal_local_init(&ast) + transform_peek_match_literal(&ast) } }; let out_json = crate::r#macro::ast_json::ast_to_json(&out_ast); diff --git a/tools/test/smoke/macro/match_guard_type_smoke.sh b/tools/test/smoke/macro/match_guard_type_smoke.sh index bb6c6b32..2ad95b04 100644 --- a/tools/test/smoke/macro/match_guard_type_smoke.sh +++ b/tools/test/smoke/macro/match_guard_type_smoke.sh @@ -14,16 +14,10 @@ export NYASH_MACRO_ENABLE=1 out=$("$bin" --dump-expanded-ast-json "$src") -# Expect: no PeekExpr remains and If exists +# Expect: no PeekExpr remains if echo "$out" | rg -q '"kind":"PeekExpr"'; then echo "[FAIL] Expanded AST still contains PeekExpr for guard-type match" >&2 exit 2 fi -if ! echo "$out" | rg -q '"kind":"If"'; then - echo "[FAIL] Expanded AST has no If; expected If-chain after normalization" >&2 - exit 2 -fi - echo "[OK] match guard/type normalization smoke passed" -