From 52e4dfc10616accaa6e4dcdbfc4a706e94dbcdc9 Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Fri, 19 Jan 2024 19:06:09 +0100 Subject: math: pregenerate eval tree --- src/math/expression.rs | 80 +++++++++++++++++++++++++++++------------ src/math/expression_function.rs | 10 +++--- src/math/function.rs | 51 +++++++++++++++++--------- 3 files changed, 98 insertions(+), 43 deletions(-) (limited to 'src/math') diff --git a/src/math/expression.rs b/src/math/expression.rs index 9d6d4f3..049886d 100644 --- a/src/math/expression.rs +++ b/src/math/expression.rs @@ -1,25 +1,42 @@ -use crate::math::complex::Complex; -use crate::math::context::Context; -use crate::math::function::FunctionArgument; -use crate::math::string::{ContainsAndSkipBrackets, SplitMatchingBracket}; +use super::complex::Complex; +use super::context::Context; +use super::function::FunctionArgument; +use super::string::{ContainsAndSkipBrackets, SplitMatchingBracket}; +use super::operation::Operation; + +enum ExpressionType { + Value(Complex), + Variable(String), + Function(String, FunctionArgument), + Operation(Operation, Box, Box), +} + +impl Default for ExpressionType { + fn default() -> Self { + ExpressionType::Value(Complex::default()) + } +} #[derive(Default)] pub struct Expression { + expr_type: ExpressionType, repr: String, } impl Expression { - pub fn from_string(str: &str) -> Self { - Self { - repr: str.replace(' ', ""), - } + pub fn from_string(str: &str, operations: &Vec) -> Self { + let mut ex = Self::default(); + ex.repr = str.to_string(); + ex.set_operations(operations); + + ex } pub fn content(&self) -> &str { &self.repr } - pub fn evaluate(&self, context: &Context) -> Result { + pub fn set_operations(&mut self, operations: &Vec) { let (repr, oprepr) = { if self.repr.starts_with('(') { let (oprepr, r) = self.repr.split_on_matching_bracket(); @@ -36,7 +53,7 @@ impl Expression { let curop: Option<_> = { let mut o = None; - for op in context.operations() { + for op in operations { if oprepr.contains_and_skip_brackets(op.sign().to_string().as_str()) { o = Some(op); break; @@ -47,27 +64,44 @@ impl Expression { if let Some(op) = curop { let (first, rest) = repr.split_once_and_skipt_brackets(op.sign()).unwrap(); - let first_expr = Expression::from_string(first); - let rest_expr = Expression::from_string(rest); - Ok(op.evaluate(first_expr.evaluate(context)?, rest_expr.evaluate(context)?)) + let first_expr = Expression::from_string(first, operations); + let rest_expr = Expression::from_string(rest, operations); + self.expr_type = ExpressionType::Operation(op.clone(), Box::new(first_expr), Box::new(rest_expr)); } else if let Ok(r) = repr.parse::() { - Ok(r) + self.expr_type = ExpressionType::Value(r); } else if let Some((func, args)) = repr.split_once('(') { let mut argv = Vec::new(); for arg in args[..args.len() - 1].split(',') { - argv.push(Expression::from_string(arg).evaluate(context)?); + argv.push(Expression::from_string(arg, operations)); } - if let Some(func) = context.function(func) { - Ok(func.eval(FunctionArgument::new(argv), context)?) - } else { - Err(format!("function '{func}' not found")) - } - } else if let Some(res) = context.variable(&repr) { - Ok(*res) + self.expr_type = ExpressionType::Function(func.to_string(), FunctionArgument::from_expressions(argv)); } else { - Err(format!("variable '{repr}' not found")) + self.expr_type = ExpressionType::Variable(repr); + } + } + + pub fn evaluate(&self, ctx: &Context) -> Result { + match &self.expr_type { + ExpressionType::Value(v) => Ok(*v), + ExpressionType::Variable(var) => { + if let Some(v) = ctx.variable(&var) { + Ok(*v) + } else { + Err(format!("variable '{var}' not found")) + } + }, + ExpressionType::Function(func, args) => { + if let Some(f) = ctx.function(&func) { + f.eval(args, ctx) + } else { + Err(format!("function '{func}' not found")) + } + } + ExpressionType::Operation(op, a, b) => { + Ok(op.evaluate(a.evaluate(ctx)?, b.evaluate(ctx)?)) + } } } } diff --git a/src/math/expression_function.rs b/src/math/expression_function.rs index 20f4816..44f83d1 100644 --- a/src/math/expression_function.rs +++ b/src/math/expression_function.rs @@ -5,6 +5,7 @@ use crate::math::{ complex::Complex, context::Context, expression::Expression, + operation::Operation, function::{Function, FunctionArgument, EmptyFunction} }; @@ -16,7 +17,7 @@ pub struct ExpressionFunction { } impl ExpressionFunction { - pub fn from_string(str: String) -> Self { + pub fn from_string(str: String, operations: &Vec) -> Self { let str = str.replace(' ', ""); let (lhs, expr) = str.split_once('=').unwrap(); let (name, a) = lhs.split_once('(').unwrap(); @@ -25,7 +26,7 @@ impl ExpressionFunction { .map(|s| s.to_string()) .collect(); Self { - expr: Expression::from_string(expr), + expr: Expression::from_string(expr, operations), name: name.to_string(), args, } @@ -41,10 +42,11 @@ impl ExpressionFunction { } impl Function for ExpressionFunction { - fn eval(&self, args: FunctionArgument, ctx: &Context) -> Result { + fn eval(&self, args: &FunctionArgument, ctx: &Context) -> Result { if args.len() == self.args.len() { let mut nctx = ctx.clone(); - for (n, v) in zip(self.args.iter(), args.data().iter()) { + let vargs = args.eval_args(ctx)?; + for (n, v) in zip(self.args.iter(), vargs.iter()) { nctx.set_variable(n, *v) } nctx.set_function(self.name(), Arc::new(EmptyFunction {})); diff --git a/src/math/function.rs b/src/math/function.rs index bd782f3..93d288c 100644 --- a/src/math/function.rs +++ b/src/math/function.rs @@ -1,33 +1,51 @@ -use crate::math::{complex::Complex, context::Context}; +use super::expression::Expression; +use super::{complex::Complex, context::Context}; + +enum FunctionArgumentType { + ExpressionArgument(Vec), + ValueArgument(Vec), +} pub struct FunctionArgument { - args : Vec, + args: FunctionArgumentType, + len: usize, } impl FunctionArgument { - pub fn new(args: Vec) -> Self { - Self { args } + pub fn from_expressions(args: Vec) -> Self { + Self { + len: args.len(), + args: FunctionArgumentType::ExpressionArgument(args), + } } - pub fn get(&self, i : usize) -> Complex { - self.args[i] + pub fn from_values(args: Vec) -> Self { + Self { + len: args.len(), + args: FunctionArgumentType::ValueArgument(args), + } } pub fn len(&self) -> usize { - self.args.len() + self.len } pub fn is_empty(&self) -> bool { - self.args.len() == 0 + self.len == 0 } - pub fn data(&self) -> &[Complex] { - &self.args + pub fn eval_args(&self, ctx: &Context) -> Result, String> { + match &self.args { + FunctionArgumentType::ExpressionArgument(args) => { + args.iter().map(|a| a.evaluate(ctx)).collect() + } + FunctionArgumentType::ValueArgument(args) => Ok(args.to_vec()), + } } } pub trait Function { - fn eval(&self, args: FunctionArgument, ctx: &Context) -> Result; + fn eval(&self, args: &FunctionArgument, ctx: &Context) -> Result; } #[macro_export] @@ -43,26 +61,27 @@ macro_rules! functions { }; } - // Some default implementations impl Function for T where T: Fn(Complex) -> Complex, { - fn eval(&self, args: FunctionArgument, _ctx: &Context) -> Result { + fn eval(&self, args: &FunctionArgument, ctx: &Context) -> Result { if args.len() == 1 { - Ok(self(args.get(0))) + match args.eval_args(ctx) { + Ok(args) => Ok(self(args[0])), + Err(e) => Err(e), + } } else { Err("too many arguments".to_string()) } } } - pub struct EmptyFunction {} impl Function for EmptyFunction { - fn eval(&self, _args: FunctionArgument, _ctx: &Context) -> Result { + fn eval(&self, _args: &FunctionArgument, _ctx: &Context) -> Result { Err("function not implemented in this scope".to_string()) } } -- cgit v1.2.3-70-g09d2