2019-03-18 17:51:09 +00:00
# evalexpr
2016-11-16 16:31:52 +00:00
2019-03-20 13:04:59 +00:00
[![Project Status: Active – The project has reached a stable, usable state and is being actively developed. ](https://www.repostatus.org/badges/latest/active.svg )](https://www.repostatus.org/#active)
2021-08-16 10:51:36 +00:00
[![Coverage Status ](https://coveralls.io/repos/github/ISibboI/evalexpr/badge.svg?branch=main )](https://coveralls.io/github/ISibboI/evalexpr?branch=main)
2019-03-20 10:07:02 +00:00
[![ ](http://meritbadge.herokuapp.com/evalexpr )](https://crates.io/crates/evalexpr)
[![ ](https://docs.rs/evalexpr/badge.svg )](https://docs.rs/evalexpr)
2016-11-16 16:12:26 +00:00
2019-03-28 11:09:06 +00:00
Evalexpr is an expression evaluator and tiny scripting language in Rust.
2019-03-19 14:47:17 +00:00
It has a small and easy to use interface and can be easily integrated into any application.
It is very lightweight and comes with no further dependencies.
Evalexpr is [available on crates.io ](https://crates.io/crates/evalexpr ), and its [API Documentation is available on docs.rs ](https://docs.rs/evalexpr ).
2017-02-25 06:51:55 +00:00
2021-06-02 14:51:51 +00:00
2023-04-13 12:53:19 +00:00
**Minimum Supported Rust Version:** 1.56.1
2021-06-02 14:51:51 +00:00
2019-03-19 08:43:52 +00:00
<!-- cargo - sync - readme start -->
2019-03-19 10:01:06 +00:00
## Quickstart
2019-03-19 08:43:52 +00:00
2019-03-19 10:01:06 +00:00
Add `evalexpr` as dependency to your `Cargo.toml` :
2019-03-19 08:43:52 +00:00
```toml
[dependencies]
2022-07-15 10:16:24 +00:00
evalexpr = "< desired version > "
2019-03-19 08:43:52 +00:00
```
2019-03-21 07:29:04 +00:00
Then you can use `evalexpr` to **evaluate expressions** like this:
2019-03-19 08:43:52 +00:00
```rust
2019-03-19 10:01:06 +00:00
use evalexpr::*;
2019-03-19 08:43:52 +00:00
2019-03-19 10:01:06 +00:00
assert_eq!(eval("1 + 2 + 3"), Ok(Value::from(6)));
2019-03-20 15:30:37 +00:00
// `eval` returns a variant of the `Value` enum,
// while `eval_[type]` returns the respective type directly.
// Both can be used interchangeably.
assert_eq!(eval_int("1 + 2 + 3"), Ok(6));
2019-03-19 10:01:06 +00:00
assert_eq!(eval("1 - 2 * 3"), Ok(Value::from(-5)));
assert_eq!(eval("1.0 + 2 * 3"), Ok(Value::from(7.0)));
assert_eq!(eval("true & & 4 > 2"), Ok(Value::from(true)));
2019-03-19 08:43:52 +00:00
```
2019-03-28 11:09:06 +00:00
You can **chain** expressions and **assign** to variables like this:
```rust
use evalexpr::*;
let mut context = HashMapContext::new();
// Assign 5 to a like this
assert_eq!(eval_empty_with_context_mut("a = 5", & mut context), Ok(EMPTY_VALUE));
// The HashMapContext is type safe, so this will fail now
2019-08-30 07:07:48 +00:00
assert_eq!(eval_empty_with_context_mut("a = 5.0", & mut context),
Err(EvalexprError::expected_int(Value::from(5.0))));
2019-03-28 11:09:06 +00:00
// We can check which value the context stores for a like this
assert_eq!(context.get_value("a"), Some(&Value::from(5)));
// And use the value in another expression like this
assert_eq!(eval_int_with_context_mut("a = a + 2; a", & mut context), Ok(7));
2021-06-15 05:38:37 +00:00
// It is also possible to save a bit of typing by using an operator-assignment operator
2019-08-29 10:21:43 +00:00
assert_eq!(eval_int_with_context_mut("a += 2; a", & mut context), Ok(9));
2019-03-28 11:09:06 +00:00
```
2019-03-21 07:29:04 +00:00
And you can use **variables** and **functions** in expressions like this:
2019-03-19 08:43:52 +00:00
```rust
2019-03-19 10:01:06 +00:00
use evalexpr::*;
2019-04-17 15:24:07 +00:00
let context = context_map! {
2019-04-26 15:33:59 +00:00
"five" => 5,
"twelve" => 12,
2021-05-28 12:14:52 +00:00
"f" => Function::new(|argument| {
2019-08-29 06:12:42 +00:00
if let Ok(int) = argument.as_int() {
2019-04-26 15:33:59 +00:00
Ok(Value::Int(int / 2))
2019-08-29 06:12:42 +00:00
} else if let Ok(float) = argument.as_float() {
2019-04-26 15:33:59 +00:00
Ok(Value::Float(float / 2.0))
} else {
2019-08-29 06:12:42 +00:00
Err(EvalexprError::expected_number(argument.clone()))
2019-04-26 15:33:59 +00:00
}
2021-05-28 12:14:52 +00:00
}),
"avg" => Function::new(|argument| {
2019-08-29 06:12:42 +00:00
let arguments = argument.as_tuple()?;
2019-04-26 15:33:59 +00:00
if let (Value::Int(a), Value::Int(b)) = (& arguments[0], & arguments[1]) {
Ok(Value::Int((a + b) / 2))
} else {
Ok(Value::Float((arguments[0].as_number()? + arguments[1].as_number()?) / 2.0))
}
2021-05-28 12:14:52 +00:00
})
2019-04-26 15:33:59 +00:00
}.unwrap(); // Do proper error handling here
2019-03-19 10:01:06 +00:00
2019-03-27 17:09:20 +00:00
assert_eq!(eval_with_context("five + 8 > f(twelve)", & context), Ok(Value::from(true)));
// `eval_with_context` returns a variant of the `Value` enum,
// while `eval_[type]_with_context` returns the respective type directly.
2019-03-20 15:30:37 +00:00
// Both can be used interchangeably.
2019-03-27 17:09:20 +00:00
assert_eq!(eval_boolean_with_context("five + 8 > f(twelve)", & context), Ok(true));
assert_eq!(eval_with_context("avg(2, 4) == 3", & context), Ok(Value::from(true)));
2019-03-19 08:43:52 +00:00
```
2019-03-21 07:29:04 +00:00
You can also **precompile** expressions like this:
2019-03-19 08:43:52 +00:00
```rust
2019-03-19 10:01:06 +00:00
use evalexpr::*;
2019-03-27 17:09:20 +00:00
let precompiled = build_operator_tree("a * b - c > 5").unwrap(); // Do proper error handling here
2019-03-19 08:43:52 +00:00
2019-04-17 15:24:07 +00:00
let mut context = context_map! {
2019-04-26 15:33:59 +00:00
"a" => 6,
"b" => 2,
"c" => 3
}.unwrap(); // Do proper error handling here
2019-03-27 17:09:20 +00:00
assert_eq!(precompiled.eval_with_context(& context), Ok(Value::from(true)));
2019-03-19 08:43:52 +00:00
2019-03-28 08:14:37 +00:00
context.set_value("c".into(), 8.into()).unwrap(); // Do proper error handling here
2019-03-27 17:09:20 +00:00
assert_eq!(precompiled.eval_with_context(& context), Ok(Value::from(false)));
// `Node::eval_with_context` returns a variant of the `Value` enum,
// while `Node::eval_[type]_with_context` returns the respective type directly.
2019-03-20 15:30:37 +00:00
// Both can be used interchangeably.
2019-03-27 17:09:20 +00:00
assert_eq!(precompiled.eval_boolean_with_context(& context), Ok(false));
2019-03-19 08:43:52 +00:00
```
2019-03-19 10:01:06 +00:00
## Features
2019-03-19 08:43:52 +00:00
2019-03-19 10:01:06 +00:00
### Operators
2019-03-19 08:43:52 +00:00
2019-03-19 14:47:17 +00:00
This crate offers a set of binary and unary operators for building expressions.
2021-06-15 05:38:37 +00:00
Operators have a precedence to determine their order of evaluation, where operators of higher precedence are evaluated first.
2019-03-19 14:47:17 +00:00
The precedence should resemble that of most common programming languages, especially Rust.
2021-06-15 05:38:37 +00:00
Variables and values have a precedence of 200, and function literals have 190.
2019-03-19 14:47:17 +00:00
2019-03-19 10:01:06 +00:00
Supported binary operators:
2019-03-19 08:43:52 +00:00
2019-03-28 14:01:20 +00:00
| Operator | Precedence | Description |
|----------|------------|-------------|
| ^ | 120 | Exponentiation |
| * | 100 | Product |
2021-08-01 12:22:10 +00:00
| / | 100 | Division (integer if both arguments are integers, otherwise float) |
| % | 100 | Modulo (integer if both arguments are integers, otherwise float) |
2019-04-13 16:27:59 +00:00
| + | 95 | Sum or String Concatenation |
2019-03-28 14:01:20 +00:00
| - | 95 | Difference |
| < | 80 | Lower than |
| \> | 80 | Greater than |
| < = | 80 | Lower than or equal |
| \>= | 80 | Greater than or equal |
| == | 80 | Equal |
| != | 80 | Not equal |
| & & | 75 | Logical and |
| | | | 70 | Logical or |
| = | 50 | Assignment |
2019-08-29 10:21:43 +00:00
| += | 50 | Sum-Assignment or String-Concatenation-Assignment |
| -= | 50 | Difference-Assignment |
| *= | 50 | Product-Assignment |
| /= | 50 | Division-Assignment |
| %= | 50 | Modulo-Assignment |
| ^= | 50 | Exponentiation-Assignment |
| & & = | 50 | Logical-And-Assignment |
| | | = | 50 | Logical-Or-Assignment |
2019-03-28 14:01:20 +00:00
| , | 40 | Aggregation |
| ; | 0 | Expression Chaining |
2019-03-19 08:43:52 +00:00
2019-03-19 10:01:06 +00:00
Supported unary operators:
2019-03-19 08:43:52 +00:00
2019-03-19 10:01:06 +00:00
| Operator | Precedence | Description |
|----------|------------|-------------|
| - | 110 | Negation |
| ! | 110 | Logical not |
2019-03-19 08:43:52 +00:00
2019-08-30 06:39:25 +00:00
Operators that take numbers as arguments can either take integers or floating point numbers.
If one of the arguments is a floating point number, all others are converted to floating point numbers as well, and the resulting value is a floating point number as well.
Otherwise, the result is an integer.
An exception to this is the exponentiation operator that always returns a floating point number.
2021-08-01 12:22:10 +00:00
Example:
```rust
use evalexpr::*;
assert_eq!(eval("1 / 2"), Ok(Value::from(0)));
assert_eq!(eval("1.0 / 2"), Ok(Value::from(0.5)));
assert_eq!(eval("2^2"), Ok(Value::from(4.0)));
```
2019-08-30 06:39:25 +00:00
2019-03-19 16:58:53 +00:00
#### The Aggregation Operator
2019-08-30 06:16:08 +00:00
The aggregation operator aggregates a set of values into a tuple.
A tuple can contain arbitrary values, it is not restricted to a single type.
The operator is n-ary, so it supports creating tuples longer than length two.
2019-03-19 16:58:53 +00:00
Example:
```rust
use evalexpr::*;
2019-08-30 07:07:48 +00:00
assert_eq!(eval("1, \"b\", 3"),
Ok(Value::from(vec![Value::from(1), Value::from("b"), Value::from(3)])));
2019-08-30 06:16:08 +00:00
```
To create nested tuples, use parentheses:
```rust
use evalexpr::*;
assert_eq!(eval("1, 2, (true, \"b\")"), Ok(Value::from(vec![
Value::from(1),
Value::from(2),
Value::from(vec![
Value::from(true),
Value::from("b")
])
])));
2019-03-19 16:58:53 +00:00
```
2019-03-28 10:46:40 +00:00
#### The Assignment Operator
This crate features the assignment operator, that allows expressions to store their result in a variable in the expression context.
If an expression uses the assignment operator, it must be evaluated with a mutable context.
2021-06-15 05:38:37 +00:00
Note that assignments are type safe when using the `HashMapContext` .
That means that if an identifier is assigned a value of a type once, it cannot be assigned a value of another type.
2019-03-28 10:46:40 +00:00
```rust
use evalexpr::*;
let mut context = HashMapContext::new();
2021-05-28 12:14:52 +00:00
assert_eq!(eval_with_context("a = 5", & context), Err(EvalexprError::ContextNotMutable));
2019-03-28 10:46:40 +00:00
assert_eq!(eval_empty_with_context_mut("a = 5", & mut context), Ok(EMPTY_VALUE));
2019-08-30 07:07:48 +00:00
assert_eq!(eval_empty_with_context_mut("a = 5.0", & mut context),
Err(EvalexprError::expected_int(5.0.into())));
2019-03-28 10:46:40 +00:00
assert_eq!(eval_int_with_context("a", & context), Ok(5));
assert_eq!(context.get_value("a"), Some(5.into()).as_ref());
```
2021-06-15 05:38:37 +00:00
For each binary operator, there exists an equivalent operator-assignment operator.
2019-08-29 10:21:43 +00:00
Here are some examples:
```rust
use evalexpr::*;
assert_eq!(eval_int("a = 2; a *= 2; a += 2; a"), Ok(6));
assert_eq!(eval_float("a = 2.2; a /= 2.0 / 4 + 1; a"), Ok(2.2 / (2.0 / 4.0 + 1.0)));
assert_eq!(eval_string("a = \"abc\"; a += \"def\"; a"), Ok("abcdef".to_string()));
assert_eq!(eval_boolean("a = true; a & & = false; a"), Ok(false));
```
2019-03-28 10:46:40 +00:00
#### The Expression Chaining Operator
The expression chaining operator works as one would expect from programming languages that use the semicolon to end statements, like `Rust` , `C` or `Java` .
It has the special feature that it returns the value of the last expression in the expression chain.
If the last expression is terminated by a semicolon as well, then `Value::Empty` is returned.
Expression chaining is useful together with assignment to create small scripts.
```rust
use evalexpr::*;
let mut context = HashMapContext::new();
assert_eq!(eval("1;2;3;4;"), Ok(Value::Empty));
assert_eq!(eval("1;2;3;4"), Ok(4.into()));
2021-06-15 05:38:37 +00:00
// Initialization of variables via script
2019-08-30 07:07:48 +00:00
assert_eq!(eval_empty_with_context_mut("hp = 1; max_hp = 5; heal_amount = 3;", & mut context),
Ok(EMPTY_VALUE));
2021-06-15 05:38:37 +00:00
// Precompile healing script
2019-03-28 10:46:40 +00:00
let healing_script = build_operator_tree("hp = min(hp + heal_amount, max_hp); hp").unwrap(); // Do proper error handling here
2021-06-15 05:38:37 +00:00
// Execute precompiled healing script
2019-03-28 10:46:40 +00:00
assert_eq!(healing_script.eval_int_with_context_mut(& mut context), Ok(4));
assert_eq!(healing_script.eval_int_with_context_mut(& mut context), Ok(5));
```
2019-08-29 07:30:55 +00:00
### Contexts
2021-06-15 05:38:37 +00:00
An expression evaluator that just evaluates expressions would be useful already, but this crate can do more.
2021-05-28 12:14:52 +00:00
It allows using [variables ](#variables ), [assignments ](#the-assignment-operator ), [statement chaining ](#the-expression-chaining-operator ) and [user-defined functions ](#user-defined-functions ) within an expression.
2019-08-29 07:30:55 +00:00
When assigning to variables, the assignment is stored in a context.
When the variable is read later on, it is read from the context.
Contexts can be preserved between multiple calls to eval by creating them yourself.
2021-06-15 05:38:37 +00:00
Here is a simple example to show the difference between preserving and not preserving context between evaluations:
2019-08-29 07:30:55 +00:00
```rust
use evalexpr::*;
assert_eq!(eval("a = 5;"), Ok(Value::from(())));
2019-08-29 07:33:12 +00:00
// The context is not preserved between eval calls
2019-08-29 07:30:55 +00:00
assert_eq!(eval("a"), Err(EvalexprError::VariableIdentifierNotFound("a".to_string())));
let mut context = HashMapContext::new();
assert_eq!(eval_with_context_mut("a = 5;", & mut context), Ok(Value::from(())));
2019-08-29 07:33:12 +00:00
// Assignments require mutable contexts
2021-05-28 12:14:52 +00:00
assert_eq!(eval_with_context("a = 6", & context), Err(EvalexprError::ContextNotMutable));
2019-08-29 07:33:12 +00:00
// The HashMapContext is type safe
2019-08-30 07:07:48 +00:00
assert_eq!(eval_with_context_mut("a = 5.5", & mut context),
Err(EvalexprError::ExpectedInt { actual: Value::from(5.5) }));
2019-08-29 07:33:12 +00:00
// Reading a variable does not require a mutable context
2019-08-29 07:30:55 +00:00
assert_eq!(eval_with_context("a", & context), Ok(Value::from(5)));
```
Note that the assignment is forgotten between the two calls to eval in the first example.
In the second part, the assignment is correctly preserved.
2021-06-15 05:38:37 +00:00
Note as well that to assign to a variable, the context needs to be passed as a mutable reference.
2019-08-29 07:30:55 +00:00
When passed as an immutable reference, an error is returned.
2021-06-15 05:38:37 +00:00
2019-08-29 07:30:55 +00:00
Also, the `HashMapContext` is type safe.
2021-06-15 05:38:37 +00:00
This means that assigning to `a` again with a different type yields an error.
2019-08-29 07:30:55 +00:00
Type unsafe contexts may be implemented if requested.
For reading `a` , it is enough to pass an immutable reference.
2019-08-29 07:47:58 +00:00
Contexts can also be manipulated in code.
Take a look at the following example:
```rust
use evalexpr::*;
let mut context = HashMapContext::new();
// We can set variables in code like this...
context.set_value("a".into(), 5.into());
// ...and read from them in expressions
assert_eq!(eval_int_with_context("a", & context), Ok(5));
// We can write or overwrite variables in expressions...
assert_eq!(eval_with_context_mut("a = 10; b = 1.0;", & mut context), Ok(().into()));
// ...and read the value in code like this
assert_eq!(context.get_value("a"), Some(&Value::from(10)));
assert_eq!(context.get_value("b"), Some(&Value::from(1.0)));
```
Contexts are also required for user-defined functions.
Those can be passed one by one with the `set_function` method, but it might be more convenient to use the `context_map!` macro instead:
```rust
use evalexpr::*;
let context = context_map!{
2021-05-28 12:14:52 +00:00
"f" => Function::new(|args| Ok(Value::from(args.as_int()? + 5))),
2019-08-29 07:50:18 +00:00
}.unwrap_or_else(|error| panic!("Error creating context: {}", error));
2019-08-29 07:47:58 +00:00
assert_eq!(eval_int_with_context("f 5", & context), Ok(10));
```
For more information about user-defined functions, refer to the respective [section ](#user-defined-functions ).
2019-03-19 18:12:19 +00:00
### Builtin Functions
2023-05-21 07:19:26 +00:00
This crate offers a set of builtin functions (see below for a full list).
They can be disabled if needed as follows:
2023-05-19 15:45:21 +00:00
2023-05-21 07:19:26 +00:00
```rust
use evalexpr::*;
let mut context = HashMapContext::new();
assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3)));
context.set_builtin_functions_disabled(true).unwrap(); // Do proper error handling here
assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max"))));
```
2019-03-19 18:12:19 +00:00
2023-05-21 07:19:26 +00:00
Not all contexts support enabling or disabling builtin functions.
Specifically the `EmptyContext` has builtin functions disabled by default, and they cannot be enabled.
Symmetrically, the `EmptyContextWithBuiltinFunctions` has builtin functions enabled by default, and they cannot be disabled.
2019-03-19 18:12:19 +00:00
2023-05-16 14:55:24 +00:00
| Identifier | Argument Amount | Argument Types | Description |
|----------------------|-----------------|-------------------------------|-------------|
| `min` | >= 1 | Numeric | Returns the minimum 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) |
| `floor` | 1 | Numeric | Returns the largest integer less than or equal to a number |
| `round` | 1 | Numeric | Returns the nearest integer to a number. Rounds half-way cases away from 0.0 |
| `ceil` | 1 | Numeric | Returns the smallest integer greater than or equal to a number |
| `if` | 3 | Boolean, Any, Any | If the first argument is true, returns the second argument, otherwise, returns the third |
| `contains` | 2 | Tuple, any non-tuple | Returns true if second argument exists in first tuple argument. |
| `contains_any` | 2 | Tuple, Tuple of any non-tuple | Returns true if one of the values in the second tuple argument exists in first tuple argument. |
| `typeof` | 1 | Any | returns "string", "float", "int", "boolean", "tuple", or "empty" depending on the type of the argument |
| `math::is_nan` | 1 | Numeric | Returns true if the argument is the floating-point value NaN, false if it is another floating-point value, and throws an error if it is not a number |
| `math::is_finite` | 1 | Numeric | Returns true if the argument is a finite floating-point number, false otherwise |
| `math::is_infinite` | 1 | Numeric | Returns true if the argument is an infinite floating-point number, false otherwise |
| `math::is_normal` | 1 | Numeric | Returns true if the argument is a floating-point number that is neither zero, infinite, [subnormal ](https://en.wikipedia.org/wiki/Subnormal_number ), or NaN, false otherwise |
| `math::ln` | 1 | Numeric | Returns the natural logarithm of the number |
| `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::exp` | 1 | Numeric | Returns `e^(number)` , (the exponential function) |
| `math::exp2` | 1 | Numeric | Returns `2^(number)` |
| `math::pow` | 2 | Numeric, Numeric | Raises a number to the power of the other number |
| `math::cos` | 1 | Numeric | Computes the cosine of a number (in radians) |
| `math::acos` | 1 | Numeric | Computes the arccosine of a number. The 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. The 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. The return value is in radians in the range [-pi/2, pi/2] |
| `math::atan2` | 2 | Numeric, Numeric | Computes the four quadrant arctangent in radians |
| `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 for a negative number |
| `math::cbrt` | 1 | Numeric | Returns the cube root of a number |
| `math::hypot` | 2 | Numeric | Calculates the length of the hypotenuse of a right-angle triangle given legs of length given by the two arguments |
2023-05-27 08:04:56 +00:00
| `math::abs` | 1 | Numeric | Returns the absolute value of a number, returning an integer if the argument was an integer, and a float otherwise |
2023-05-16 14:55:24 +00:00
| `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 |
| `bitand` | 2 | Int | Computes the bitwise and of the given integers |
| `bitor` | 2 | Int | Computes the bitwise or of the given integers |
| `bitxor` | 2 | Int | Computes the bitwise xor of the given integers |
| `bitnot` | 1 | Int | Computes the bitwise not of the given integer |
| `shl` | 2 | Int | Computes the given integer bitwise shifted left by the other given integer |
| `shr` | 2 | Int | Computes the given integer bitwise shifted right by the other given integer |
| `random` | 0 | Empty | Return a random float between 0 and 1. Requires the `rand` feature flag. |
2019-03-19 18:12:19 +00:00
The `min` and `max` functions can deal with a mixture of integer and floating point arguments.
2019-04-13 16:27:59 +00:00
If the maximum or minimum is an integer, then an integer is returned.
Otherwise, a float is returned.
The regex functions require the feature flag `regex_support` .
2019-03-19 18:12:19 +00:00
2019-03-19 10:01:06 +00:00
### Values
2019-03-19 08:43:52 +00:00
2019-03-19 10:01:06 +00:00
Operators take values as arguments and produce values as results.
2021-06-15 05:38:37 +00:00
Values can be booleans, integer or floating point numbers, strings, tuples or the empty type.
2019-03-19 10:01:06 +00:00
Values are denoted as displayed in the following table.
2019-03-19 08:43:52 +00:00
2019-03-19 10:01:06 +00:00
| Value type | Example |
|------------|---------|
2019-03-30 10:54:19 +00:00
| `Value::String` | `"abc"` , `""` , `"a\"b\\c"` |
2019-03-19 10:01:06 +00:00
| `Value::Boolean` | `true` , `false` |
| `Value::Int` | `3` , `-9` , `0` , `135412` |
2021-07-21 10:42:50 +00:00
| `Value::Float` | `3.` , `.35` , `1.00` , `0.5` , `123.554` , `23e4` , `-2e-3` , `3.54e+2` |
2019-03-28 09:37:43 +00:00
| `Value::Tuple` | `(3, 55.0, false, ())` , `(1, 2)` |
| `Value::Empty` | `()` |
2019-03-19 10:01:06 +00:00
Integers are internally represented as `i64` , and floating point numbers are represented as `f64` .
2021-06-15 05:38:37 +00:00
Tuples are represented as `Vec<Value>` and empty values are not stored, but represented by Rust's unit type `()` where necessary.
2019-03-28 09:37:43 +00:00
There exist type aliases for some of the types.
They include `IntType` , `FloatType` , `TupleType` and `EmptyType` .
2019-03-20 15:30:37 +00:00
Values can be constructed either directly or using the `From` trait.
2021-06-15 05:38:37 +00:00
They can be decomposed using the `Value::as_[type]` methods.
2019-03-20 15:30:37 +00:00
The type of a value can be checked using the `Value::is_[type]` methods.
**Examples for constructing a value:**
| Code | Result |
|------|--------|
| `Value::from(4)` | `Value::Int(4)` |
| `Value::from(4.4)` | `Value::Float(4.4)` |
| `Value::from(true)` | `Value::Boolean(true)` |
| `Value::from(vec![Value::from(3)])` | `Value::Tuple(vec![Value::Int(3)])` |
**Examples for deconstructing a value:**
| Code | Result |
|------|--------|
| `Value::from(4).as_int()` | `Ok(4)` |
| `Value::from(4.4).as_float()` | `Ok(4.4)` |
| `Value::from(true).as_int()` | `Err(Error::ExpectedInt {actual: Value::Boolean(true)})` |
2019-03-19 10:22:06 +00:00
Values have a precedence of 200.
2019-03-19 10:01:06 +00:00
### Variables
This crate allows to compile parameterizable formulas by using variables.
A variable is a literal in the formula, that does not contain whitespace or can be parsed as value.
2019-08-30 06:39:25 +00:00
For working with variables, a [context ](#contexts ) is required.
It stores the mappings from variables to their values.
2019-03-19 10:01:06 +00:00
2019-03-28 07:41:40 +00:00
Variables do not have fixed types in the expression itself, but are typed by the context.
2019-08-30 06:39:25 +00:00
Once a variable is assigned a value of a specific type, it cannot be assigned a value of another type.
This might change in the future and can be changed by using a type-unsafe context (not provided by this crate as of now).
Here are some examples and counter-examples on expressions that are interpreted as variables:
| Expression | Variable? | Explanation |
|------------|--------|-------------|
| `a` | yes | |
| `abc` | yes | |
| `a<b` | no | Expression is interpreted as variable `a` , operator `<` and variable `b` |
| `a b` | no | Expression is interpreted as function `a` applied to argument `b` |
| `123` | no | Expression is interpreted as `Value::Int` |
| `true` | no | Expression is interpreted as `Value::Bool` |
| `.34` | no | Expression is interpreted as `Value::Float` |
2019-03-19 10:01:06 +00:00
2019-03-19 10:22:06 +00:00
Variables have a precedence of 200.
2019-03-19 18:12:19 +00:00
### User-Defined Functions
2019-03-19 10:01:06 +00:00
2021-06-15 05:38:37 +00:00
This crate allows to define arbitrary functions to be used in parsed expressions.
A function is defined as a `Function` instance, wrapping an `fn(&Value) -> EvalexprResult<Value>` .
2019-08-30 06:39:25 +00:00
The definition needs to be included in the [`Context` ](#contexts ) that is used for evaluation.
As of now, functions cannot be defined within the expression, but that might change in the future.
2019-03-19 10:22:06 +00:00
2019-08-30 06:39:25 +00:00
The function gets passed what ever value is directly behind it, be it a tuple or a single values.
If there is no value behind a function, it is interpreted as a variable instead.
More specifically, a function needs to be followed by either an opening brace `(` , another literal, or a value.
While not including special support for multi-valued functions, they can be realized by requiring a single tuple argument.
2019-03-19 17:05:21 +00:00
2019-03-19 10:22:06 +00:00
Be aware that functions need to verify the types of values that are passed to them.
The `error` module contains some shortcuts for verification, and error types for passing a wrong value type.
2021-06-15 05:38:37 +00:00
Also, most numeric functions need to distinguish between being called with integers or floating point numbers, and act accordingly.
2019-03-19 10:22:06 +00:00
2019-08-30 06:39:25 +00:00
Here are some examples and counter-examples on expressions that are interpreted as function calls:
2019-03-19 10:22:06 +00:00
2021-06-15 05:38:37 +00:00
| Expression | Function? | Explanation |
2019-03-19 10:22:06 +00:00
|------------|--------|-------------|
2019-08-30 06:39:25 +00:00
| `a v` | yes | |
| `x 5.5` | yes | |
| `a (3, true)` | yes | |
| `a b 4` | yes | Call `a` with the result of calling `b` with `4` |
| `5 b` | no | Error, value cannot be followed by a literal |
| `12 3` | no | Error, value cannot be followed by a value |
| `a 5 6` | no | Error, function call cannot be followed by a value |
Functions have a precedence of 190.
2019-03-19 08:43:52 +00:00
2019-03-27 15:16:59 +00:00
### [Serde](https://serde.rs)
2019-03-23 13:34:05 +00:00
2019-03-28 14:39:50 +00:00
To use this crate with serde, the `serde_support` feature flag has to be set.
2019-03-27 17:34:49 +00:00
This can be done like this in the `Cargo.toml` :
```toml
[dependencies]
2021-07-13 11:43:59 +00:00
evalexpr = {version = "7", features = ["serde_support"]}
2019-03-27 17:34:49 +00:00
```
2019-03-23 13:34:05 +00:00
This crate implements `serde::de::Deserialize` for its type `Node` that represents a parsed expression tree.
2019-03-27 15:16:59 +00:00
The implementation expects a [serde `string` ](https://serde.rs/data-model.html ) as input.
2019-03-23 13:34:05 +00:00
Example parsing with [ron format ](docs.rs/ron ):
```rust
extern crate ron;
use evalexpr::*;
2019-04-26 15:33:59 +00:00
let mut context = context_map!{
"five" => 5
}.unwrap(); // Do proper error handling here
2019-03-23 13:34:05 +00:00
// In ron format, strings are surrounded by "
let serialized_free = "\"five * five\"";
match ron::de::from_str::< Node > (serialized_free) {
2019-03-27 17:09:20 +00:00
Ok(free) => assert_eq!(free.eval_with_context(& context), Ok(Value::from(25))),
2019-03-23 13:34:05 +00:00
Err(error) => {
2019-03-27 17:09:20 +00:00
() // Handle error
2019-08-29 06:12:42 +00:00
}
2019-03-23 13:34:05 +00:00
}
```
With `serde` , expressions can be integrated into arbitrarily complex data.
2021-06-15 05:38:37 +00:00
The crate also implements `Serialize` and `Deserialize` for the `HashMapContext` ,
but note that only the variables get (de)serialized, not the functions.
2019-03-28 14:39:50 +00:00
2019-03-19 10:01:06 +00:00
## License
2019-03-19 08:43:52 +00:00
2019-03-19 10:01:06 +00:00
This crate is primarily distributed under the terms of the MIT license.
See [LICENSE ](LICENSE ) for details.
2019-03-19 08:43:52 +00:00
<!-- cargo - sync - readme end -->
2019-03-19 14:47:17 +00:00
2019-03-20 10:18:25 +00:00
## No Panicking
2019-03-20 10:52:07 +00:00
This crate makes extensive use of the `Result` pattern and is intended to never panic.
The *exception* are panics caused by *failed allocations* .
2019-03-20 10:18:25 +00:00
But unfortunately, Rust does not provide any features to prove this behavior.
The developer of this crate has not found a good solution to ensure no-panic behavior in any way.
2019-03-20 13:17:06 +00:00
Please report a panic in this crate immediately as issue on [github ](https://github.com/ISibboI/evalexpr/issues ).
2019-03-20 10:18:25 +00:00
Even if the crate itself is panic free, it allows the user to define custom functions that are executed by the crate.
2021-06-15 05:38:37 +00:00
The user needs to ensure that the functions they provide to the crate never panic.
2019-03-20 10:18:25 +00:00
2022-01-18 12:41:21 +00:00
## Untrusted input
This crate was not built with untrusted input in mind, but due to its simplicity and freedom of panics it is likely secure, keeping the following in mind:
* Limit the length of the untrusted input.
* If a mutable context is maintained between evaluations of untrusted input, the untrusted input might fill it gradually until the application runs out of memory.
* If no context is provided, a temporary mutable context is implicitly provided. This is freed after evaluation of every single string, so gradual filling cannot happen.
* If no context or a mutable context is provided, and the `regex_support` feature is activated, the `regex_replace` builtin function can be used to build an exponentially sized string.
2019-04-20 16:09:59 +00:00
## Contribution
2019-03-19 14:47:17 +00:00
2021-06-15 05:38:37 +00:00
If you have any ideas for features or see any problems in the code, architecture, interface, algorithmics or documentation, please open an issue on [github ](https://github.com/ISibboI/evalexpr/issues ).
2019-03-19 14:54:52 +00:00
If there is already an issue describing what you want to say, please add a thumbs up or whatever emoji you think fits to the issue, so I know which ones I should prioritize.
2019-04-20 16:09:59 +00:00
**Notes for contributors:**
* This crate uses the [`sync-readme` ](https://github.com/phaazon/cargo-sync-readme ) cargo subcommand to keep the documentation in `src/lib.rs` and `README.md` in sync.
The subcommand only syncs from the documentation in `src/lib.rs` to `README.md` .
2022-01-18 12:41:21 +00:00
So please alter the documentation in the `src/lib.rs` rather than altering anything in between `<!-- cargo-sync-readme start -->` and `<!-- cargo-sync-readme end -->` in the `README.md` .