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)
|
||||||
|
}
|
||||||
|
}
|
139
src/error/mod.rs
139
src/error/mod.rs
@ -1,95 +1,54 @@
|
|||||||
|
use crate::value::Value;
|
||||||
|
|
||||||
use serde_json::Value;
|
#[derive(Debug, PartialEq)]
|
||||||
use operator::Operator;
|
pub enum Error {
|
||||||
|
WrongArgumentAmount {
|
||||||
|
expected: usize,
|
||||||
|
actual: usize,
|
||||||
|
},
|
||||||
|
ExpectedNumber {
|
||||||
|
actual: Value,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The given expression is empty
|
||||||
|
EmptyExpression,
|
||||||
|
|
||||||
quick_error! {
|
/// Tried to evaluate the root node.
|
||||||
/// Expression parsing error
|
/// The root node should only be used as dummy node.
|
||||||
#[derive(Debug, PartialEq)]
|
EvaluatedRootNode,
|
||||||
pub enum Error {
|
|
||||||
/// Unsupported operator yet.
|
/// Tried to append a child to a leaf node.
|
||||||
UnsupportedOperator(operator: String) {
|
/// Leaf nodes cannot have children.
|
||||||
display("Unsupported operator: {:?}", operator)
|
AppendedToLeafNode,
|
||||||
}
|
|
||||||
/// This operator does not support execution.
|
/// Tried to append a child to a node such that the precedence of the child is not higher.
|
||||||
CanNotExec(operator: Operator) {
|
PrecedenceViolation,
|
||||||
display("This operator does not support execution: {:?}", operator)
|
|
||||||
}
|
/// An identifier operation did not find its value in the configuration.
|
||||||
/// Your expression may start with non-value operator like ( + * )
|
IdentifierNotFound,
|
||||||
StartWithNonValueOperator {
|
}
|
||||||
display("Your expression may start with non-value operator like ( + * ).")
|
|
||||||
}
|
impl Error {
|
||||||
/// Unpaired brackets, left brackets count does not equal right brackets count
|
pub fn wrong_argument_amount(actual: usize, expected: usize) -> Self {
|
||||||
UnpairedBrackets {
|
Error::WrongArgumentAmount { actual, expected }
|
||||||
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
|
pub fn expected_number(actual: Value) -> Self {
|
||||||
DuplicateValueNode {
|
Error::ExpectedNumber { actual }
|
||||||
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 {
|
pub fn expect_argument_amount(actual: usize, expected: usize) -> Result<(), Error> {
|
||||||
display("Duplicate operators node, you may have (+ +) but there is no values between them.")
|
if actual == expected {
|
||||||
}
|
Ok(())
|
||||||
/// You have a comma(,) , but there is no function in front of it.
|
} else {
|
||||||
CommaNotWithFunction {
|
Err(Error::wrong_argument_amount(actual, expected))
|
||||||
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 {
|
pub fn expect_number(actual: &Value) -> Result<f64, Error> {
|
||||||
display("You have empty brackets () , but there is no function in front of it.")
|
match actual {
|
||||||
}
|
Value::Number(number) => Ok(*number),
|
||||||
/// Function not exists.
|
_ => Err(Error::expected_number(actual.clone())),
|
||||||
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.
|
|
||||||
ExpectedNumber {
|
|
||||||
display("Expected number.")
|
|
||||||
}
|
|
||||||
/// Failed to parse, no final expression.
|
|
||||||
NoFinalNode {
|
|
||||||
display("Failed to parse, no final expression.")
|
|
||||||
}
|
|
||||||
/// The number of arguments is greater than the maximum limit.
|
|
||||||
ArgumentsGreater(max: usize) {
|
|
||||||
display("The number of arguments is greater than the maximum limit: {}", max)
|
|
||||||
}
|
|
||||||
/// The number of arguments is less than the minimum limit.
|
|
||||||
ArgumentsLess(min: usize) {
|
|
||||||
display("The number of arguments is less than the minimum limit: {}", min)
|
|
||||||
}
|
|
||||||
/// This two value types are different or do not support mathematical calculations.
|
|
||||||
UnsupportedTypes(a: String, b: String) {
|
|
||||||
display("This two value types are different or do not support mathematical calculations: {}, {}", a, b)
|
|
||||||
}
|
|
||||||
/// Invalid 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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.
|
use configuration::EmptyConfiguration;
|
||||||
//!
|
use error::Error;
|
||||||
//! Supported operators: `!` `!=` `""` `''` `()` `[]` `.` `,` `>` `<` `>=` `<=`
|
use value::Value;
|
||||||
//! `==` `+` 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))]
|
|
||||||
|
|
||||||
#[macro_use]
|
mod configuration;
|
||||||
extern crate quick_error;
|
|
||||||
extern crate serde;
|
|
||||||
extern crate serde_json;
|
|
||||||
|
|
||||||
mod math;
|
|
||||||
mod function;
|
|
||||||
mod operator;
|
|
||||||
mod node;
|
|
||||||
mod tree;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod builtin;
|
mod operator;
|
||||||
mod expr;
|
mod token;
|
||||||
|
mod tree;
|
||||||
|
mod value;
|
||||||
|
|
||||||
mod reboot;
|
pub fn eval(string: &str) -> Result<Value, Error> {
|
||||||
|
tree::tokens_to_operator_tree(token::tokenize(string))?.eval(&EmptyConfiguration)
|
||||||
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod test {
|
||||||
use to_value;
|
use crate::{eval, value::Value};
|
||||||
use error::Error;
|
|
||||||
use Expr;
|
|
||||||
use tree::Tree;
|
|
||||||
use Value;
|
|
||||||
use eval;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add() {
|
fn test_simple_examples() {
|
||||||
assert_eq!(eval("2 + 3"), Ok(to_value(5)));
|
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)));
|
||||||
#[test]
|
assert_eq!(eval("3-5"), Ok(Value::Number(-2.0)));
|
||||||
fn test_brackets_add() {
|
assert_eq!(eval("5-3"), Ok(Value::Number(2.0)));
|
||||||
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)"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
pub trait Operator {
|
||||||
use serde_json::Value;
|
/// Returns the precedence of the operator.
|
||||||
use to_value;
|
/// A high precedence means that the operator has priority to be deeper in the tree.
|
||||||
use error::Error;
|
// Make this a const fn once #57563 is resolved
|
||||||
use node::Node;
|
fn precedence(&self) -> i32;
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
/// Returns the amount of arguments required by this operator.
|
||||||
pub enum Operator {
|
// Make this a const fn once #57563 is resolved
|
||||||
Neg(u8),
|
fn argument_amount(&self) -> usize;
|
||||||
Add(u8),
|
|
||||||
Mul(u8),
|
/// Evaluates the operator with the given arguments and configuration.
|
||||||
Sub(u8),
|
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error>;
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Operator {
|
pub struct RootNode;
|
||||||
pub fn is_identifier(&self) -> bool {
|
pub struct Add;
|
||||||
match *self {
|
pub struct Sub;
|
||||||
Operator::Identifier(_) => true,
|
pub struct Mul;
|
||||||
_ => false,
|
pub struct Div;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn can_at_beginning(&self) -> bool {
|
pub struct Const {
|
||||||
match *self {
|
value: Value,
|
||||||
Operator::Neg(_) |
|
}
|
||||||
Operator::Not(_) |
|
|
||||||
Operator::Function(_) |
|
|
||||||
Operator::LeftParenthesis => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_max_args(&self) -> Option<usize> {
|
impl Const {
|
||||||
match *self {
|
pub fn new(value: Value) -> Self {
|
||||||
Operator::Add(_) | Operator::Sub(_) | Operator::Mul(_) | Operator::Div(_) |
|
Self { value }
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_left_parenthesis(&self) -> bool {
|
|
||||||
*self == Operator::LeftParenthesis
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_not(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Operator::Not(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_left_square_bracket(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Operator::LeftSquareBracket(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_dot(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Operator::Dot(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_value_or_ident(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Operator::Value(_) |
|
|
||||||
Operator::Identifier(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn can_have_child(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Operator::Function(_) |
|
|
||||||
Operator::Add(_) |
|
|
||||||
Operator::Sub(_) |
|
|
||||||
Operator::Div(_) |
|
|
||||||
Operator::Mul(_) |
|
|
||||||
Operator::Rem(_) |
|
|
||||||
Operator::Eq(_) |
|
|
||||||
Operator::Ne(_) |
|
|
||||||
Operator::Gt(_) |
|
|
||||||
Operator::Lt(_) |
|
|
||||||
Operator::And(_) |
|
|
||||||
Operator::Or(_) |
|
|
||||||
Operator::Ge(_) |
|
|
||||||
Operator::Not(_) |
|
|
||||||
Operator::Neg(_) |
|
|
||||||
Operator::Dot(_) |
|
|
||||||
Operator::LeftSquareBracket(_) |
|
|
||||||
Operator::Le(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_left(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Operator::LeftParenthesis |
|
|
||||||
Operator::LeftSquareBracket(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_left(&self) -> Operator {
|
|
||||||
match *self {
|
|
||||||
Operator::RightParenthesis => Operator::LeftParenthesis,
|
|
||||||
Operator::RightSquareBracket => Operator::LeftSquareBracket(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 {
|
pub struct Identifier {
|
||||||
type Err = Error;
|
identifier: String,
|
||||||
|
}
|
||||||
|
|
||||||
fn from_str(raw: &str) -> Result<Operator, Error> {
|
impl Identifier {
|
||||||
match raw {
|
pub fn new(identifier: String) -> Self {
|
||||||
"+" => Ok(Operator::Add(8)),
|
Self { identifier }
|
||||||
"-" => Ok(Operator::Sub(8)),
|
}
|
||||||
"*" => Ok(Operator::Mul(10)),
|
}
|
||||||
"/" => Ok(Operator::Div(10)),
|
|
||||||
"%" => Ok(Operator::Rem(10)),
|
impl Operator for RootNode {
|
||||||
"(" => Ok(Operator::LeftParenthesis),
|
fn precedence(&self) -> i32 {
|
||||||
")" => Ok(Operator::RightParenthesis),
|
i32::min_value()
|
||||||
"[" => Ok(Operator::LeftSquareBracket(100)),
|
}
|
||||||
"]" => Ok(Operator::RightSquareBracket),
|
|
||||||
"." => Ok(Operator::Dot(100)),
|
fn argument_amount(&self) -> usize {
|
||||||
"\"" => Ok(Operator::DoubleQuotes),
|
1
|
||||||
"'" => Ok(Operator::SingleQuote),
|
}
|
||||||
" " => Ok(Operator::WhiteSpace),
|
|
||||||
"," => Ok(Operator::Comma),
|
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
|
||||||
"!" => Ok(Operator::Not(99)),
|
Err(Error::EvaluatedRootNode)
|
||||||
"false" => Ok(Operator::Value(to_value(false))),
|
}
|
||||||
"true" => Ok(Operator::Value(to_value(true))),
|
}
|
||||||
"==" => Ok(Operator::Eq(6)),
|
|
||||||
"!=" => Ok(Operator::Ne(6)),
|
impl Operator for Add {
|
||||||
">" => Ok(Operator::Gt(6)),
|
fn precedence(&self) -> i32 {
|
||||||
"<" => Ok(Operator::Lt(6)),
|
95
|
||||||
">=" => Ok(Operator::Ge(6)),
|
}
|
||||||
"<=" => Ok(Operator::Le(6)),
|
|
||||||
"&&" => Ok(Operator::And(4)),
|
fn argument_amount(&self) -> usize {
|
||||||
"||" => Ok(Operator::Or(2)),
|
2
|
||||||
_ => Ok(Operator::Identifier(raw.to_owned())),
|
}
|
||||||
}
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Operator for Mul {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Operator for Identifier {
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
729
src/tree/mod.rs
729
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;
|
pub struct Node {
|
||||||
use std::clone::Clone;
|
children: Vec<Node>,
|
||||||
use serde_json::Value;
|
operator: Box<dyn Operator>,
|
||||||
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>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tree {
|
impl Node {
|
||||||
pub fn new<T: Into<String>>(raw: T) -> Tree {
|
fn new(operator: Box<dyn Operator>) -> Self {
|
||||||
Tree { raw: raw.into(), ..Default::default() }
|
Self {
|
||||||
}
|
children: Vec::new(),
|
||||||
|
operator,
|
||||||
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);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pos.push(self.raw.len());
|
|
||||||
|
|
||||||
self.pos = pos;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_operators(&mut self) -> Result<(), Error> {
|
fn root_node() -> Self {
|
||||||
let mut operators = Vec::new();
|
Self {
|
||||||
let mut start;
|
children: Vec::new(),
|
||||||
let mut end = 0;
|
operator: Box::new(RootNode),
|
||||||
let mut parenthesis = 0;
|
}
|
||||||
let mut quote = None;
|
}
|
||||||
let mut prev = String::new();
|
|
||||||
let mut number = String::new();
|
|
||||||
|
|
||||||
for pos_ref in &self.pos {
|
pub fn eval(&self, configuration: &Configuration) -> Result<Value, Error> {
|
||||||
let pos = *pos_ref;
|
let mut arguments = Vec::new();
|
||||||
if pos == 0 {
|
for child in self.children() {
|
||||||
continue;
|
arguments.push(child.eval(configuration)?);
|
||||||
} else {
|
}
|
||||||
start = end;
|
self.operator().eval(&arguments, configuration)
|
||||||
end = pos;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let raw = self.raw[start..end].to_owned();
|
pub fn children(&self) -> &[Node] {
|
||||||
|
&self.children
|
||||||
|
}
|
||||||
|
|
||||||
if raw.is_empty() {
|
pub fn operator(&self) -> &Box<dyn Operator> {
|
||||||
continue;
|
&self.operator
|
||||||
}
|
}
|
||||||
|
|
||||||
let operator = Operator::from_str(&raw).unwrap();
|
fn has_correct_amount_of_children(&self) -> bool {
|
||||||
match operator {
|
self.children().len() == self.operator().argument_amount()
|
||||||
Operator::DoubleQuotes | Operator::SingleQuote => {
|
}
|
||||||
if quote.is_some() {
|
|
||||||
if quote.as_ref() == Some(&operator) {
|
fn insert_back_prioritized(&mut self, mut operator: Box<dyn Operator>) -> Result<(), Error> {
|
||||||
operators.push(Operator::Value(to_value(&prev)));
|
if self.operator().precedence() < operator.precedence() {
|
||||||
prev.clear();
|
if self.operator().is_leaf() {
|
||||||
quote = None;
|
Err(Error::AppendedToLeafNode)
|
||||||
continue;
|
} 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 {
|
} else {
|
||||||
quote = Some(operator);
|
new_node.children.push(last_child);
|
||||||
prev.clear();
|
Ok(())
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
} else {
|
||||||
};
|
self.children.push(Node::new(operator));
|
||||||
|
Ok(())
|
||||||
if quote.is_some() {
|
|
||||||
prev += &raw;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
} else {
|
|
||||||
prev = raw;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else if raw == "!" || raw == ">" || raw == "<" {
|
|
||||||
if prev == "!" || prev == ">" || prev == "<" {
|
|
||||||
operators.push(Operator::from_str(&prev).unwrap());
|
|
||||||
prev.clear();
|
|
||||||
} else {
|
|
||||||
prev = raw;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
} else if prev == "!" || prev == ">" || prev == "<" {
|
|
||||||
operators.push(Operator::from_str(&prev).unwrap());
|
|
||||||
prev.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (raw == "&" || raw == "|") && (prev == "&" || prev == "|") {
|
|
||||||
if raw == prev {
|
|
||||||
prev.push_str(&raw);
|
|
||||||
operators.push(Operator::from_str(&prev).unwrap());
|
|
||||||
prev.clear();
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
return Err(Error::UnsupportedOperator(prev));
|
|
||||||
}
|
|
||||||
} else if raw == "&" || raw == "|" {
|
|
||||||
prev = raw;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match operator {
|
|
||||||
Operator::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 {
|
} else {
|
||||||
self.operators = operators;
|
Err(Error::PrecedenceViolation)
|
||||||
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>,
|
pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
|
||||||
operator: &Operator)
|
let mut root = Node::root_node();
|
||||||
-> Result<(), Error> {
|
|
||||||
let mut node = operator.to_node();
|
|
||||||
node.closed = true;
|
|
||||||
|
|
||||||
if let Some(mut prev) = parsing_nodes.pop() {
|
for token in tokens {
|
||||||
if prev.is_dot() {
|
let operator: Option<Box<dyn Operator>> = match token {
|
||||||
prev.add_child(node);
|
Token::Plus => Some(Box::new(Add)),
|
||||||
prev.closed = true;
|
Token::Minus => Some(Box::new(Sub)),
|
||||||
parsing_nodes.push(prev);
|
Token::Star => Some(Box::new(Mul)),
|
||||||
} else if prev.is_left_square_bracket() {
|
Token::Slash => Some(Box::new(Div)),
|
||||||
parsing_nodes.push(prev);
|
Token::Whitespace => None,
|
||||||
parsing_nodes.push(node);
|
Token::Identifier(identifier) => Some(Box::new(Identifier::new(identifier))),
|
||||||
} else if prev.is_value_or_full_children() {
|
Token::Number(number) => Some(Box::new(Const::new(Value::Number(number)))),
|
||||||
return Err(Error::DuplicateValueNode);
|
Token::Boolean(boolean) => Some(Box::new(Const::new(Value::Boolean(boolean)))),
|
||||||
} else if prev.is_enough() {
|
};
|
||||||
parsing_nodes.push(prev);
|
|
||||||
parsing_nodes.push(node);
|
if let Some(operator) = operator {
|
||||||
} else if prev.operator.can_have_child() {
|
root.insert_back_prioritized(operator);
|
||||||
prev.add_child(node);
|
|
||||||
parsing_nodes.push(prev);
|
|
||||||
} else {
|
|
||||||
return Err(Error::CanNotAddChild);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if root.children().len() == 1 {
|
||||||
|
Ok(root.children.pop().unwrap())
|
||||||
} else {
|
} else {
|
||||||
parsing_nodes.push(node);
|
Err(Error::EmptyExpression)
|
||||||
}
|
|
||||||
|
|
||||||
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