Implement functions with arbitrary argument amount

Implements #7
This commit is contained in:
Sebastian Schmidt 2019-03-19 18:58:53 +02:00
parent 8e898044c0
commit fd3243e555
3 changed files with 28 additions and 13 deletions

View File

@ -45,7 +45,7 @@ use evalexpr::error::expect_number;
let mut configuration = HashMapConfiguration::new(); let mut configuration = HashMapConfiguration::new();
configuration.insert_variable("five", 5); configuration.insert_variable("five", 5);
configuration.insert_variable("twelve", 12); configuration.insert_variable("twelve", 12);
configuration.insert_function("f", Function::new(1 /* argument amount */, Box::new(|arguments| { configuration.insert_function("f", Function::new(Some(1) /* argument amount */, Box::new(|arguments| {
if let Value::Int(int) = arguments[0] { if let Value::Int(int) = arguments[0] {
Ok(Value::Int(int / 2)) Ok(Value::Int(int / 2))
} else if let Value::Float(float) = arguments[0] { } else if let Value::Float(float) = arguments[0] {
@ -54,7 +54,7 @@ configuration.insert_function("f", Function::new(1 /* argument amount */, Box::n
Err(Error::expected_number(arguments[0].clone())) Err(Error::expected_number(arguments[0].clone()))
} }
}))); })));
configuration.insert_function("avg", Function::new(2 /* argument amount */, Box::new(|arguments| { configuration.insert_function("avg", Function::new(Some(2) /* argument amount */, Box::new(|arguments| {
expect_number(&arguments[0])?; expect_number(&arguments[0])?;
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
@ -115,6 +115,18 @@ Supported unary operators:
| - | 110 | Negation | | - | 110 | Negation |
| ! | 110 | Logical not | | ! | 110 | Logical not |
#### The Aggregation Operator
The aggregation operator aggregates two values into a tuple.
If one of the values is a tuple already, the resulting tuple will be flattened.
Example:
```rust
use evalexpr::*;
assert_eq!(eval("1, 2, 3"), Ok(Value::from(vec![Value::from(1), Value::from(2), Value::from(3)])));
```
### Values ### Values
Operators take values as arguments and produce values as results. Operators take values as arguments and produce values as results.
@ -157,7 +169,7 @@ This crate also allows to define arbitrary functions to be used in parsed expres
A function is defined as a `Function` instance. A function is defined as a `Function` instance.
It contains two properties, the `argument_amount` and the `function`. It contains two properties, the `argument_amount` and the `function`.
The `function` is a boxed `Fn(&[Value]) -> Result<Value, Error>`. The `function` is a boxed `Fn(&[Value]) -> Result<Value, Error>`.
The `argument_amount` determines the length of the slice that is passed to `function`. The `argument_amount` determines the length of the slice that is passed to `function` if it is `Some(_)`, otherwise the function is defined to take an arbitrary amount of arguments.
It is verified on execution by the crate and does not need to be verified by the `function`. It is verified on execution by the crate and does not need to be verified by the `function`.
Be aware that functions need to verify the types of values that are passed to them. Be aware that functions need to verify the types of values that are passed to them.

View File

@ -2,13 +2,13 @@ use error::{self, Error};
use value::Value; use value::Value;
pub struct Function { pub struct Function {
argument_amount: usize, argument_amount: Option<usize>,
function: Box<Fn(&[Value]) -> Result<Value, Error>>, function: Box<Fn(&[Value]) -> Result<Value, Error>>,
} }
impl Function { impl Function {
pub fn new( pub fn new(
argument_amount: usize, argument_amount: Option<usize>,
function: Box<Fn(&[Value]) -> Result<Value, Error>>, function: Box<Fn(&[Value]) -> Result<Value, Error>>,
) -> Self { ) -> Self {
Self { Self {
@ -18,7 +18,10 @@ impl Function {
} }
pub fn call(&self, arguments: &[Value]) -> Result<Value, Error> { pub fn call(&self, arguments: &[Value]) -> Result<Value, Error> {
error::expect_function_argument_amount(arguments.len(), self.argument_amount)?; if let Some(argument_amount) = self.argument_amount {
error::expect_function_argument_amount(arguments.len(), argument_amount)?;
}
(self.function)(arguments) (self.function)(arguments)
} }
} }

View File

@ -34,7 +34,7 @@
//! let mut configuration = HashMapConfiguration::new(); //! let mut configuration = HashMapConfiguration::new();
//! configuration.insert_variable("five", 5); //! configuration.insert_variable("five", 5);
//! configuration.insert_variable("twelve", 12); //! configuration.insert_variable("twelve", 12);
//! configuration.insert_function("f", Function::new(1 /* argument amount */, Box::new(|arguments| { //! configuration.insert_function("f", Function::new(Some(1) /* argument amount */, Box::new(|arguments| {
//! if let Value::Int(int) = arguments[0] { //! if let Value::Int(int) = arguments[0] {
//! Ok(Value::Int(int / 2)) //! Ok(Value::Int(int / 2))
//! } else if let Value::Float(float) = arguments[0] { //! } else if let Value::Float(float) = arguments[0] {
@ -43,7 +43,7 @@
//! Err(Error::expected_number(arguments[0].clone())) //! Err(Error::expected_number(arguments[0].clone()))
//! } //! }
//! }))); //! })));
//! configuration.insert_function("avg", Function::new(2 /* argument amount */, Box::new(|arguments| { //! configuration.insert_function("avg", Function::new(Some(2) /* argument amount */, Box::new(|arguments| {
//! expect_number(&arguments[0])?; //! expect_number(&arguments[0])?;
//! expect_number(&arguments[1])?; //! expect_number(&arguments[1])?;
//! //!
@ -158,7 +158,7 @@
//! A function is defined as a `Function` instance. //! A function is defined as a `Function` instance.
//! It contains two properties, the `argument_amount` and the `function`. //! It contains two properties, the `argument_amount` and the `function`.
//! The `function` is a boxed `Fn(&[Value]) -> Result<Value, Error>`. //! The `function` is a boxed `Fn(&[Value]) -> Result<Value, Error>`.
//! The `argument_amount` determines the length of the slice that is passed to `function`. //! The `argument_amount` determines the length of the slice that is passed to `function` if it is `Some(_)`, otherwise the function is defined to take an arbitrary amount of arguments.
//! It is verified on execution by the crate and does not need to be verified by the `function`. //! It is verified on execution by the crate and does not need to be verified by the `function`.
//! //!
//! Be aware that functions need to verify the types of values that are passed to them. //! Be aware that functions need to verify the types of values that are passed to them.
@ -365,7 +365,7 @@ mod test {
configuration.insert_function( configuration.insert_function(
"sub2".to_string(), "sub2".to_string(),
Function::new( Function::new(
1, Some(1),
Box::new(|arguments| { Box::new(|arguments| {
if let Value::Int(int) = arguments[0] { if let Value::Int(int) = arguments[0] {
Ok(Value::Int(int - 2)) Ok(Value::Int(int - 2))
@ -407,7 +407,7 @@ mod test {
configuration.insert_function( configuration.insert_function(
"sub2", "sub2",
Function::new( Function::new(
1, Some(1),
Box::new(|arguments| { Box::new(|arguments| {
if let Value::Int(int) = arguments[0] { if let Value::Int(int) = arguments[0] {
Ok(Value::Int(int - 2)) Ok(Value::Int(int - 2))
@ -422,7 +422,7 @@ mod test {
configuration.insert_function( configuration.insert_function(
"avg", "avg",
Function::new( Function::new(
2, Some(2),
Box::new(|arguments| { Box::new(|arguments| {
expect_number(&arguments[0])?; expect_number(&arguments[0])?;
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
@ -440,7 +440,7 @@ mod test {
configuration.insert_function( configuration.insert_function(
"muladd", "muladd",
Function::new( Function::new(
3, Some(3),
Box::new(|arguments| { Box::new(|arguments| {
expect_number(&arguments[0])?; expect_number(&arguments[0])?;
expect_number(&arguments[1])?; expect_number(&arguments[1])?;