From bbc0ef6045b676a793ffbb3f3f2a2007deee326b Mon Sep 17 00:00:00 2001 From: ozzy <2+ozzy@noreply.localhost> Date: Sat, 26 Sep 2026 15:31:33 -0500 Subject: [PATCH] feat(eval): add functions and constants added constants pi, e added functions sin, cos, tan, asin, acos, atan, abs, and sqrt fixed displaying decimals without normalizing them todo: add imaginary number support --- src/math/display.rs | 2 +- src/math/eval.rs | 19 +++++++++++++++---- src/math/functions.rs | 31 +++++++++++++++++++++++++++++++ src/math/math_constants.rs | 9 +++++++++ src/math/mod.rs | 2 ++ 5 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 src/math/functions.rs create mode 100644 src/math/math_constants.rs 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;