diff --git a/src/function/builtin.rs b/src/function/builtin.rs index fa776d4..5ea0aac 100644 --- a/src/function/builtin.rs +++ b/src/function/builtin.rs @@ -6,8 +6,49 @@ use crate::{ 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 { 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| { let arguments = argument.as_tuple()?; let mut min_int = IntType::max_value(); @@ -52,7 +93,6 @@ pub fn builtin_function(identifier: &str) -> Option { Ok(Value::Float(max_float)) } })), - "len" => Some(Function::new(|argument| { if let Ok(subject) = argument.as_string() { Ok(Value::from(subject.len() as i64)) @@ -65,8 +105,7 @@ pub fn builtin_function(identifier: &str) -> Option { )) } })), - - // string functions + // String functions #[cfg(feature = "regex_support")] "str::regex_matches" => Some(Function::new(|argument| { let arguments = argument.as_tuple()?; @@ -110,6 +149,9 @@ pub fn builtin_function(identifier: &str) -> Option { let subject = argument.as_string()?; Ok(Value::from(subject.trim())) })), + "str::from" => Some(Function::new(|argument| { + Ok(Value::String(argument.to_string())) + })), _ => None, } } diff --git a/tests/integration.rs b/tests/integration.rs index 1bdede7..972c0ac 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -271,10 +271,44 @@ fn test_n_ary_functions() { #[test] 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("max(4.0, 3)"), Ok(Value::Float(4.0))); assert_eq!(eval("len(\"foobar\")"), Ok(Value::Int(6))); assert_eq!(eval("len(\"a\", \"b\")"), Ok(Value::Int(2))); + // String assert_eq!( eval("str::to_lowercase(\"FOOBAR\")"), Ok(Value::from("foobar")) @@ -287,6 +321,12 @@ fn test_builtin_functions() { eval("str::trim(\" 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]