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:
5 files changed
+58
-5
No files matched your search
+1
-1
@@ -19,7 +19,7 @@ impl Display for Operator {
|
|||||||
impl Display for Expression {
|
impl Display for Expression {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Expression::Number(num) => write!(f, "{}", num),
|
Expression::Number(num) => write!(f, "{}", num.normalize()),
|
||||||
Expression::Variable(var) => write!(f, "{}", var),
|
Expression::Variable(var) => write!(f, "{}", var),
|
||||||
Expression::Fraction(numerator, denominator) => {
|
Expression::Fraction(numerator, denominator) => {
|
||||||
write!(f, "({} / {})", numerator, denominator)
|
write!(f, "({} / {})", numerator, denominator)
|
||||||
|
|||||||
+15
-4
@@ -1,11 +1,20 @@
|
|||||||
use rust_decimal::MathematicalOps;
|
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 {
|
pub fn eval_expr(expr: Expression) -> Expression {
|
||||||
match expr {
|
match expr {
|
||||||
Expression::Number(_) => 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) => {
|
Expression::Fraction(numberator, denominator) => {
|
||||||
// TODO: implement proper fraction handling
|
// TODO: implement proper fraction handling
|
||||||
eval_expr(Expression::Operation(
|
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::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::Modulo => Expression::Number(left % right),
|
||||||
Operator::Power => Expression::Number(left.powd(*right)),
|
Operator::Power => Expression::Number(left.powd(*right)),
|
||||||
},
|
},
|
||||||
// TODO: implement handling for variables
|
|
||||||
_ => Expression::Operation(operator, Box::new(left), Box::new(right)),
|
_ => Expression::Operation(operator, Box::new(left), Box::new(right)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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(¶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")
|
||||||
|
}
|
||||||
@@ -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,7 +2,9 @@ use crate::math::types::Expression;
|
|||||||
|
|
||||||
mod display;
|
mod display;
|
||||||
mod eval;
|
mod eval;
|
||||||
|
mod functions;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
|
mod math_constants;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
Reference in new issue
Block a user