use std::fmt;
use crate::decompiler::cfg::ssa::variable::SsaVariable;
use crate::decompiler::ir::{BinOp, Literal, UnaryOp};
#[derive(Debug, Clone, PartialEq)]
pub enum SsaExpr {
Variable(SsaVariable),
Literal(Literal),
Binary {
op: BinOp,
left: Box<SsaExpr>,
right: Box<SsaExpr>,
},
Unary {
op: UnaryOp,
operand: Box<SsaExpr>,
},
Call {
name: String,
args: Vec<SsaExpr>,
},
Index {
base: Box<SsaExpr>,
index: Box<SsaExpr>,
},
Member {
base: Box<SsaExpr>,
name: String,
},
Cast {
expr: Box<SsaExpr>,
target_type: String,
},
Array(Vec<SsaExpr>),
Map(Vec<(SsaExpr, SsaExpr)>),
Ternary {
condition: Box<SsaExpr>,
then_expr: Box<SsaExpr>,
else_expr: Box<SsaExpr>,
},
}
impl SsaExpr {
#[must_use]
pub fn var(var: SsaVariable) -> Self {
Self::Variable(var)
}
#[must_use]
pub const fn lit(literal: Literal) -> Self {
Self::Literal(literal)
}
#[must_use]
pub fn binary(op: BinOp, left: SsaExpr, right: SsaExpr) -> Self {
Self::Binary {
op,
left: Box::new(left),
right: Box::new(right),
}
}
#[must_use]
pub fn unary(op: UnaryOp, operand: SsaExpr) -> Self {
Self::Unary {
op,
operand: Box::new(operand),
}
}
#[must_use]
pub fn call(name: String, args: Vec<SsaExpr>) -> Self {
Self::Call { name, args }
}
}
impl fmt::Display for SsaExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Variable(var) => write!(f, "{}", var),
Self::Literal(lit) => write!(f, "{}", lit),
Self::Binary { op, left, right } => write!(f, "({} {} {})", left, op, right),
Self::Unary { op, operand } => write!(f, "{}({})", op, operand),
Self::Call { name, args } => write_call(f, name, args),
Self::Index { base, index } => write!(f, "{}[{}]", base, index),
Self::Member { base, name } => write!(f, "{}.{}", base, name),
Self::Cast { expr, target_type } => write!(f, "{} as {}", expr, target_type),
Self::Array(elements) => write_sequence(f, "[", "]", elements),
Self::Map(pairs) => write_map(f, pairs),
Self::Ternary {
condition,
then_expr,
else_expr,
} => write!(f, "{} ? {} : {}", condition, then_expr, else_expr),
}
}
}
fn write_call(f: &mut fmt::Formatter<'_>, name: &str, args: &[SsaExpr]) -> fmt::Result {
write!(f, "{name}(")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
}
write!(f, ")")
}
fn write_sequence(
f: &mut fmt::Formatter<'_>,
open: &str,
close: &str,
elements: &[SsaExpr],
) -> fmt::Result {
write!(f, "{open}")?;
for (i, elem) in elements.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", elem)?;
}
write!(f, "{close}")
}
fn write_map(f: &mut fmt::Formatter<'_>, pairs: &[(SsaExpr, SsaExpr)]) -> fmt::Result {
write!(f, "{{")?;
for (i, (key, value)) in pairs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", key, value)?;
}
write!(f, "}}")
}