use crate::Real;
use crate::error::ExprError;
use crate::types::{AstExpr, FunctionName, HString};
use alloc::format;
#[derive(Clone)]
pub enum EvalOp<'arena> {
Eval {
expr: &'arena AstExpr<'arena>,
ctx_id: usize,
},
ApplyUnary { op: UnaryOp },
CompleteBinary { op: BinaryOp },
ShortCircuitAnd {
right_expr: &'arena AstExpr<'arena>,
ctx_id: usize,
},
ShortCircuitOr {
right_expr: &'arena AstExpr<'arena>,
ctx_id: usize,
},
CompleteAnd,
CompleteOr,
ApplyFunction {
name: FunctionName,
arg_count: usize,
ctx_id: usize,
},
TernaryCondition {
true_branch: &'arena AstExpr<'arena>,
false_branch: &'arena AstExpr<'arena>,
ctx_id: usize,
},
LookupVariable { name: HString, ctx_id: usize },
AccessArray { array_name: HString, ctx_id: usize },
AccessAttribute {
object_name: HString,
attr_name: HString,
ctx_id: usize,
},
RestoreFunctionParams {
params: Option<&'arena [(crate::types::HString, crate::Real)]>,
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum UnaryOp {
Negate,
Not,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BinaryOp {
Add,
Subtract,
Multiply,
Divide,
Modulo,
Power,
Less,
Greater,
LessEqual,
GreaterEqual,
Equal,
NotEqual,
}
impl UnaryOp {
pub fn apply(self, operand: Real) -> Real {
match self {
UnaryOp::Negate => -operand,
UnaryOp::Not => {
if operand == 0.0 {
1.0
} else {
0.0
}
}
}
}
}
impl BinaryOp {
pub fn apply(self, left: Real, right: Real) -> Real {
match self {
BinaryOp::Add => left + right,
BinaryOp::Subtract => left - right,
BinaryOp::Multiply => left * right,
BinaryOp::Divide => left / right,
BinaryOp::Modulo => left % right,
BinaryOp::Power => {
#[cfg(feature = "libm")]
{
crate::functions::pow(left, right)
}
#[cfg(not(feature = "libm"))]
{
panic!("Power operation requires libm feature or registered pow function")
}
}
BinaryOp::Less => {
if left < right {
1.0
} else {
0.0
}
}
BinaryOp::Greater => {
if left > right {
1.0
} else {
0.0
}
}
BinaryOp::LessEqual => {
if left <= right {
1.0
} else {
0.0
}
}
BinaryOp::GreaterEqual => {
if left >= right {
1.0
} else {
0.0
}
}
BinaryOp::Equal => {
if left == right {
1.0
} else {
0.0
}
}
BinaryOp::NotEqual => {
if left != right {
1.0
} else {
0.0
}
}
}
}
}
pub fn ast_to_stack_op(op: &str) -> Result<BinaryOp, ExprError> {
match op {
"+" => Ok(BinaryOp::Add),
"-" => Ok(BinaryOp::Subtract),
"*" => Ok(BinaryOp::Multiply),
"/" => Ok(BinaryOp::Divide),
"%" => Ok(BinaryOp::Modulo),
"^" | "**" => Ok(BinaryOp::Power),
"<" => Ok(BinaryOp::Less),
">" => Ok(BinaryOp::Greater),
"<=" => Ok(BinaryOp::LessEqual),
">=" => Ok(BinaryOp::GreaterEqual),
"==" => Ok(BinaryOp::Equal),
"!=" => Ok(BinaryOp::NotEqual),
_ => Err(ExprError::Syntax(format!("Unknown operator: {}", op))),
}
}
pub fn is_binary_operator(op: &str) -> bool {
matches!(
op,
"+" | "-" | "*" | "/" | "%" | "^" | "**" | "<" | ">" | "<=" | ">=" | "==" | "!="
)
}
impl<'arena> core::fmt::Debug for EvalOp<'arena> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
EvalOp::Eval { expr: _, ctx_id } => {
write!(f, "Eval {{ expr: <AstExpr>, ctx_id: {} }}", ctx_id)
}
EvalOp::ApplyUnary { op } => {
write!(f, "ApplyUnary {{ op: {:?} }}", op)
}
EvalOp::CompleteBinary { op } => {
write!(f, "CompleteBinary {{ op: {:?} }}", op)
}
EvalOp::ShortCircuitAnd {
right_expr: _,
ctx_id,
} => {
write!(
f,
"ShortCircuitAnd {{ right_expr: <AstExpr>, ctx_id: {} }}",
ctx_id
)
}
EvalOp::ShortCircuitOr {
right_expr: _,
ctx_id,
} => {
write!(
f,
"ShortCircuitOr {{ right_expr: <AstExpr>, ctx_id: {} }}",
ctx_id
)
}
EvalOp::CompleteAnd => write!(f, "CompleteAnd"),
EvalOp::CompleteOr => write!(f, "CompleteOr"),
EvalOp::ApplyFunction {
name,
arg_count,
ctx_id,
} => {
write!(
f,
"ApplyFunction {{ name: {:?}, arg_count: {}, ctx_id: {} }}",
name, arg_count, ctx_id
)
}
EvalOp::LookupVariable { name, ctx_id } => {
write!(
f,
"LookupVariable {{ name: {:?}, ctx_id: {} }}",
name, ctx_id
)
}
EvalOp::TernaryCondition {
true_branch: _,
false_branch: _,
ctx_id,
} => {
write!(
f,
"TernaryCondition {{ true_branch: <AstExpr>, false_branch: <AstExpr>, ctx_id: {} }}",
ctx_id
)
}
EvalOp::AccessArray { array_name, ctx_id } => {
write!(
f,
"AccessArray {{ array_name: {:?}, ctx_id: {} }}",
array_name, ctx_id
)
}
EvalOp::AccessAttribute {
object_name,
attr_name,
ctx_id,
} => {
write!(
f,
"AccessAttribute {{ object_name: {:?}, attr_name: {:?}, ctx_id: {} }}",
object_name, attr_name, ctx_id
)
}
EvalOp::RestoreFunctionParams { params } => {
write!(f, "RestoreFunctionParams {{ params: {} }}", params.is_some())
}
}
}
}