Complete rewrite of crate. Simple tokenizer and treeifyer (Or however the compiler people call these.).
This commit is contained in:
parent
c313ec8ce5
commit
b38c102c1d
5
rustfmt.toml
Normal file
5
rustfmt.toml
Normal file
@ -0,0 +1,5 @@
|
||||
edition = "2018"
|
||||
reorder_imports=true
|
||||
reorder_modules=true
|
||||
format_strings=true
|
||||
merge_imports=true
|
@ -1,200 +0,0 @@
|
||||
|
||||
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("len".to_owned(), create_len_fuction());
|
||||
functions.insert("is_empty".to_owned(), create_is_empty_fuction());
|
||||
functions.insert("array".to_owned(), create_array_function());
|
||||
functions.insert("converge".to_owned(), create_converge_function());
|
||||
functions
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_number_f64(value: &Value) -> Result<f64, Error> {
|
||||
if let Some(number) = value.as_f64() {
|
||||
Ok(number)
|
||||
} else {
|
||||
Err(Error::Custom(format!("Expected number that can be represented as f64. But the given is: {:?}", value)))
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_normal_f64(number: f64) -> Result<f64, Error> {
|
||||
if number.is_normal() {
|
||||
Ok(number)
|
||||
} else {
|
||||
Err(Error::Custom(format!("Expected normal number. But the given is: {:?}", number)))
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_finite_f64(number: f64) -> Result<f64, Error> {
|
||||
if number.is_finite() {
|
||||
Ok(number)
|
||||
} else {
|
||||
Err(Error::Custom(format!("Expected finite number. But the given is: {:?}", number)))
|
||||
}
|
||||
}
|
||||
|
||||
#[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(to_value(string.is_empty())),
|
||||
Value::Array(ref array) => Ok(to_value(array.is_empty())),
|
||||
Value::Object(ref object) => Ok(to_value(object.is_empty())),
|
||||
Value::Null => Ok(to_value(true)),
|
||||
_ => Ok(to_value(false)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_len_fuction() -> Function {
|
||||
Function {
|
||||
max_args: Some(1),
|
||||
min_args: Some(1),
|
||||
compiled: Box::new(|values| {
|
||||
let value = values.first().unwrap();
|
||||
match *value {
|
||||
Value::String(ref string) => Ok(to_value(string.len())),
|
||||
Value::Array(ref array) => Ok(to_value(array.len())),
|
||||
Value::Object(ref object) => Ok(to_value(object.len())),
|
||||
Value::Null => Ok(to_value(0)),
|
||||
_ => {
|
||||
Err(Error::Custom(format!("len() only accept string, array, object and \
|
||||
null. But the given is: {:?}",
|
||||
value)))
|
||||
}
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_array_function() -> Function {
|
||||
Function::new(|values| Ok(to_value(values)))
|
||||
}
|
||||
|
||||
/// Converges exponentially agains `conv_y`, starting from `start_y`.
|
||||
/// The first four parameters parameterize a function `f`, the last one is the `x`-value where the function should be evaluated.
|
||||
/// The result of the call is `f(x)`.
|
||||
///
|
||||
/// The parameters `start_x` and `start_y` set the starting point of the function.
|
||||
/// It holds that `f(start_x) = start_y`.
|
||||
/// The parameter `conv_y` is the convergence target value.
|
||||
/// It is never reached (assuming no numerical errors).
|
||||
/// The parameter `step_x` is the "speed" of the convergence, that is how fast the function converges against `conv_y`.
|
||||
/// In detail, the absolute difference between `start_y` and `conv_y` halves if `x` is increased by `step_x`.
|
||||
///
|
||||
/// All parameters are expected to be numbers.
|
||||
/// The parameters `start_x`, `start_y` and `conv_y` are expected to be finite.
|
||||
/// The parameter `step_x` is expected to be `normal`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The function `2^(-x)` is expressed as `conv(0, 1, 1, 0, x)`.
|
||||
/// This is the same as `converge(1, 0.5, 1, 0, x)`.
|
||||
fn create_converge_function() -> Function {
|
||||
Function {
|
||||
max_args: Some(5),
|
||||
min_args: Some(5),
|
||||
compiled: Box::new(|values| {
|
||||
let start_x = expect_finite_f64(expect_number_f64(&values[0])?)?;
|
||||
let start_y = expect_finite_f64(expect_number_f64(&values[1])?)?;
|
||||
let step_x = expect_normal_f64(expect_number_f64(&values[2])?)?;
|
||||
let conv_y = expect_finite_f64(expect_number_f64(&values[3])?)?;
|
||||
let x = expect_number_f64(&values[4])?;
|
||||
|
||||
let units = (x - start_x) / step_x;
|
||||
let interpolation_factor = 2.0_f64.powf(-units);
|
||||
Ok(to_value(interpolation_factor * start_y + (1.0 - interpolation_factor) * conv_y))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{Expr, to_value};
|
||||
|
||||
#[test]
|
||||
fn test_conv() {
|
||||
let valid_test_cases = vec![
|
||||
("converge(0, 4, 10, 0, 0)", "4.0"),
|
||||
("converge(0, 4, 10, 0, 10)", "2.0"),
|
||||
("converge(0, 4, 10, 0, 5)", "2.8284271247461900976033774484193961571393437507538961"),
|
||||
("converge(0, 4, 10, 0, 20)", "1.0"),
|
||||
("converge(0, 4, 10, 0, 0-10)", "8.0"),
|
||||
];
|
||||
|
||||
for (term, result) in valid_test_cases {
|
||||
assert_eq!(Expr::new(format!("({term} < {result} * 1.0001) && ({term} > {result} / 1.0001)", term = term, result = result)).exec(), Ok(to_value(true)));
|
||||
}
|
||||
}
|
||||
}
|
13
src/configuration/mod.rs
Normal file
13
src/configuration/mod.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use crate::{error::Error, value::Value};
|
||||
|
||||
pub trait Configuration {
|
||||
fn get_value(&self, identifier: &str) -> Result<Value, Error>;
|
||||
}
|
||||
|
||||
pub struct EmptyConfiguration;
|
||||
|
||||
impl Configuration for EmptyConfiguration {
|
||||
fn get_value(&self, identifier: &str) -> Result<Value, Error> {
|
||||
Err(Error::IdentifierNotFound)
|
||||
}
|
||||
}
|
131
src/error/mod.rs
131
src/error/mod.rs
@ -1,95 +1,54 @@
|
||||
use crate::value::Value;
|
||||
|
||||
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 a boolean but the given value isn't.
|
||||
ExpectedBoolean(value: Value) {
|
||||
display("Expected a boolean, found: {}", value)
|
||||
}
|
||||
/// Expected ident.
|
||||
ExpectedIdentifier {
|
||||
display("Expected ident.")
|
||||
}
|
||||
/// Expected array.
|
||||
ExpectedArray {
|
||||
display("Expected array.")
|
||||
}
|
||||
/// Expected object.
|
||||
ExpectedObject {
|
||||
display("Expected object.")
|
||||
}
|
||||
/// Expect number.
|
||||
WrongArgumentAmount {
|
||||
expected: usize,
|
||||
actual: usize,
|
||||
},
|
||||
ExpectedNumber {
|
||||
display("Expected number.")
|
||||
actual: Value,
|
||||
},
|
||||
|
||||
/// The given expression is empty
|
||||
EmptyExpression,
|
||||
|
||||
/// Tried to evaluate the root node.
|
||||
/// The root node should only be used as dummy node.
|
||||
EvaluatedRootNode,
|
||||
|
||||
/// Tried to append a child to a leaf node.
|
||||
/// Leaf nodes cannot have children.
|
||||
AppendedToLeafNode,
|
||||
|
||||
/// Tried to append a child to a node such that the precedence of the child is not higher.
|
||||
PrecedenceViolation,
|
||||
|
||||
/// An identifier operation did not find its value in the configuration.
|
||||
IdentifierNotFound,
|
||||
}
|
||||
/// Failed to parse, no final expression.
|
||||
NoFinalNode {
|
||||
display("Failed to parse, no final expression.")
|
||||
|
||||
impl Error {
|
||||
pub fn wrong_argument_amount(actual: usize, expected: usize) -> Self {
|
||||
Error::WrongArgumentAmount { actual, expected }
|
||||
}
|
||||
/// 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 range expression like `1..2..3`
|
||||
InvalidRange(ident: String) {
|
||||
display("Invalid range expression: {}", ident)
|
||||
}
|
||||
/// Can not add child node.
|
||||
CanNotAddChild {
|
||||
display("Can not add child node.")
|
||||
}
|
||||
/// Custom error.
|
||||
Custom(detail: String) {
|
||||
display("{}", detail)
|
||||
|
||||
pub fn expected_number(actual: Value) -> Self {
|
||||
Error::ExpectedNumber { actual }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_argument_amount(actual: usize, expected: usize) -> Result<(), Error> {
|
||||
if actual == expected {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::wrong_argument_amount(actual, expected))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_number(actual: &Value) -> Result<f64, Error> {
|
||||
match actual {
|
||||
Value::Number(number) => Ok(*number),
|
||||
_ => Err(Error::expected_number(actual.clone())),
|
||||
}
|
||||
}
|
||||
|
152
src/expr/mod.rs
152
src/expr/mod.rs
@ -1,152 +0,0 @@
|
||||
|
||||
use {Function, Functions, Context, Contexts, Compiled, Value};
|
||||
use tree::Tree;
|
||||
use error::Error;
|
||||
use serde::Serialize;
|
||||
use to_value;
|
||||
use std::fmt;
|
||||
|
||||
|
||||
/// Expression builder
|
||||
pub struct Expr {
|
||||
expression: String,
|
||||
compiled: Option<Compiled>,
|
||||
functions: Functions,
|
||||
contexts: Contexts,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
/// Create an expression.
|
||||
pub fn new<T: Into<String>>(expr: T) -> Expr {
|
||||
Expr {
|
||||
expression: expr.into(),
|
||||
compiled: None,
|
||||
functions: Functions::new(),
|
||||
contexts: create_empty_contexts(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set function.
|
||||
pub fn function<T, F>(mut self, name: T, function: F) -> Expr
|
||||
where T: Into<String>,
|
||||
F: 'static + Fn(Vec<Value>) -> Result<Value, Error> + Sync + Send
|
||||
{
|
||||
self.functions.insert(name.into(), Function::new(function));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set value.
|
||||
pub fn value<T, V>(mut self, name: T, value: V) -> Expr
|
||||
where T: Into<String>,
|
||||
V: Serialize
|
||||
{
|
||||
self.contexts.last_mut().unwrap().insert(name.into(), to_value(value));
|
||||
self
|
||||
}
|
||||
|
||||
/// Compile an expression.
|
||||
/// An expression can be compiled only once and then invoked multiple times with different context and function.
|
||||
/// You can also execute a expression without compile.
|
||||
pub fn compile(mut self) -> Result<Expr, Error> {
|
||||
self.compiled = Some(Tree::new(self.expression.clone()).compile()?);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Execute the expression.
|
||||
pub fn exec(&self) -> Result<Value, Error> {
|
||||
if self.compiled.is_none() {
|
||||
Tree::new(self.expression.clone()).compile()?(&self.contexts, &self.functions)
|
||||
} else {
|
||||
self.compiled.as_ref().unwrap()(&self.contexts, &self.functions)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_compiled(&self) -> Option<&Compiled> {
|
||||
self.compiled.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Expr {
|
||||
/// Returns a copy of the value. Notice that functions can not be cloned. The cloned expr's functions will be empty.
|
||||
fn clone(&self) -> Expr {
|
||||
Expr {
|
||||
expression: self.expression.clone(),
|
||||
compiled: if self.compiled.is_some() {
|
||||
Some(Tree::new(self.expression.clone()).compile().unwrap())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
contexts: self.contexts.clone(),
|
||||
functions: Functions::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Expr {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(formatter, "{:?}", self.expression)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Execute options
|
||||
pub struct ExecOptions<'a> {
|
||||
expr: &'a Expr,
|
||||
contexts: Option<&'a [Context]>,
|
||||
functions: Option<&'a Functions>,
|
||||
}
|
||||
|
||||
impl<'a> ExecOptions<'a> {
|
||||
/// Create an option.
|
||||
pub fn new(expr: &'a Expr) -> ExecOptions<'a> {
|
||||
ExecOptions {
|
||||
expr: expr,
|
||||
contexts: None,
|
||||
functions: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set contexts.
|
||||
pub fn contexts(&mut self, contexts: &'a [Context]) -> &'a mut ExecOptions {
|
||||
self.contexts = Some(contexts);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set functions.
|
||||
pub fn functions(&mut self, functions: &'a Functions) -> &'a mut ExecOptions {
|
||||
self.functions = Some(functions);
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute the compiled expression.
|
||||
pub fn exec(&self) -> Result<Value, Error> {
|
||||
let empty_contexts = create_empty_contexts();
|
||||
let empty_functions = Functions::new();
|
||||
|
||||
let contexts = if self.contexts.is_some() {
|
||||
self.contexts.unwrap()
|
||||
} else {
|
||||
&empty_contexts
|
||||
};
|
||||
|
||||
let functions = if self.functions.is_some() {
|
||||
self.functions.unwrap()
|
||||
} else {
|
||||
&empty_functions
|
||||
};
|
||||
|
||||
let compiled = self.expr.get_compiled();
|
||||
if compiled.is_none() {
|
||||
Tree::new(self.expr.expression.clone()).compile()?(contexts, functions)
|
||||
} else {
|
||||
compiled.unwrap()(contexts, functions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn create_empty_contexts() -> Contexts {
|
||||
let mut contexts = Contexts::new();
|
||||
contexts.push(Context::new());
|
||||
contexts
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
692
src/lib-old.rs
Normal file
692
src/lib-old.rs
Normal file
@ -0,0 +1,692 @@
|
||||
//! Eval is a powerful expression evaluator.
|
||||
//!
|
||||
//! Supported operators: `!` `!=` `""` `''` `()` `[]` `.` `,` `>` `<` `>=` `<=`
|
||||
//! `==` `+` unary/binary `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||
//!
|
||||
//! Built-in functions: `min()` `max()` `len()` `is_empty()` `array()` `converge()`.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! You can do mathematical calculations with supported operators:
|
||||
//!
|
||||
//! ```
|
||||
//! use evalexpr::{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 evalexpr::{Expr, to_value};
|
||||
//!
|
||||
//! assert_eq!(Expr::new("foo == bar")
|
||||
//! .value("foo", true)
|
||||
//! .value("bar", true)
|
||||
//! .exec(),
|
||||
//! Ok(to_value(true)));
|
||||
//! ```
|
||||
//!
|
||||
//! You can access data like javascript by using `.` and `[]`. `[]` supports expression.
|
||||
//!
|
||||
//! ```
|
||||
//! use evalexpr::{Expr, to_value};
|
||||
//! use std::collections::HashMap;
|
||||
//!
|
||||
//! let mut object = HashMap::new();
|
||||
//! object.insert("foos", vec!["Hello", "world", "!"]);
|
||||
//!
|
||||
//! assert_eq!(Expr::new("object.foos[2-1] == 'world'") // Access field `foos` and index `2-1`
|
||||
//! .value("object", object)
|
||||
//! .exec(),
|
||||
//! Ok(to_value(true)));
|
||||
//! ```
|
||||
//!
|
||||
//! You can eval with function:
|
||||
//!
|
||||
//! ```
|
||||
//! use evalexpr::{Expr, to_value};
|
||||
//!
|
||||
//! assert_eq!(Expr::new("say_hello()")
|
||||
//! .function("say_hello", |_| Ok(to_value("Hello world!")))
|
||||
//! .exec(),
|
||||
//! Ok(to_value("Hello world!")));
|
||||
//! ```
|
||||
//!
|
||||
//! You can create an array with `array()`:
|
||||
//!
|
||||
//! ```
|
||||
//! use evalexpr::{eval, to_value};
|
||||
//!
|
||||
//! assert_eq!(eval("array(1, 2, 3, 4, 5)"), Ok(to_value(vec![1, 2, 3, 4, 5])));
|
||||
//! ```
|
||||
//!
|
||||
//! You can create an integer array with `n..m`:
|
||||
//!
|
||||
//! ```
|
||||
//! use evalexpr::{eval, to_value};
|
||||
//!
|
||||
//! assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||
//! ```
|
||||
//!
|
||||
//! ## Built-in functions
|
||||
//!
|
||||
//! ### min()
|
||||
//! Accept multiple arguments and return the minimum value.
|
||||
//!
|
||||
//! ### max()
|
||||
//! Accept multiple arguments and return the maximum value.
|
||||
//!
|
||||
//! ### len()
|
||||
//! Accept single arguments and return the length of value. Only accept String, Array, Object and Null.
|
||||
//!
|
||||
//! ### is_empty()
|
||||
//! Accept single arguments and return a boolean. Check whether the value is empty or not.
|
||||
//!
|
||||
//! ### array()
|
||||
//! Accept multiple arguments and return an array.
|
||||
//!
|
||||
//! ### converge()
|
||||
//! See `builtin::create_converge_function()`.
|
||||
//!
|
||||
//!
|
||||
#![recursion_limit="100"]
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(all(feature = "unstable", test), feature(test))]
|
||||
|
||||
#[macro_use]
|
||||
extern crate quick_error;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
mod math;
|
||||
mod function;
|
||||
mod operator;
|
||||
mod node;
|
||||
mod tree;
|
||||
mod error;
|
||||
mod builtin;
|
||||
mod expr;
|
||||
|
||||
mod reboot;
|
||||
|
||||
pub use expr::ExecOptions;
|
||||
pub use serde_json::Value;
|
||||
pub use error::Error;
|
||||
pub use function::Function;
|
||||
pub use expr::Expr;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use serde_json::to_value as json_to_value;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Convert variable to `serde_json::Value`
|
||||
pub fn to_value<S: Serialize>(v: S) -> Value {
|
||||
json_to_value(v).unwrap()
|
||||
}
|
||||
|
||||
/// Custom context.
|
||||
pub type Context = HashMap<String, Value>;
|
||||
/// Custom contexts. The value of the last context is searched first.
|
||||
pub type Contexts = Vec<Context>;
|
||||
/// Custom functions.
|
||||
pub type Functions = HashMap<String, Function>;
|
||||
|
||||
/// Evaluates the value of an expression.
|
||||
pub fn eval(expr: &str) -> Result<Value, Error> {
|
||||
Expr::new(expr).compile()?.exec()
|
||||
}
|
||||
|
||||
|
||||
type Compiled = Box<Fn(&[Context], &Functions) -> Result<Value, Error>>;
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use to_value;
|
||||
use error::Error;
|
||||
use Expr;
|
||||
use tree::Tree;
|
||||
use Value;
|
||||
use eval;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[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_len_array() {
|
||||
assert_eq!(eval("len(array(2, 3, 4, 5, 6))"), Ok(to_value(5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_and_number() {
|
||||
assert_eq!(eval("hos != 0"), Ok(to_value(true)));
|
||||
assert_eq!(eval("hos > 0"), Ok(to_value(false)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len_string() {
|
||||
assert_eq!(eval("len('Hello world!')"), Ok(to_value(12)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len_object() {
|
||||
let mut object = HashMap::new();
|
||||
object.insert("field1", "value1");
|
||||
object.insert("field2", "value2");
|
||||
object.insert("field3", "value3");
|
||||
assert_eq!(
|
||||
Expr::new("len(object)").value("object", object).exec(),
|
||||
Ok(to_value(3))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_brackets_1() {
|
||||
assert_eq!(eval("(5) + (min(3, 4, 5)) + 20"), Ok(to_value(28)));
|
||||
}
|
||||
|
||||
#[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("array(1, 2, 3, 4)"), Ok(to_value(vec![1, 2, 3, 4])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range() {
|
||||
assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_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_object_access() {
|
||||
let mut object = HashMap::new();
|
||||
object.insert("foo", "Foo, hello world!");
|
||||
object.insert("bar", "Bar, hello world!");
|
||||
assert_eq!(
|
||||
Expr::new("object.foo == 'Foo, hello world!'")
|
||||
.value("object", object)
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_object_dynamic_access() {
|
||||
let mut object = HashMap::new();
|
||||
object.insert("foo", "Foo, hello world!");
|
||||
object.insert("bar", "Bar, hello world!");
|
||||
assert_eq!(
|
||||
Expr::new("object['foo'] == 'Foo, hello world!'")
|
||||
.value("object", object)
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_object_dynamic_access_2() {
|
||||
let mut object = HashMap::new();
|
||||
object.insert("foo", "Foo, hello world!");
|
||||
object.insert("bar", "Bar, hello world!");
|
||||
assert_eq!(
|
||||
Expr::new("object[foo] == 'Foo, hello world!'")
|
||||
.value("object", object)
|
||||
.value("foo", "foo")
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path() {
|
||||
assert_eq!(Expr::new("array[2-2].foo[2-2]").exec(), Ok(Value::Null));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_access() {
|
||||
let array = vec!["hello", "world", "!"];
|
||||
assert_eq!(
|
||||
Expr::new(
|
||||
"array[1-1] == 'hello' && array[1] == 'world' && array[2] == '!'",
|
||||
).value("array", array)
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builtin_is_empty() {
|
||||
assert_eq!(
|
||||
Expr::new("is_empty(array)")
|
||||
.value("array", Vec::<String>::new())
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builtin_min() {
|
||||
assert_eq!(
|
||||
Expr::new("min(array)")
|
||||
.value("array", vec![23, 34, 45, 2])
|
||||
.exec(),
|
||||
Ok(to_value(2))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_function() {
|
||||
assert_eq!(
|
||||
Expr::new("output()")
|
||||
.function(
|
||||
"output",
|
||||
|_| Ok(to_value("This is custom function's output")),
|
||||
)
|
||||
.exec(),
|
||||
Ok(to_value("This is custom function's output"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_start_with_non_value_operator() {
|
||||
let mut tree = Tree {
|
||||
raw: "+ + 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_node(), Err(Error::StartWithNonValueOperator));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_duplicate_operator() {
|
||||
let mut tree = Tree {
|
||||
raw: "5 + + 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_node(), Err(Error::DuplicateOperatorNode));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_duplicate_value() {
|
||||
let mut tree = Tree {
|
||||
raw: "2 + 6 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_node(), Err(Error::DuplicateValueNode));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_unpaired_brackets() {
|
||||
let mut tree = Tree {
|
||||
raw: "(2 + 3)) * 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_operators(), Err(Error::UnpairedBrackets));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_comma() {
|
||||
let mut tree = Tree {
|
||||
raw: ", 2 + 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_node(), Err(Error::CommaNotWithFunction));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eval_issue_2() {
|
||||
assert_eq!(eval("2 * (4 + 0) + 4"), Ok(to_value(12)));
|
||||
assert_eq!(eval("2 * (2 + 2) + (1 + 3)"), Ok(to_value(12)));
|
||||
assert_eq!(eval("2 * (4) + (4)"), Ok(to_value(12)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unary_minus() {
|
||||
assert_eq!(eval("3 + -4"), Ok(to_value(-1)));
|
||||
assert_eq!(eval("3+-4"), Ok(to_value(-1)));
|
||||
assert_eq!(eval("true && !false"), Ok(to_value(true)));
|
||||
assert_eq!(eval("!true || !true"), Ok(to_value(false)));
|
||||
assert_eq!(eval("-5 + -4"), Ok(to_value(-9)));
|
||||
assert_eq!(eval("-3-4"), Ok(to_value(-7)));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benches {
|
||||
extern crate test;
|
||||
use eval;
|
||||
use tree::Tree;
|
||||
use Expr;
|
||||
|
||||
#[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 tree = Tree {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
b.iter(|| tree.parse_pos().unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_operators(b: &mut test::Bencher) {
|
||||
let mut tree = Tree {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
b.iter(|| tree.parse_operators().unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_nodes(b: &mut test::Bencher) {
|
||||
let mut tree = Tree {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
b.iter(|| tree.parse_node().unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_compile(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let mut tree = Tree {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
tree.parse_node().unwrap();
|
||||
tree.compile().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_exec(b: &mut test::Bencher) {
|
||||
let expr = Expr::new("(2 + (3 + 4) + (6 + (6 + 7)) + 5)")
|
||||
.compile()
|
||||
.unwrap();
|
||||
b.iter(|| expr.exec().unwrap())
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_eval(b: &mut test::Bencher) {
|
||||
b.iter(|| eval("(2 + (3 + 4) + (6 + (6 + 7)) + 5)"));
|
||||
}
|
||||
}
|
700
src/lib.rs
700
src/lib.rs
@ -1,692 +1,28 @@
|
||||
//! Eval is a powerful expression evaluator.
|
||||
//!
|
||||
//! Supported operators: `!` `!=` `""` `''` `()` `[]` `.` `,` `>` `<` `>=` `<=`
|
||||
//! `==` `+` unary/binary `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||
//!
|
||||
//! Built-in functions: `min()` `max()` `len()` `is_empty()` `array()` `converge()`.
|
||||
//!
|
||||
//! ## 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::{Expr, to_value};
|
||||
//!
|
||||
//! assert_eq!(Expr::new("foo == bar")
|
||||
//! .value("foo", true)
|
||||
//! .value("bar", true)
|
||||
//! .exec(),
|
||||
//! Ok(to_value(true)));
|
||||
//! ```
|
||||
//!
|
||||
//! You can access data like javascript by using `.` and `[]`. `[]` supports expression.
|
||||
//!
|
||||
//! ```
|
||||
//! use eval::{Expr, to_value};
|
||||
//! use std::collections::HashMap;
|
||||
//!
|
||||
//! let mut object = HashMap::new();
|
||||
//! object.insert("foos", vec!["Hello", "world", "!"]);
|
||||
//!
|
||||
//! assert_eq!(Expr::new("object.foos[2-1] == 'world'") // Access field `foos` and index `2-1`
|
||||
//! .value("object", object)
|
||||
//! .exec(),
|
||||
//! Ok(to_value(true)));
|
||||
//! ```
|
||||
//!
|
||||
//! You can eval with function:
|
||||
//!
|
||||
//! ```
|
||||
//! use eval::{Expr, to_value};
|
||||
//!
|
||||
//! assert_eq!(Expr::new("say_hello()")
|
||||
//! .function("say_hello", |_| Ok(to_value("Hello world!")))
|
||||
//! .exec(),
|
||||
//! Ok(to_value("Hello world!")));
|
||||
//! ```
|
||||
//!
|
||||
//! You can create an array with `array()`:
|
||||
//!
|
||||
//! ```
|
||||
//! use eval::{eval, to_value};
|
||||
//!
|
||||
//! assert_eq!(eval("array(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])));
|
||||
//! ```
|
||||
//!
|
||||
//! ## Built-in functions
|
||||
//!
|
||||
//! ### min()
|
||||
//! Accept multiple arguments and return the minimum value.
|
||||
//!
|
||||
//! ### max()
|
||||
//! Accept multiple arguments and return the maximum value.
|
||||
//!
|
||||
//! ### len()
|
||||
//! Accept single arguments and return the length of value. Only accept String, Array, Object and Null.
|
||||
//!
|
||||
//! ### is_empty()
|
||||
//! Accept single arguments and return a boolean. Check whether the value is empty or not.
|
||||
//!
|
||||
//! ### array()
|
||||
//! Accept multiple arguments and return an array.
|
||||
//!
|
||||
//! ### converge()
|
||||
//! See `builtin::create_converge_function()`.
|
||||
//!
|
||||
//!
|
||||
#![recursion_limit="100"]
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(all(feature = "unstable", test), feature(test))]
|
||||
use configuration::EmptyConfiguration;
|
||||
use error::Error;
|
||||
use value::Value;
|
||||
|
||||
#[macro_use]
|
||||
extern crate quick_error;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
mod math;
|
||||
mod function;
|
||||
mod operator;
|
||||
mod node;
|
||||
mod tree;
|
||||
mod configuration;
|
||||
mod error;
|
||||
mod builtin;
|
||||
mod expr;
|
||||
mod operator;
|
||||
mod token;
|
||||
mod tree;
|
||||
mod value;
|
||||
|
||||
mod reboot;
|
||||
|
||||
pub use expr::ExecOptions;
|
||||
pub use serde_json::Value;
|
||||
pub use error::Error;
|
||||
pub use function::Function;
|
||||
pub use expr::Expr;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use serde_json::to_value as json_to_value;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Convert variable to `serde_json::Value`
|
||||
pub fn to_value<S: Serialize>(v: S) -> Value {
|
||||
json_to_value(v).unwrap()
|
||||
pub fn eval(string: &str) -> Result<Value, Error> {
|
||||
tree::tokens_to_operator_tree(token::tokenize(string))?.eval(&EmptyConfiguration)
|
||||
}
|
||||
|
||||
/// Custom context.
|
||||
pub type Context = HashMap<String, Value>;
|
||||
/// Custom contexts. The value of the last context is searched first.
|
||||
pub type Contexts = Vec<Context>;
|
||||
/// Custom functions.
|
||||
pub type Functions = HashMap<String, Function>;
|
||||
|
||||
/// Evaluates the value of an expression.
|
||||
pub fn eval(expr: &str) -> Result<Value, Error> {
|
||||
Expr::new(expr).compile()?.exec()
|
||||
}
|
||||
|
||||
|
||||
type Compiled = Box<Fn(&[Context], &Functions) -> Result<Value, Error>>;
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use to_value;
|
||||
use error::Error;
|
||||
use Expr;
|
||||
use tree::Tree;
|
||||
use Value;
|
||||
use eval;
|
||||
use std::collections::HashMap;
|
||||
mod test {
|
||||
use crate::{eval, value::Value};
|
||||
|
||||
#[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_len_array() {
|
||||
assert_eq!(eval("len(array(2, 3, 4, 5, 6))"), Ok(to_value(5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_and_number() {
|
||||
assert_eq!(eval("hos != 0"), Ok(to_value(true)));
|
||||
assert_eq!(eval("hos > 0"), Ok(to_value(false)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len_string() {
|
||||
assert_eq!(eval("len('Hello world!')"), Ok(to_value(12)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_len_object() {
|
||||
let mut object = HashMap::new();
|
||||
object.insert("field1", "value1");
|
||||
object.insert("field2", "value2");
|
||||
object.insert("field3", "value3");
|
||||
assert_eq!(
|
||||
Expr::new("len(object)").value("object", object).exec(),
|
||||
Ok(to_value(3))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_brackets_1() {
|
||||
assert_eq!(eval("(5) + (min(3, 4, 5)) + 20"), Ok(to_value(28)));
|
||||
}
|
||||
|
||||
#[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("array(1, 2, 3, 4)"), Ok(to_value(vec![1, 2, 3, 4])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range() {
|
||||
assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_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_object_access() {
|
||||
let mut object = HashMap::new();
|
||||
object.insert("foo", "Foo, hello world!");
|
||||
object.insert("bar", "Bar, hello world!");
|
||||
assert_eq!(
|
||||
Expr::new("object.foo == 'Foo, hello world!'")
|
||||
.value("object", object)
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_object_dynamic_access() {
|
||||
let mut object = HashMap::new();
|
||||
object.insert("foo", "Foo, hello world!");
|
||||
object.insert("bar", "Bar, hello world!");
|
||||
assert_eq!(
|
||||
Expr::new("object['foo'] == 'Foo, hello world!'")
|
||||
.value("object", object)
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_object_dynamic_access_2() {
|
||||
let mut object = HashMap::new();
|
||||
object.insert("foo", "Foo, hello world!");
|
||||
object.insert("bar", "Bar, hello world!");
|
||||
assert_eq!(
|
||||
Expr::new("object[foo] == 'Foo, hello world!'")
|
||||
.value("object", object)
|
||||
.value("foo", "foo")
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path() {
|
||||
assert_eq!(Expr::new("array[2-2].foo[2-2]").exec(), Ok(Value::Null));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_access() {
|
||||
let array = vec!["hello", "world", "!"];
|
||||
assert_eq!(
|
||||
Expr::new(
|
||||
"array[1-1] == 'hello' && array[1] == 'world' && array[2] == '!'",
|
||||
).value("array", array)
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builtin_is_empty() {
|
||||
assert_eq!(
|
||||
Expr::new("is_empty(array)")
|
||||
.value("array", Vec::<String>::new())
|
||||
.exec(),
|
||||
Ok(to_value(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builtin_min() {
|
||||
assert_eq!(
|
||||
Expr::new("min(array)")
|
||||
.value("array", vec![23, 34, 45, 2])
|
||||
.exec(),
|
||||
Ok(to_value(2))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_function() {
|
||||
assert_eq!(
|
||||
Expr::new("output()")
|
||||
.function(
|
||||
"output",
|
||||
|_| Ok(to_value("This is custom function's output")),
|
||||
)
|
||||
.exec(),
|
||||
Ok(to_value("This is custom function's output"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_start_with_non_value_operator() {
|
||||
let mut tree = Tree {
|
||||
raw: "+ + 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_node(), Err(Error::StartWithNonValueOperator));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_duplicate_operator() {
|
||||
let mut tree = Tree {
|
||||
raw: "5 + + 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_node(), Err(Error::DuplicateOperatorNode));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_duplicate_value() {
|
||||
let mut tree = Tree {
|
||||
raw: "2 + 6 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_node(), Err(Error::DuplicateValueNode));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_unpaired_brackets() {
|
||||
let mut tree = Tree {
|
||||
raw: "(2 + 3)) * 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_operators(), Err(Error::UnpairedBrackets));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_comma() {
|
||||
let mut tree = Tree {
|
||||
raw: ", 2 + 5".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
|
||||
assert_eq!(tree.parse_node(), Err(Error::CommaNotWithFunction));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eval_issue_2() {
|
||||
assert_eq!(eval("2 * (4 + 0) + 4"), Ok(to_value(12)));
|
||||
assert_eq!(eval("2 * (2 + 2) + (1 + 3)"), Ok(to_value(12)));
|
||||
assert_eq!(eval("2 * (4) + (4)"), Ok(to_value(12)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unary_minus() {
|
||||
assert_eq!(eval("3 + -4"), Ok(to_value(-1)));
|
||||
assert_eq!(eval("3+-4"), Ok(to_value(-1)));
|
||||
assert_eq!(eval("true && !false"), Ok(to_value(true)));
|
||||
assert_eq!(eval("!true || !true"), Ok(to_value(false)));
|
||||
assert_eq!(eval("-5 + -4"), Ok(to_value(-9)));
|
||||
assert_eq!(eval("-3-4"), Ok(to_value(-7)));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benches {
|
||||
extern crate test;
|
||||
use eval;
|
||||
use tree::Tree;
|
||||
use Expr;
|
||||
|
||||
#[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 tree = Tree {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
b.iter(|| tree.parse_pos().unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_operators(b: &mut test::Bencher) {
|
||||
let mut tree = Tree {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
b.iter(|| tree.parse_operators().unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_parse_nodes(b: &mut test::Bencher) {
|
||||
let mut tree = Tree {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
b.iter(|| tree.parse_node().unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_compile(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let mut tree = Tree {
|
||||
raw: "(2 + (3 + 4) + (6 + (6 + 7)) + 5)".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
tree.parse_pos().unwrap();
|
||||
tree.parse_operators().unwrap();
|
||||
tree.parse_node().unwrap();
|
||||
tree.compile().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_exec(b: &mut test::Bencher) {
|
||||
let expr = Expr::new("(2 + (3 + 4) + (6 + (6 + 7)) + 5)")
|
||||
.compile()
|
||||
.unwrap();
|
||||
b.iter(|| expr.exec().unwrap())
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_eval(b: &mut test::Bencher) {
|
||||
b.iter(|| eval("(2 + (3 + 4) + (6 + (6 + 7)) + 5)"));
|
||||
fn test_simple_examples() {
|
||||
assert_eq!(eval("3"), Ok(Value::Number(3.0)));
|
||||
assert_eq!(eval("1+3"), Ok(Value::Number(4.0)));
|
||||
assert_eq!(eval("3+1"), Ok(Value::Number(4.0)));
|
||||
assert_eq!(eval("3-5"), Ok(Value::Number(-2.0)));
|
||||
assert_eq!(eval("5-3"), Ok(Value::Number(2.0)));
|
||||
}
|
||||
}
|
||||
|
206
src/math/mod.rs
206
src/math/mod.rs
@ -1,206 +0,0 @@
|
||||
|
||||
use serde_json::Value;
|
||||
use 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_null() || value.is_null() {
|
||||
return Ok(to_value(false));
|
||||
}
|
||||
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_null() || value.is_null() {
|
||||
return Ok(to_value(false));
|
||||
}
|
||||
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_null() || value.is_null() {
|
||||
return Ok(to_value(false));
|
||||
}
|
||||
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_null() || value.is_null() {
|
||||
return Ok(to_value(false));
|
||||
}
|
||||
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::Number(ref n) => n.as_f64().unwrap(),
|
||||
_ => 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)
|
||||
}
|
||||
}
|
107
src/node/mod.rs
107
src/node/mod.rs
@ -1,107 +0,0 @@
|
||||
|
||||
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,
|
||||
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_full_children(&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_arithmetic(&self) -> bool {
|
||||
return !self.closed && self.operator.can_have_child() && self.operator.can_have_child()
|
||||
}
|
||||
|
||||
pub fn is_unclosed_function(&self) -> bool {
|
||||
match self.operator {
|
||||
Operator::Function(_) => !self.closed,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_unclosed_square_bracket(&self) -> bool {
|
||||
match self.operator {
|
||||
Operator::LeftSquareBracket(_) => !self.closed,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_left_square_bracket(&self) -> bool {
|
||||
match self.operator {
|
||||
Operator::LeftSquareBracket(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dot(&self) -> bool {
|
||||
match self.operator {
|
||||
Operator::Dot(_) => true,
|
||||
_ => 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()
|
||||
}
|
||||
}
|
@ -1,228 +1,165 @@
|
||||
use crate::{configuration::Configuration, error::*, value::Value};
|
||||
|
||||
use std::str::FromStr;
|
||||
use serde_json::Value;
|
||||
use to_value;
|
||||
use error::Error;
|
||||
use node::Node;
|
||||
pub trait Operator {
|
||||
/// Returns the precedence of the operator.
|
||||
/// A high precedence means that the operator has priority to be deeper in the tree.
|
||||
// Make this a const fn once #57563 is resolved
|
||||
fn precedence(&self) -> i32;
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Operator {
|
||||
Neg(u8),
|
||||
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),
|
||||
Dot(u8),
|
||||
LeftParenthesis,
|
||||
RightParenthesis,
|
||||
LeftSquareBracket(u8),
|
||||
RightSquareBracket,
|
||||
DoubleQuotes,
|
||||
SingleQuote,
|
||||
WhiteSpace,
|
||||
Comma,
|
||||
Function(String),
|
||||
Identifier(String),
|
||||
Value(Value),
|
||||
/// True if this operator is a leaf, meaning it accepts no arguments.
|
||||
// Make this a const fn once #57563 is resolved
|
||||
fn is_leaf(&self) -> bool {
|
||||
self.argument_amount() == 0
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
pub fn is_identifier(&self) -> bool {
|
||||
match *self {
|
||||
Operator::Identifier(_) => true,
|
||||
_ => false,
|
||||
/// Returns the amount of arguments required by this operator.
|
||||
// Make this a const fn once #57563 is resolved
|
||||
fn argument_amount(&self) -> usize;
|
||||
|
||||
/// Evaluates the operator with the given arguments and configuration.
|
||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error>;
|
||||
}
|
||||
|
||||
pub struct RootNode;
|
||||
pub struct Add;
|
||||
pub struct Sub;
|
||||
pub struct Mul;
|
||||
pub struct Div;
|
||||
|
||||
pub struct Const {
|
||||
value: Value,
|
||||
}
|
||||
|
||||
impl Const {
|
||||
pub fn new(value: Value) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_at_beginning(&self) -> bool {
|
||||
match *self {
|
||||
Operator::Neg(_) |
|
||||
Operator::Not(_) |
|
||||
Operator::Function(_) |
|
||||
Operator::LeftParenthesis => true,
|
||||
_ => false,
|
||||
pub struct Identifier {
|
||||
identifier: String,
|
||||
}
|
||||
|
||||
impl Identifier {
|
||||
pub fn new(identifier: String) -> Self {
|
||||
Self { identifier }
|
||||
}
|
||||
}
|
||||
|
||||
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(_) | Operator::Neg(_) => Some(1),
|
||||
Operator::Function(_) => None,
|
||||
_ => Some(0),
|
||||
impl Operator for RootNode {
|
||||
fn precedence(&self) -> i32 {
|
||||
i32::min_value()
|
||||
}
|
||||
|
||||
fn argument_amount(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||
Err(Error::EvaluatedRootNode)
|
||||
}
|
||||
}
|
||||
|
||||
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(_) | Operator::Neg(_) => Some(1),
|
||||
Operator::Function(_) => None,
|
||||
_ => Some(0),
|
||||
impl Operator for Add {
|
||||
fn precedence(&self) -> i32 {
|
||||
95
|
||||
}
|
||||
|
||||
fn argument_amount(&self) -> usize {
|
||||
2
|
||||
}
|
||||
|
||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||
expect_argument_amount(arguments.len(), 2)?;
|
||||
let a = expect_number(&arguments[0])?;
|
||||
let b = expect_number(&arguments[1])?;
|
||||
|
||||
Ok(Value::Number(a + b))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_priority(&self) -> u8 {
|
||||
match *self {
|
||||
Operator::Neg(priority) |
|
||||
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,
|
||||
impl Operator for Sub {
|
||||
fn precedence(&self) -> i32 {
|
||||
95
|
||||
}
|
||||
|
||||
fn argument_amount(&self) -> usize {
|
||||
2
|
||||
}
|
||||
|
||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||
expect_argument_amount(arguments.len(), 2)?;
|
||||
let a = expect_number(&arguments[0])?;
|
||||
let b = expect_number(&arguments[1])?;
|
||||
|
||||
Ok(Value::Number(a - b))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_left_parenthesis(&self) -> bool {
|
||||
*self == Operator::LeftParenthesis
|
||||
impl Operator for Mul {
|
||||
fn precedence(&self) -> i32 {
|
||||
100
|
||||
}
|
||||
|
||||
pub fn is_not(&self) -> bool {
|
||||
match *self {
|
||||
Operator::Not(_) => true,
|
||||
_ => false,
|
||||
fn argument_amount(&self) -> usize {
|
||||
2
|
||||
}
|
||||
|
||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||
expect_argument_amount(arguments.len(), 2)?;
|
||||
let a = expect_number(&arguments[0])?;
|
||||
let b = expect_number(&arguments[1])?;
|
||||
|
||||
Ok(Value::Number(a * b))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_left_square_bracket(&self) -> bool {
|
||||
match *self {
|
||||
Operator::LeftSquareBracket(_) => true,
|
||||
_ => false,
|
||||
impl Operator for Div {
|
||||
fn precedence(&self) -> i32 {
|
||||
100
|
||||
}
|
||||
|
||||
fn argument_amount(&self) -> usize {
|
||||
2
|
||||
}
|
||||
|
||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||
expect_argument_amount(arguments.len(), 2)?;
|
||||
let a = expect_number(&arguments[0])?;
|
||||
let b = expect_number(&arguments[1])?;
|
||||
|
||||
Ok(Value::Number(a / b))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dot(&self) -> bool {
|
||||
match *self {
|
||||
Operator::Dot(_) => true,
|
||||
_ => false,
|
||||
impl Operator for Const {
|
||||
fn precedence(&self) -> i32 {
|
||||
200
|
||||
}
|
||||
|
||||
fn argument_amount(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||
expect_argument_amount(arguments.len(), 0)?;
|
||||
|
||||
Ok(self.value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_value_or_ident(&self) -> bool {
|
||||
match *self {
|
||||
Operator::Value(_) |
|
||||
Operator::Identifier(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
impl Operator for Identifier {
|
||||
fn precedence(&self) -> i32 {
|
||||
200
|
||||
}
|
||||
|
||||
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::Not(_) |
|
||||
Operator::Neg(_) |
|
||||
Operator::Dot(_) |
|
||||
Operator::LeftSquareBracket(_) |
|
||||
Operator::Le(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
fn argument_amount(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
pub fn is_left(&self) -> bool {
|
||||
match *self {
|
||||
Operator::LeftParenthesis |
|
||||
Operator::LeftSquareBracket(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||
expect_argument_amount(arguments.len(), 0)?;
|
||||
|
||||
pub fn get_left(&self) -> Operator {
|
||||
match *self {
|
||||
Operator::RightParenthesis => Operator::LeftParenthesis,
|
||||
Operator::RightSquareBracket => Operator::LeftSquareBracket(100),
|
||||
_ => 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) -> &str {
|
||||
match *self {
|
||||
Operator::Identifier(ref ident) => ident,
|
||||
_ => 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(100)),
|
||||
"]" => Ok(Operator::RightSquareBracket),
|
||||
"." => Ok(Operator::Dot(100)),
|
||||
"\"" => 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())),
|
||||
}
|
||||
configuration.get_value(&self.identifier)
|
||||
}
|
||||
}
|
||||
|
82
src/token/mod.rs
Normal file
82
src/token/mod.rs
Normal file
@ -0,0 +1,82 @@
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Token {
|
||||
// Single character tokens
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
Whitespace,
|
||||
|
||||
// Complex tokens
|
||||
Identifier(String),
|
||||
Number(f64),
|
||||
Boolean(bool),
|
||||
}
|
||||
|
||||
enum PartialToken {
|
||||
Token(Token),
|
||||
Literal(String),
|
||||
}
|
||||
|
||||
// Make this a const fn as soon as match gets stable (issue #57563)
|
||||
fn char_to_token(c: char) -> PartialToken {
|
||||
match c {
|
||||
'+' => PartialToken::Token(Token::Plus),
|
||||
'-' => PartialToken::Token(Token::Minus),
|
||||
'*' => PartialToken::Token(Token::Star),
|
||||
'/' => PartialToken::Token(Token::Slash),
|
||||
c => {
|
||||
if c.is_whitespace() {
|
||||
PartialToken::Token(Token::Whitespace)
|
||||
} else {
|
||||
PartialToken::Literal(c.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a string to a vector of partial tokens.
|
||||
fn str_to_tokens(string: &str) -> Vec<PartialToken> {
|
||||
let mut result = Vec::new();
|
||||
for c in string.chars() {
|
||||
let partial_token = char_to_token(c);
|
||||
|
||||
let if_let_successful =
|
||||
if let (Some(PartialToken::Literal(last)), PartialToken::Literal(literal)) =
|
||||
(result.last_mut(), &partial_token)
|
||||
{
|
||||
last.push_str(literal);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !if_let_successful {
|
||||
result.push(partial_token);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Resolves all literals in the given vector of partial tokens by converting them to complex tokens.
|
||||
fn resolve_literals(tokens: &Vec<PartialToken>) -> Vec<Token> {
|
||||
tokens
|
||||
.iter()
|
||||
.map(|token| match token {
|
||||
PartialToken::Token(token) => token.clone(),
|
||||
PartialToken::Literal(literal) => {
|
||||
if let Ok(number) = literal.parse::<f64>() {
|
||||
Token::Number(number)
|
||||
} else if let Ok(boolean) = literal.parse::<bool>() {
|
||||
Token::Boolean(boolean)
|
||||
} else {
|
||||
Token::Identifier(literal.to_string())
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn tokenize(string: &str) -> Vec<Token> {
|
||||
resolve_literals(&str_to_tokens(string))
|
||||
}
|
737
src/tree/mod.rs
737
src/tree/mod.rs
@ -1,673 +1,104 @@
|
||||
use crate::{configuration::Configuration, error::Error, operator::*, value::Value};
|
||||
use std::mem;
|
||||
use token::Token;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::clone::Clone;
|
||||
use serde_json::Value;
|
||||
use to_value;
|
||||
use math::Math;
|
||||
use operator::Operator;
|
||||
use node::Node;
|
||||
use {Context, Functions};
|
||||
use error::Error;
|
||||
use Compiled;
|
||||
use builtin::BuiltIn;
|
||||
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Tree {
|
||||
pub raw: String,
|
||||
pub pos: Vec<usize>,
|
||||
pub operators: Vec<Operator>,
|
||||
pub node: Option<Node>,
|
||||
pub struct Node {
|
||||
children: Vec<Node>,
|
||||
operator: Box<dyn Operator>,
|
||||
}
|
||||
|
||||
impl Tree {
|
||||
pub fn new<T: Into<String>>(raw: T) -> Tree {
|
||||
Tree { raw: raw.into(), ..Default::default() }
|
||||
}
|
||||
|
||||
pub fn parse_pos(&mut self) -> Result<(), Error> {
|
||||
let mut found_quote = false;
|
||||
let mut pos = Vec::new();
|
||||
|
||||
for (index, cur) in self.raw.chars().enumerate() {
|
||||
match cur {
|
||||
'(' | ')' | '+' | '-' | '*' | '/' | ',' | ' ' | '!' | '=' | '>' | '<' | '\'' |
|
||||
'[' | ']' | '.' | '%' | '&' | '|' => {
|
||||
if !found_quote {
|
||||
pos.push(index);
|
||||
pos.push(index + 1);
|
||||
}
|
||||
}
|
||||
'"' => {
|
||||
found_quote = !found_quote;
|
||||
pos.push(index);
|
||||
pos.push(index + 1);
|
||||
}
|
||||
_ => (),
|
||||
impl Node {
|
||||
fn new(operator: Box<dyn Operator>) -> Self {
|
||||
Self {
|
||||
children: Vec::new(),
|
||||
operator,
|
||||
}
|
||||
}
|
||||
|
||||
pos.push(self.raw.len());
|
||||
fn root_node() -> Self {
|
||||
Self {
|
||||
children: Vec::new(),
|
||||
operator: Box::new(RootNode),
|
||||
}
|
||||
}
|
||||
|
||||
self.pos = pos;
|
||||
pub fn eval(&self, configuration: &Configuration) -> Result<Value, Error> {
|
||||
let mut arguments = Vec::new();
|
||||
for child in self.children() {
|
||||
arguments.push(child.eval(configuration)?);
|
||||
}
|
||||
self.operator().eval(&arguments, configuration)
|
||||
}
|
||||
|
||||
pub fn children(&self) -> &[Node] {
|
||||
&self.children
|
||||
}
|
||||
|
||||
pub fn operator(&self) -> &Box<dyn Operator> {
|
||||
&self.operator
|
||||
}
|
||||
|
||||
fn has_correct_amount_of_children(&self) -> bool {
|
||||
self.children().len() == self.operator().argument_amount()
|
||||
}
|
||||
|
||||
fn insert_back_prioritized(&mut self, mut operator: Box<dyn Operator>) -> Result<(), Error> {
|
||||
if self.operator().precedence() < operator.precedence() {
|
||||
if self.operator().is_leaf() {
|
||||
Err(Error::AppendedToLeafNode)
|
||||
} else if self.has_correct_amount_of_children() {
|
||||
if self.children.last_mut().unwrap().operator().precedence() < operator.precedence()
|
||||
{
|
||||
self.children
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.insert_back_prioritized(operator)
|
||||
} else {
|
||||
let mut new_node = Node::new(operator);
|
||||
let last_child = self.children.pop().unwrap();
|
||||
self.children.push(new_node);
|
||||
let new_node = self.children.last_mut().unwrap();
|
||||
|
||||
if new_node.operator().is_leaf() {
|
||||
Err(Error::AppendedToLeafNode)
|
||||
} else {
|
||||
new_node.children.push(last_child);
|
||||
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 quote = None;
|
||||
let mut prev = String::new();
|
||||
let mut number = String::new();
|
||||
|
||||
for pos_ref in &self.pos {
|
||||
let pos = *pos_ref;
|
||||
if pos == 0 {
|
||||
continue;
|
||||
} else {
|
||||
start = end;
|
||||
end = pos;
|
||||
}
|
||||
|
||||
let raw = self.raw[start..end].to_owned();
|
||||
|
||||
if raw.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
self.children.push(Node::new(operator));
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
Err(Error::PrecedenceViolation)
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
|
||||
let mut root = Node::root_node();
|
||||
|
||||
for token in tokens {
|
||||
let operator: Option<Box<dyn Operator>> = match token {
|
||||
Token::Plus => Some(Box::new(Add)),
|
||||
Token::Minus => Some(Box::new(Sub)),
|
||||
Token::Star => Some(Box::new(Mul)),
|
||||
Token::Slash => Some(Box::new(Div)),
|
||||
Token::Whitespace => None,
|
||||
Token::Identifier(identifier) => Some(Box::new(Identifier::new(identifier))),
|
||||
Token::Number(number) => Some(Box::new(Const::new(Value::Number(number)))),
|
||||
Token::Boolean(boolean) => Some(Box::new(Const::new(Value::Boolean(boolean)))),
|
||||
};
|
||||
|
||||
if quote.is_some() {
|
||||
prev += &raw;
|
||||
continue;
|
||||
if let Some(operator) = operator {
|
||||
root.insert_back_prioritized(operator);
|
||||
}
|
||||
}
|
||||
|
||||
if parse_number(&raw).is_some() || operator.is_dot() {
|
||||
number += &raw;
|
||||
continue;
|
||||
} else if !number.is_empty() {
|
||||
operators.push(Operator::from_str(&number).unwrap());
|
||||
number.clear();
|
||||
}
|
||||
|
||||
if raw == "=" {
|
||||
if prev == "!" || prev == ">" || prev == "<" || prev == "=" {
|
||||
prev.push_str("=");
|
||||
operators.push(Operator::from_str(&prev).unwrap());
|
||||
prev.clear();
|
||||
if root.children().len() == 1 {
|
||||
Ok(root.children.pop().unwrap())
|
||||
} else {
|
||||
prev = raw;
|
||||
Err(Error::EmptyExpression)
|
||||
}
|
||||
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::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()
|
||||
.to_owned()));
|
||||
operators.push(operator);
|
||||
continue;
|
||||
} else {
|
||||
operators.push(prev_operator);
|
||||
}
|
||||
}
|
||||
}
|
||||
Operator::RightParenthesis => parenthesis -= 1,
|
||||
Operator::WhiteSpace => continue,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
prev = raw;
|
||||
operators.push(operator);
|
||||
}
|
||||
|
||||
if !number.is_empty() {
|
||||
operators.push(Operator::from_str(&number).unwrap());
|
||||
}
|
||||
|
||||
if parenthesis != 0 {
|
||||
Err(Error::UnpairedBrackets)
|
||||
} else {
|
||||
self.operators = operators;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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::Dot(priority) |
|
||||
Operator::LeftSquareBracket(priority) |
|
||||
Operator::Rem(priority) => {
|
||||
if !parsing_nodes.is_empty() {
|
||||
let prev = parsing_nodes.pop().unwrap();
|
||||
if prev.is_value_or_full_children() {
|
||||
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 => 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_value_to_last_node(&mut parsing_nodes, operator)?,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
self.node = Some(get_final_node(parsing_nodes)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compile(mut self) -> Result<Compiled, Error> {
|
||||
self.parse_pos()?;
|
||||
self.parse_operators()?;
|
||||
self.parse_node()?;
|
||||
let node = self.node.unwrap();
|
||||
let builtin = BuiltIn::new();
|
||||
|
||||
Ok(Box::new(move |contexts, functions| -> Result<Value, Error> {
|
||||
return exec_node(&node, &builtin, contexts, functions);
|
||||
|
||||
fn exec_node(node: &Node,
|
||||
builtin: &Functions,
|
||||
contexts: &[Context],
|
||||
functions: &Functions)
|
||||
-> Result<Value, Error> {
|
||||
match node.operator {
|
||||
Operator::Add(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.add(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Mul(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.mul(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Sub(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.sub(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Div(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.div(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Rem(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.rem(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Eq(_) => {
|
||||
Math::eq(&exec_node(&node.get_first_child(), builtin, contexts, functions)?,
|
||||
&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Ne(_) => {
|
||||
Math::ne(&exec_node(&node.get_first_child(), builtin, contexts, functions)?,
|
||||
&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Gt(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.gt(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Lt(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.lt(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Ge(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.ge(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Le(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.le(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::And(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.and(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Or(_) => {
|
||||
exec_node(&node.get_first_child(), builtin, contexts, functions)
|
||||
?
|
||||
.or(&exec_node(&node.get_last_child(), builtin, contexts, functions)?)
|
||||
}
|
||||
Operator::Function(ref ident) => {
|
||||
let function_option = if functions.contains_key(ident) {
|
||||
functions.get(ident)
|
||||
} else {
|
||||
builtin.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, builtin, contexts, 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(), builtin, contexts, functions)?;
|
||||
match value {
|
||||
Value::Bool(boolean) => Ok(Value::Bool(!boolean)),
|
||||
Value::Null => Ok(Value::Bool(true)),
|
||||
_ => Err(Error::ExpectedBoolean(value)),
|
||||
}
|
||||
}
|
||||
Operator::Dot(_) => {
|
||||
let mut value = None;
|
||||
for child in &node.children {
|
||||
if value.is_none() {
|
||||
let name = exec_node(child, builtin, contexts, functions)?;
|
||||
if name.is_string() {
|
||||
value = find(contexts, name.as_str().unwrap());
|
||||
if value.is_none() {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
} else if name.is_object() {
|
||||
value = Some(name);
|
||||
} else if name.is_null() {
|
||||
return Ok(Value::Null);
|
||||
} else {
|
||||
return Err(Error::ExpectedObject);
|
||||
}
|
||||
} else {
|
||||
if child.operator.is_identifier() {
|
||||
value = value.as_ref()
|
||||
.unwrap()
|
||||
.get(child.operator.get_identifier())
|
||||
.cloned();
|
||||
} else {
|
||||
return Err(Error::ExpectedIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if value.is_some() {
|
||||
return Ok(value.unwrap());
|
||||
} else {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
}
|
||||
Operator::LeftSquareBracket(_) => {
|
||||
let mut value = None;
|
||||
for child in &node.children {
|
||||
let name = exec_node(child, builtin, contexts, functions)?;
|
||||
if value.is_none() {
|
||||
if name.is_string() {
|
||||
value = find(contexts, name.as_str().unwrap());
|
||||
if value.is_none() {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
} else if name.is_array() {
|
||||
value = Some(name);
|
||||
} else if name.is_object() {
|
||||
value = Some(name);
|
||||
} else if name.is_null() {
|
||||
return Ok(Value::Null);
|
||||
} else {
|
||||
return Err(Error::ExpectedArray);
|
||||
}
|
||||
} else if value.as_ref().unwrap().is_object() {
|
||||
if name.is_string() {
|
||||
value = value.as_ref()
|
||||
.unwrap()
|
||||
.get(name.as_str().unwrap())
|
||||
.cloned();
|
||||
} else {
|
||||
return Err(Error::ExpectedIdentifier);
|
||||
}
|
||||
} else {
|
||||
if name.is_u64() {
|
||||
if value.as_ref().unwrap().is_array() {
|
||||
value = value.as_ref()
|
||||
.unwrap()
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.get(name.as_u64().unwrap() as usize)
|
||||
.cloned();
|
||||
} else {
|
||||
return Err(Error::ExpectedArray);
|
||||
}
|
||||
} else {
|
||||
return Err(Error::ExpectedNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
if value.is_some() {
|
||||
return Ok(value.unwrap());
|
||||
} else {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
}
|
||||
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())),
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn append_value_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_dot() {
|
||||
prev.add_child(node);
|
||||
prev.closed = true;
|
||||
parsing_nodes.push(prev);
|
||||
} else if prev.is_left_square_bracket() {
|
||||
parsing_nodes.push(prev);
|
||||
parsing_nodes.push(node);
|
||||
} else if prev.is_value_or_full_children() {
|
||||
return Err(Error::DuplicateValueNode);
|
||||
} else if prev.is_enough() {
|
||||
parsing_nodes.push(prev);
|
||||
parsing_nodes.push(node);
|
||||
} else if prev.operator.can_have_child() {
|
||||
prev.add_child(node);
|
||||
parsing_nodes.push(prev);
|
||||
} else {
|
||||
return Err(Error::CanNotAddChild);
|
||||
}
|
||||
} 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 last = parsing_nodes.pop().unwrap();
|
||||
let mut prev = parsing_nodes.pop().unwrap();
|
||||
if prev.operator.can_have_child() {
|
||||
prev.add_child(last);
|
||||
parsing_nodes.push(prev);
|
||||
} else {
|
||||
return Err(Error::CanNotAddChild);
|
||||
}
|
||||
}
|
||||
|
||||
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.is_left_square_bracket() {
|
||||
return Err(Error::BracketNotWithFunction);
|
||||
} else if prev.operator.is_left_square_bracket() {
|
||||
prev.add_child(current);
|
||||
prev.closed = true;
|
||||
parsing_nodes.push(prev);
|
||||
break;
|
||||
} else 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 if penult.is_unclosed_arithmetic() {
|
||||
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: &[Context], key: &str) -> Option<Value> {
|
||||
for context in contexts.iter().rev() {
|
||||
match context.get(key) {
|
||||
Some(value) => return Some(value.clone()),
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
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::InvalidRange(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::InvalidRange(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
|
||||
}
|
||||
|
6
src/value/mod.rs
Normal file
6
src/value/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Value {
|
||||
String(String),
|
||||
Number(f64),
|
||||
Boolean(bool),
|
||||
}
|
Loading…
Reference in New Issue
Block a user