use crate::eval::Num; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BinOp { Pow, Mul, Div, IntDiv, Mod, Add, Sub, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum UnOp { Neg, Sin, Cos, Tan, Asin, Acos, Atan, Sqrt, Log, Log10, Log2, Floor, Ceil, Round, Abs, } #[derive(Debug, PartialEq)] pub enum Expr { Num(Num), UnOp { op: UnOp, x: Box, }, BinOp { op: BinOp, x: Box, y: Box, }, } impl BinOp { pub fn expr(self, x: Expr, y: Expr) -> Expr { Expr::BinOp { op: self, x: Box::new(x), y: Box::new(y), } } fn eval(self, x: Num, y: Num) -> Num { match self { BinOp::Pow => x.pow(y), BinOp::Mul => x.mul(y), BinOp::Div => x.div(y), BinOp::IntDiv => x.int_div(y), BinOp::Mod => x.modulo(y), BinOp::Add => x.add(y), BinOp::Sub => x.sub(y), } } } impl UnOp { pub fn expr(self, x: Expr) -> Expr { Expr::UnOp { op: self, x: Box::new(x), } } fn eval(self, x: Num) -> Num { match self { UnOp::Neg => Num::Int(0).sub(x), UnOp::Sin => x.sin(), UnOp::Cos => x.cos(), UnOp::Tan => x.tan(), UnOp::Asin => x.asin(), UnOp::Acos => x.acos(), UnOp::Atan => x.atan(), UnOp::Sqrt => x.sqrt(), UnOp::Log => x.log(), UnOp::Log10 => x.log10(), UnOp::Log2 => x.log2(), UnOp::Floor => x.floor(), UnOp::Ceil => x.ceil(), UnOp::Round => x.round(), UnOp::Abs => x.abs(), } } } impl Expr { pub fn eval(&self) -> Num { enum Ex<'a> { Expr(&'a Expr), Un(UnOp), Bin(BinOp), } let mut exprs = Vec::new(); let mut nums = Vec::new(); exprs.push(Ex::Expr(self)); while let Some(e) = exprs.pop() { match e { Ex::Expr(Expr::Num(n)) => nums.push(*n), Ex::Expr(Expr::UnOp { op, x }) => { exprs.push(Ex::Un(*op)); exprs.push(Ex::Expr(x)); } Ex::Expr(Expr::BinOp { op, x, y }) => { exprs.push(Ex::Bin(*op)); exprs.push(Ex::Expr(y)); exprs.push(Ex::Expr(x)); } Ex::Un(op) => { let n = nums.pop().unwrap(); nums.push(op.eval(n)); } Ex::Bin(op) => { let n2 = nums.pop().unwrap(); let n1 = nums.pop().unwrap(); nums.push(op.eval(n1, n2)); } } } nums[0] } }