diff --git a/src/math/display.rs b/src/math/display.rs index 23ae892..76a24d9 100644 --- a/src/math/display.rs +++ b/src/math/display.rs @@ -19,7 +19,7 @@ impl Display for Operator { impl Display for Expression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Expression::Number(num) => write!(f, "{}", num), + Expression::Number(num) => write!(f, "{}", num.normalize()), Expression::Variable(var) => write!(f, "{}", var), Expression::Fraction(numerator, denominator) => { write!(f, "({} / {})", numerator, denominator) diff --git a/src/math/eval.rs b/src/math/eval.rs index 6c0145f..f5771ab 100644 --- a/src/math/eval.rs +++ b/src/math/eval.rs @@ -1,11 +1,20 @@ use rust_decimal::MathematicalOps; -use crate::math::types::{Expression, Operator}; +use crate::math::{ + functions, math_constants, + types::{Expression, Operator}, +}; pub fn eval_expr(expr: Expression) -> Expression { match expr { Expression::Number(_) => expr, - Expression::Variable(_) => expr, + Expression::Variable(name) => { + if let Some(value) = math_constants::get(&name) { + Expression::Number(value) + } else { + Expression::Variable(name) + } + } Expression::Fraction(numberator, denominator) => { // TODO: implement proper fraction handling eval_expr(Expression::Operation( @@ -15,7 +24,10 @@ pub fn eval_expr(expr: Expression) -> Expression { )) } Expression::Operation(operator, left, right) => eval_operation(operator, *left, *right), - Expression::Function(_name, _params) => todo!(), + Expression::Function(name, params) => { + let params = params.into_iter().map(eval_expr).collect(); + functions::eval_function(&name, params) + } } } @@ -31,7 +43,6 @@ fn eval_operation(operator: Operator, left: Expression, right: Expression) -> Ex Operator::Modulo => Expression::Number(left % right), Operator::Power => Expression::Number(left.powd(*right)), }, - // TODO: implement handling for variables _ => Expression::Operation(operator, Box::new(left), Box::new(right)), } } diff --git a/src/math/functions.rs b/src/math/functions.rs new file mode 100644 index 0000000..16d53d4 --- /dev/null +++ b/src/math/functions.rs @@ -0,0 +1,31 @@ +use crate::math::types::Expression; +use rust_decimal::Decimal; +use rust_decimal::MathematicalOps; +use rust_decimal::prelude::ToPrimitive; + +pub fn eval_function(func_name: &str, params: Vec) -> Expression { + match func_name { + "sin" => map_decimal(¶ms[0], |d| d.sin()), + "cos" => map_decimal(¶ms[0], |d| d.cos()), + "tan" => map_decimal(¶ms[0], |d| d.tan()), + "asin" => map_decimal(¶ms[0], |d| map_f64(&d, |f| f.asin())), + "acos" => map_decimal(¶ms[0], |d| map_f64(&d, |f| f.acos())), + "atan" => map_decimal(¶ms[0], |d| map_f64(&d, |f| f.atan())), + "abs" => map_decimal(¶ms[0], |d| d.abs()), + "sqrt" => map_decimal(¶ms[0], |d| d.abs().sqrt().expect("impossible")), + _ => panic!("unknown function"), + } +} + +fn map_decimal(expr: &Expression, f: fn(Decimal) -> Decimal) -> Expression { + match expr { + Expression::Number(d) => Expression::Number(f(*d)), + _ => panic!("expected decimal"), + } +} + +fn map_f64(decimal: &Decimal, f: fn(f64) -> f64) -> Decimal { + let num = decimal.to_f64().expect("unable to unwrap decimal"); + let num = f(num); + Decimal::from_f64_retain(num).expect("unable to wrap decimal") +} diff --git a/src/math/math_constants.rs b/src/math/math_constants.rs new file mode 100644 index 0000000..bce5a39 --- /dev/null +++ b/src/math/math_constants.rs @@ -0,0 +1,9 @@ +use rust_decimal::Decimal; + +pub fn get(name: &str) -> Option { + match &name.to_lowercase()[..] { + "pi" => Some(Decimal::PI), + "e" => Some(Decimal::E), + _ => None, + } +} diff --git a/src/math/mod.rs b/src/math/mod.rs index c272e33..e8e4b3a 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -2,7 +2,9 @@ use crate::math::types::Expression; mod display; mod eval; +mod functions; mod lexer; +mod math_constants; mod parser; pub mod types;