//! //! ## Quickstart //! //! Add `evalexpr` as dependency to your `Cargo.toml`: //! //! ```toml //! [dependencies] //! evalexpr = "5" //! ``` //! //! Add the `extern crate` definition to your `main.rs` or `lib.rs`: //! //! ```rust //! extern crate evalexpr; //! ``` //! //! Then you can use `evalexpr` to **evaluate expressions** like this: //! //! ```rust //! use evalexpr::*; //! //! assert_eq!(eval("1 + 2 + 3"), Ok(Value::from(6))); //! // `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)); //! 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))); //! ``` //! //! 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 //! assert_eq!(eval_empty_with_context_mut("a = 5.0", &mut context), //! Err(EvalexprError::expected_int(Value::from(5.0)))); //! // 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)); //! // It is also possible to safe a bit of typing by using an operator-assignment operator //! assert_eq!(eval_int_with_context_mut("a += 2; a", &mut context), Ok(9)); //! ``` //! //! And you can use **variables** and **functions** in expressions like this: //! //! ```rust //! use evalexpr::*; //! //! let context = context_map! { //! "five" => 5, //! "twelve" => 12, //! "f" => Function::new(Box::new(|argument| { //! if let Ok(int) = argument.as_int() { //! Ok(Value::Int(int / 2)) //! } else if let Ok(float) = argument.as_float() { //! Ok(Value::Float(float / 2.0)) //! } else { //! Err(EvalexprError::expected_number(argument.clone())) //! } //! })), //! "avg" => Function::new(Box::new(|argument| { //! let arguments = argument.as_tuple()?; //! //! 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)) //! } //! })) //! }.unwrap(); // Do proper error handling here //! //! 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. //! // Both can be used interchangeably. //! 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))); //! ``` //! //! You can also **precompile** expressions like this: //! //! ```rust //! use evalexpr::*; //! //! let precompiled = build_operator_tree("a * b - c > 5").unwrap(); // Do proper error handling here //! //! let mut context = context_map! { //! "a" => 6, //! "b" => 2, //! "c" => 3 //! }.unwrap(); // Do proper error handling here //! assert_eq!(precompiled.eval_with_context(&context), Ok(Value::from(true))); //! //! context.set_value("c".into(), 8.into()).unwrap(); // Do proper error handling here //! 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. //! // Both can be used interchangeably. //! assert_eq!(precompiled.eval_boolean_with_context(&context), Ok(false)); //! ``` //! //! ## Features //! //! ### Operators //! //! This crate offers a set of binary and unary operators for building expressions. //! Operators have a precedence to determine their order of evaluation. //! The precedence should resemble that of most common programming languages, especially Rust. //! The precedence of variables and values is 200, and the precedence of function literals is 190. //! //! Supported binary operators: //! //! | Operator | Precedence | Description | //! |----------|------------|-------------| //! | ^ | 120 | Exponentiation | //! | * | 100 | Product | //! | / | 100 | Division | //! | % | 100 | Modulo | //! | + | 95 | Sum or String Concatenation | //! | - | 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 | //! | += | 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 | //! | , | 40 | Aggregation | //! | ; | 0 | Expression Chaining | //! //! Supported unary operators: //! //! | Operator | Precedence | Description | //! |----------|------------|-------------| //! | - | 110 | Negation | //! | ! | 110 | Logical not | //! //! 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. //! //! #### The Aggregation Operator //! //! 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. //! Example: //! //! ```rust //! use evalexpr::*; //! //! assert_eq!(eval("1, \"b\", 3"), //! Ok(Value::from(vec![Value::from(1), Value::from("b"), Value::from(3)]))); //! ``` //! //! 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") //! ]) //! ]))); //! ``` //! //! #### 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. //! Note that assignments are type safe, meaning if an identifier is assigned a value of a type once, it cannot be assigned a value of another type. //! //! ```rust //! use evalexpr::*; //! //! let mut context = HashMapContext::new(); //! assert_eq!(eval_with_context("a = 5", &context), Err(EvalexprError::ContextNotMutable)); //! assert_eq!(eval_empty_with_context_mut("a = 5", &mut context), Ok(EMPTY_VALUE)); //! assert_eq!(eval_empty_with_context_mut("a = 5.0", &mut context), //! Err(EvalexprError::expected_int(5.0.into()))); //! assert_eq!(eval_int_with_context("a", &context), Ok(5)); //! assert_eq!(context.get_value("a"), Some(5.into()).as_ref()); //! ``` //! //! For each binary operator, there exists and equivalent operator-assignment operator. //! 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)); //! ``` //! //! #### 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())); //! //! // Initialization of variables via script. //! assert_eq!(eval_empty_with_context_mut("hp = 1; max_hp = 5; heal_amount = 3;", &mut context), //! Ok(EMPTY_VALUE)); //! // Precompile healing script. //! let healing_script = build_operator_tree("hp = min(hp + heal_amount, max_hp); hp").unwrap(); // Do proper error handling here //! // Execute precompiled healing script. //! 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)); //! ``` //! //! ### Contexts //! //! An expression evaluator that just evaluates expressions would be useful already, but this crate can to more. //! 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. //! 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. //! Here is a simple example to show the difference between preserving context between evaluations, and not preserving: //! //! ```rust //! use evalexpr::*; //! //! assert_eq!(eval("a = 5;"), Ok(Value::from(()))); //! // The context is not preserved between eval calls //! 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(()))); //! // Assignments require mutable contexts //! assert_eq!(eval_with_context("a = 6", &context), Err(EvalexprError::ContextNotMutable)); //! // The HashMapContext is type safe //! assert_eq!(eval_with_context_mut("a = 5.5", &mut context), //! Err(EvalexprError::ExpectedInt { actual: Value::from(5.5) })); //! // Reading a variable does not require a mutable context //! 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. //! Note here, that to assign to a variable, the context needs to be passed as a mutable reference. //! When passed as an immutable reference, an error is returned. //! Also, the `HashMapContext` is type safe. //! This means that assigning to `a` again with a different type yields an error as well. //! Type unsafe contexts may be implemented if requested. //! For reading `a`, it is enough to pass an immutable reference. //! //! 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!{ //! "f" => Function::new(Box::new(|args| Ok(Value::from(args.as_int()? + 5)))), //! }.unwrap_or_else(|error| panic!("Error creating context: {}", error)); //! 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). //! //! ### Builtin Functions //! //! This crate offers a set of builtin functions. //! //! | 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) | //! | `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 | //! //! 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. //! Otherwise, a float is returned. //! //! The regex functions require the feature flag `regex_support`. //! //! ### Values //! //! Operators take values as arguments and produce values as results. //! Values can be boolean, integer or floating point numbers, strings, tuples or the empty type. //! Values are denoted as displayed in the following table. //! //! | Value type | Example | //! |------------|---------| //! | `Value::String` | `"abc"`, `""`, `"a\"b\\c"` | //! | `Value::Boolean` | `true`, `false` | //! | `Value::Int` | `3`, `-9`, `0`, `135412` | //! | `Value::Float` | `3.`, `.35`, `1.00`, `0.5`, `123.554` | //! | `Value::Tuple` | `(3, 55.0, false, ())`, `(1, 2)` | //! | `Value::Empty` | `()` | //! //! Integers are internally represented as `i64`, and floating point numbers are represented as `f64`. //! Tuples are represented as `Vec` and empty values are not stored, but represented by rust's unit type `()` where necessary. //! //! There exist type aliases for some of the types. //! They include `IntType`, `FloatType`, `TupleType` and `EmptyType`. //! //! Values can be constructed either directly or using the `From` trait. //! Values can be decomposed using the `Value::as_[type]` methods. //! 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)})` | //! //! Values have a precedence of 200. //! //! ### 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. //! For working with variables, a [context](#contexts) is required. //! It stores the mappings from variables to their values. //! //! Variables do not have fixed types in the expression itself, but are typed by the context. //! 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 EvalexprResult`. //! 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. //! //! 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. //! //! 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. //! Also, most numeric functions need to differentiate between being called with integers or floating point numbers, and act accordingly. //! //! Here are some examples and counter-examples on expressions that are interpreted as function calls: //! //! | Expression | Variable? | Explanation | //! |------------|--------|-------------| //! | `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. //! //! ### [Serde](https://serde.rs) //! //! To use this crate with serde, the `serde_support` feature flag has to be set. //! This can be done like this in the `Cargo.toml`: //! //! ```toml //! [dependencies] //! evalexpr = {version = "5", features = ["serde_support"]} //! ``` //! //! This crate implements `serde::de::Deserialize` for its type `Node` that represents a parsed expression tree. //! The implementation expects a [serde `string`](https://serde.rs/data-model.html) as input. //! Example parsing with [ron format](docs.rs/ron): //! //! ```rust //! # #[cfg(feature = "serde_support")] { //! extern crate ron; //! use evalexpr::*; //! //! let mut context = context_map!{ //! "five" => 5 //! }.unwrap(); // Do proper error handling here //! //! // In ron format, strings are surrounded by " //! let serialized_free = "\"five * five\""; //! match ron::de::from_str::(serialized_free) { //! Ok(free) => assert_eq!(free.eval_with_context(&context), Ok(Value::from(25))), //! Err(error) => { //! () // Handle error //! } //! } //! # } //! ``` //! //! With `serde`, expressions can be integrated into arbitrarily complex data. //! //! The crate also implements `Serialize` and `Deserialize` for the `HashMapContext`. //! But note that only the variables get serialized, not the functions. //! //! ## License //! //! This crate is primarily distributed under the terms of the MIT license. //! See [LICENSE](LICENSE) for details. //! #![deny(missing_docs)] #![forbid(unsafe_code)] #[cfg(feature = "regex_support")] extern crate regex; #[cfg(test)] extern crate ron; #[cfg(feature = "serde_support")] extern crate serde; #[cfg(feature = "serde_support")] #[macro_use] extern crate serde_derive; pub use crate::{ context::{ Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext, HashMapContext, }, error::{EvalexprError, EvalexprResult}, function::Function, interface::*, operator::Operator, tree::Node, value::{value_type::ValueType, EmptyType, FloatType, IntType, TupleType, Value, EMPTY_VALUE}, }; mod context; pub mod error; #[cfg(feature = "serde_support")] mod feature_serde; mod function; mod interface; mod operator; mod token; mod tree; mod value; // Exports