use crate::hir::common::{
HirBinaryOpKind, HirDecisionTarget, HirExpr, HirUnaryOpKind,
};
pub(super) fn expr_is_side_effect_free(expr: &HirExpr) -> bool {
match expr {
HirExpr::Nil
| HirExpr::Boolean(_)
| HirExpr::Integer(_)
| HirExpr::Number(_)
| HirExpr::String(_)
| HirExpr::Int64(_)
| HirExpr::UInt64(_)
| HirExpr::Complex { .. }
| HirExpr::ParamRef(_)
| HirExpr::LocalRef(_)
| HirExpr::UpvalueRef(_)
| HirExpr::TempRef(_)
| HirExpr::GlobalRef(_) => true,
HirExpr::Unary(unary) => expr_is_side_effect_free(&unary.expr),
HirExpr::Binary(binary) => {
expr_is_side_effect_free(&binary.lhs) && expr_is_side_effect_free(&binary.rhs)
}
HirExpr::LogicalAnd(logical) | HirExpr::LogicalOr(logical) => {
expr_is_side_effect_free(&logical.lhs) && expr_is_side_effect_free(&logical.rhs)
}
HirExpr::Decision(decision) => decision.nodes.iter().all(|node| {
expr_is_side_effect_free(&node.test)
&& decision_target_is_side_effect_free(&node.truthy)
&& decision_target_is_side_effect_free(&node.falsy)
}),
HirExpr::TableAccess(_)
| HirExpr::Call(_)
| HirExpr::VarArg
| HirExpr::TableConstructor(_)
| HirExpr::Closure(_)
| HirExpr::Unresolved(_) => false,
}
}
fn decision_target_is_side_effect_free(target: &HirDecisionTarget) -> bool {
match target {
HirDecisionTarget::Node(_) | HirDecisionTarget::CurrentValue => true,
HirDecisionTarget::Expr(expr) => expr_is_side_effect_free(expr),
}
}
pub(super) fn expr_truthiness(expr: &HirExpr) -> Option<bool> {
match expr {
HirExpr::Nil => Some(false),
HirExpr::Boolean(value) => Some(*value),
HirExpr::Integer(_)
| HirExpr::Number(_)
| HirExpr::String(_)
| HirExpr::Int64(_)
| HirExpr::UInt64(_)
| HirExpr::Complex { .. }
| HirExpr::Closure(_)
| HirExpr::TableConstructor(_) => Some(true),
HirExpr::ParamRef(_)
| HirExpr::LocalRef(_)
| HirExpr::UpvalueRef(_)
| HirExpr::TempRef(_)
| HirExpr::GlobalRef(_)
| HirExpr::TableAccess(_)
| HirExpr::Unary(_)
| HirExpr::Binary(_)
| HirExpr::LogicalAnd(_)
| HirExpr::LogicalOr(_)
| HirExpr::Decision(_)
| HirExpr::Call(_)
| HirExpr::VarArg
| HirExpr::Unresolved(_) => None,
}
}
pub(super) fn expr_is_boolean_valued(expr: &HirExpr) -> bool {
match expr {
HirExpr::Boolean(_) => true,
HirExpr::Unary(unary) if unary.op == HirUnaryOpKind::Not => true,
HirExpr::Binary(binary) => matches!(
binary.op,
HirBinaryOpKind::Eq | HirBinaryOpKind::Lt | HirBinaryOpKind::Le
),
HirExpr::Decision(decision) => decision.nodes.iter().all(|node| {
decision_target_is_boolean(&node.truthy) && decision_target_is_boolean(&node.falsy)
}),
_ => false,
}
}
fn decision_target_is_boolean(target: &HirDecisionTarget) -> bool {
match target {
HirDecisionTarget::Node(_) | HirDecisionTarget::CurrentValue => false,
HirDecisionTarget::Expr(expr) => expr_is_boolean_valued(expr),
}
}
pub(super) fn fold_associative_duplicate_and(lhs: &HirExpr, rhs: &HirExpr) -> Option<HirExpr> {
match lhs {
HirExpr::LogicalAnd(inner) if rhs == &inner.lhs || rhs == &inner.rhs => Some(lhs.clone()),
_ => match rhs {
HirExpr::LogicalAnd(inner) if lhs == &inner.lhs || lhs == &inner.rhs => {
Some(rhs.clone())
}
_ => None,
},
}
}
pub(super) fn fold_associative_duplicate_or(lhs: &HirExpr, rhs: &HirExpr) -> Option<HirExpr> {
match lhs {
HirExpr::LogicalOr(inner) if rhs == &inner.lhs || rhs == &inner.rhs => Some(lhs.clone()),
_ => match rhs {
HirExpr::LogicalOr(inner) if lhs == &inner.lhs || lhs == &inner.rhs => {
Some(rhs.clone())
}
_ => None,
},
}
}