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}