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
This commit is contained in:
ozzy committed 2026-09-26 15:31:33 -05:00
1 parent 234b2ef2dc
commit bbc0ef6045
5 files changed
+58 -5

No files matched your search

+1 -1
View File
@@ -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)
+15 -4
View File
@@ -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)),
}
}
+31
View File
@@ -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>) -> Expression {
match func_name {
"sin" => map_decimal(&params[0], |d| d.sin()),
"cos" => map_decimal(&params[0], |d| d.cos()),
"tan" => map_decimal(&params[0], |d| d.tan()),
"asin" => map_decimal(&params[0], |d| map_f64(&d, |f| f.asin())),
"acos" => map_decimal(&params[0], |d| map_f64(&d, |f| f.acos())),
"atan" => map_decimal(&params[0], |d| map_f64(&d, |f| f.atan())),
"abs" => map_decimal(&params[0], |d| d.abs()),
"sqrt" => map_decimal(&params[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")
}
+9
View File
@@ -0,0 +1,9 @@
use rust_decimal::Decimal;
pub fn get(name: &str) -> Option<Decimal> {
match &name.to_lowercase()[..] {
"pi" => Some(Decimal::PI),
"e" => Some(Decimal::E),
_ => None,
}
}
+2
View File
@@ -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;