use crate::ir::Statement;
use super::super::optimization::dce::collect_stmt_refs;
pub fn hoist_scoped_bindings(stmts: Vec<Statement>) -> Vec<Statement> {
if stmts.len() < 2 {
return stmts;
}
let mut all_referenced = std::collections::HashSet::new();
for stmt in &stmts {
collect_stmt_refs(stmt, &mut all_referenced);
}
let mut result = Vec::new();
for stmt in stmts {
match stmt {
Statement::If { condition, then_body, else_body } => {
let then_hoisted = hoist_scoped_bindings(then_body);
let else_hoisted = hoist_scoped_bindings(else_body);
let should_flatten = has_externally_referenced_bindings(
&then_hoisted, &all_referenced,
) || has_externally_referenced_bindings(
&else_hoisted, &all_referenced,
);
if should_flatten {
result.extend(then_hoisted);
result.extend(else_hoisted);
} else {
result.push(Statement::If {
condition,
then_body: then_hoisted,
else_body: else_hoisted,
});
}
}
Statement::While { condition, body } => {
result.push(Statement::While {
condition,
body: hoist_scoped_bindings(body),
});
}
Statement::Loop { body } => {
result.push(Statement::Loop {
body: hoist_scoped_bindings(body),
});
}
Statement::ForEach { var_name, collection, body } => {
result.push(Statement::ForEach {
var_name,
collection,
body: hoist_scoped_bindings(body),
});
}
Statement::ForRange { var_name, bound, body } => {
result.push(Statement::ForRange {
var_name,
bound,
body: hoist_scoped_bindings(body),
});
}
other => result.push(other),
}
}
result
}
fn has_externally_referenced_bindings(
stmts: &[Statement],
external_refs: &std::collections::HashSet<String>,
) -> bool {
for stmt in stmts {
if let Statement::Let { name, .. } = stmt {
let check_name = name.replace('.', "_").replace('[', "_").replace(']', "");
if external_refs.contains(name) || external_refs.contains(&check_name) {
return true;
}
}
match stmt {
Statement::If { then_body, else_body, .. } => {
if has_externally_referenced_bindings(then_body, external_refs) {
return true;
}
if has_externally_referenced_bindings(else_body, external_refs) {
return true;
}
}
Statement::While { body, .. }
| Statement::Loop { body }
| Statement::ForEach { body, .. }
| Statement::ForRange { body, .. } => {
if has_externally_referenced_bindings(body, external_refs) {
return true;
}
}
_ => {}
}
}
false
}