Merge pull request #79 from olback/common-math-functions

Add common math functions
This commit is contained in:
ISibboI 2021-06-02 16:43:23 +03:00 committed by GitHub
commit 7d2f2f86c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 158 additions and 23 deletions

View File

@ -320,16 +320,38 @@ For more information about user-defined functions, refer to the respective [sect
This crate offers a set of builtin functions. This crate offers a set of builtin functions.
| Identifier | Argument Amount | Argument Types | Description | | Identifier | Argument Amount | Argument Types | Description |
|------------|-----------------|----------------|-------------| |----------------------|-----------------|------------------------|-------------|
| `min` | >= 1 | Numeric | Returns the minimum of the arguments | | `min` | >= 1 | Numeric | Returns the minimum of the arguments |
| `max` | >= 1 | Numeric | Returns the maximum of the arguments | | `max` | >= 1 | Numeric | Returns the maximum of the arguments |
| `len` | 1 | String/Tuple | Returns the character length of a string, or the amount of elements in a tuple (not recursively) | | `len` | 1 | String/Tuple | Returns the character length of a string, or the amount of elements in a tuple (not recursively) |
| `str::regex_matches` | 2 | String, String | Returns true if the first argument matches the regex in the second argument (Requires `regex_support` feature flag) | | `floor` | 1 | Numeric | Returns the largest integer less than or equal to a number |
| `str::regex_replace` | 3 | String, String, String | Returns the first argument with all matches of the regex in the second argument replaced by the third argument (Requires `regex_support` feature flag) | | `round` | 1 | Numeric | Returns the nearest integer to a number. Round half-way cases away from 0.0 |
| `str::to_lowercase` | 1 | String | Returns the lower-case version of the string | | `ceil` | 1 | Numeric | Returns the smallest integer greater than or equal to a number |
| `str::to_uppercase` | 1 | String | Returns the upper-case version of the string | | `math::ln` | 1 | Numeric | Returns the natural logarithm of the number |
| `str::trim` | 1 | String | Strips whitespace from the start and the end of the string | | `math::log` | 2 | Numeric, Numeric | Returns the logarithm of the number with respect to an arbitrary base |
| `math::log2` | 1 | Numeric | Returns the base 2 logarithm of the number |
| `math::log10` | 1 | Numeric | Returns the base 10 logarithm of the number |
| `math::cos` | 1 | Numeric | Computes the cosine of a number (in radians) |
| `math::acos` | 1 | Numeric | Computes the arccosine of a number. Return value is in radians in the range [0, pi] or NaN if the number is outside the range [-1, 1] |
| `math::cosh` | 1 | Numeric | Hyperbolic cosine function |
| `math::acosh` | 1 | Numeric | Inverse hyperbolic cosine function |
| `math::sin` | 1 | Numeric | Computes the sine of a number (in radians) |
| `math::asin` | 1 | Numeric | Computes the arcsine of a number. Return value is in radians in the range [-pi/2, pi/2] or NaN if the number is outside the range [-1, 1] |
| `math::sinh` | 1 | Numeric | Hyperbolic sine function |
| `math::asinh` | 1 | Numeric | Inverse hyperbolic sine function |
| `math::tan` | 1 | Numeric | Computes the tangent of a number (in radians) |
| `math::atan` | 1 | Numeric | Computes the arctangent of a number. Return value is in radians in the range [-pi/2, pi/2] |
| `math::tanh` | 1 | Numeric | Hyperbolic tangent function |
| `math::atanh` | 1 | Numeric | Inverse hyperbolic tangent function. |
| `math::sqrt` | 1 | Numeric | Returns the square root of a number. Returns NaN if a negative number |
| `math::cbrt` | 1 | Numeric | Returns the cube root of a number |
| `str::regex_matches` | 2 | String, String | Returns true if the first argument matches the regex in the second argument (Requires `regex_support` feature flag) |
| `str::regex_replace` | 3 | String, String, String | Returns the first argument with all matches of the regex in the second argument replaced by the third argument (Requires `regex_support` feature flag) |
| `str::to_lowercase` | 1 | String | Returns the lower-case version of the string |
| `str::to_uppercase` | 1 | String | Returns the upper-case version of the string |
| `str::trim` | 1 | String | Strips whitespace from the start and the end of the string |
| `str::from` | >= 0 | Any | Returns passed value as string |
The `min` and `max` functions can deal with a mixture of integer and floating point arguments. The `min` and `max` functions can deal with a mixture of integer and floating point arguments.
If the maximum or minimum is an integer, then an integer is returned. If the maximum or minimum is an integer, then an integer is returned.

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
"math::ln" => simple_math!(ln),
"math::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)))
})),
"math::log2" => simple_math!(log2),
"math::log10" => simple_math!(log10),
// Cos
"math::cos" => simple_math!(cos),
"math::acos" => simple_math!(acos),
"math::cosh" => simple_math!(cosh),
"math::acosh" => simple_math!(acosh),
// Sin
"math::sin" => simple_math!(sin),
"math::asin" => simple_math!(asin),
"math::sinh" => simple_math!(sinh),
"math::asinh" => simple_math!(asinh),
// Tan
"math::tan" => simple_math!(tan),
"math::atan" => simple_math!(atan),
"math::tanh" => simple_math!(tanh),
"math::atanh" => simple_math!(atanh),
// Root
"math::sqrt" => simple_math!(sqrt),
"math::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

@ -307,16 +307,38 @@
//! //!
//! This crate offers a set of builtin functions. //! This crate offers a set of builtin functions.
//! //!
//! | Identifier | Argument Amount | Argument Types | Description | //! | Identifier | Argument Amount | Argument Types | Description |
//! |------------|-----------------|----------------|-------------| //! |----------------------|-----------------|------------------------|-------------|
//! | `min` | >= 1 | Numeric | Returns the minimum of the arguments | //! | `min` | >= 1 | Numeric | Returns the minimum of the arguments |
//! | `max` | >= 1 | Numeric | Returns the maximum of the arguments | //! | `max` | >= 1 | Numeric | Returns the maximum of the arguments |
//! | `len` | 1 | String/Tuple | Returns the character length of a string, or the amount of elements in a tuple (not recursively) | //! | `len` | 1 | String/Tuple | Returns the character length of a string, or the amount of elements in a tuple (not recursively) |
//! | `str::regex_matches` | 2 | String, String | Returns true if the first argument matches the regex in the second argument (Requires `regex_support` feature flag) | //! | `floor` | 1 | Numeric | Returns the largest integer less than or equal to a number |
//! | `str::regex_replace` | 3 | String, String, String | Returns the first argument with all matches of the regex in the second argument replaced by the third argument (Requires `regex_support` feature flag) | //! | `round` | 1 | Numeric | Returns the nearest integer to a number. Round half-way cases away from 0.0 |
//! | `str::to_lowercase` | 1 | String | Returns the lower-case version of the string | //! | `ceil` | 1 | Numeric | Returns the smallest integer greater than or equal to a number |
//! | `str::to_uppercase` | 1 | String | Returns the upper-case version of the string | //! | `math::ln` | 1 | Numeric | Returns the natural logarithm of the number |
//! | `str::trim` | 1 | String | Strips whitespace from the start and the end of the string | //! | `math::log` | 2 | Numeric, Numeric | Returns the logarithm of the number with respect to an arbitrary base |
//! | `math::log2` | 1 | Numeric | Returns the base 2 logarithm of the number |
//! | `math::log10` | 1 | Numeric | Returns the base 10 logarithm of the number |
//! | `math::cos` | 1 | Numeric | Computes the cosine of a number (in radians) |
//! | `math::acos` | 1 | Numeric | Computes the arccosine of a number. Return value is in radians in the range [0, pi] or NaN if the number is outside the range [-1, 1] |
//! | `math::cosh` | 1 | Numeric | Hyperbolic cosine function |
//! | `math::acosh` | 1 | Numeric | Inverse hyperbolic cosine function |
//! | `math::sin` | 1 | Numeric | Computes the sine of a number (in radians) |
//! | `math::asin` | 1 | Numeric | Computes the arcsine of a number. Return value is in radians in the range [-pi/2, pi/2] or NaN if the number is outside the range [-1, 1] |
//! | `math::sinh` | 1 | Numeric | Hyperbolic sine function |
//! | `math::asinh` | 1 | Numeric | Inverse hyperbolic sine function |
//! | `math::tan` | 1 | Numeric | Computes the tangent of a number (in radians) |
//! | `math::atan` | 1 | Numeric | Computes the arctangent of a number. Return value is in radians in the range [-pi/2, pi/2] |
//! | `math::tanh` | 1 | Numeric | Hyperbolic tangent function |
//! | `math::atanh` | 1 | Numeric | Inverse hyperbolic tangent function. |
//! | `math::sqrt` | 1 | Numeric | Returns the square root of a number. Returns NaN if a negative number |
//! | `math::cbrt` | 1 | Numeric | Returns the cube root of a number |
//! | `str::regex_matches` | 2 | String, String | Returns true if the first argument matches the regex in the second argument (Requires `regex_support` feature flag) |
//! | `str::regex_replace` | 3 | String, String, String | Returns the first argument with all matches of the regex in the second argument replaced by the third argument (Requires `regex_support` feature flag) |
//! | `str::to_lowercase` | 1 | String | Returns the lower-case version of the string |
//! | `str::to_uppercase` | 1 | String | Returns the upper-case version of the string |
//! | `str::trim` | 1 | String | Strips whitespace from the start and the end of the string |
//! | `str::from` | >= 0 | Any | Returns passed value as string |
//! //!
//! The `min` and `max` functions can deal with a mixture of integer and floating point arguments. //! The `min` and `max` functions can deal with a mixture of integer and floating point arguments.
//! If the maximum or minimum is an integer, then an integer is returned. //! If the maximum or minimum is an integer, then an integer is returned.

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("math::ln(2.718281828459045)"), Ok(Value::Float(1.0)));
assert_eq!(eval("math::log(9, 9)"), Ok(Value::Float(1.0)));
assert_eq!(eval("math::log2(2)"), Ok(Value::Float(1.0)));
assert_eq!(eval("math::log10(10)"), Ok(Value::Float(1.0)));
// Cos
assert_eq!(eval("math::cos(0)"), Ok(Value::Float(1.0)));
assert_eq!(eval("math::acos(1)"), Ok(Value::Float(0.0)));
assert_eq!(eval("math::cosh(0)"), Ok(Value::Float(1.0)));
assert_eq!(eval("math::acosh(1)"), Ok(Value::Float(0.0)));
// Sin
assert_eq!(eval("math::sin(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("math::asin(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("math::sinh(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("math::asinh(0)"), Ok(Value::Float(0.0)));
// Tan
assert_eq!(eval("math::tan(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("math::atan(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("math::tanh(0)"), Ok(Value::Float(0.0)));
assert_eq!(eval("math::atanh(0)"), Ok(Value::Float(0.0)));
// Root
assert_eq!(eval("math::sqrt(25)"), Ok(Value::Float(5.0)));
assert_eq!(eval("math::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,21 @@ 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]