diff --git a/src/function/builtin.rs b/src/function/builtin.rs new file mode 100644 index 0000000..7a3d128 --- /dev/null +++ b/src/function/builtin.rs @@ -0,0 +1,58 @@ +use value::{FloatType, IntType}; +use Error; +use Function; +use Value; + +pub fn builtin_function(identifier: &str) -> Option { + match identifier { + "min" => Some(Function::new( + None, + Box::new(|arguments| { + let mut min_int = IntType::max_value(); + let mut min_float = 1.0f64 / 0.0f64; + debug_assert!(min_float.is_infinite()); + + for argument in arguments { + if let Value::Float(float) = argument { + min_float = min_float.min(*float); + } else if let Value::Int(int) = argument { + min_int = min_int.min(*int); + } else { + return Err(Error::expected_number(argument.clone())); + } + } + + if (min_int as FloatType) < min_float { + Ok(Value::Int(min_int)) + } else { + Ok(Value::Float(min_float)) + } + }), + )), + "max" => Some(Function::new( + None, + Box::new(|arguments| { + let mut max_int = IntType::min_value(); + let mut max_float = -1.0f64 / 0.0f64; + debug_assert!(max_float.is_infinite()); + + for argument in arguments { + if let Value::Float(float) = argument { + max_float = max_float.max(*float); + } else if let Value::Int(int) = argument { + max_int = max_int.max(*int); + } else { + return Err(Error::expected_number(argument.clone())); + } + } + + if (max_int as FloatType) > max_float { + Ok(Value::Int(max_int)) + } else { + Ok(Value::Float(max_float)) + } + }), + )), + _ => None, + } +} diff --git a/src/function/mod.rs b/src/function/mod.rs index d449d0b..baa0877 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -1,6 +1,8 @@ use error::{self, Error}; use value::Value; +pub mod builtin; + pub struct Function { argument_amount: Option, function: Box Result>, diff --git a/src/lib.rs b/src/lib.rs index 5cf528b..35240a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,6 +116,18 @@ //! assert_eq!(eval("1, 2, 3"), Ok(Value::from(vec![Value::from(1), Value::from(2), Value::from(3)]))); //! ``` //! +//! ### Builtin Functions +//! +//! This crate offers a set of builtin functions. +//! +//! | Identifier | Argument Amount | Description | +//! |------------|-----------------|-------------| +//! | min | >= 1 | Returns the minimum of the arguments | +//! | max | >= 1 | Returns the maximum of the arguments | +//! +//! The `min` and `max` functions can deal with a mixture of integer and floating point arguments. +//! They return the result as the type it was passed into the function. +//! //! ### Values //! //! Operators take values as arguments and produce values as results. @@ -152,7 +164,7 @@ //! //! Variables have a precedence of 200. //! -//! ### Functions +//! ### User-Defined Functions //! //! This crate also allows to define arbitrary functions to be used in parsed expressions. //! A function is defined as a `Function` instance. @@ -504,6 +516,15 @@ mod test { eval_with_configuration("count 5", &configuration), Ok(Value::Int(1)) ); + + assert_eq!( + eval_with_configuration("min(4.0, 3)", &configuration), + Ok(Value::Int(3)) + ); + assert_eq!( + eval_with_configuration("max(4.0, 3)", &configuration), + Ok(Value::Float(4.0)) + ); } #[test] diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 63cc174..a9664ce 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -1,4 +1,5 @@ use crate::{configuration::Configuration, error::*, value::Value}; +use function::builtin::builtin_function; use std::fmt::{Debug, Display}; mod display; @@ -687,6 +688,8 @@ impl Operator for FunctionIdentifier { if let Some(function) = configuration.get_function(&self.identifier) { function.call(arguments) + } else if let Some(builtin_function) = builtin_function(&self.identifier) { + builtin_function.call(arguments) } else { Err(Error::FunctionIdentifierNotFound(self.identifier.clone())) }