evalx/
lib.rs

1//! Evalx is a powerful expression evaluator.
2//!
3//! This crate is a maintained fork and further development of the original
4//! [`eval` by fengcen](https://github.com/fengcen/eval). It includes bugfixes
5//! and updates such as migrating the Rust edition to 2021 while keeping the
6//! public API compatible.
7//!
8//! Supported operators: `!` `!=` `""` `''` `()` `[]` `.` `,` `>` `<` `>=` `<=`
9//! `==` `+` `-` `*` `/` `%` `&&` `||` `n..m`.
10//!
11//! Built-in functions: `min()` `max()` `len()` `is_empty()` `array()`.
12//!
13//! ## Examples
14//!
15//! You can do mathematical calculations with supported operators:
16//!
17//! ```
18//! use evalx::{eval, to_value};
19//!
20//! assert_eq!(eval("1 + 2 + 3"), Ok(to_value(6)));
21//! assert_eq!(eval("2 * 2 + 3"), Ok(to_value(7)));
22//! assert_eq!(eval("2 / 2 + 3"), Ok(to_value(4.0)));
23//! assert_eq!(eval("2 / 2 + 3 / 3"), Ok(to_value(2.0)));
24//! ```
25//!
26//! You can eval with context:
27//!
28//! ```
29//! use evalx::{Expr, to_value};
30//!
31//! assert_eq!(Expr::new("foo == bar")
32//!                .value("foo", true)
33//!                .value("bar", true)
34//!                .exec(),
35//!            Ok(to_value(true)));
36//! ```
37//!
38//! You can access data like javascript by using `.` and `[]`. `[]` supports expression.
39//!
40//! ```
41//! use evalx::{Expr, to_value};
42//! use std::collections::HashMap;
43//!
44//! let mut object = HashMap::new();
45//! object.insert("foos", vec!["Hello", "world", "!"]);
46//!
47//! assert_eq!(Expr::new("object.foos[2-1] == 'world'") // Access field `foos` and index `2-1`
48//!                .value("object", object)
49//!                .exec(),
50//!            Ok(to_value(true)));
51//! ```
52//!
53//! You can eval with function:
54//!
55//! ```
56//! use evalx::{Expr, to_value};
57//!
58//! assert_eq!(Expr::new("say_hello()")
59//!                .function("say_hello", |_| Ok(to_value("Hello world!")))
60//!                .exec(),
61//!            Ok(to_value("Hello world!")));
62//! ```
63//!
64//! You can create an array with `array()`:
65//!
66//! ```
67//! use evalx::{eval, to_value};
68//!
69//! assert_eq!(eval("array(1, 2, 3, 4, 5)"), Ok(to_value(vec![1, 2, 3, 4, 5])));
70//! ```
71//!
72//! You can create an integer array with `n..m`:
73//!
74//! ```
75//! use evalx::{eval, to_value};
76//!
77//! assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
78//! ```
79//!
80//! ## Built-in functions
81//!
82//! ### `min()`
83//! Accept multiple arguments and return the minimum value.
84//!
85//! ### `max()`
86//! Accept multiple arguments and return the maximum value.
87//!
88//! ### `len()`
89//! Accept single arguments and return the length of value. Only accept String, Array, Object and Null.
90//!
91//! ### `is_empty()`
92//! Accept single arguments and return a boolean. Check whether the value is empty or not.
93//!
94//! ### `array()`
95//! Accept multiple arguments and return an array.
96//!
97//!
98#![recursion_limit="100"]
99#![deny(missing_docs)]
100#![cfg_attr(all(feature = "unstable", test), feature(test))]
101
102mod math;
103mod function;
104mod operator;
105mod node;
106mod tree;
107mod error;
108mod builtin;
109mod expr;
110
111pub use expr::ExecOptions;
112pub use serde_json::Value;
113pub use error::Error;
114pub use function::Function;
115pub use expr::Expr;
116
117use std::collections::HashMap;
118use serde_json::to_value as json_to_value;
119use serde::Serialize;
120
121/// Convert variable to `serde_json::Value`
122pub fn to_value<S: Serialize>(v: S) -> Value {
123    json_to_value(v).unwrap()
124}
125
126/// Custom context.
127pub type Context = HashMap<String, Value>;
128/// Custom contexts. The value of the last context is searched first.
129pub type Contexts = Vec<Context>;
130/// Custom functions.
131pub type Functions = HashMap<String, Function>;
132
133/// Evaluates the value of an expression.
134pub fn eval(expr: &str) -> Result<Value, Error> {
135    Expr::new(expr).compile()?.exec()
136}
137
138
139type Compiled = Box<dyn Fn(&[Context], &Functions) -> Result<Value, Error>>;
140
141
142
143#[cfg(all(feature = "unstable", test))]
144mod benches {
145    extern crate test;
146    use crate::eval;
147    use crate::tree::Tree;
148    use crate::Expr;
149
150    #[bench]
151    fn bench_deep_brackets(b: &mut test::Bencher) {
152        b.iter(|| eval("(2 + (3 + 4) + (6 + (6 + 7)) + 5)"));
153    }
154
155    #[bench]
156    fn bench_parse_pos(b: &mut test::Bencher) {
157        let mut tree = Tree {
158            raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
159            ..Default::default()
160        };
161
162        b.iter(|| tree.parse_pos().unwrap());
163    }
164
165    #[bench]
166    fn bench_parse_operators(b: &mut test::Bencher) {
167        let mut tree = Tree {
168            raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
169            ..Default::default()
170        };
171
172        tree.parse_pos().unwrap();
173        b.iter(|| tree.parse_operators().unwrap());
174    }
175
176    #[bench]
177    fn bench_parse_nodes(b: &mut test::Bencher) {
178        let mut tree = Tree {
179            raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
180            ..Default::default()
181        };
182
183        tree.parse_pos().unwrap();
184        tree.parse_operators().unwrap();
185        b.iter(|| tree.parse_node().unwrap());
186    }
187
188    #[bench]
189    fn bench_compile(b: &mut test::Bencher) {
190        b.iter(|| {
191            let mut tree = Tree {
192                raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
193                ..Default::default()
194            };
195            tree.parse_pos().unwrap();
196            tree.parse_operators().unwrap();
197            tree.parse_node().unwrap();
198            tree.compile().unwrap();
199        });
200    }
201
202    #[bench]
203    fn bench_exec(b: &mut test::Bencher) {
204        let expr = Expr::new("(2 + (3 + 4) + (6 + (6 + 7)) + 5)")
205            .compile()
206            .unwrap();
207        b.iter(|| expr.exec().unwrap())
208    }
209
210    #[bench]
211    fn bench_eval(b: &mut test::Bencher) {
212        b.iter(|| eval("(2 + (3 + 4) + (6 + (6 + 7)) + 5)"));
213    }
214}