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();
configuration.insert_variable("five", 5);
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] {
Ok(Value::Int(int / 2))
} 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()))
}
})));
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[1])?;
@ -115,6 +115,18 @@ Supported unary operators:
| - | 110 | Negation |
| ! | 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
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.
It contains two properties, the `argument_amount` and the `function`.
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`.
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;
pub struct Function {
argument_amount: usize,
argument_amount: Option<usize>,
function: Box<Fn(&[Value]) -> Result<Value, Error>>,
}
impl Function {
pub fn new(
argument_amount: usize,
argument_amount: Option<usize>,
function: Box<Fn(&[Value]) -> Result<Value, Error>>,
) -> Self {
Self {
@ -18,7 +18,10 @@ impl Function {
}
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)
}
}

View File

@ -34,7 +34,7 @@
//! let mut configuration = HashMapConfiguration::new();
//! configuration.insert_variable("five", 5);
//! 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] {
//! Ok(Value::Int(int / 2))
//! } else if let Value::Float(float) = arguments[0] {
@ -43,7 +43,7 @@
//! 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[1])?;
//!
@ -158,7 +158,7 @@
//! A function is defined as a `Function` instance.
//! It contains two properties, the `argument_amount` and the `function`.
//! 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`.
//!
//! 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(
"sub2".to_string(),
Function::new(
1,
Some(1),
Box::new(|arguments| {
if let Value::Int(int) = arguments[0] {
Ok(Value::Int(int - 2))
@ -407,7 +407,7 @@ mod test {
configuration.insert_function(
"sub2",
Function::new(
1,
Some(1),
Box::new(|arguments| {
if let Value::Int(int) = arguments[0] {
Ok(Value::Int(int - 2))
@ -422,7 +422,7 @@ mod test {
configuration.insert_function(
"avg",
Function::new(
2,
Some(2),
Box::new(|arguments| {
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -440,7 +440,7 @@ mod test {
configuration.insert_function(
"muladd",
Function::new(
3,
Some(3),
Box::new(|arguments| {
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;