init repository.
This commit is contained in:
commit
1d84eea270
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.DS_Store
|
||||||
|
*~
|
||||||
|
*#
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
*.swp
|
||||||
|
*.dylib
|
||||||
|
*.dSYM
|
||||||
|
*.dll
|
||||||
|
*.rlib
|
||||||
|
*.dummy
|
||||||
|
*.exe
|
||||||
|
*-test
|
||||||
|
target/
|
||||||
|
Cargo.lock
|
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "eval"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Expression evaluator"
|
||||||
|
keywords = ["expression", "evaluate", "evaluator", "expr"]
|
||||||
|
authors = ["fengcen <fengcen.love@gmail.com>"]
|
||||||
|
repository = "https://github.com/fengcen/eval.git"
|
||||||
|
homepage = "https://github.com/fengcen/eval"
|
||||||
|
documentation = "https://github.com/fengcen/eval"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "eval"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde_json = "^0.8"
|
||||||
|
quick-error = "^1.1"
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 fengcen
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
84
README.md
Normal file
84
README.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
eval
|
||||||
|
===
|
||||||
|
Expression evaluator for Rust.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
Supported operators: `!` `!=` `""` `''` `()` `[]` `,` `>` `<` `>=` `<=` `==`
|
||||||
|
`+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||||
|
|
||||||
|
Built-in functions: `min()` `max()` `is_empty()`.
|
||||||
|
|
||||||
|
## Where can eval be used?
|
||||||
|
* Template engine
|
||||||
|
* ...
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Add dependency to Cargo.toml
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
eval = "^0.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
In your `main.rs` or `lib.rs`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
extern crate eval;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
You can do mathematical calculations with supported operators:
|
||||||
|
|
||||||
|
```
|
||||||
|
use eval::{eval, to_value};
|
||||||
|
|
||||||
|
assert_eq!(eval("1 + 2 + 3"), Ok(to_value(6)));
|
||||||
|
assert_eq!(eval("2 * 2 + 3"), Ok(to_value(7)));
|
||||||
|
assert_eq!(eval("2 / 2 + 3"), Ok(to_value(4.0)));
|
||||||
|
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};
|
||||||
|
|
||||||
|
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)));
|
||||||
|
```
|
||||||
|
|
||||||
|
You can eval with functions:
|
||||||
|
|
||||||
|
```
|
||||||
|
use eval::{eval_with_functions, Functions, Function, 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!")));
|
||||||
|
```
|
||||||
|
|
||||||
|
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`:
|
||||||
|
|
||||||
|
```
|
||||||
|
use eval::{eval, to_value};
|
||||||
|
|
||||||
|
assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
eval is primarily distributed under the terms of the MIT license.
|
||||||
|
See [LICENSE](LICENSE) for details.
|
101
src/builtin/mod.rs
Normal file
101
src/builtin/mod.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
|
||||||
|
use {Function, Functions, Value, to_value};
|
||||||
|
use math::Math;
|
||||||
|
use error::Error;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct BuiltIn {}
|
||||||
|
|
||||||
|
impl BuiltIn {
|
||||||
|
pub fn new() -> Functions {
|
||||||
|
let mut functions = Functions::new();
|
||||||
|
functions.insert("min".to_owned(), create_min_fuction());
|
||||||
|
functions.insert("max".to_owned(), create_max_fuction());
|
||||||
|
functions.insert("is_empty".to_owned(), create_is_empty_fuction());
|
||||||
|
functions.insert("array".to_owned(), create_array_function());
|
||||||
|
functions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum Compare {
|
||||||
|
Min,
|
||||||
|
Max
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_min_fuction() -> Function {
|
||||||
|
compare(Compare::Min)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_max_fuction() -> Function {
|
||||||
|
compare(Compare::Max)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare(compare: Compare) -> Function {
|
||||||
|
Function {
|
||||||
|
max_args: None,
|
||||||
|
min_args: Some(1),
|
||||||
|
compiled: Box::new(move |values| {
|
||||||
|
let mut prev: Result<Value, Error> = Err(Error::Custom("can't find min value.".to_owned()));
|
||||||
|
|
||||||
|
for value in values {
|
||||||
|
match value {
|
||||||
|
Value::Array(array) => {
|
||||||
|
for value in array {
|
||||||
|
if prev.is_ok() {
|
||||||
|
if compare == Compare::Min {
|
||||||
|
if value.lt(prev.as_ref().unwrap())? == to_value(true) {
|
||||||
|
prev = Ok(value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if value.gt(prev.as_ref().unwrap())? == to_value(true) {
|
||||||
|
prev = Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prev = Ok(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
if prev.is_ok() {
|
||||||
|
if compare == Compare::Min {
|
||||||
|
if value.lt(prev.as_ref().unwrap())? == to_value(true) {
|
||||||
|
prev = Ok(value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if value.gt(prev.as_ref().unwrap())? == to_value(true) {
|
||||||
|
prev = Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prev = Ok(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn create_is_empty_fuction() -> Function {
|
||||||
|
Function {
|
||||||
|
max_args: Some(1),
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_array_function() -> Function {
|
||||||
|
Function::new(|values|Ok(to_value(values)))
|
||||||
|
}
|
75
src/error/mod.rs
Normal file
75
src/error/mod.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
use serde_json::Value;
|
||||||
|
use operator::Operator;
|
||||||
|
|
||||||
|
|
||||||
|
quick_error! {
|
||||||
|
/// Expression parsing error
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Unsupported operator yet.
|
||||||
|
UnsupportedOperator(operator: String) {
|
||||||
|
display("Unsupported operator: {:?}", operator)
|
||||||
|
}
|
||||||
|
/// This operator does not support execution.
|
||||||
|
CanNotExec(operator: Operator) {
|
||||||
|
display("This operator does not support execution: {:?}", operator)
|
||||||
|
}
|
||||||
|
/// Your expression may start with non-value operator like ( + * )
|
||||||
|
StartWithNonValueOperator {
|
||||||
|
display("Your expression may start with non-value operator like ( + * ).")
|
||||||
|
}
|
||||||
|
/// Unpaired brackets, left brackets count does not equal right brackets count
|
||||||
|
UnpairedBrackets {
|
||||||
|
display("Unpaired brackets, left brackets count does not equal right brackets count.")
|
||||||
|
}
|
||||||
|
/// Duplicate values node, you may have (2 3) but there is no operators between them
|
||||||
|
DuplicateValueNode {
|
||||||
|
display("Duplicate values node, you may have (2 3) but there is no operators between them.")
|
||||||
|
}
|
||||||
|
/// Duplicate operators node, you may have (+ +) but there is no values between them
|
||||||
|
DuplicateOperatorNode {
|
||||||
|
display("Duplicate operators node, you may have (+ +) but there is no values between them.")
|
||||||
|
}
|
||||||
|
/// You have a comma(,) , but there is no function in front of it.
|
||||||
|
CommaNotWithFunction {
|
||||||
|
display("You have a comma(,) , but there is no function in front of it.")
|
||||||
|
}
|
||||||
|
/// You have empty brackets () , but there is no function in front of it.
|
||||||
|
BracketNotWithFunction {
|
||||||
|
display("You have empty brackets () , but there is no function in front of it.")
|
||||||
|
}
|
||||||
|
/// Function not exists.
|
||||||
|
FunctionNotExists(ident: String) {
|
||||||
|
display("Function not exists: {}", ident)
|
||||||
|
}
|
||||||
|
/// Expected boolean type but the given value isn't.
|
||||||
|
NotBoolean(value: Value) {
|
||||||
|
display("Expected boolean type, found: {}", value)
|
||||||
|
}
|
||||||
|
/// Failed to parse, no final expression.
|
||||||
|
NoFinalNode {
|
||||||
|
display("Failed to parse, no final expression.")
|
||||||
|
}
|
||||||
|
/// The number of arguments is greater than the maximum limit.
|
||||||
|
ArgumentsGreater(max: usize) {
|
||||||
|
display("The number of arguments is greater than the maximum limit: {}", max)
|
||||||
|
}
|
||||||
|
/// The number of arguments is less than the minimum limit.
|
||||||
|
ArgumentsLess(min: usize) {
|
||||||
|
display("The number of arguments is less than the minimum limit: {}", min)
|
||||||
|
}
|
||||||
|
/// This two value types are different or do not support mathematical calculations.
|
||||||
|
UnsupportedTypes(a: String, b: String) {
|
||||||
|
display("This two value types are different or do not support mathematical calculations: {}, {}", a, b)
|
||||||
|
}
|
||||||
|
/// Invalid array expression like `1..2..3`
|
||||||
|
InvalidArray(ident: String) {
|
||||||
|
display("Invalid array expression: {}", ident)
|
||||||
|
}
|
||||||
|
/// Custom error.
|
||||||
|
Custom(detail: String) {
|
||||||
|
display("{}", detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
509
src/expression/mod.rs
Normal file
509
src/expression/mod.rs
Normal file
@ -0,0 +1,509 @@
|
|||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::clone::Clone;
|
||||||
|
use serde_json::{Value, to_value};
|
||||||
|
use math::Math;
|
||||||
|
use operator::Operator;
|
||||||
|
use node::Node;
|
||||||
|
use {Context, Functions};
|
||||||
|
use error::Error;
|
||||||
|
use ContextsRef;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Expression {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_pos(&mut self) -> Result<(), Error> {
|
||||||
|
let mut found_quote = false;
|
||||||
|
|
||||||
|
for (index, cur) in self.raw.chars().enumerate() {
|
||||||
|
match cur {
|
||||||
|
'(' | ')' | '+' | '-' | '*' | '/' | ',' | ' ' | '!' | '=' |
|
||||||
|
'>' | '<' | '\'' | '[' | ']' | '%' | '&' | '|' => {
|
||||||
|
if ! found_quote {
|
||||||
|
self.pos.push(index);
|
||||||
|
self.pos.push(index + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'"' => {
|
||||||
|
found_quote = ! found_quote;
|
||||||
|
self.pos.push(index);
|
||||||
|
self.pos.push(index + 1);
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pos.push(self.raw.len());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_operators(&mut self) -> Result<(), Error> {
|
||||||
|
let mut operators = Vec::new();
|
||||||
|
let mut start;
|
||||||
|
let mut end = 0;
|
||||||
|
let mut parenthesis = 0;
|
||||||
|
let mut square_brackets = 0;
|
||||||
|
let mut quote = None;
|
||||||
|
let mut prev = String::new();
|
||||||
|
|
||||||
|
for pos in self.pos.clone() {
|
||||||
|
if pos == 0 {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
start = end;
|
||||||
|
end = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
let raw = self.raw[start..end].to_owned();
|
||||||
|
|
||||||
|
let operator = Operator::from_str(&raw).unwrap();
|
||||||
|
match operator {
|
||||||
|
Operator::DoubleQuotes | Operator::SingleQuote => {
|
||||||
|
if quote.is_some() {
|
||||||
|
if quote.as_ref() == Some(&operator) {
|
||||||
|
operators.push(Operator::Value(to_value(&prev)));
|
||||||
|
prev.clear();
|
||||||
|
quote = None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote = Some(operator);
|
||||||
|
prev.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
|
};
|
||||||
|
|
||||||
|
if quote.is_some() {
|
||||||
|
prev += &raw;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw == "=" {
|
||||||
|
if prev == "!" || prev == ">" || prev == "<" || prev == "=" {
|
||||||
|
prev.push_str("=");
|
||||||
|
operators.push(Operator::from_str(&prev).unwrap());
|
||||||
|
prev.clear();
|
||||||
|
} else {
|
||||||
|
prev = raw;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if raw == "!" || raw == ">" || raw == "<" {
|
||||||
|
if prev == "!" || prev == ">" || prev == "<" {
|
||||||
|
operators.push(Operator::from_str(&prev).unwrap());
|
||||||
|
prev.clear();
|
||||||
|
} else {
|
||||||
|
prev = raw;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if prev == "!" || prev == ">" || prev == "<" {
|
||||||
|
operators.push(Operator::from_str(&prev).unwrap());
|
||||||
|
prev.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw == "&" || raw == "|") && (prev == "&" || prev == "|") {
|
||||||
|
if raw == prev {
|
||||||
|
prev.push_str(&raw);
|
||||||
|
operators.push(Operator::from_str(&prev).unwrap());
|
||||||
|
prev.clear();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return Err(Error::UnsupportedOperator(prev));
|
||||||
|
}
|
||||||
|
} else if raw == "&" || raw == "|" {
|
||||||
|
prev = raw;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match operator {
|
||||||
|
Operator::LeftSquareBracket => {
|
||||||
|
square_brackets += 1;
|
||||||
|
operators.push(Operator::Function("array".to_owned()));
|
||||||
|
operators.push(operator);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
Operator::LeftParenthesis => {
|
||||||
|
parenthesis += 1;
|
||||||
|
|
||||||
|
if ! operators.is_empty() {
|
||||||
|
let prev_operator = operators.pop().unwrap();
|
||||||
|
if prev_operator.is_identifier() {
|
||||||
|
operators.push(Operator::Function(prev_operator.get_identifier()));
|
||||||
|
operators.push(operator);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
operators.push(prev_operator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Operator::RightParenthesis => parenthesis -= 1,
|
||||||
|
Operator::RightSquareBracket => square_brackets -= 1,
|
||||||
|
Operator::WhiteSpace => continue,
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = raw;
|
||||||
|
operators.push(operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if parenthesis != 0 || square_brackets != 0 {
|
||||||
|
Err(Error::UnpairedBrackets)
|
||||||
|
} else {
|
||||||
|
self.operators = operators;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(&self) -> Box<Fn(ContextsRef, &Functions, &Functions) -> Result<Value, Error>> {
|
||||||
|
let node = self.node.clone().unwrap();
|
||||||
|
|
||||||
|
Box::new(move |contexts, buildin, functions| -> Result<Value, Error> {
|
||||||
|
return exec_node(&node, contexts, buildin, functions);
|
||||||
|
|
||||||
|
fn exec_node(node: &Node, contexts: ContextsRef, buildin: &Functions, functions: &Functions) -> Result<Value, Error> {
|
||||||
|
match node.operator {
|
||||||
|
Operator::Add(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.add(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Mul(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.mul(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Sub(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.sub(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Div(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.div(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Rem(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.rem(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Eq(_) => Math::eq(&exec_node(&node.get_first_child(), contexts, buildin, functions)?, &exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Ne(_) => Math::ne(&exec_node(&node.get_first_child(), contexts, buildin, functions)?, &exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Gt(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.gt(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Lt(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.lt(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Ge(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.ge(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Le(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.le(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::And(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.and(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Or(_) => exec_node(&node.get_first_child(), contexts, buildin, functions)?.or(&exec_node(&node.get_last_child(), contexts, buildin, functions)?),
|
||||||
|
Operator::Function(ref ident) => {
|
||||||
|
let function_option = if functions.contains_key(ident) {
|
||||||
|
functions.get(ident)
|
||||||
|
} else {
|
||||||
|
buildin.get(ident)
|
||||||
|
};
|
||||||
|
|
||||||
|
if function_option.is_some() {
|
||||||
|
let function = function_option.unwrap();
|
||||||
|
node.check_function_args(function)?;
|
||||||
|
let mut values = Vec::new();
|
||||||
|
for node in &node.children {
|
||||||
|
values.push(exec_node(node, contexts, buildin, functions)?);
|
||||||
|
}
|
||||||
|
(function.compiled)(values)
|
||||||
|
} else {
|
||||||
|
Err(Error::FunctionNotExists(ident.to_owned()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Operator::Value(ref value) => Ok(value.clone()),
|
||||||
|
Operator::Not(_) => {
|
||||||
|
let value = exec_node(&node.get_first_child(), contexts, buildin, functions)?;
|
||||||
|
match value {
|
||||||
|
Value::Bool(boolean) => Ok(Value::Bool(!boolean)),
|
||||||
|
Value::Null => Ok(Value::Bool(true)),
|
||||||
|
_ => Err(Error::NotBoolean(value))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Operator::Identifier(ref ident) => {
|
||||||
|
let number = parse_number(ident);
|
||||||
|
if number.is_some() {
|
||||||
|
Ok(number.unwrap())
|
||||||
|
} else if is_range(ident) {
|
||||||
|
parse_range(ident)
|
||||||
|
} else {
|
||||||
|
match find(contexts, ident) {
|
||||||
|
Some(value) => Ok(value),
|
||||||
|
None => Ok(Value::Null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => Err(Error::CanNotExec(node.operator.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_node(&mut self) -> Result<(), Error> {
|
||||||
|
let mut parsing_nodes = Vec::<Node>::new();
|
||||||
|
|
||||||
|
for operator in &self.operators {
|
||||||
|
match *operator {
|
||||||
|
Operator::Add(priority) | Operator::Sub(priority) |
|
||||||
|
Operator::Mul(priority) | Operator::Div(priority) |
|
||||||
|
Operator::Not(priority) | Operator::Eq(priority) |
|
||||||
|
Operator::Ne(priority) | Operator::Gt(priority) |
|
||||||
|
Operator::Lt(priority) | Operator::Ge(priority) |
|
||||||
|
Operator::And(priority) | Operator::Or(priority) |
|
||||||
|
Operator::Le(priority) | Operator::Rem(priority) => {
|
||||||
|
if ! parsing_nodes.is_empty() {
|
||||||
|
let prev = parsing_nodes.pop().unwrap();
|
||||||
|
if prev.is_value_or_enough() {
|
||||||
|
if prev.operator.get_priority() < priority && ! prev.closed {
|
||||||
|
parsing_nodes.extend_from_slice(&rob_to(prev, operator.to_node()));
|
||||||
|
} else {
|
||||||
|
parsing_nodes.push(operator.children_to_node(vec![prev]));
|
||||||
|
}
|
||||||
|
} else if prev.operator.can_at_beginning() {
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
parsing_nodes.push(operator.to_node());
|
||||||
|
} else {
|
||||||
|
return Err(Error::DuplicateOperatorNode);
|
||||||
|
}
|
||||||
|
} else if operator.can_at_beginning() {
|
||||||
|
parsing_nodes.push(operator.to_node());
|
||||||
|
} else {
|
||||||
|
return Err(Error::StartWithNonValueOperator);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Operator::Function(_) | Operator::LeftParenthesis | Operator::LeftSquareBracket => parsing_nodes.push(operator.to_node()),
|
||||||
|
Operator::Comma => close_comma(&mut parsing_nodes)?,
|
||||||
|
Operator::RightParenthesis | Operator::RightSquareBracket => close_bracket(&mut parsing_nodes, operator.get_left())?,
|
||||||
|
Operator::Value(_) | Operator::Identifier(_) => append_child_to_last_node(&mut parsing_nodes, operator)?,
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.node = Some(get_final_node(parsing_nodes)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_child_to_last_node(parsing_nodes: &mut Vec<Node>, operator: &Operator) -> Result<(), Error> {
|
||||||
|
let mut node = operator.to_node();
|
||||||
|
node.closed = true;
|
||||||
|
|
||||||
|
if let Some(mut prev) = parsing_nodes.pop() {
|
||||||
|
if prev.is_value_or_enough() {
|
||||||
|
return Err(Error::DuplicateValueNode);
|
||||||
|
} else if prev.is_enough() {
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
parsing_nodes.push(node);
|
||||||
|
} else {
|
||||||
|
prev.add_child(node);
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parsing_nodes.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_final_node(mut parsing_nodes: Vec<Node>) -> Result<Node, Error> {
|
||||||
|
if parsing_nodes.is_empty() {
|
||||||
|
return Err(Error::NoFinalNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
while parsing_nodes.len() != 1 {
|
||||||
|
let mut last_node = parsing_nodes.pop().unwrap();
|
||||||
|
let mut prev_node = parsing_nodes.pop().unwrap();
|
||||||
|
prev_node.add_child(last_node);
|
||||||
|
last_node = prev_node;
|
||||||
|
parsing_nodes.push(last_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(parsing_nodes.pop().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close_bracket(parsing_nodes: &mut Vec<Node>, bracket: Operator) -> Result<(), Error> {
|
||||||
|
loop {
|
||||||
|
let mut current = parsing_nodes.pop().unwrap();
|
||||||
|
let mut prev = parsing_nodes.pop().unwrap();
|
||||||
|
|
||||||
|
if current.operator == bracket {
|
||||||
|
if prev.is_unclosed_function() {
|
||||||
|
prev.closed = true;
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return Err(Error::BracketNotWithFunction);
|
||||||
|
}
|
||||||
|
} else if prev.operator == bracket {
|
||||||
|
if ! current.closed {
|
||||||
|
current.closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut penult) = parsing_nodes.pop() {
|
||||||
|
if penult.is_unclosed_function() {
|
||||||
|
penult.closed = true;
|
||||||
|
penult.add_child(current);
|
||||||
|
parsing_nodes.push(penult);
|
||||||
|
} else {
|
||||||
|
parsing_nodes.push(penult);
|
||||||
|
parsing_nodes.push(current);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parsing_nodes.push(current);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if ! prev.closed {
|
||||||
|
prev.add_child(current);
|
||||||
|
if prev.is_enough() {
|
||||||
|
prev.closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! parsing_nodes.is_empty() {
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
} else {
|
||||||
|
return Err(Error::StartWithNonValueOperator);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::StartWithNonValueOperator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close_comma(parsing_nodes: &mut Vec<Node>) -> Result<(), Error> {
|
||||||
|
if parsing_nodes.len() < 2 {
|
||||||
|
return Err(Error::CommaNotWithFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let current = parsing_nodes.pop().unwrap();
|
||||||
|
let mut prev = parsing_nodes.pop().unwrap();
|
||||||
|
|
||||||
|
if current.operator == Operator::Comma {
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
break;
|
||||||
|
} else if current.operator.is_left() {
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
parsing_nodes.push(current);
|
||||||
|
break;
|
||||||
|
} else if prev.operator.is_left() {
|
||||||
|
if let Some(mut penult) = parsing_nodes.pop() {
|
||||||
|
if penult.is_unclosed_function() {
|
||||||
|
penult.add_child(current);
|
||||||
|
parsing_nodes.push(penult);
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return Err(Error::CommaNotWithFunction);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::CommaNotWithFunction);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ! prev.closed {
|
||||||
|
prev.add_child(current);
|
||||||
|
if prev.is_enough() {
|
||||||
|
prev.closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! parsing_nodes.is_empty() {
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
} else {
|
||||||
|
return Err(Error::StartWithNonValueOperator);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::StartWithNonValueOperator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rob_to(mut was_robed: Node, mut rober: Node) -> Vec<Node> {
|
||||||
|
let moveout_node = was_robed.moveout_last_node();
|
||||||
|
rober.add_child(moveout_node);
|
||||||
|
vec![was_robed, rober]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find(contexts: ContextsRef, key: &str) -> Option<Value> {
|
||||||
|
for context in contexts.iter().rev() {
|
||||||
|
let value = get(context, key);
|
||||||
|
match value {
|
||||||
|
Some(_) => return value,
|
||||||
|
None => continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(context: &Context, key: &str) -> Option<Value> {
|
||||||
|
let mut keys = key.split('.').collect::<Vec<_>>();
|
||||||
|
let context_key = keys.remove(0);
|
||||||
|
let context_value_option = context.get(context_key);
|
||||||
|
|
||||||
|
if context_value_option.is_none() {
|
||||||
|
None
|
||||||
|
} else if ! keys.is_empty() {
|
||||||
|
match context_value_option.unwrap().search(&keys.join(".")) {
|
||||||
|
Some(value) => Some(value.clone()),
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(context_value_option.unwrap().clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_range(ident: &str) -> bool {
|
||||||
|
ident.contains("..")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_range(ident: &str) -> Result<Value, Error> {
|
||||||
|
let segments = ident.split("..").collect::<Vec<_>>();
|
||||||
|
if segments.len() != 2 {
|
||||||
|
Err(Error::InvalidArray(ident.to_owned()))
|
||||||
|
} else {
|
||||||
|
let start = segments[0].parse::<i64>();
|
||||||
|
let end = segments[1].parse::<i64>();
|
||||||
|
|
||||||
|
if start.is_ok() && end.is_ok() {
|
||||||
|
let mut array = Vec::new();
|
||||||
|
for n in start.unwrap()..end.unwrap() {
|
||||||
|
array.push(n);
|
||||||
|
}
|
||||||
|
Ok(to_value(array))
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidArray(ident.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_number(ident: &str) -> Option<Value> {
|
||||||
|
let number = ident.parse::<u64>();
|
||||||
|
if number.is_ok() {
|
||||||
|
return Some(to_value(number.unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let number = ident.parse::<i64>();
|
||||||
|
if number.is_ok() {
|
||||||
|
return Some(to_value(number.unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let number = ident.parse::<f64>();
|
||||||
|
if number.is_ok() {
|
||||||
|
return Some(to_value(number.unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
32
src/function/mod.rs
Normal file
32
src/function/mod.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use serde_json::Value;
|
||||||
|
use error::Error;
|
||||||
|
|
||||||
|
|
||||||
|
/// Custom function
|
||||||
|
pub struct Function {
|
||||||
|
/// Maximum number of arguments.
|
||||||
|
pub max_args: Option<usize>,
|
||||||
|
/// Minimum number of arguments.
|
||||||
|
pub min_args: Option<usize>,
|
||||||
|
/// Accept values and return a result which contains a value.
|
||||||
|
pub compiled: Box<Fn(Vec<Value>) -> Result<Value, Error> + Sync + Send>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function {
|
||||||
|
/// Create a function with a closure.
|
||||||
|
pub fn new<F>(closure: F) -> Function where F: 'static + Fn(Vec<Value>) -> Result<Value, Error> + Sync + Send {
|
||||||
|
Function {
|
||||||
|
max_args: None,
|
||||||
|
min_args: None,
|
||||||
|
compiled: Box::new(closure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Function {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Function {{ max_args: {:?}, min_args: {:?} }}", self.max_args, self.min_args)
|
||||||
|
}
|
||||||
|
}
|
540
src/lib.rs
Normal file
540
src/lib.rs
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
//! Expression evaluator.
|
||||||
|
//!
|
||||||
|
//! Supported operators: `!` `!=` `""` `''` `()` `[]` `,` `>` `<` `>=` `<=`
|
||||||
|
//! `==` `+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||||
|
//!
|
||||||
|
//! Built-in functions: `min()` `max()` `is_empty()`.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! You can do mathematical calculations with supported operators:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use eval::{eval, to_value};
|
||||||
|
//!
|
||||||
|
//! assert_eq!(eval("1 + 2 + 3"), Ok(to_value(6)));
|
||||||
|
//! assert_eq!(eval("2 * 2 + 3"), Ok(to_value(7)));
|
||||||
|
//! assert_eq!(eval("2 / 2 + 3"), Ok(to_value(4.0)));
|
||||||
|
//! 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};
|
||||||
|
//!
|
||||||
|
//! 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)));
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! You can eval with functions:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use eval::{eval_with_functions, Functions, Function, 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!")));
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 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`:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use eval::{eval, to_value};
|
||||||
|
//!
|
||||||
|
//! assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
#![recursion_limit="100"]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![feature(proc_macro, test)]
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate quick_error;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
|
||||||
|
mod math;
|
||||||
|
mod function;
|
||||||
|
mod operator;
|
||||||
|
mod node;
|
||||||
|
mod expression;
|
||||||
|
mod error;
|
||||||
|
mod builtin;
|
||||||
|
|
||||||
|
|
||||||
|
pub use serde_json::{Value, to_value};
|
||||||
|
pub use error::Error;
|
||||||
|
pub use function::Function;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use expression::Expression;
|
||||||
|
use builtin::BuiltIn;
|
||||||
|
|
||||||
|
type ContextsRef<'a> = &'a [Context];
|
||||||
|
|
||||||
|
/// Eval context.
|
||||||
|
pub type Context = HashMap<String, Value>;
|
||||||
|
/// Eval contexts. The value of the last context is searched first.
|
||||||
|
pub type Contexts = Vec<Context>;
|
||||||
|
/// Eval 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 eval;
|
||||||
|
use eval_with_context;
|
||||||
|
use eval_with_functions;
|
||||||
|
use {Function, Functions};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add() {
|
||||||
|
assert_eq!(eval("2 + 3"), Ok(to_value(5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_brackets_add() {
|
||||||
|
assert_eq!(eval("(2 + 3) + (3 + 5)"), Ok(to_value(13)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_brackets_float_add() {
|
||||||
|
assert_eq!(eval("(2 + 3.2) + (3 + 5)"), Ok(to_value(13.2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_brackets_float_mul() {
|
||||||
|
assert_eq!(eval("(2 + 3.2) * 5"), Ok(to_value(26.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_brackets_sub() {
|
||||||
|
assert_eq!(eval("(4 - 3) * 5"), Ok(to_value(5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_useless_brackets() {
|
||||||
|
assert_eq!(eval("2 + 3 + (5)"), Ok(to_value(10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_brackets_not_with_function() {
|
||||||
|
assert_eq!(eval("5 + ()"), Err(Error::BracketNotWithFunction));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deep_brackets() {
|
||||||
|
assert_eq!(eval("(2 + (3 + 4) + (6 + (6 + 7)) + 5)"), Ok(to_value(33)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_brackets_div() {
|
||||||
|
assert_eq!(eval("(4 / (2 + 2)) * 5"), Ok(to_value(5.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_min() {
|
||||||
|
assert_eq!(eval("min(30, 5, 245, 20)"), Ok(to_value(5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_min_brackets() {
|
||||||
|
assert_eq!(eval("(min(30, 5, 245, 20) * 10 + (5 + 5) * 5)"), Ok(to_value(100)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_min_and_mul() {
|
||||||
|
assert_eq!(eval("min(30, 5, 245, 20) * 10"), Ok(to_value(50)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_max() {
|
||||||
|
assert_eq!(eval("max(30, 5, 245, 20)"), Ok(to_value(245)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_max_brackets() {
|
||||||
|
assert_eq!(eval("(max(30, 5, 245, 20) * 10 + (5 + 5) * 5)"), Ok(to_value(2500)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_max_and_mul() {
|
||||||
|
assert_eq!(eval("max(30, 5, 245, 20) * 10"), Ok(to_value(2450)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_brackets_1() {
|
||||||
|
assert_eq!(eval("(5) + (min(3, 4, 5)) + 20"), Ok(to_value(28)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_brackets_2() {
|
||||||
|
assert_eq!(eval("(((5) / 5))"), Ok(to_value(1.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string_add() {
|
||||||
|
assert_eq!(eval(r#""Hello"+", world!""#), Ok(to_value("Hello, world!")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_equal() {
|
||||||
|
assert_eq!(eval("1 == 1"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_equal() {
|
||||||
|
assert_eq!(eval("1 != 2"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multiple_equal() {
|
||||||
|
assert_eq!(eval("(1 == 2) == (2 == 3)"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multiple_not_equal() {
|
||||||
|
assert_eq!(eval("(1 != 2) == (2 != 3)"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_greater_than() {
|
||||||
|
assert_eq!(eval("1 > 2"), Ok(to_value(false)));
|
||||||
|
assert_eq!(eval("2 > 1"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_less_than() {
|
||||||
|
assert_eq!(eval("2 < 1"), Ok(to_value(false)));
|
||||||
|
assert_eq!(eval("1 < 2"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_greater_and_less() {
|
||||||
|
assert_eq!(eval("(2 > 1) == (1 < 2)"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ge() {
|
||||||
|
assert_eq!(eval("2 >= 1"), Ok(to_value(true)));
|
||||||
|
assert_eq!(eval("2 >= 2"), Ok(to_value(true)));
|
||||||
|
assert_eq!(eval("2 >= 3"), Ok(to_value(false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_le() {
|
||||||
|
assert_eq!(eval("2 <= 1"), Ok(to_value(false)));
|
||||||
|
assert_eq!(eval("2 <= 2"), Ok(to_value(true)));
|
||||||
|
assert_eq!(eval("2 <= 3"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quotes() {
|
||||||
|
assert_eq!(eval(r#""1><2" + "3<>4""#), Ok(to_value("1><23<>4")));
|
||||||
|
assert_eq!(eval(r#""1==2" + "3--4""#), Ok(to_value("1==23--4")));
|
||||||
|
assert_eq!(eval(r#""1!=2" + "3>>4""#), Ok(to_value("1!=23>>4")));
|
||||||
|
assert_eq!(eval(r#""><1!=2" + "3>>4""#), Ok(to_value("><1!=23>>4")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_single_quote() {
|
||||||
|
assert_eq!(eval(r#"'1><2' + '3<>4'"#), Ok(to_value("1><23<>4")));
|
||||||
|
assert_eq!(eval(r#"'1==2' + '3--4'"#), Ok(to_value("1==23--4")));
|
||||||
|
assert_eq!(eval(r#"'1!=2' + '3>>4'"#), Ok(to_value("1!=23>>4")));
|
||||||
|
assert_eq!(eval(r#"'!=1<>2' + '3>>4'"#), Ok(to_value("!=1<>23>>4")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_single_and_double_quote() {
|
||||||
|
assert_eq!(eval(r#"' """" ' + ' """" '"#), Ok(to_value(r#" """" """" "#)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_double_and_single_quote() {
|
||||||
|
assert_eq!(eval(r#"" '''' " + " '''' ""#), Ok(to_value(r#" '''' '''' "#)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_array() {
|
||||||
|
assert_eq!(eval("[1, 2, 3, 4]"), Ok(to_value(vec![1, 2, 3, 4])));
|
||||||
|
assert_eq!(eval("array(1, 2, 3, 4)"), Ok(to_value(vec![1, 2, 3, 4])));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_array_ident() {
|
||||||
|
assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_array_ident_and_min() {
|
||||||
|
assert_eq!(eval("min(0..5)"), Ok(to_value(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rem_1() {
|
||||||
|
assert_eq!(eval("2 % 2"), Ok(to_value(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rem_2() {
|
||||||
|
assert_eq!(eval("5 % 56 % 5"), Ok(to_value(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rem_3() {
|
||||||
|
assert_eq!(eval("5.5 % 23"), Ok(to_value(5.5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rem_4() {
|
||||||
|
assert_eq!(eval("23 % 5.5"), Ok(to_value(1.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_and_1() {
|
||||||
|
assert_eq!(eval("3 > 2 && 2 > 1"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_and_2() {
|
||||||
|
assert_eq!(eval("3 == 2 && 2 == 1"), Ok(to_value(false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_and_3() {
|
||||||
|
assert_eq!(eval("3 > 2 && 2 == 1"), Ok(to_value(false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_or_1() {
|
||||||
|
assert_eq!(eval("3 > 2 || 2 > 1"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_or_2() {
|
||||||
|
assert_eq!(eval("3 < 2 || 2 < 1"), Ok(to_value(false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_or_3() {
|
||||||
|
assert_eq!(eval("3 > 2 || 2 < 1"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_or_4() {
|
||||||
|
assert_eq!(eval("3 < 2 || 2 > 1"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not() {
|
||||||
|
assert_eq!(eval("!false"), Ok(to_value(true)));
|
||||||
|
assert_eq!(eval("!true"), Ok(to_value(false)));
|
||||||
|
assert_eq!(eval("!(1 != 2)"), Ok(to_value(false)));
|
||||||
|
assert_eq!(eval("!(1 == 2)"), Ok(to_value(true)));
|
||||||
|
assert_eq!(eval("!(1 == 2) == true"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_and_brackets() {
|
||||||
|
assert_eq!(eval("(!(1 == 2)) == true"), Ok(to_value(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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), 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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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), 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
|
||||||
|
};
|
||||||
|
|
||||||
|
expr.parse_pos().unwrap();
|
||||||
|
expr.parse_operators().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(expr.parse_node(), Err(Error::StartWithNonValueOperator));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_duplicate_operator() {
|
||||||
|
let mut expr = Expression {
|
||||||
|
raw: "5 + + 5".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
expr.parse_pos().unwrap();
|
||||||
|
expr.parse_operators().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(expr.parse_node(), Err(Error::DuplicateOperatorNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_duplicate_value() {
|
||||||
|
let mut expr = Expression {
|
||||||
|
raw: "2 + 6 5".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
expr.parse_pos().unwrap();
|
||||||
|
expr.parse_operators().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(expr.parse_node(), Err(Error::DuplicateValueNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_unpaired_brackets() {
|
||||||
|
let mut expr = Expression {
|
||||||
|
raw: "(2 + 3)) * 5".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
expr.parse_pos().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(expr.parse_operators(), Err(Error::UnpairedBrackets));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_comma() {
|
||||||
|
let mut expr = Expression {
|
||||||
|
raw: ", 2 + 5".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
expr.parse_pos().unwrap();
|
||||||
|
expr.parse_operators().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(expr.parse_node(), Err(Error::CommaNotWithFunction));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_deep_brackets(b: &mut test::Bencher) {
|
||||||
|
b.iter(|| eval("(2 + (3 + 4) + (6 + (6 + 7)) + 5)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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()
|
||||||
|
};
|
||||||
|
|
||||||
|
b.iter(|| expr.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()
|
||||||
|
};
|
||||||
|
|
||||||
|
expr.parse_pos().unwrap();
|
||||||
|
b.iter(|| expr.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()
|
||||||
|
};
|
||||||
|
|
||||||
|
expr.parse_pos().unwrap();
|
||||||
|
expr.parse_operators().unwrap();
|
||||||
|
b.iter(|| expr.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()
|
||||||
|
};
|
||||||
|
|
||||||
|
expr.parse_pos().unwrap();
|
||||||
|
expr.parse_operators().unwrap();
|
||||||
|
expr.parse_node().unwrap();
|
||||||
|
b.iter(|| expr.compile());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_eval(b: &mut test::Bencher) {
|
||||||
|
b.iter(|| eval("(2 + (3 + 4) + (6 + (6 + 7)) + 5)"));
|
||||||
|
}
|
||||||
|
}
|
195
src/math/mod.rs
Normal file
195
src/math/mod.rs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
|
||||||
|
use serde_json::{Value, to_value};
|
||||||
|
use error::Error;
|
||||||
|
|
||||||
|
pub trait Math {
|
||||||
|
fn add(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn mul(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn sub(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn div(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn rem(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn eq(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn ne(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn gt(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn lt(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn ge(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn le(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn and(&self, &Value) -> Result<Value, Error>;
|
||||||
|
fn or(&self, &Value) -> Result<Value, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Math for Value {
|
||||||
|
fn add(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
if self.is_f64() || value.is_f64() {
|
||||||
|
Ok(to_value(self.get_f64() + value.get_f64()))
|
||||||
|
} else if self.is_i64() || value.is_i64() {
|
||||||
|
Ok(to_value(self.get_i64() + value.get_i64()))
|
||||||
|
} else {
|
||||||
|
Ok(to_value(self.get_u64() + value.get_u64()))
|
||||||
|
}
|
||||||
|
} else if self.is_string() && value.is_string() {
|
||||||
|
Ok(to_value(self.get_string() + value.get_str()))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mul(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
if self.is_f64() || value.is_f64() {
|
||||||
|
Ok(to_value(self.get_f64() * value.get_f64()))
|
||||||
|
} else if self.is_i64() || value.is_i64() {
|
||||||
|
Ok(to_value(self.get_i64() * value.get_i64()))
|
||||||
|
} else {
|
||||||
|
Ok(to_value(self.get_u64() * value.get_u64()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
if self.is_f64() || value.is_f64() {
|
||||||
|
Ok(to_value(self.get_f64() - value.get_f64()))
|
||||||
|
} else {
|
||||||
|
Ok(to_value(self.get_i64() - value.get_i64()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn div(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
Ok(to_value(self.get_f64() / value.get_f64()))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rem(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
if self.is_f64() || value.is_f64() {
|
||||||
|
Ok(to_value(self.get_f64() % value.get_f64()))
|
||||||
|
} else if self.is_i64() || value.is_i64() {
|
||||||
|
Ok(to_value(self.get_i64() % value.get_i64()))
|
||||||
|
} else {
|
||||||
|
Ok(to_value(self.get_u64() % value.get_u64()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eq(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
Ok(to_value(self.get_f64() == value.get_f64()))
|
||||||
|
} else {
|
||||||
|
Ok(to_value(self == value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ne(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
Ok(to_value(self.get_f64() != value.get_f64()))
|
||||||
|
} else {
|
||||||
|
Ok(to_value(self != value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gt(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
Ok(to_value(self.get_f64() > value.get_f64()))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lt(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
Ok(to_value(self.get_f64() < value.get_f64()))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ge(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
Ok(to_value(self.get_f64() >= value.get_f64()))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn le(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_number() && value.is_number() {
|
||||||
|
Ok(to_value(self.get_f64() <= value.get_f64()))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn and(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_boolean() && value.is_boolean() {
|
||||||
|
Ok(to_value(self.get_boolean() && value.get_boolean()))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn or(&self, value: &Value) -> Result<Value, Error> {
|
||||||
|
if self.is_boolean() && value.is_boolean() {
|
||||||
|
Ok(to_value(self.get_boolean() || value.get_boolean()))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnsupportedTypes(self.format(), value.format()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
trait Type {
|
||||||
|
fn get_f64(&self) -> f64;
|
||||||
|
fn get_string(&self) -> String;
|
||||||
|
fn get_str(&self) -> &str;
|
||||||
|
fn get_u64(&self) -> u64;
|
||||||
|
fn get_i64(&self) -> i64;
|
||||||
|
fn get_boolean(&self) -> bool;
|
||||||
|
fn format(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type for Value {
|
||||||
|
fn get_f64(&self) -> f64 {
|
||||||
|
match *self {
|
||||||
|
Value::F64(f) => f,
|
||||||
|
Value::I64(f) => f as f64,
|
||||||
|
Value::U64(f) => f as f64,
|
||||||
|
_ => panic!("not a number")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_string(&self) -> String {
|
||||||
|
self.as_str().unwrap().to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_str(&self) -> &str {
|
||||||
|
self.as_str().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_u64(&self) -> u64 {
|
||||||
|
self.as_u64().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_i64(&self) -> i64 {
|
||||||
|
self.as_i64().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_boolean(&self) -> bool {
|
||||||
|
self.as_bool().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self) -> String {
|
||||||
|
format!("{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
86
src/node/mod.rs
Normal file
86
src/node/mod.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
use operator::Operator;
|
||||||
|
use error::Error;
|
||||||
|
use Function;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Node {
|
||||||
|
pub operator: Operator,
|
||||||
|
pub children: Vec<Node>,
|
||||||
|
pub closed: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
pub fn new(operator: Operator) -> Node {
|
||||||
|
Node {
|
||||||
|
operator: operator,
|
||||||
|
children: Vec::new(),
|
||||||
|
closed: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_function_args(&self, function: &Function) -> Result<(), Error> {
|
||||||
|
let args_length = self.children.len();
|
||||||
|
|
||||||
|
if let Some(len) = function.max_args {
|
||||||
|
if args_length > len {
|
||||||
|
return Err(Error::ArgumentsGreater(len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(len) = function.min_args {
|
||||||
|
if args_length < len {
|
||||||
|
return Err(Error::ArgumentsLess(len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_enough(&self) -> bool {
|
||||||
|
let num = self.operator.get_max_args();
|
||||||
|
if num.is_none() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
self.children.len() == num.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_value_or_enough(&self) -> bool {
|
||||||
|
if self.operator.is_value_or_ident() {
|
||||||
|
true
|
||||||
|
} else if self.operator.can_have_child() {
|
||||||
|
if self.closed {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
self.is_enough()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_unclosed_function(&self) -> bool {
|
||||||
|
match self.operator {
|
||||||
|
Operator::Function(_) => ! self.closed,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_child(&mut self, node: Node) {
|
||||||
|
self.children.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_first_child(&self) -> Node {
|
||||||
|
self.children.first().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_last_child(&self) -> Node {
|
||||||
|
self.children.last().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn moveout_last_node(&mut self) -> Node {
|
||||||
|
self.children.pop().unwrap()
|
||||||
|
}
|
||||||
|
}
|
192
src/operator/mod.rs
Normal file
192
src/operator/mod.rs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
use serde_json::{Value, to_value};
|
||||||
|
use error::Error;
|
||||||
|
use node::Node;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Operator {
|
||||||
|
Add(u8),
|
||||||
|
Mul(u8),
|
||||||
|
Sub(u8),
|
||||||
|
Div(u8),
|
||||||
|
Rem(u8),
|
||||||
|
Not(u8),
|
||||||
|
Eq(u8),
|
||||||
|
Ne(u8),
|
||||||
|
Gt(u8),
|
||||||
|
Lt(u8),
|
||||||
|
Ge(u8),
|
||||||
|
Le(u8),
|
||||||
|
And(u8),
|
||||||
|
Or(u8),
|
||||||
|
LeftParenthesis,
|
||||||
|
RightParenthesis,
|
||||||
|
LeftSquareBracket,
|
||||||
|
RightSquareBracket,
|
||||||
|
DoubleQuotes,
|
||||||
|
SingleQuote,
|
||||||
|
WhiteSpace,
|
||||||
|
Comma,
|
||||||
|
Function(String),
|
||||||
|
Identifier(String),
|
||||||
|
Value(Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Operator {
|
||||||
|
pub fn is_identifier(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Operator::Identifier(_) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_at_beginning(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Operator::Not(_) | Operator::Function(_) | Operator::LeftParenthesis => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max_args(&self) -> Option<usize> {
|
||||||
|
match *self {
|
||||||
|
Operator::Add(_) | Operator::Sub(_) |
|
||||||
|
Operator::Mul(_) | Operator::Div(_) |
|
||||||
|
Operator::Eq(_) | Operator::Ne(_) |
|
||||||
|
Operator::Gt(_) | Operator::Lt(_) |
|
||||||
|
Operator::Ge(_) | Operator::Le(_) |
|
||||||
|
Operator::And(_) | Operator::Or(_) |
|
||||||
|
Operator::Rem(_)=> Some(2),
|
||||||
|
Operator::Not(_) => Some(1),
|
||||||
|
Operator::Function(_) => None,
|
||||||
|
_ => Some(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_min_args(&self) -> Option<usize> {
|
||||||
|
match *self {
|
||||||
|
Operator::Add(_) | Operator::Sub(_) |
|
||||||
|
Operator::Mul(_) | Operator::Div(_) |
|
||||||
|
Operator::Eq(_) | Operator::Ne(_) |
|
||||||
|
Operator::Gt(_) | Operator::Lt(_) |
|
||||||
|
Operator::Ge(_) | Operator::Le(_) |
|
||||||
|
Operator::And(_) | Operator::Or(_) |
|
||||||
|
Operator::Rem(_) => Some(2),
|
||||||
|
Operator::Not(_) => Some(1),
|
||||||
|
Operator::Function(_) => None,
|
||||||
|
_ => Some(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_priority(&self) -> u8 {
|
||||||
|
match *self {
|
||||||
|
Operator::Add(priority) | Operator::Sub(priority) |
|
||||||
|
Operator::Div(priority) | Operator::Mul(priority) |
|
||||||
|
Operator::Eq(priority) | Operator::Ne(priority) |
|
||||||
|
Operator::Gt(priority) | Operator::Lt(priority) |
|
||||||
|
Operator::Ge(priority) | Operator::Le(priority) |
|
||||||
|
Operator::And(priority) | Operator::Or(priority) |
|
||||||
|
Operator::Rem(priority) => priority,
|
||||||
|
Operator::Value(_) | Operator::Identifier(_) => 0,
|
||||||
|
_ => 99
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_left_parenthesis(&self) -> bool {
|
||||||
|
*self == Operator::LeftParenthesis
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_not(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Operator::Not(_) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_value_or_ident(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Operator::Value(_) | Operator::Identifier(_) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_have_child(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Operator::Function(_) | Operator::Add(_) |
|
||||||
|
Operator::Sub(_) | Operator::Div(_) |
|
||||||
|
Operator::Mul(_) | Operator::Rem(_) |
|
||||||
|
Operator::Eq(_) | Operator::Ne(_) |
|
||||||
|
Operator::Gt(_) | Operator::Lt(_) |
|
||||||
|
Operator::And(_) | Operator::Or(_) |
|
||||||
|
Operator::Ge(_) | Operator::Le(_) => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_left(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Operator::LeftParenthesis | Operator::LeftSquareBracket => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_left(&self) -> Operator {
|
||||||
|
match *self {
|
||||||
|
Operator::RightParenthesis => Operator::LeftParenthesis,
|
||||||
|
Operator::RightSquareBracket => Operator::LeftSquareBracket,
|
||||||
|
_ => panic!("not bracket")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_node(&self) -> Node {
|
||||||
|
Node::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn children_to_node(&self, children: Vec<Node>) -> Node {
|
||||||
|
let mut node = self.to_node();
|
||||||
|
node.children = children;
|
||||||
|
node
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_identifier(&self) -> String {
|
||||||
|
match *self {
|
||||||
|
Operator::Identifier(ref ident) => ident.to_owned(),
|
||||||
|
_ => panic!("not identifier")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Operator {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(raw: &str) -> Result<Operator, Error> {
|
||||||
|
match raw {
|
||||||
|
"+" => Ok(Operator::Add(8)),
|
||||||
|
"-" => Ok(Operator::Sub(8)),
|
||||||
|
"*" => Ok(Operator::Mul(10)),
|
||||||
|
"/" => Ok(Operator::Div(10)),
|
||||||
|
"%" => Ok(Operator::Rem(10)),
|
||||||
|
"(" => Ok(Operator::LeftParenthesis),
|
||||||
|
")" => Ok(Operator::RightParenthesis),
|
||||||
|
"[" => Ok(Operator::LeftSquareBracket),
|
||||||
|
"]" => Ok(Operator::RightSquareBracket),
|
||||||
|
"\"" => Ok(Operator::DoubleQuotes),
|
||||||
|
"'" => Ok(Operator::SingleQuote),
|
||||||
|
" " => Ok(Operator::WhiteSpace),
|
||||||
|
"," => Ok(Operator::Comma),
|
||||||
|
"!" => Ok(Operator::Not(99)),
|
||||||
|
"false" => Ok(Operator::Value(to_value(false))),
|
||||||
|
"true" => Ok(Operator::Value(to_value(true))),
|
||||||
|
"==" => Ok(Operator::Eq(6)),
|
||||||
|
"!=" => Ok(Operator::Ne(6)),
|
||||||
|
">" => Ok(Operator::Gt(6)),
|
||||||
|
"<" => Ok(Operator::Lt(6)),
|
||||||
|
">=" => Ok(Operator::Ge(6)),
|
||||||
|
"<=" => Ok(Operator::Le(6)),
|
||||||
|
"&&" => Ok(Operator::And(4)),
|
||||||
|
"||" => Ok(Operator::Or(2)),
|
||||||
|
_ => Ok(Operator::Identifier(raw.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user