fix(joinir): Phase 96 next_non_ws break condition SSOT

This commit is contained in:
nyash-codex
2025-12-17 01:59:21 +09:00
parent db4453eb3c
commit bc1a09f2c3
6 changed files with 106 additions and 40 deletions

View File

@ -63,19 +63,13 @@ impl BreakConditionAnalyzer {
} = stmt
{
// Pattern 1: Check if the then_body contains a break statement
if then_body
.iter()
.any(|node| matches!(node, ASTNode::Break { .. }))
{
if Self::has_break_in_stmts(then_body) {
return Ok(condition.as_ref());
}
// Pattern 2: Check if the else_body contains a break statement
if let Some(else_stmts) = else_body {
if else_stmts
.iter()
.any(|node| matches!(node, ASTNode::Break { .. }))
{
if Self::has_break_in_stmts(else_stmts) {
// For else-break pattern, return the condition
// Note: Caller must negate this condition
return Ok(condition.as_ref());
@ -86,6 +80,36 @@ impl BreakConditionAnalyzer {
Err("No if-else-break pattern found".to_string())
}
/// Extract a break condition as an owned AST node suitable for lowering.
///
/// This returns the condition in the "break when <cond> is true" form:
/// - `if cond { break }` -> `cond`
/// - `if cond { ... } else { break }` -> `!cond`
///
/// Use this when the caller needs a normalized break condition without separately
/// re-checking whether the break was in then/else.
pub fn extract_break_condition_node(body: &[ASTNode]) -> Result<ASTNode, String> {
for stmt in body {
if let ASTNode::If {
condition,
then_body,
else_body,
..
} = stmt
{
if Self::has_break_in_stmts(then_body) {
return Ok(condition.as_ref().clone());
}
if let Some(else_stmts) = else_body {
if Self::has_break_in_stmts(else_stmts) {
return Ok(Self::negate_condition(condition.as_ref()));
}
}
}
}
Err("No if-else-break pattern found".to_string())
}
/// Check if break exists in else clause
///
/// Helper function to determine if a break statement is in the else clause
@ -198,11 +222,27 @@ impl BreakConditionAnalyzer {
}
}
// Helper: Check if statements contain break
// Helper: Check if statements contain break (recursive)
fn has_break_in_stmts(stmts: &[ASTNode]) -> bool {
stmts
.iter()
.any(|stmt| matches!(stmt, ASTNode::Break { .. }))
stmts.iter().any(Self::has_break_node)
}
fn has_break_node(node: &ASTNode) -> bool {
match node {
ASTNode::Break { .. } => true,
ASTNode::If {
then_body,
else_body,
..
} => {
then_body.iter().any(Self::has_break_node)
|| else_body
.as_ref()
.map_or(false, |e| e.iter().any(Self::has_break_node))
}
ASTNode::Loop { body, .. } => body.iter().any(Self::has_break_node),
_ => false,
}
}
// Helper: Recursively collect variables