use crate::env::VariableEnvironment;
use crate::error::ExpansionError;
use crate::eval::ArithEval;
use conch_parser::ast::Arithmetic;
use conch_parser::ast::Arithmetic::*;
use std::borrow::Borrow;
impl<T, E: ?Sized> ArithEval<E> for Arithmetic<T>
where
T: Borrow<String> + Clone,
E: VariableEnvironment,
E::VarName: Borrow<String> + From<T>,
E::Var: Borrow<String> + From<String>,
{
fn eval(&self, env: &mut E) -> Result<isize, ExpansionError> {
let get_var = |env: &E, var: &T| {
env.var(var.borrow())
.and_then(|s| s.borrow().as_str().parse().ok())
.unwrap_or(0)
};
let ret = match *self {
Literal(lit) => lit,
Var(ref var) => get_var(env, var),
PostIncr(ref var) => {
let value = get_var(env, var);
env.set_var(var.clone().into(), (value + 1).to_string().into());
value
}
PostDecr(ref var) => {
let value = get_var(env, var);
env.set_var(var.clone().into(), (value - 1).to_string().into());
value
}
PreIncr(ref var) => {
let value = get_var(env, var) + 1;
env.set_var(var.clone().into(), value.to_string().into());
value
}
PreDecr(ref var) => {
let value = get_var(env, var) - 1;
env.set_var(var.clone().into(), value.to_string().into());
value
}
UnaryPlus(ref expr) => expr.eval(env)?.abs(),
UnaryMinus(ref expr) => -expr.eval(env)?,
BitwiseNot(ref expr) => expr.eval(env)? ^ !0,
LogicalNot(ref expr) => {
if expr.eval(env)? == 0 {
1
} else {
0
}
}
Less(ref left, ref right) => {
if left.eval(env)? < right.eval(env)? {
1
} else {
0
}
}
LessEq(ref left, ref right) => {
if left.eval(env)? <= right.eval(env)? {
1
} else {
0
}
}
Great(ref left, ref right) => {
if left.eval(env)? > right.eval(env)? {
1
} else {
0
}
}
GreatEq(ref left, ref right) => {
if left.eval(env)? >= right.eval(env)? {
1
} else {
0
}
}
Eq(ref left, ref right) => {
if left.eval(env)? == right.eval(env)? {
1
} else {
0
}
}
NotEq(ref left, ref right) => {
if left.eval(env)? != right.eval(env)? {
1
} else {
0
}
}
Pow(ref left, ref right) => {
let right = right.eval(env)?;
if right.is_negative() {
return Err(ExpansionError::NegativeExponent);
} else {
left.eval(env)?.pow(right as u32)
}
}
Div(ref left, ref right) => {
let right = right.eval(env)?;
if right == 0 {
return Err(ExpansionError::DivideByZero);
} else {
left.eval(env)? / right
}
}
Modulo(ref left, ref right) => {
let right = right.eval(env)?;
if right == 0 {
return Err(ExpansionError::DivideByZero);
} else {
left.eval(env)? % right
}
}
Mult(ref left, ref right) => left.eval(env)? * right.eval(env)?,
Add(ref left, ref right) => left.eval(env)? + right.eval(env)?,
Sub(ref left, ref right) => left.eval(env)? - right.eval(env)?,
ShiftLeft(ref left, ref right) => left.eval(env)? << right.eval(env)?,
ShiftRight(ref left, ref right) => left.eval(env)? >> right.eval(env)?,
BitwiseAnd(ref left, ref right) => left.eval(env)? & right.eval(env)?,
BitwiseXor(ref left, ref right) => left.eval(env)? ^ right.eval(env)?,
BitwiseOr(ref left, ref right) => left.eval(env)? | right.eval(env)?,
LogicalAnd(ref left, ref right) => {
if left.eval(env)? != 0 {
if right.eval(env)? != 0 {
1
} else {
0
}
} else {
0
}
}
LogicalOr(ref left, ref right) => {
if left.eval(env)? == 0 {
if right.eval(env)? != 0 {
1
} else {
0
}
} else {
1
}
}
Ternary(ref guard, ref thn, ref els) => {
if guard.eval(env)? != 0 {
thn.eval(env)?
} else {
els.eval(env)?
}
}
Assign(ref var, ref value) => {
let value = value.eval(env)?;
env.set_var(var.clone().into(), value.to_string().into());
value
}
Sequence(ref exprs) => {
let mut last = 0;
for e in exprs.iter() {
last = e.eval(env)?;
}
last
}
};
Ok(ret)
}
}