aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2024-01-17 23:28:48 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2024-01-17 23:28:48 +0100
commit9cc61497ed8a2336f33407d3262181e4ac3b46cb (patch)
treeaac75b57b0fffea64abcd23cbac4d27c875fee48
parent77cf9aa7535a1d9481f0bd3caeea26e2b85c5019 (diff)
add commonsense and expression_functions
-rw-r--r--src/commonsense.rs89
-rw-r--r--src/complex.rs8
-rw-r--r--src/context.rs2
-rw-r--r--src/expression_function.rs60
-rw-r--r--src/function.rs6
-rw-r--r--src/main.rs67
-rw-r--r--src/operation.rs2
7 files changed, 171 insertions, 63 deletions
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<String, Box<dyn Function>> = 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<String, Complex> = 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<String, Complex> = 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<String>,
+}
+
+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<String> = 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<Complex, String> {
+ 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<String, Box<dyn Function>> = 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)),
)*]