start on #13
This commit is contained in:
@@ -148,6 +148,10 @@ impl Grid {
|
|||||||
// panic!("Will not be able to parse this equation, cell {e} not found")
|
// panic!("Will not be able to parse this equation, cell {e} not found")
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
EvalexprError::TypeError { expected: _, actual: _ } => {
|
||||||
|
// IE: You put a string into a function that wants a float
|
||||||
|
return None
|
||||||
|
}
|
||||||
_ => panic!("{}", e),
|
_ => panic!("{}", e),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -431,3 +435,74 @@ fn grid_max() {
|
|||||||
assert_eq!(mx, 1);
|
assert_eq!(mx, 1);
|
||||||
assert_eq!(my, 5);
|
assert_eq!(my, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn avg_function() {
|
||||||
|
let mut grid = Grid::new();
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=avg(5)".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_some());
|
||||||
|
assert_eq!(res.unwrap(), 5.);
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=avg(5,10)".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_some());
|
||||||
|
assert_eq!(res.unwrap(), 7.5);
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=avg(5,10,15)".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_some());
|
||||||
|
assert_eq!(res.unwrap(), 10.);
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=avg(foo)".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_none());
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=avg(1, foo)".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_none());
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=avg()".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sum_function() {
|
||||||
|
let mut grid = Grid::new();
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=sum(5)".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_some());
|
||||||
|
assert_eq!(res.unwrap(), 5.);
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=sum(5,10)".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_some());
|
||||||
|
assert_eq!(res.unwrap(), 15.);
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=sum(foo)".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_none());
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=sum(1, foo)".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_none());
|
||||||
|
|
||||||
|
grid.set_cell("A0", "=sum()".to_string());
|
||||||
|
let cell = grid.get_cell("A0").as_ref().expect("Just set the cell");
|
||||||
|
let res = grid.evaluate(&cell.to_string());
|
||||||
|
assert!(res.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,76 @@
|
|||||||
use std::{collections::HashMap, sync::RwLock};
|
use std::{collections::HashMap, process::id, sync::RwLock};
|
||||||
|
|
||||||
use evalexpr::{error::EvalexprResultValue, *};
|
use evalexpr::{error::EvalexprResultValue, *};
|
||||||
|
|
||||||
use crate::app::logic::calc::Grid;
|
use crate::app::logic::calc::Grid;
|
||||||
|
|
||||||
pub struct CallbackContext<'a, T: EvalexprNumericTypes = DefaultNumericTypes> {
|
pub struct CallbackContext<'a> {
|
||||||
variables: &'a Grid,
|
variables: &'a Grid,
|
||||||
eval_depth: RwLock<usize>,
|
eval_depth: RwLock<usize>,
|
||||||
functions: HashMap<String, Function<T>>,
|
functions: HashMap<String, Function<DefaultNumericTypes>>,
|
||||||
|
|
||||||
/// True if builtin functions are disabled.
|
/// True if builtin functions are disabled.
|
||||||
without_builtin_functions: bool,
|
without_builtin_functions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, NumericTypes: EvalexprNumericTypes> CallbackContext<'a, NumericTypes> {
|
impl<'a> CallbackContext<'a> {
|
||||||
|
fn get_functions() -> HashMap<String, Function<DefaultNumericTypes>> {
|
||||||
|
let mut functions = HashMap::new();
|
||||||
|
|
||||||
|
functions.insert(
|
||||||
|
"avg".to_string(),
|
||||||
|
Function::new(|arg| {
|
||||||
|
if arg.is_tuple() {
|
||||||
|
let args = arg.as_tuple()?;
|
||||||
|
let mut total: f64 = 0.;
|
||||||
|
let mut count = 0f64;
|
||||||
|
for i in args {
|
||||||
|
total += i.as_number()?;
|
||||||
|
count += 1.;
|
||||||
|
}
|
||||||
|
let res = total / count;
|
||||||
|
Ok(Value::Float(res))
|
||||||
|
} else if arg.is_float() {
|
||||||
|
Ok(Value::Float(arg.as_float()?))
|
||||||
|
} else {
|
||||||
|
Err(EvalexprError::TypeError {
|
||||||
|
expected: vec![ValueType::Float],
|
||||||
|
actual: arg.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
functions.insert(
|
||||||
|
"sum".to_string(),
|
||||||
|
Function::new(|arg| {
|
||||||
|
if arg.is_tuple() {
|
||||||
|
let args = arg.as_tuple()?;
|
||||||
|
let mut total: f64 = 0.;
|
||||||
|
for i in args {
|
||||||
|
total += i.as_number()?;
|
||||||
|
}
|
||||||
|
let res = total;
|
||||||
|
Ok(Value::Float(res))
|
||||||
|
} else if arg.is_float() {
|
||||||
|
Ok(Value::Float(arg.as_float()?))
|
||||||
|
} else {
|
||||||
|
Err(EvalexprError::TypeError {
|
||||||
|
expected: vec![ValueType::Float],
|
||||||
|
actual: arg.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
functions
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(grid: &'a Grid) -> Self {
|
pub fn new(grid: &'a Grid) -> Self {
|
||||||
Self {
|
Self {
|
||||||
eval_depth: RwLock::new(0),
|
eval_depth: RwLock::new(0),
|
||||||
variables: grid,
|
variables: grid,
|
||||||
functions: Default::default(),
|
functions: Self::get_functions(),
|
||||||
without_builtin_functions: false,
|
without_builtin_functions: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,12 +89,11 @@ impl<'a, NumericTypes: EvalexprNumericTypes> CallbackContext<'a, NumericTypes> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context for CallbackContext<'a, DefaultNumericTypes> {
|
impl<'a> Context for CallbackContext<'a> {
|
||||||
type NumericTypes = DefaultNumericTypes;
|
type NumericTypes = DefaultNumericTypes;
|
||||||
|
|
||||||
fn get_value(&self, identifier: &str) -> Option<Value<Self::NumericTypes>> {
|
fn get_value(&self, identifier: &str) -> Option<Value<Self::NumericTypes>> {
|
||||||
if let Some(v) = self.variables.get_cell(identifier) {
|
if let Some(v) = self.variables.get_cell(identifier) {
|
||||||
|
|
||||||
match v {
|
match v {
|
||||||
super::calc::CellType::Number(n) => return Some(Value::Float(n.to_owned())),
|
super::calc::CellType::Number(n) => return Some(Value::Float(n.to_owned())),
|
||||||
super::calc::CellType::String(_) => return None,
|
super::calc::CellType::String(_) => return None,
|
||||||
@@ -52,12 +103,12 @@ impl<'a> Context for CallbackContext<'a, DefaultNumericTypes> {
|
|||||||
}
|
}
|
||||||
if let Ok(depth) = self.eval_depth.read() {
|
if let Ok(depth) = self.eval_depth.read() {
|
||||||
if *depth > 10 {
|
if *depth > 10 {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// It would be unsafe to continue to process without knowing how
|
// It would be unsafe to continue to process without knowing how
|
||||||
// deep we've gone.
|
// deep we've gone.
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
match eval_with_context(&eq[1..], self) {
|
match eval_with_context(&eq[1..], self) {
|
||||||
Ok(e) => return Some(e),
|
Ok(e) => return Some(e),
|
||||||
@@ -67,14 +118,14 @@ impl<'a> Context for CallbackContext<'a, DefaultNumericTypes> {
|
|||||||
// If the variable isn't found, that's ~~probably~~ because
|
// If the variable isn't found, that's ~~probably~~ because
|
||||||
// of recursive reference, considering all references
|
// of recursive reference, considering all references
|
||||||
// are grabbed straight from the table.
|
// are grabbed straight from the table.
|
||||||
return None
|
return None;
|
||||||
},
|
}
|
||||||
|
|
||||||
e => panic!("> Error {e}\n> Equation: '{eq}'"),
|
e => panic!("> Error {e}\n> Equation: '{eq}'"),
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
@@ -85,17 +136,18 @@ impl<'a> Context for CallbackContext<'a, DefaultNumericTypes> {
|
|||||||
identifier: &str,
|
identifier: &str,
|
||||||
argument: &Value<Self::NumericTypes>,
|
argument: &Value<Self::NumericTypes>,
|
||||||
) -> EvalexprResultValue<Self::NumericTypes> {
|
) -> EvalexprResultValue<Self::NumericTypes> {
|
||||||
todo!()
|
if let Some(function) = self.functions.get(identifier) {
|
||||||
|
function.call(argument)
|
||||||
|
} else {
|
||||||
|
Err(EvalexprError::FunctionIdentifierNotFound(identifier.to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn are_builtin_functions_disabled(&self) -> bool {
|
fn are_builtin_functions_disabled(&self) -> bool {
|
||||||
self.without_builtin_functions
|
self.without_builtin_functions
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_builtin_functions_disabled(
|
fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<(), Self::NumericTypes> {
|
||||||
&mut self,
|
|
||||||
disabled: bool,
|
|
||||||
) -> EvalexprResult<(), Self::NumericTypes> {
|
|
||||||
self.without_builtin_functions = disabled;
|
self.without_builtin_functions = disabled;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user