add common math functions

This commit is contained in:
Edwin Svensson 2021-05-30 08:52:28 +02:00
parent 1bed7e1f9f
commit 8491bc61fc
No known key found for this signature in database
GPG Key ID: 7F9EC4DD0C67951F
2 changed files with 85 additions and 3 deletions

View File

@ -6,8 +6,49 @@ use crate::{
EvalexprError, Function, Value, ValueType, EvalexprError, Function, Value, ValueType,
}; };
macro_rules! simple_math {
($func:ident) => {
Some(Function::new(|argument| {
let num = argument.as_number()?;
Ok(Value::Float(num.$func()))
}))
};
}
pub fn builtin_function(identifier: &str) -> Option<Function> { pub fn builtin_function(identifier: &str) -> Option<Function> {
match identifier { match identifier {
// Log
"ln" => simple_math!(ln),
"log" => Some(Function::new(|argument| {
let tuple = argument.as_fixed_len_tuple(2)?;
let (a, b) = (tuple[0].as_number()?, tuple[1].as_number()?);
Ok(Value::Float(a.log(b)))
})),
"log2" => simple_math!(log2),
"log10" => simple_math!(log10),
// Cos
"cos" => simple_math!(cos),
"acos" => simple_math!(acos),
"cosh" => simple_math!(cosh),
"acosh" => simple_math!(acosh),
// Sin
"sin" => simple_math!(sin),
"asin" => simple_math!(asin),
"sinh" => simple_math!(sinh),
"asinh" => simple_math!(asinh),
// Tan
"tan" => simple_math!(tan),
"atan" => simple_math!(atan),
"tanh" => simple_math!(tanh),
"atanh" => simple_math!(atanh),
// Root
"sqrt" => simple_math!(sqrt),
"cbrt" => simple_math!(cbrt),
// Rounding
"floor" => simple_math!(floor),
"round" => simple_math!(round),
"ceil" => simple_math!(ceil),
// Other
"min" => Some(Function::new(|argument| { "min" => Some(Function::new(|argument| {
let arguments = argument.as_tuple()?; let arguments = argument.as_tuple()?;
let mut min_int = IntType::max_value(); let mut min_int = IntType::max_value();
@ -52,7 +93,6 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
Ok(Value::Float(max_float)) Ok(Value::Float(max_float))
} }
})), })),
"len" => Some(Function::new(|argument| { "len" => Some(Function::new(|argument| {
if let Ok(subject) = argument.as_string() { if let Ok(subject) = argument.as_string() {
Ok(Value::from(subject.len() as i64)) Ok(Value::from(subject.len() as i64))
@ -65,8 +105,7 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
)) ))
} }
})), })),
// String functions
// string functions
#[cfg(feature = "regex_support")] #[cfg(feature = "regex_support")]
"str::regex_matches" => Some(Function::new(|argument| { "str::regex_matches" => Some(Function::new(|argument| {
let arguments = argument.as_tuple()?; let arguments = argument.as_tuple()?;
@ -110,6 +149,9 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
let subject = argument.as_string()?; let subject = argument.as_string()?;
Ok(Value::from(subject.trim())) Ok(Value::from(subject.trim()))
})), })),
"str::from" => Some(Function::new(|argument| {
Ok(Value::String(argument.to_string()))
})),
_ => None, _ => None,
} }
} }

View File

@ -271,10 +271,44 @@ fn test_n_ary_functions() {
#[test] #[test]
fn test_builtin_functions() { fn test_builtin_functions() {
// Log
assert_eq!(eval("ln(2.718281828459045)"), Ok(Value::Float(1.0)));
assert_eq!(eval("log(9, 9)"), Ok(Value::Float(1.0)));
assert_eq!(eval("log2(2)"), Ok(Value::Float(1.0)));
assert_eq!(eval("log10(10)"), Ok(Value::Float(1.0)));
// Cos
assert_eq!(eval("cos(0)"), Ok(Value::Float(1.0)));
assert_eq!(eval("acos(1)"), Ok(Value::Float(0.0)));
assert_eq!(eval("cosh(0)"), Ok(Value::Float(1.0)));
assert_eq!(eval("acosh(1)"), Ok(Value::Float(0.0)));
// Sin
assert_eq!(eval("sin(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("asin(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("sinh(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("asinh(0)"), Ok(Value::Float(0.0)));
// Tan
assert_eq!(eval("tan(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("atan(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("tanh(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("atanh(0)"), Ok(Value::Float(0.0)));
// Root
assert_eq!(eval("sqrt(25)"), Ok(Value::Float(5.0)));
assert_eq!(eval("cbrt(8)"), Ok(Value::Float(2.0)));
// Rounding
assert_eq!(eval("floor(1.1)"), Ok(Value::Float(1.0)));
assert_eq!(eval("floor(1.9)"), Ok(Value::Float(1.0)));
assert_eq!(eval("round(1.1)"), Ok(Value::Float(1.0)));
assert_eq!(eval("round(1.5)"), Ok(Value::Float(2.0)));
assert_eq!(eval("round(2.5)"), Ok(Value::Float(3.0)));
assert_eq!(eval("round(1.9)"), Ok(Value::Float(2.0)));
assert_eq!(eval("ceil(1.1)"), Ok(Value::Float(2.0)));
assert_eq!(eval("ceil(1.9)"), Ok(Value::Float(2.0)));
// Other
assert_eq!(eval("min(4.0, 3)"), Ok(Value::Int(3))); assert_eq!(eval("min(4.0, 3)"), Ok(Value::Int(3)));
assert_eq!(eval("max(4.0, 3)"), Ok(Value::Float(4.0))); assert_eq!(eval("max(4.0, 3)"), Ok(Value::Float(4.0)));
assert_eq!(eval("len(\"foobar\")"), Ok(Value::Int(6))); assert_eq!(eval("len(\"foobar\")"), Ok(Value::Int(6)));
assert_eq!(eval("len(\"a\", \"b\")"), Ok(Value::Int(2))); assert_eq!(eval("len(\"a\", \"b\")"), Ok(Value::Int(2)));
// String
assert_eq!( assert_eq!(
eval("str::to_lowercase(\"FOOBAR\")"), eval("str::to_lowercase(\"FOOBAR\")"),
Ok(Value::from("foobar")) Ok(Value::from("foobar"))
@ -287,6 +321,12 @@ fn test_builtin_functions() {
eval("str::trim(\" foo bar \")"), eval("str::trim(\" foo bar \")"),
Ok(Value::from("foo bar")) Ok(Value::from("foo bar"))
); );
assert_eq!(eval("str::from(\"a\")"), Ok(Value::String(String::from("\"a\""))));
assert_eq!(eval("str::from(1.0)"), Ok(Value::String(String::from("1"))));
assert_eq!(eval("str::from(1)"), Ok(Value::String(String::from("1"))));
assert_eq!(eval("str::from(true)"), Ok(Value::String(String::from("true"))));
assert_eq!(eval("str::from(1, 2, 3)"), Ok(Value::String(String::from("(1, 2, 3)"))));
assert_eq!(eval("str::from()"), Ok(Value::String(String::from("()"))));
} }
#[test] #[test]