From 9cc61497ed8a2336f33407d3262181e4ac3b46cb Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Wed, 17 Jan 2024 23:28:48 +0100 Subject: add commonsense and expression_functions --- src/commonsense.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++ src/complex.rs | 8 +++-- src/context.rs | 2 +- src/expression_function.rs | 60 +++++++++++++++++++++++++++++++ src/function.rs | 6 +++- src/main.rs | 67 +++++----------------------------- src/operation.rs | 2 +- 7 files changed, 171 insertions(+), 63 deletions(-) create mode 100644 src/commonsense.rs create mode 100644 src/expression_function.rs (limited to 'src') diff --git a/src/commonsense.rs b/src/commonsense.rs new file mode 100644 index 0000000..df688fb --- /dev/null +++ b/src/commonsense.rs @@ -0,0 +1,89 @@ +use crate::complex::Complex; + +pub fn add(a: Complex, b: Complex) -> Complex { + a + b +} + +pub fn sub(a: Complex, b: Complex) -> Complex { + a - b +} + +pub fn mul(a: Complex, b: Complex) -> Complex { + a * b +} + +pub fn div(a: Complex, b: Complex) -> Complex { + a / b +} + +pub fn pow(a: Complex, b: Complex) -> Complex { + a.pow(b) +} + +pub fn sqrt(a: Complex) -> Complex { + a.sqrt() +} + +pub fn sin(a: Complex) -> Complex { + a.sin() +} + +pub fn cos(a: Complex) -> Complex { + a.cos() +} + +pub fn tan(a: Complex) -> Complex { + a.tan() +} + +#[macro_export] +macro_rules! commonsense_functions { + {$($x:expr => $y:expr), *} => { + { + let mut h : HashMap> = HashMap::new(); + h.extend(functions!{ + "sqrt" => &crate::commonsense::sqrt, + "sin" => &crate::commonsense::sin, + "cos" => &crate::commonsense::cos, + "tan" => &crate::commonsense::tan + }); + $( + h.insert($x.to_string(), Box::new($y)); + )* + h + } + }; +} + +#[macro_export] +macro_rules! commonsense_operations { + {$($x:expr => $y:expr), *} => { + vec![ + Operation::new('+', Box::new(&crate::commonsense::add)), + Operation::new('-', Box::new(&crate::commonsense::sub)), + Operation::new('*', Box::new(&crate::commonsense::mul)), + Operation::new('/', Box::new(&crate::commonsense::div)), + Operation::new('^', Box::new(&crate::commonsense::pow)) + $( + Operation::new($x, Box::new($y)), + )*] + }; +} + +#[macro_export] +macro_rules! commonsense_variables { + {$($x:expr => $y:expr), *} => { + { + let mut h : HashMap = HashMap::new(); + h.extend(variables!{ + "pi" => Complex::new(std::f64::consts::PI, 0.0), + "e" => Complex::new(std::f64::consts::E, 0.0) + }); + $( + h.insert($x.to_string(), $y); + )* + h + } + }; +} + diff --git a/src/complex.rs b/src/complex.rs index 642122f..e498234 100644 --- a/src/complex.rs +++ b/src/complex.rs @@ -2,7 +2,7 @@ use std::num::ParseFloatError; use std::str::FromStr; use std::f64::consts::E; -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy, Default, Debug)] pub struct Complex { pub real: f64, pub imag: f64, @@ -103,7 +103,11 @@ impl FromStr for Complex { } } else { if s.contains('i') { - c.imag = s[..s.len() - 1].parse()?; + if s.len() == 1 { + c.imag = 1.0; + } else { + c.imag = s[..s.len() - 1].parse()?; + } } else { c.real = s.parse()?; } diff --git a/src/context.rs b/src/context.rs index 47be92b..de1d09d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -54,7 +54,7 @@ impl Context { #[macro_export] macro_rules! variables { - ({$($x:expr => $y:expr), *}) => { + {$($x:expr => $y:expr), *} => { { let mut h : HashMap = HashMap::new(); $( diff --git a/src/expression_function.rs b/src/expression_function.rs new file mode 100644 index 0000000..35eba5f --- /dev/null +++ b/src/expression_function.rs @@ -0,0 +1,60 @@ +use std::{collections::HashMap, iter::zip}; + +use crate::{ + commonsense_functions, commonsense_operations, functions, + complex::Complex, + context::Context, + expression::Expression, + operation::Operation, + function::{Function, FunctionArgument} +}; + +pub struct ExpressionFunction { + expr: Expression, + name: String, + args: Vec, +} + +impl ExpressionFunction { + pub fn from_string(str: String) -> Self { + let str = str.replace(' ', ""); + let (lhs, expr) = str.split_once('=').unwrap(); + let (name, a) = lhs.split_once('(').unwrap(); + let args: Vec = a[0..a.len() - 1] + .split(',') + .into_iter() + .map(|s| s.to_string()) + .collect(); + Self { + expr: Expression::from_string(expr), + name: name.to_string(), + args, + } + } + + pub fn name(&self) -> &str { + &self.name + } +} + +impl Function for ExpressionFunction { + fn eval(&self, args: FunctionArgument) -> Result { + if args.len() == self.args.len() { + let mut vars = HashMap::new(); + for (n, v) in zip(self.args.iter(), args.data().iter()) { + vars.insert(n.to_string(), v.clone()); + } + let ctx = Context::new() + .with_variables(vars) + .with_functions(commonsense_functions! {}) + .with_operations(commonsense_operations! {}); + self.expr.evaluate(&ctx) + } else { + Err(format!( + "{} takes {} parameters", + self.name, + self.args.len() + )) + } + } +} diff --git a/src/function.rs b/src/function.rs index 442d987..0da4574 100644 --- a/src/function.rs +++ b/src/function.rs @@ -16,6 +16,10 @@ impl FunctionArgument { pub fn len(&self) -> usize { self.args.len() } + + pub fn data(&self) -> &[Complex] { + &self.args + } } pub trait Function { @@ -24,7 +28,7 @@ pub trait Function { #[macro_export] macro_rules! functions { - ({$($x:expr => $y:expr), *}) => { + {$($x:expr => $y:expr), *} => { { let mut h : HashMap> = HashMap::new(); $( diff --git a/src/main.rs b/src/main.rs index d1e914d..466ad09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ pub mod expression; pub mod function; pub mod operation; pub mod string; +pub mod commonsense; +pub mod expression_function; use std::collections::HashMap; @@ -12,72 +14,21 @@ use context::Context; use expression::Expression; use function::Function; use operation::Operation; - -fn add(a: Complex, b: Complex) -> Complex { - a + b -} - -fn sub(a: Complex, b: Complex) -> Complex { - a - b -} - -fn mul(a: Complex, b: Complex) -> Complex { - a * b -} - -fn div(a: Complex, b: Complex) -> Complex { - a / b -} - -fn pow(a: Complex, b: Complex) -> Complex { - a.pow(b) -} - -fn sqrt(a: Complex) -> Complex { - a.sqrt() -} - -fn sin(a: Complex) -> Complex { - a.sin() -} - -fn cos(a: Complex) -> Complex { - a.cos() -} - -fn tan(a: Complex) -> Complex { - a.tan() -} +use expression_function::ExpressionFunction; fn main() { - let expr = "cos(3)"; + let func = ExpressionFunction::from_string("f(x) = x^2".to_string()); + + let expr = "f(3)"; let ctx: Context = Context::new() - .with_operations(operations![{ - '+' => &add, - '-' => &sub, - '*' => &mul, - '/' => &div, - '^' => &pow - }]) - .with_functions(functions!({"sqrt" => &sqrt, "sin" => &sin, "cos" => &cos, "tan" => &tan})) - .with_variables(variables!({"x" => Complex::new(5.0, 0.0)})); + .with_operations(commonsense_operations!{}) + .with_functions(commonsense_functions!{func.name() => func}) + .with_variables(commonsense_variables!{}); let value = Expression::from_string(expr); match value.evaluate(&ctx) { Ok(res) => println!("{} = {}", expr, res), Err(err) => println!("Error: {}", err), } - - let value = Expression::from_string("sin(3)"); - match value.evaluate(&ctx) { - Ok(res) => println!("{} = {}", expr, res), - Err(err) => println!("Error: {}", err), - } - - let value = Expression::from_string("tan(3)"); - match value.evaluate(&ctx) { - Ok(res) => println!("{} = {}", expr, res), - Err(err) => println!("Error: {}", err), - } } diff --git a/src/operation.rs b/src/operation.rs index 1ee3d5c..a61ae1f 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -23,7 +23,7 @@ impl Operation { #[macro_export] macro_rules! operations { - ({$($x:expr => $y:expr), *}) => { + {$($x:expr => $y:expr), *} => { vec![$( Operation::new($x, Box::new($y)), )*] -- cgit v1.2.3-70-g09d2