add Expr builder. add ExecOptions. add len()
This commit is contained in:
parent
1c7cd61b59
commit
032e57df43
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "eval"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
description = "Expression evaluator"
|
||||
keywords = ["expression", "evaluate", "evaluator", "expr", "template"]
|
||||
authors = ["fengcen <fengcen.love@gmail.com>"]
|
||||
@ -15,5 +15,6 @@ name = "eval"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
serde = "^0.8"
|
||||
serde_json = "^0.8"
|
||||
quick-error = "^1.1"
|
||||
|
30
README.md
30
README.md
@ -1,7 +1,7 @@
|
||||
|
||||
eval
|
||||
===
|
||||
[![docs](https://docs.rs/eval/badge.svg?version=0.1.1 "docs")](https://docs.rs/eval)
|
||||
[![docs](https://docs.rs/eval/badge.svg?version=0.2.0 "docs")](https://docs.rs/eval)
|
||||
|
||||
Eval is a powerful expression evaluator.
|
||||
|
||||
@ -11,7 +11,7 @@ Eval is a powerful expression evaluator.
|
||||
Supported operators: `!` `!=` `""` `''` `()` `[]` `,` `>` `<` `>=` `<=` `==`
|
||||
`+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||
|
||||
Built-in functions: `min()` `max()` `is_empty()`.
|
||||
Built-in functions: `min()` `max()` `len()` `is_empty()`.
|
||||
|
||||
## Where can eval be used?
|
||||
* Template engine
|
||||
@ -22,7 +22,7 @@ Add dependency to Cargo.toml
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
eval = "^0.1"
|
||||
eval = "^0.2"
|
||||
```
|
||||
|
||||
In your `main.rs` or `lib.rs`:
|
||||
@ -47,22 +47,24 @@ assert_eq!(eval("2 / 2 + 3 / 3"), Ok(to_value(2.0)));
|
||||
You can eval with context:
|
||||
|
||||
```
|
||||
use eval::{eval_with_context, Context, to_value};
|
||||
use eval::{Expr, to_value};
|
||||
|
||||
let mut context = Context::new();
|
||||
context.insert("foo".to_owned(), to_value(true));
|
||||
context.insert("bar".to_owned(), to_value(true));
|
||||
assert_eq!(eval_with_context("foo == bar", &context), Ok(to_value(true)));
|
||||
assert_eq!(Expr::new("foo == bar")
|
||||
.value("foo", true)
|
||||
.value("bar", true)
|
||||
.exec(),
|
||||
Ok(to_value(true)));
|
||||
```
|
||||
|
||||
You can eval with functions:
|
||||
You can eval with function:
|
||||
|
||||
```
|
||||
use eval::{eval_with_functions, Functions, Function, to_value};
|
||||
use eval::{Expr, to_value};
|
||||
|
||||
let mut functions = Functions::new();
|
||||
functions.insert("say_hello".to_owned(), Function::new(|_| Ok(to_value("Hello world!"))));
|
||||
assert_eq!(eval_with_functions("say_hello()", &functions), Ok(to_value("Hello world!")));
|
||||
assert_eq!(Expr::new("say_hello()")
|
||||
.function("say_hello", |_| Ok(to_value("Hello world!")))
|
||||
.exec(),
|
||||
Ok(to_value("Hello world!")));
|
||||
```
|
||||
|
||||
You can create an array with `[]`:
|
||||
@ -71,7 +73,6 @@ You can create an array with `[]`:
|
||||
use eval::{eval, to_value};
|
||||
|
||||
assert_eq!(eval("[1, 2, 3, 4, 5]"), Ok(to_value(vec![1, 2, 3, 4, 5])));
|
||||
|
||||
```
|
||||
|
||||
You can create an integer array with `n..m`:
|
||||
@ -80,7 +81,6 @@ You can create an integer array with `n..m`:
|
||||
use eval::{eval, to_value};
|
||||
|
||||
assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||
|
||||
```
|
||||
|
||||
## License
|
||||
|
@ -11,6 +11,7 @@ impl BuiltIn {
|
||||
let mut functions = Functions::new();
|
||||
functions.insert("min".to_owned(), create_min_fuction());
|
||||
functions.insert("max".to_owned(), create_max_fuction());
|
||||
functions.insert("len".to_owned(), create_len_fuction());
|
||||
functions.insert("is_empty".to_owned(), create_is_empty_fuction());
|
||||
functions.insert("array".to_owned(), create_array_function());
|
||||
functions
|
||||
@ -48,11 +49,9 @@ fn compare(compare: Compare) -> Function {
|
||||
if value.lt(prev.as_ref().unwrap())? == to_value(true) {
|
||||
prev = Ok(value)
|
||||
}
|
||||
} else {
|
||||
if value.gt(prev.as_ref().unwrap())? == to_value(true) {
|
||||
} else if value.gt(prev.as_ref().unwrap())? == to_value(true) {
|
||||
prev = Ok(value)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prev = Ok(value);
|
||||
}
|
||||
@ -64,11 +63,9 @@ fn compare(compare: Compare) -> Function {
|
||||
if value.lt(prev.as_ref().unwrap())? == to_value(true) {
|
||||
prev = Ok(value)
|
||||
}
|
||||
} else {
|
||||
if value.gt(prev.as_ref().unwrap())? == to_value(true) {
|
||||
} else if value.gt(prev.as_ref().unwrap())? == to_value(true) {
|
||||
prev = Ok(value)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prev = Ok(value);
|
||||
}
|
||||
@ -87,11 +84,32 @@ fn create_is_empty_fuction() -> Function {
|
||||
min_args: Some(1),
|
||||
compiled: Box::new(|values| {
|
||||
match *values.first().unwrap() {
|
||||
Value::String(ref string) => Ok(Value::Bool(string.is_empty())),
|
||||
Value::Array(ref array) => Ok(Value::Bool(array.is_empty())),
|
||||
Value::Object(ref object) => Ok(Value::Bool(object.is_empty())),
|
||||
Value::Null => Ok(Value::Bool(true)),
|
||||
_ => Ok(Value::Bool(false)),
|
||||
Value::String(ref string) => Ok(to_value(string.is_empty())),
|
||||
Value::Array(ref array) => Ok(to_value(array.is_empty())),
|
||||
Value::Object(ref object) => Ok(to_value(object.is_empty())),
|
||||
Value::Null => Ok(to_value(true)),
|
||||
_ => Ok(to_value(false)),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_len_fuction() -> Function {
|
||||
Function {
|
||||
max_args: Some(1),
|
||||
min_args: Some(1),
|
||||
compiled: Box::new(|values| {
|
||||
let value = values.first().unwrap();
|
||||
match *value {
|
||||
Value::String(ref string) => Ok(to_value(string.len())),
|
||||
Value::Array(ref array) => Ok(to_value(array.len())),
|
||||
Value::Object(ref object) => Ok(to_value(object.len())),
|
||||
Value::Null => Ok(to_value(0)),
|
||||
_ => {
|
||||
Err(Error::Custom(format!("len() only accept string, array, object and \
|
||||
null. But the given is: {:?}",
|
||||
value)))
|
||||
}
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
152
src/expr/mod.rs
Normal file
152
src/expr/mod.rs
Normal file
@ -0,0 +1,152 @@
|
||||
|
||||
use {Function, Functions, Context, Contexts, Compiled, Value};
|
||||
use tree::Tree;
|
||||
use error::Error;
|
||||
use serde::Serialize;
|
||||
use to_value;
|
||||
use std::fmt;
|
||||
|
||||
|
||||
/// Expression builder
|
||||
pub struct Expr {
|
||||
expression: String,
|
||||
compiled: Option<Compiled>,
|
||||
functions: Functions,
|
||||
contexts: Contexts,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
/// Create an expression.
|
||||
pub fn new<T: Into<String>>(expr: T) -> Expr {
|
||||
Expr {
|
||||
expression: expr.into(),
|
||||
compiled: None,
|
||||
functions: Functions::new(),
|
||||
contexts: create_empty_contexts(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set function.
|
||||
pub fn function<T, F>(mut self, name: T, function: F) -> Expr
|
||||
where T: Into<String>,
|
||||
F: 'static + Fn(Vec<Value>) -> Result<Value, Error> + Sync + Send
|
||||
{
|
||||
self.functions.insert(name.into(), Function::new(function));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set value.
|
||||
pub fn value<T, V>(mut self, name: T, value: V) -> Expr
|
||||
where T: Into<String>,
|
||||
V: Serialize
|
||||
{
|
||||
self.contexts.last_mut().unwrap().insert(name.into(), to_value(value));
|
||||
self
|
||||
}
|
||||
|
||||
/// Compile an expression.
|
||||
/// An expression can be compiled only once and then invoked multiple times with different context and function.
|
||||
/// You can also execute a expression without compile.
|
||||
pub fn compile(mut self) -> Result<Expr, Error> {
|
||||
self.compiled = Some(Tree::new(self.expression.clone()).compile()?);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Execute the expression.
|
||||
pub fn exec(&self) -> Result<Value, Error> {
|
||||
if self.compiled.is_none() {
|
||||
Tree::new(self.expression.clone()).compile()?(&self.contexts, &self.functions)
|
||||
} else {
|
||||
self.compiled.as_ref().unwrap()(&self.contexts, &self.functions)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_compiled(&self) -> Option<&Compiled> {
|
||||
self.compiled.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Expr {
|
||||
/// Returns a copy of the value. Notice that functions can not be cloned. The cloned expr's functions will be empty.
|
||||
fn clone(&self) -> Expr {
|
||||
Expr {
|
||||
expression: self.expression.clone(),
|
||||
compiled: if self.compiled.is_some() {
|
||||
Some(Tree::new(self.expression.clone()).compile().unwrap())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
contexts: self.contexts.clone(),
|
||||
functions: Functions::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Expr {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(formatter, "{:?}", self.expression)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Execute options
|
||||
pub struct ExecOptions<'a> {
|
||||
expr: &'a Expr,
|
||||
contexts: Option<&'a [Context]>,
|
||||
functions: Option<&'a Functions>,
|
||||
}
|
||||
|
||||
impl<'a> ExecOptions<'a> {
|
||||
/// Create an option.
|
||||
pub fn new(expr: &'a Expr) -> ExecOptions<'a> {
|
||||
ExecOptions {
|
||||
expr: expr,
|
||||
contexts: None,
|
||||
functions: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set contexts.
|
||||
pub fn contexts(&mut self, contexts: &'a [Context]) -> &'a mut ExecOptions {
|
||||
self.contexts = Some(contexts);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set functions.
|
||||
pub fn functions(&mut self, functions: &'a Functions) -> &'a mut ExecOptions {
|
||||
self.functions = Some(functions);
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute the compiled expression.
|
||||
pub fn exec(&self) -> Result<Value, Error> {
|
||||
let empty_contexts = create_empty_contexts();
|
||||
let empty_functions = Functions::new();
|
||||
|
||||
let contexts = if self.contexts.is_some() {
|
||||
self.contexts.unwrap()
|
||||
} else {
|
||||
&empty_contexts
|
||||
};
|
||||
|
||||
let functions = if self.functions.is_some() {
|
||||
self.functions.unwrap()
|
||||
} else {
|
||||
&empty_functions
|
||||
};
|
||||
|
||||
let compiled = self.expr.get_compiled();
|
||||
if compiled.is_none() {
|
||||
Tree::new(self.expr.expression.clone()).compile()?(contexts, functions)
|
||||
} else {
|
||||
compiled.unwrap()(contexts, functions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn create_empty_contexts() -> Contexts {
|
||||
let mut contexts = Contexts::new();
|
||||
contexts.push(Context::new());
|
||||
contexts
|
||||
}
|
254
src/lib.rs
254
src/lib.rs
@ -3,9 +3,9 @@
|
||||
//! Supported operators: `!` `!=` `""` `''` `()` `[]` `,` `>` `<` `>=` `<=`
|
||||
//! `==` `+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||
//!
|
||||
//! Built-in functions: `min()` `max()` `is_empty()`.
|
||||
//! Built-in functions: `min()` `max()` `len()` `is_empty()`.
|
||||
//!
|
||||
//! # Examples
|
||||
//! ## Examples
|
||||
//!
|
||||
//! You can do mathematical calculations with supported operators:
|
||||
//!
|
||||
@ -21,22 +21,24 @@
|
||||
//! You can eval with context:
|
||||
//!
|
||||
//! ```
|
||||
//! use eval::{eval_with_context, Context, to_value};
|
||||
//! use eval::{Expr, to_value};
|
||||
//!
|
||||
//! let mut context = Context::new();
|
||||
//! context.insert("foo".to_owned(), to_value(true));
|
||||
//! context.insert("bar".to_owned(), to_value(true));
|
||||
//! assert_eq!(eval_with_context("foo == bar", &context), Ok(to_value(true)));
|
||||
//! assert_eq!(Expr::new("foo == bar")
|
||||
//! .value("foo", true)
|
||||
//! .value("bar", true)
|
||||
//! .exec(),
|
||||
//! Ok(to_value(true)));
|
||||
//! ```
|
||||
//!
|
||||
//! You can eval with functions:
|
||||
//! You can eval with function:
|
||||
//!
|
||||
//! ```
|
||||
//! use eval::{eval_with_functions, Functions, Function, to_value};
|
||||
//! use eval::{Expr, to_value};
|
||||
//!
|
||||
//! let mut functions = Functions::new();
|
||||
//! functions.insert("say_hello".to_owned(), Function::new(|_| Ok(to_value("Hello world!"))));
|
||||
//! assert_eq!(eval_with_functions("say_hello()", &functions), Ok(to_value("Hello world!")));
|
||||
//! assert_eq!(Expr::new("say_hello()")
|
||||
//! .function("say_hello", |_| Ok(to_value("Hello world!")))
|
||||
//! .exec(),
|
||||
//! Ok(to_value("Hello world!")));
|
||||
//! ```
|
||||
//!
|
||||
//! You can create an array with `[]`:
|
||||
@ -45,7 +47,6 @@
|
||||
//! use eval::{eval, to_value};
|
||||
//!
|
||||
//! assert_eq!(eval("[1, 2, 3, 4, 5]"), Ok(to_value(vec![1, 2, 3, 4, 5])));
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! You can create an integer array with `n..m`:
|
||||
@ -54,9 +55,23 @@
|
||||
//! use eval::{eval, to_value};
|
||||
//!
|
||||
//! assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! ## Built-in functions
|
||||
//!
|
||||
//! ### min()
|
||||
//! Accept multiple arguments and return the minimum value.
|
||||
//!
|
||||
//! ### max()
|
||||
//! Accept multiple arguments and return the maximum value.
|
||||
//!
|
||||
//! ### len()
|
||||
//! Accept single arguments and return the length of value. Only accept String, Array, Object and Null.
|
||||
//!
|
||||
//! ### is_empty()
|
||||
//! Accept single arguments and return the a boolean. Check whether the value is empty or not.
|
||||
//!
|
||||
//!
|
||||
#![recursion_limit="100"]
|
||||
#![deny(missing_docs)]
|
||||
#![feature(proc_macro, test)]
|
||||
@ -64,89 +79,51 @@ extern crate test;
|
||||
|
||||
#[macro_use]
|
||||
extern crate quick_error;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
|
||||
mod math;
|
||||
mod function;
|
||||
mod operator;
|
||||
mod node;
|
||||
mod expression;
|
||||
mod tree;
|
||||
mod error;
|
||||
mod builtin;
|
||||
mod expr;
|
||||
|
||||
|
||||
pub use expr::ExecOptions;
|
||||
pub use serde_json::{Value, to_value};
|
||||
pub use error::Error;
|
||||
pub use function::Function;
|
||||
|
||||
pub use expr::Expr;
|
||||
use std::collections::HashMap;
|
||||
use expression::Expression;
|
||||
use builtin::BuiltIn;
|
||||
|
||||
type ContextsRef<'a> = &'a [Context];
|
||||
|
||||
/// Eval context.
|
||||
/// Custom context.
|
||||
pub type Context = HashMap<String, Value>;
|
||||
/// Eval contexts. The value of the last context is searched first.
|
||||
/// Custom contexts. The value of the last context is searched first.
|
||||
pub type Contexts = Vec<Context>;
|
||||
/// Eval functions.
|
||||
/// Custom functions.
|
||||
pub type Functions = HashMap<String, Function>;
|
||||
|
||||
/// Evaluates the value of an expression.
|
||||
pub fn eval(expr: &str) -> Result<Value, Error> {
|
||||
Expression::new(expr)?.compile()(&Contexts::new(), &BuiltIn::new(), &Functions::new())
|
||||
Expr::new(expr).compile()?.exec()
|
||||
}
|
||||
|
||||
/// Evaluates the value of an expression with the given context.
|
||||
pub fn eval_with_context(expr: &str, context: &Context) -> Result<Value, Error> {
|
||||
let mut contexts = Contexts::new();
|
||||
contexts.push(context.clone());
|
||||
eval_with_contexts(expr, &contexts)
|
||||
}
|
||||
|
||||
/// Evaluates the value of an expression with the given contexts.<br>
|
||||
/// The value of the last context is searched first.
|
||||
pub fn eval_with_contexts(expr: &str, contexts: ContextsRef) -> Result<Value, Error> {
|
||||
Expression::new(expr)?.compile()(contexts, &BuiltIn::new(), &Functions::new())
|
||||
}
|
||||
type Compiled = Box<Fn(&[Context], &Functions) -> Result<Value, Error>>;
|
||||
|
||||
/// Evaluates the value of an expression with the given functions.
|
||||
pub fn eval_with_functions(expr: &str, functions: &Functions) -> Result<Value, Error> {
|
||||
Expression::new(expr)?.compile()(&Contexts::new(), &BuiltIn::new(), functions)
|
||||
}
|
||||
|
||||
/// Evaluates the value of an expression with the given context and functions.
|
||||
pub fn eval_with_context_and_functions(expr: &str,
|
||||
context: &Context,
|
||||
functions: &Functions)
|
||||
-> Result<Value, Error> {
|
||||
let mut contexts = Contexts::new();
|
||||
contexts.push(context.clone());
|
||||
eval_with_contexts_and_functions(expr, &contexts, functions)
|
||||
}
|
||||
|
||||
/// Evaluates the value of an expression with the given contexts and functions.<br>
|
||||
/// The value of the last context is searched first.
|
||||
pub fn eval_with_contexts_and_functions(expr: &str,
|
||||
contexts: ContextsRef,
|
||||
functions: &Functions)
|
||||
-> Result<Value, Error> {
|
||||
Expression::new(expr)?.compile()(contexts, &BuiltIn::new(), functions)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test;
|
||||
use serde_json::to_value;
|
||||
use expression::Expression;
|
||||
use error::Error;
|
||||
use Context;
|
||||
use Expr;
|
||||
use tree::Tree;
|
||||
use eval;
|
||||
use eval_with_context;
|
||||
use eval_with_functions;
|
||||
use {Function, Functions};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
@ -225,6 +202,28 @@ mod tests {
|
||||
assert_eq!(eval("max(30, 5, 245, 20) * 10"), Ok(to_value(2450)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len_array() {
|
||||
assert_eq!(eval("len([2, 3, 4, 5, 6])"), Ok(to_value(5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len_string() {
|
||||
assert_eq!(eval("len('Hello world!')"), Ok(to_value(12)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len_object() {
|
||||
let mut object = HashMap::new();
|
||||
object.insert("field1", "value1");
|
||||
object.insert("field2", "value2");
|
||||
object.insert("field3", "value3");
|
||||
assert_eq!(Expr::new("len(object)")
|
||||
.value("object", object)
|
||||
.exec(),
|
||||
Ok(to_value(3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_brackets_1() {
|
||||
assert_eq!(eval("(5) + (min(3, 4, 5)) + 20"), Ok(to_value(28)));
|
||||
@ -405,81 +404,83 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buildin_is_empty() {
|
||||
let mut context = Context::new();
|
||||
context.insert("array".to_owned(), to_value(Vec::<String>::new()));
|
||||
assert_eq!(eval_with_context("is_empty(array)", &context),
|
||||
fn test_builtin_is_empty() {
|
||||
assert_eq!(Expr::new("is_empty(array)")
|
||||
.value("array", Vec::<String>::new())
|
||||
.compile()
|
||||
.unwrap()
|
||||
.exec(),
|
||||
Ok(to_value(true)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buildin_min() {
|
||||
let mut context = Context::new();
|
||||
context.insert("array".to_owned(), to_value(vec![23, 34, 45, 2]));
|
||||
assert_eq!(eval_with_context("min(array)", &context), Ok(to_value(2)));
|
||||
fn test_builtin_min() {
|
||||
assert_eq!(Expr::new("min(array)")
|
||||
.value("array", vec![23, 34, 45, 2])
|
||||
.compile()
|
||||
.unwrap()
|
||||
.exec(),
|
||||
Ok(to_value(2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_function() {
|
||||
let mut functions = Functions::new();
|
||||
functions.insert("output".to_owned(),
|
||||
Function::new(|_| Ok(to_value("This is custom function's output"))));
|
||||
assert_eq!(eval_with_functions("output()", &functions),
|
||||
assert_eq!(Expr::new("output()")
|
||||
.function("output",
|
||||
|_| Ok(to_value("This is custom function's output")))
|
||||
.compile()
|
||||
.unwrap()
|
||||
.exec(),
|
||||
Ok(to_value("This is custom function's output")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_start_with_non_value_operator() {
|
||||
let mut expr = Expression {
|
||||
raw: "+ + 5".to_owned(),
|
||||
pos: Vec::new(),
|
||||
operators: Vec::new(),
|
||||
node: None,
|
||||
};
|
||||
let mut tree = Tree { raw: "+ + 5".to_owned(), ..Default::default() };
|
||||
|
||||
expr.parse_pos().unwrap();
|
||||
expr.parse_operators().unwrap();
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(expr.parse_node(), Err(Error::StartWithNonValueOperator));
|
||||
assert_eq!(tree.parse_node(), Err(Error::StartWithNonValueOperator));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_duplicate_operator() {
|
||||
let mut expr = Expression { raw: "5 + + 5".to_owned(), ..Default::default() };
|
||||
let mut tree = Tree { raw: "5 + + 5".to_owned(), ..Default::default() };
|
||||
|
||||
expr.parse_pos().unwrap();
|
||||
expr.parse_operators().unwrap();
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(expr.parse_node(), Err(Error::DuplicateOperatorNode));
|
||||
assert_eq!(tree.parse_node(), Err(Error::DuplicateOperatorNode));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_duplicate_value() {
|
||||
let mut expr = Expression { raw: "2 + 6 5".to_owned(), ..Default::default() };
|
||||
let mut tree = Tree { raw: "2 + 6 5".to_owned(), ..Default::default() };
|
||||
|
||||
expr.parse_pos().unwrap();
|
||||
expr.parse_operators().unwrap();
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(expr.parse_node(), Err(Error::DuplicateValueNode));
|
||||
assert_eq!(tree.parse_node(), Err(Error::DuplicateValueNode));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_unpaired_brackets() {
|
||||
let mut expr = Expression { raw: "(2 + 3)) * 5".to_owned(), ..Default::default() };
|
||||
let mut tree = Tree { raw: "(2 + 3)) * 5".to_owned(), ..Default::default() };
|
||||
|
||||
expr.parse_pos().unwrap();
|
||||
tree.parse_pos().unwrap();
|
||||
|
||||
assert_eq!(expr.parse_operators(), Err(Error::UnpairedBrackets));
|
||||
assert_eq!(tree.parse_operators(), Err(Error::UnpairedBrackets));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_comma() {
|
||||
let mut expr = Expression { raw: ", 2 + 5".to_owned(), ..Default::default() };
|
||||
let mut tree = Tree { raw: ", 2 + 5".to_owned(), ..Default::default() };
|
||||
|
||||
expr.parse_pos().unwrap();
|
||||
expr.parse_operators().unwrap();
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(expr.parse_node(), Err(Error::CommaNotWithFunction));
|
||||
assert_eq!(tree.parse_node(), Err(Error::CommaNotWithFunction));
|
||||
}
|
||||
|
||||
|
||||
@ -490,48 +491,47 @@ mod tests {
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_pos(b: &mut test::Bencher) {
|
||||
let mut expr = Expression {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut tree =
|
||||
Tree { raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(), ..Default::default() };
|
||||
|
||||
b.iter(|| expr.parse_pos().unwrap());
|
||||
b.iter(|| tree.parse_pos().unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_operators(b: &mut test::Bencher) {
|
||||
let mut expr = Expression {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut tree =
|
||||
Tree { raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(), ..Default::default() };
|
||||
|
||||
expr.parse_pos().unwrap();
|
||||
b.iter(|| expr.parse_operators().unwrap());
|
||||
tree.parse_pos().unwrap();
|
||||
b.iter(|| tree.parse_operators().unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_nodes(b: &mut test::Bencher) {
|
||||
let mut expr = Expression {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut tree =
|
||||
Tree { raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(), ..Default::default() };
|
||||
|
||||
expr.parse_pos().unwrap();
|
||||
expr.parse_operators().unwrap();
|
||||
b.iter(|| expr.parse_node().unwrap());
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
b.iter(|| tree.parse_node().unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_compile(b: &mut test::Bencher) {
|
||||
let mut expr = Expression {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
b.iter(|| {
|
||||
let mut tree =
|
||||
Tree { raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(), ..Default::default() };
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
tree.parse_node().unwrap();
|
||||
tree.compile().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
expr.parse_pos().unwrap();
|
||||
expr.parse_operators().unwrap();
|
||||
expr.parse_node().unwrap();
|
||||
b.iter(|| expr.compile());
|
||||
#[bench]
|
||||
fn bench_exec(b: &mut test::Bencher) {
|
||||
let expr = Expr::new("(2 + (3 + 4) + (6 + (6 + 7)) + 5)").compile().unwrap();
|
||||
b.iter(|| expr.exec().unwrap())
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
@ -7,49 +7,48 @@ use operator::Operator;
|
||||
use node::Node;
|
||||
use {Context, Functions};
|
||||
use error::Error;
|
||||
use ContextsRef;
|
||||
use Compiled;
|
||||
use builtin::BuiltIn;
|
||||
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Expression {
|
||||
pub struct Tree {
|
||||
pub raw: String,
|
||||
pub pos: Vec<usize>,
|
||||
pub operators: Vec<Operator>,
|
||||
pub node: Option<Node>,
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
pub fn new<T: Into<String>>(raw: T) -> Result<Expression, Error> {
|
||||
let mut expr = Expression { raw: raw.into(), ..Default::default() };
|
||||
|
||||
expr.parse_pos()?;
|
||||
expr.parse_operators()?;
|
||||
expr.parse_node()?;
|
||||
Ok(expr)
|
||||
impl Tree {
|
||||
pub fn new<T: Into<String>>(raw: T) -> Tree {
|
||||
Tree { raw: raw.into(), ..Default::default() }
|
||||
}
|
||||
|
||||
pub fn parse_pos(&mut self) -> Result<(), Error> {
|
||||
let mut found_quote = false;
|
||||
let mut pos = Vec::new();
|
||||
|
||||
for (index, cur) in self.raw.chars().enumerate() {
|
||||
match cur {
|
||||
'(' | ')' | '+' | '-' | '*' | '/' | ',' | ' ' | '!' | '=' | '>' | '<' | '\'' |
|
||||
'[' | ']' | '%' | '&' | '|' => {
|
||||
if !found_quote {
|
||||
self.pos.push(index);
|
||||
self.pos.push(index + 1);
|
||||
pos.push(index);
|
||||
pos.push(index + 1);
|
||||
}
|
||||
}
|
||||
'"' => {
|
||||
found_quote = !found_quote;
|
||||
self.pos.push(index);
|
||||
self.pos.push(index + 1);
|
||||
pos.push(index);
|
||||
pos.push(index + 1);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
self.pos.push(self.raw.len());
|
||||
pos.push(self.raw.len());
|
||||
|
||||
self.pos = pos;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -62,7 +61,8 @@ impl Expression {
|
||||
let mut quote = None;
|
||||
let mut prev = String::new();
|
||||
|
||||
for pos in self.pos.clone() {
|
||||
for pos_ref in &self.pos {
|
||||
let pos = *pos_ref;
|
||||
if pos == 0 {
|
||||
continue;
|
||||
} else {
|
||||
@ -117,12 +117,10 @@ impl Expression {
|
||||
prev = raw;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
if prev == "!" || prev == ">" || prev == "<" {
|
||||
} else if prev == "!" || prev == ">" || prev == "<" {
|
||||
operators.push(Operator::from_str(&prev).unwrap());
|
||||
prev.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (raw == "&" || raw == "|") && (prev == "&" || prev == "|") {
|
||||
if raw == prev {
|
||||
@ -234,86 +232,90 @@ impl Expression {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compile(&self) -> Box<Fn(ContextsRef, &Functions, &Functions) -> Result<Value, Error>> {
|
||||
let node = self.node.clone().unwrap();
|
||||
pub fn compile(mut self) -> Result<Compiled, Error> {
|
||||
self.parse_pos()?;
|
||||
self.parse_operators()?;
|
||||
self.parse_node()?;
|
||||
let node = self.node.unwrap();
|
||||
let builtin = BuiltIn::new();
|
||||
|
||||
Box::new(move |contexts, buildin, functions| -> Result<Value, Error> {
|
||||
return exec_node(&node, contexts, buildin, functions);
|
||||
Ok(Box::new(move |contexts, functions| -> Result<Value, Error> {
|
||||
return exec_node(&node, &builtin, contexts, functions);
|
||||
|
||||
fn exec_node(node: &Node,
|
||||
contexts: ContextsRef,
|
||||
buildin: &Functions,
|
||||
builtin: &Functions,
|
||||
contexts: &[Context],
|
||||
functions: &Functions)
|
||||
-> Result<Value, Error> {
|
||||
match node.operator {
|
||||
Operator::Add(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.add(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.add(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Mul(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.mul(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.mul(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Sub(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.sub(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.sub(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Div(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.div(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.div(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Rem(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.rem(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.rem(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Eq(_) => {
|
||||
Math::eq(&exec_node(&node.get_first_child(), contexts, buildin, functions)?,
|
||||
&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
Math::eq(&exec_node(&node.get_first_child(), builtin, contexts, functions)?,
|
||||
&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Ne(_) => {
|
||||
Math::ne(&exec_node(&node.get_first_child(), contexts, buildin, functions)?,
|
||||
&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
Math::ne(&exec_node(&node.get_first_child(), builtin, contexts, functions)?,
|
||||
&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Gt(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.gt(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.gt(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Lt(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.lt(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.lt(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Ge(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.ge(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.ge(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Le(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.le(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.le(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::And(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.and(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.and(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Or(_) => {
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.or(&exec_node(&node.get_last_child(), contexts, buildin, functions)?)
|
||||
.or(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Function(ref ident) => {
|
||||
let function_option = if functions.contains_key(ident) {
|
||||
functions.get(ident)
|
||||
} else {
|
||||
buildin.get(ident)
|
||||
builtin.get(ident)
|
||||
};
|
||||
|
||||
if function_option.is_some() {
|
||||
@ -321,7 +323,7 @@ impl Expression {
|
||||
node.check_function_args(function)?;
|
||||
let mut values = Vec::new();
|
||||
for node in &node.children {
|
||||
values.push(exec_node(node, contexts, buildin, functions)?);
|
||||
values.push(exec_node(node, builtin, contexts, functions)?);
|
||||
}
|
||||
(function.compiled)(values)
|
||||
} else {
|
||||
@ -331,7 +333,7 @@ impl Expression {
|
||||
Operator::Value(ref value) => Ok(value.clone()),
|
||||
Operator::Not(_) => {
|
||||
let value =
|
||||
exec_node(&node.get_first_child(), contexts, buildin, functions)?;
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)?;
|
||||
match value {
|
||||
Value::Bool(boolean) => Ok(Value::Bool(!boolean)),
|
||||
Value::Null => Ok(Value::Bool(true)),
|
||||
@ -354,7 +356,7 @@ impl Expression {
|
||||
_ => Err(Error::CanNotExec(node.operator.clone())),
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -427,8 +429,7 @@ fn close_bracket(parsing_nodes: &mut Vec<Node>, bracket: Operator) -> Result<(),
|
||||
parsing_nodes.push(current);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if !prev.closed {
|
||||
} else if !prev.closed {
|
||||
prev.add_child(current);
|
||||
if prev.is_enough() {
|
||||
prev.closed = true;
|
||||
@ -443,7 +444,7 @@ fn close_bracket(parsing_nodes: &mut Vec<Node>, bracket: Operator) -> Result<(),
|
||||
return Err(Error::StartWithNonValueOperator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -476,8 +477,7 @@ fn close_comma(parsing_nodes: &mut Vec<Node>) -> Result<(), Error> {
|
||||
} else {
|
||||
return Err(Error::CommaNotWithFunction);
|
||||
}
|
||||
} else {
|
||||
if !prev.closed {
|
||||
} else if !prev.closed {
|
||||
prev.add_child(current);
|
||||
if prev.is_enough() {
|
||||
prev.closed = true;
|
||||
@ -492,7 +492,6 @@ fn close_comma(parsing_nodes: &mut Vec<Node>) -> Result<(), Error> {
|
||||
return Err(Error::StartWithNonValueOperator);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -502,7 +501,7 @@ fn rob_to(mut was_robed: Node, mut rober: Node) -> Vec<Node> {
|
||||
vec![was_robed, rober]
|
||||
}
|
||||
|
||||
fn find(contexts: ContextsRef, key: &str) -> Option<Value> {
|
||||
fn find(contexts: &[Context], key: &str) -> Option<Value> {
|
||||
for context in contexts.iter().rev() {
|
||||
let value = get(context, key);
|
||||
match value {
|
Loading…
Reference in New Issue
Block a user