From 1d84eea270bd36f6052e07bdb8e1bdad2191a716 Mon Sep 17 00:00:00 2001 From: fengcen Date: Thu, 17 Nov 2016 00:12:26 +0800 Subject: [PATCH] init repository. --- .gitignore | 15 ++ Cargo.toml | 19 ++ LICENSE | 21 ++ README.md | 84 +++++++ src/builtin/mod.rs | 101 ++++++++ src/error/mod.rs | 75 ++++++ src/expression/mod.rs | 509 +++++++++++++++++++++++++++++++++++++++ src/function/mod.rs | 32 +++ src/lib.rs | 540 ++++++++++++++++++++++++++++++++++++++++++ src/math/mod.rs | 195 +++++++++++++++ src/node/mod.rs | 86 +++++++ src/operator/mod.rs | 192 +++++++++++++++ 12 files changed, 1869 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/builtin/mod.rs create mode 100644 src/error/mod.rs create mode 100644 src/expression/mod.rs create mode 100644 src/function/mod.rs create mode 100644 src/lib.rs create mode 100644 src/math/mod.rs create mode 100644 src/node/mod.rs create mode 100644 src/operator/mod.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..137a958 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.DS_Store +*~ +*# +*.o +*.so +*.swp +*.dylib +*.dSYM +*.dll +*.rlib +*.dummy +*.exe +*-test +target/ +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f986261 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "eval" +version = "0.1.0" +description = "Expression evaluator" +keywords = ["expression", "evaluate", "evaluator", "expr"] +authors = ["fengcen "] +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" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2ca1de6 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2784fec --- /dev/null +++ b/README.md @@ -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. diff --git a/src/builtin/mod.rs b/src/builtin/mod.rs new file mode 100644 index 0000000..0e79ba5 --- /dev/null +++ b/src/builtin/mod.rs @@ -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 = 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))) +} diff --git a/src/error/mod.rs b/src/error/mod.rs new file mode 100644 index 0000000..733e364 --- /dev/null +++ b/src/error/mod.rs @@ -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) + } + } +} diff --git a/src/expression/mod.rs b/src/expression/mod.rs new file mode 100644 index 0000000..4788886 --- /dev/null +++ b/src/expression/mod.rs @@ -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, + pub operators: Vec, + pub node: Option +} + +impl Expression { + pub fn new>(raw: T) -> Result { + 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 Result> { + let node = self.node.clone().unwrap(); + + Box::new(move |contexts, buildin, functions| -> Result { + return exec_node(&node, contexts, buildin, functions); + + fn exec_node(node: &Node, contexts: ContextsRef, buildin: &Functions, functions: &Functions) -> Result { + 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::::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, 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) -> Result { + 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, 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) -> 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 { + let moveout_node = was_robed.moveout_last_node(); + rober.add_child(moveout_node); + vec![was_robed, rober] +} + +fn find(contexts: ContextsRef, key: &str) -> Option { + 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 { + let mut keys = key.split('.').collect::>(); + 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 { + let segments = ident.split("..").collect::>(); + if segments.len() != 2 { + Err(Error::InvalidArray(ident.to_owned())) + } else { + let start = segments[0].parse::(); + let end = segments[1].parse::(); + + 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 { + let number = ident.parse::(); + if number.is_ok() { + return Some(to_value(number.unwrap())); + } + + let number = ident.parse::(); + if number.is_ok() { + return Some(to_value(number.unwrap())); + } + + let number = ident.parse::(); + if number.is_ok() { + return Some(to_value(number.unwrap())); + } + + None +} diff --git a/src/function/mod.rs b/src/function/mod.rs new file mode 100644 index 0000000..0d341a3 --- /dev/null +++ b/src/function/mod.rs @@ -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, + /// Minimum number of arguments. + pub min_args: Option, + /// Accept values and return a result which contains a value. + pub compiled: Box) -> Result + Sync + Send> +} + +impl Function { + /// Create a function with a closure. + pub fn new(closure: F) -> Function where F: 'static + Fn(Vec) -> Result + 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) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8f35b18 --- /dev/null +++ b/src/lib.rs @@ -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; +/// Eval contexts. The value of the last context is searched first. +pub type Contexts = Vec; +/// Eval functions. +pub type Functions = HashMap; + +/// Evaluates the value of an expression. +pub fn eval(expr: &str) -> Result { + 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 { + let mut contexts = Contexts::new(); + contexts.push(context.clone()); + eval_with_contexts(expr, &contexts) +} + +/// Evaluates the value of an expression with the given contexts.
+/// The value of the last context is searched first. +pub fn eval_with_contexts(expr: &str, contexts: ContextsRef) -> Result { + 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 { + 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 { + 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.
+/// The value of the last context is searched first. +pub fn eval_with_contexts_and_functions(expr: &str, contexts: ContextsRef, functions: &Functions) -> Result { + 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::::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)")); + } +} diff --git a/src/math/mod.rs b/src/math/mod.rs new file mode 100644 index 0000000..ff5b082 --- /dev/null +++ b/src/math/mod.rs @@ -0,0 +1,195 @@ + +use serde_json::{Value, to_value}; +use error::Error; + +pub trait Math { + fn add(&self, &Value) -> Result; + fn mul(&self, &Value) -> Result; + fn sub(&self, &Value) -> Result; + fn div(&self, &Value) -> Result; + fn rem(&self, &Value) -> Result; + fn eq(&self, &Value) -> Result; + fn ne(&self, &Value) -> Result; + fn gt(&self, &Value) -> Result; + fn lt(&self, &Value) -> Result; + fn ge(&self, &Value) -> Result; + fn le(&self, &Value) -> Result; + fn and(&self, &Value) -> Result; + fn or(&self, &Value) -> Result; +} + +impl Math for Value { + fn add(&self, value: &Value) -> Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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) + } +} diff --git a/src/node/mod.rs b/src/node/mod.rs new file mode 100644 index 0000000..7c70353 --- /dev/null +++ b/src/node/mod.rs @@ -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, + 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() + } +} diff --git a/src/operator/mod.rs b/src/operator/mod.rs new file mode 100644 index 0000000..59fd957 --- /dev/null +++ b/src/operator/mod.rs @@ -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 { + 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 { + 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 { + 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 { + 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())) + } + } +}