add .
and []
support.
This commit is contained in:
parent
032e57df43
commit
3cb3197f6f
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "eval"
|
name = "eval"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
description = "Expression evaluator"
|
description = "Expression evaluator"
|
||||||
keywords = ["expression", "evaluate", "evaluator", "expr", "template"]
|
keywords = ["expression", "evaluate", "evaluator", "expr", "template"]
|
||||||
authors = ["fengcen <fengcen.love@gmail.com>"]
|
authors = ["fengcen <fengcen.love@gmail.com>"]
|
||||||
|
21
README.md
21
README.md
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
eval
|
eval
|
||||||
===
|
===
|
||||||
[![docs](https://docs.rs/eval/badge.svg?version=0.2.0 "docs")](https://docs.rs/eval)
|
[![docs](https://docs.rs/eval/badge.svg?version=0.2.1 "docs")](https://docs.rs/eval)
|
||||||
|
|
||||||
Eval is a powerful expression evaluator.
|
Eval is a powerful expression evaluator.
|
||||||
|
|
||||||
@ -56,6 +56,23 @@ assert_eq!(Expr::new("foo == bar")
|
|||||||
Ok(to_value(true)));
|
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[1-1] == 'Hello'")
|
||||||
|
.value("object", object)
|
||||||
|
.exec(),
|
||||||
|
Ok(to_value(true)));
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
You can eval with function:
|
You can eval with function:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -67,6 +84,7 @@ assert_eq!(Expr::new("say_hello()")
|
|||||||
Ok(to_value("Hello world!")));
|
Ok(to_value("Hello world!")));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
You can create an array with `[]`:
|
You can create an array with `[]`:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -75,6 +93,7 @@ use eval::{eval, to_value};
|
|||||||
assert_eq!(eval("[1, 2, 3, 4, 5]"), Ok(to_value(vec![1, 2, 3, 4, 5])));
|
assert_eq!(eval("[1, 2, 3, 4, 5]"), Ok(to_value(vec![1, 2, 3, 4, 5])));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
You can create an integer array with `n..m`:
|
You can create an integer array with `n..m`:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -4,70 +4,90 @@ use operator::Operator;
|
|||||||
|
|
||||||
|
|
||||||
quick_error! {
|
quick_error! {
|
||||||
/// Expression parsing error
|
/// Expression parsing error
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Unsupported operator yet.
|
/// Unsupported operator yet.
|
||||||
UnsupportedOperator(operator: String) {
|
UnsupportedOperator(operator: String) {
|
||||||
display("Unsupported operator: {:?}", operator)
|
display("Unsupported operator: {:?}", operator)
|
||||||
}
|
}
|
||||||
/// This operator does not support execution.
|
/// This operator does not support execution.
|
||||||
CanNotExec(operator: Operator) {
|
CanNotExec(operator: Operator) {
|
||||||
display("This operator does not support execution: {:?}", operator)
|
display("This operator does not support execution: {:?}", operator)
|
||||||
}
|
}
|
||||||
/// Your expression may start with non-value operator like ( + * )
|
/// Your expression may start with non-value operator like ( + * )
|
||||||
StartWithNonValueOperator {
|
StartWithNonValueOperator {
|
||||||
display("Your expression may start with non-value operator like ( + * ).")
|
display("Your expression may start with non-value operator like ( + * ).")
|
||||||
}
|
}
|
||||||
/// Unpaired brackets, left brackets count does not equal right brackets count
|
/// Unpaired brackets, left brackets count does not equal right brackets count
|
||||||
UnpairedBrackets {
|
UnpairedBrackets {
|
||||||
display("Unpaired brackets, left brackets count does not equal right brackets count.")
|
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
|
/// Duplicate values node, you may have (2 3) but there is no operators between them
|
||||||
DuplicateValueNode {
|
DuplicateValueNode {
|
||||||
display("Duplicate values node, you may have (2 3) but there is no operators between them.")
|
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
|
/// Duplicate operators node, you may have (+ +) but there is no values between them
|
||||||
DuplicateOperatorNode {
|
DuplicateOperatorNode {
|
||||||
display("Duplicate operators node, you may have (+ +) but there is no values between them.")
|
display("Duplicate operators node, you may have (+ +) but there is no values between them.")
|
||||||
}
|
}
|
||||||
/// You have a comma(,) , but there is no function in front of it.
|
/// You have a comma(,) , but there is no function in front of it.
|
||||||
CommaNotWithFunction {
|
CommaNotWithFunction {
|
||||||
display("You have a comma(,) , but there is no function in front of it.")
|
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.
|
/// You have empty brackets () , but there is no function in front of it.
|
||||||
BracketNotWithFunction {
|
BracketNotWithFunction {
|
||||||
display("You have empty brackets () , but there is no function in front of it.")
|
display("You have empty brackets () , but there is no function in front of it.")
|
||||||
}
|
}
|
||||||
/// Function not exists.
|
/// Function not exists.
|
||||||
FunctionNotExists(ident: String) {
|
FunctionNotExists(ident: String) {
|
||||||
display("Function not exists: {}", ident)
|
display("Function not exists: {}", ident)
|
||||||
}
|
}
|
||||||
/// Expected boolean type but the given value isn't.
|
/// Expected a boolean but the given value isn't.
|
||||||
NotBoolean(value: Value) {
|
ExpectedBoolean(value: Value) {
|
||||||
display("Expected boolean type, found: {}", value)
|
display("Expected a boolean, found: {}", value)
|
||||||
}
|
}
|
||||||
/// Failed to parse, no final expression.
|
/// 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 {
|
NoFinalNode {
|
||||||
display("Failed to parse, no final expression.")
|
display("Failed to parse, no final expression.")
|
||||||
}
|
}
|
||||||
/// The number of arguments is greater than the maximum limit.
|
/// The number of arguments is greater than the maximum limit.
|
||||||
ArgumentsGreater(max: usize) {
|
ArgumentsGreater(max: usize) {
|
||||||
display("The number of arguments is greater than the maximum limit: {}", max)
|
display("The number of arguments is greater than the maximum limit: {}", max)
|
||||||
}
|
}
|
||||||
/// The number of arguments is less than the minimum limit.
|
/// The number of arguments is less than the minimum limit.
|
||||||
ArgumentsLess(min: usize) {
|
ArgumentsLess(min: usize) {
|
||||||
display("The number of arguments is less than the minimum limit: {}", min)
|
display("The number of arguments is less than the minimum limit: {}", min)
|
||||||
}
|
}
|
||||||
/// This two value types are different or do not support mathematical calculations.
|
/// This two value types are different or do not support mathematical calculations.
|
||||||
UnsupportedTypes(a: String, b: String) {
|
UnsupportedTypes(a: String, b: String) {
|
||||||
display("This two value types are different or do not support mathematical calculations: {}, {}", a, b)
|
display("This two value types are different or do not support mathematical calculations: {}, {}", a, b)
|
||||||
}
|
}
|
||||||
/// Invalid range expression like `1..2..3`
|
/// Invalid range expression like `1..2..3`
|
||||||
InvalidRange(ident: String) {
|
InvalidRange(ident: String) {
|
||||||
display("Invalid range expression: {}", ident)
|
display("Invalid range expression: {}", ident)
|
||||||
}
|
}
|
||||||
/// Custom error.
|
/// Can not add child node.
|
||||||
|
CanNotAddChild {
|
||||||
|
display("Can not add child node.")
|
||||||
|
}
|
||||||
|
/// Custom error.
|
||||||
Custom(detail: String) {
|
Custom(detail: String) {
|
||||||
display("{}", detail)
|
display("{}", detail)
|
||||||
}
|
}
|
||||||
|
90
src/lib.rs
90
src/lib.rs
@ -1,9 +1,9 @@
|
|||||||
//! Eval is a powerful expression evaluator.
|
//! Eval is a powerful expression evaluator.
|
||||||
//!
|
//!
|
||||||
//! Supported operators: `!` `!=` `""` `''` `()` `[]` `,` `>` `<` `>=` `<=`
|
//! Supported operators: `!` `!=` `""` `''` `()` `[]` `.` `,` `>` `<` `>=` `<=`
|
||||||
//! `==` `+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
//! `==` `+` `-` `*` `/` `%` `&&` `||` `n..m`.
|
||||||
//!
|
//!
|
||||||
//! Built-in functions: `min()` `max()` `len()` `is_empty()`.
|
//! Built-in functions: `min()` `max()` `len()` `is_empty()` `array()`.
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
@ -30,6 +30,21 @@
|
|||||||
//! Ok(to_value(true)));
|
//! 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:
|
//! You can eval with function:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
@ -41,12 +56,12 @@
|
|||||||
//! Ok(to_value("Hello world!")));
|
//! Ok(to_value("Hello world!")));
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! You can create an array with `[]`:
|
//! You can create an array with `array()`:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use eval::{eval, to_value};
|
//! use eval::{eval, to_value};
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(eval("[1, 2, 3, 4, 5]"), Ok(to_value(vec![1, 2, 3, 4, 5])));
|
//! 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`:
|
//! You can create an integer array with `n..m`:
|
||||||
@ -69,7 +84,10 @@
|
|||||||
//! Accept single arguments and return the length of value. Only accept String, Array, Object and Null.
|
//! Accept single arguments and return the length of value. Only accept String, Array, Object and Null.
|
||||||
//!
|
//!
|
||||||
//! ### is_empty()
|
//! ### is_empty()
|
||||||
//! Accept single arguments and return the a boolean. Check whether the value is empty or not.
|
//! Accept single arguments and return a boolean. Check whether the value is empty or not.
|
||||||
|
//!
|
||||||
|
//! ### array()
|
||||||
|
//! Accept multiple arguments and return an array.
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
#![recursion_limit="100"]
|
#![recursion_limit="100"]
|
||||||
@ -122,6 +140,7 @@ mod tests {
|
|||||||
use error::Error;
|
use error::Error;
|
||||||
use Expr;
|
use Expr;
|
||||||
use tree::Tree;
|
use tree::Tree;
|
||||||
|
use Value;
|
||||||
use eval;
|
use eval;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -204,7 +223,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_len_array() {
|
fn test_len_array() {
|
||||||
assert_eq!(eval("len([2, 3, 4, 5, 6])"), Ok(to_value(5)));
|
assert_eq!(eval("len(array(2, 3, 4, 5, 6))"), Ok(to_value(5)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -320,17 +339,16 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array() {
|
fn test_array() {
|
||||||
assert_eq!(eval("[1, 2, 3, 4]"), Ok(to_value(vec![1, 2, 3, 4])));
|
|
||||||
assert_eq!(eval("array(1, 2, 3, 4)"), Ok(to_value(vec![1, 2, 3, 4])));
|
assert_eq!(eval("array(1, 2, 3, 4)"), Ok(to_value(vec![1, 2, 3, 4])));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_ident() {
|
fn test_range() {
|
||||||
assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
assert_eq!(eval("0..5"), Ok(to_value(vec![0, 1, 2, 3, 4])));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_ident_and_min() {
|
fn test_range_and_min() {
|
||||||
assert_eq!(eval("min(0..5)"), Ok(to_value(0)));
|
assert_eq!(eval("min(0..5)"), Ok(to_value(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,12 +421,58 @@ mod tests {
|
|||||||
assert_eq!(eval("(!(1 == 2)) == true"), Ok(to_value(true)));
|
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]
|
#[test]
|
||||||
fn test_builtin_is_empty() {
|
fn test_builtin_is_empty() {
|
||||||
assert_eq!(Expr::new("is_empty(array)")
|
assert_eq!(Expr::new("is_empty(array)")
|
||||||
.value("array", Vec::<String>::new())
|
.value("array", Vec::<String>::new())
|
||||||
.compile()
|
|
||||||
.unwrap()
|
|
||||||
.exec(),
|
.exec(),
|
||||||
Ok(to_value(true)));
|
Ok(to_value(true)));
|
||||||
}
|
}
|
||||||
@ -417,8 +481,6 @@ mod tests {
|
|||||||
fn test_builtin_min() {
|
fn test_builtin_min() {
|
||||||
assert_eq!(Expr::new("min(array)")
|
assert_eq!(Expr::new("min(array)")
|
||||||
.value("array", vec![23, 34, 45, 2])
|
.value("array", vec![23, 34, 45, 2])
|
||||||
.compile()
|
|
||||||
.unwrap()
|
|
||||||
.exec(),
|
.exec(),
|
||||||
Ok(to_value(2)));
|
Ok(to_value(2)));
|
||||||
}
|
}
|
||||||
@ -428,8 +490,6 @@ mod tests {
|
|||||||
assert_eq!(Expr::new("output()")
|
assert_eq!(Expr::new("output()")
|
||||||
.function("output",
|
.function("output",
|
||||||
|_| Ok(to_value("This is custom function's output")))
|
|_| Ok(to_value("This is custom function's output")))
|
||||||
.compile()
|
|
||||||
.unwrap()
|
|
||||||
.exec(),
|
.exec(),
|
||||||
Ok(to_value("This is custom function's output")));
|
Ok(to_value("This is custom function's output")));
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,27 @@ impl Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
pub fn add_child(&mut self, node: Node) {
|
||||||
self.children.push(node);
|
self.children.push(node);
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,10 @@ pub enum Operator {
|
|||||||
Le(u8),
|
Le(u8),
|
||||||
And(u8),
|
And(u8),
|
||||||
Or(u8),
|
Or(u8),
|
||||||
|
Dot(u8),
|
||||||
LeftParenthesis,
|
LeftParenthesis,
|
||||||
RightParenthesis,
|
RightParenthesis,
|
||||||
LeftSquareBracket,
|
LeftSquareBracket(u8),
|
||||||
RightSquareBracket,
|
RightSquareBracket,
|
||||||
DoubleQuotes,
|
DoubleQuotes,
|
||||||
SingleQuote,
|
SingleQuote,
|
||||||
@ -107,6 +108,20 @@ impl Operator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pub fn is_value_or_ident(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
Operator::Value(_) |
|
Operator::Value(_) |
|
||||||
@ -130,6 +145,9 @@ impl Operator {
|
|||||||
Operator::And(_) |
|
Operator::And(_) |
|
||||||
Operator::Or(_) |
|
Operator::Or(_) |
|
||||||
Operator::Ge(_) |
|
Operator::Ge(_) |
|
||||||
|
Operator::Not(_) |
|
||||||
|
Operator::Dot(_) |
|
||||||
|
Operator::LeftSquareBracket(_) |
|
||||||
Operator::Le(_) => true,
|
Operator::Le(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -138,7 +156,7 @@ impl Operator {
|
|||||||
pub fn is_left(&self) -> bool {
|
pub fn is_left(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
Operator::LeftParenthesis |
|
Operator::LeftParenthesis |
|
||||||
Operator::LeftSquareBracket => true,
|
Operator::LeftSquareBracket(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,7 +164,7 @@ impl Operator {
|
|||||||
pub fn get_left(&self) -> Operator {
|
pub fn get_left(&self) -> Operator {
|
||||||
match *self {
|
match *self {
|
||||||
Operator::RightParenthesis => Operator::LeftParenthesis,
|
Operator::RightParenthesis => Operator::LeftParenthesis,
|
||||||
Operator::RightSquareBracket => Operator::LeftSquareBracket,
|
Operator::RightSquareBracket => Operator::LeftSquareBracket(100),
|
||||||
_ => panic!("not bracket"),
|
_ => panic!("not bracket"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,9 +179,9 @@ impl Operator {
|
|||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_identifier(&self) -> String {
|
pub fn get_identifier(&self) -> &str {
|
||||||
match *self {
|
match *self {
|
||||||
Operator::Identifier(ref ident) => ident.to_owned(),
|
Operator::Identifier(ref ident) => ident,
|
||||||
_ => panic!("not identifier"),
|
_ => panic!("not identifier"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,8 +199,9 @@ impl FromStr for Operator {
|
|||||||
"%" => Ok(Operator::Rem(10)),
|
"%" => Ok(Operator::Rem(10)),
|
||||||
"(" => Ok(Operator::LeftParenthesis),
|
"(" => Ok(Operator::LeftParenthesis),
|
||||||
")" => Ok(Operator::RightParenthesis),
|
")" => Ok(Operator::RightParenthesis),
|
||||||
"[" => Ok(Operator::LeftSquareBracket),
|
"[" => Ok(Operator::LeftSquareBracket(100)),
|
||||||
"]" => Ok(Operator::RightSquareBracket),
|
"]" => Ok(Operator::RightSquareBracket),
|
||||||
|
"." => Ok(Operator::Dot(100)),
|
||||||
"\"" => Ok(Operator::DoubleQuotes),
|
"\"" => Ok(Operator::DoubleQuotes),
|
||||||
"'" => Ok(Operator::SingleQuote),
|
"'" => Ok(Operator::SingleQuote),
|
||||||
" " => Ok(Operator::WhiteSpace),
|
" " => Ok(Operator::WhiteSpace),
|
||||||
|
179
src/tree/mod.rs
179
src/tree/mod.rs
@ -31,7 +31,7 @@ impl Tree {
|
|||||||
for (index, cur) in self.raw.chars().enumerate() {
|
for (index, cur) in self.raw.chars().enumerate() {
|
||||||
match cur {
|
match cur {
|
||||||
'(' | ')' | '+' | '-' | '*' | '/' | ',' | ' ' | '!' | '=' | '>' | '<' | '\'' |
|
'(' | ')' | '+' | '-' | '*' | '/' | ',' | ' ' | '!' | '=' | '>' | '<' | '\'' |
|
||||||
'[' | ']' | '%' | '&' | '|' => {
|
'[' | ']' | '.' | '%' | '&' | '|' => {
|
||||||
if !found_quote {
|
if !found_quote {
|
||||||
pos.push(index);
|
pos.push(index);
|
||||||
pos.push(index + 1);
|
pos.push(index + 1);
|
||||||
@ -57,9 +57,9 @@ impl Tree {
|
|||||||
let mut start;
|
let mut start;
|
||||||
let mut end = 0;
|
let mut end = 0;
|
||||||
let mut parenthesis = 0;
|
let mut parenthesis = 0;
|
||||||
let mut square_brackets = 0;
|
|
||||||
let mut quote = None;
|
let mut quote = None;
|
||||||
let mut prev = String::new();
|
let mut prev = String::new();
|
||||||
|
let mut number = String::new();
|
||||||
|
|
||||||
for pos_ref in &self.pos {
|
for pos_ref in &self.pos {
|
||||||
let pos = *pos_ref;
|
let pos = *pos_ref;
|
||||||
@ -72,6 +72,10 @@ impl Tree {
|
|||||||
|
|
||||||
let raw = self.raw[start..end].to_owned();
|
let raw = self.raw[start..end].to_owned();
|
||||||
|
|
||||||
|
if raw.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let operator = Operator::from_str(&raw).unwrap();
|
let operator = Operator::from_str(&raw).unwrap();
|
||||||
match operator {
|
match operator {
|
||||||
Operator::DoubleQuotes | Operator::SingleQuote => {
|
Operator::DoubleQuotes | Operator::SingleQuote => {
|
||||||
@ -96,8 +100,12 @@ impl Tree {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw.is_empty() {
|
if parse_number(&raw).is_some() || operator.is_dot() {
|
||||||
|
number += &raw;
|
||||||
continue;
|
continue;
|
||||||
|
} else if !number.is_empty() {
|
||||||
|
operators.push(Operator::from_str(&number).unwrap());
|
||||||
|
number.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw == "=" {
|
if raw == "=" {
|
||||||
@ -137,19 +145,14 @@ impl Tree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match operator {
|
match operator {
|
||||||
Operator::LeftSquareBracket => {
|
|
||||||
square_brackets += 1;
|
|
||||||
operators.push(Operator::Function("array".to_owned()));
|
|
||||||
operators.push(operator);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Operator::LeftParenthesis => {
|
Operator::LeftParenthesis => {
|
||||||
parenthesis += 1;
|
parenthesis += 1;
|
||||||
|
|
||||||
if !operators.is_empty() {
|
if !operators.is_empty() {
|
||||||
let prev_operator = operators.pop().unwrap();
|
let prev_operator = operators.pop().unwrap();
|
||||||
if prev_operator.is_identifier() {
|
if prev_operator.is_identifier() {
|
||||||
operators.push(Operator::Function(prev_operator.get_identifier()));
|
operators.push(Operator::Function(prev_operator.get_identifier()
|
||||||
|
.to_owned()));
|
||||||
operators.push(operator);
|
operators.push(operator);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
@ -158,7 +161,6 @@ impl Tree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::RightParenthesis => parenthesis -= 1,
|
Operator::RightParenthesis => parenthesis -= 1,
|
||||||
Operator::RightSquareBracket => square_brackets -= 1,
|
|
||||||
Operator::WhiteSpace => continue,
|
Operator::WhiteSpace => continue,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -167,7 +169,11 @@ impl Tree {
|
|||||||
operators.push(operator);
|
operators.push(operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if parenthesis != 0 || square_brackets != 0 {
|
if !number.is_empty() {
|
||||||
|
operators.push(Operator::from_str(&number).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
if parenthesis != 0 {
|
||||||
Err(Error::UnpairedBrackets)
|
Err(Error::UnpairedBrackets)
|
||||||
} else {
|
} else {
|
||||||
self.operators = operators;
|
self.operators = operators;
|
||||||
@ -193,6 +199,8 @@ impl Tree {
|
|||||||
Operator::And(priority) |
|
Operator::And(priority) |
|
||||||
Operator::Or(priority) |
|
Operator::Or(priority) |
|
||||||
Operator::Le(priority) |
|
Operator::Le(priority) |
|
||||||
|
Operator::Dot(priority) |
|
||||||
|
Operator::LeftSquareBracket(priority) |
|
||||||
Operator::Rem(priority) => {
|
Operator::Rem(priority) => {
|
||||||
if !parsing_nodes.is_empty() {
|
if !parsing_nodes.is_empty() {
|
||||||
let prev = parsing_nodes.pop().unwrap();
|
let prev = parsing_nodes.pop().unwrap();
|
||||||
@ -215,15 +223,14 @@ impl Tree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::Function(_) |
|
Operator::Function(_) |
|
||||||
Operator::LeftParenthesis |
|
Operator::LeftParenthesis => parsing_nodes.push(operator.to_node()),
|
||||||
Operator::LeftSquareBracket => parsing_nodes.push(operator.to_node()),
|
|
||||||
Operator::Comma => close_comma(&mut parsing_nodes)?,
|
Operator::Comma => close_comma(&mut parsing_nodes)?,
|
||||||
Operator::RightParenthesis |
|
Operator::RightParenthesis |
|
||||||
Operator::RightSquareBracket => {
|
Operator::RightSquareBracket => {
|
||||||
close_bracket(&mut parsing_nodes, operator.get_left())?
|
close_bracket(&mut parsing_nodes, operator.get_left())?
|
||||||
}
|
}
|
||||||
Operator::Value(_) |
|
Operator::Value(_) |
|
||||||
Operator::Identifier(_) => append_child_to_last_node(&mut parsing_nodes, operator)?,
|
Operator::Identifier(_) => append_value_to_last_node(&mut parsing_nodes, operator)?,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,7 +344,93 @@ impl Tree {
|
|||||||
match value {
|
match value {
|
||||||
Value::Bool(boolean) => Ok(Value::Bool(!boolean)),
|
Value::Bool(boolean) => Ok(Value::Bool(!boolean)),
|
||||||
Value::Null => Ok(Value::Bool(true)),
|
Value::Null => Ok(Value::Bool(true)),
|
||||||
_ => Err(Error::NotBoolean(value)),
|
_ => 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()
|
||||||
|
.find(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()
|
||||||
|
.find(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) => {
|
Operator::Identifier(ref ident) => {
|
||||||
@ -360,21 +453,30 @@ impl Tree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_child_to_last_node(parsing_nodes: &mut Vec<Node>,
|
fn append_value_to_last_node(parsing_nodes: &mut Vec<Node>,
|
||||||
operator: &Operator)
|
operator: &Operator)
|
||||||
-> Result<(), Error> {
|
-> Result<(), Error> {
|
||||||
let mut node = operator.to_node();
|
let mut node = operator.to_node();
|
||||||
node.closed = true;
|
node.closed = true;
|
||||||
|
|
||||||
if let Some(mut prev) = parsing_nodes.pop() {
|
if let Some(mut prev) = parsing_nodes.pop() {
|
||||||
if prev.is_value_or_enough() {
|
if prev.is_dot() {
|
||||||
|
prev.add_child(node);
|
||||||
|
prev.closed = true;
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
} else if prev.is_left_square_bracket() {
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
parsing_nodes.push(node);
|
||||||
|
} else if prev.is_value_or_enough() {
|
||||||
return Err(Error::DuplicateValueNode);
|
return Err(Error::DuplicateValueNode);
|
||||||
} else if prev.is_enough() {
|
} else if prev.is_enough() {
|
||||||
parsing_nodes.push(prev);
|
parsing_nodes.push(prev);
|
||||||
parsing_nodes.push(node);
|
parsing_nodes.push(node);
|
||||||
} else {
|
} else if prev.operator.can_have_child() {
|
||||||
prev.add_child(node);
|
prev.add_child(node);
|
||||||
parsing_nodes.push(prev);
|
parsing_nodes.push(prev);
|
||||||
|
} else {
|
||||||
|
return Err(Error::CanNotAddChild);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parsing_nodes.push(node);
|
parsing_nodes.push(node);
|
||||||
@ -391,8 +493,12 @@ fn get_final_node(mut parsing_nodes: Vec<Node>) -> Result<Node, Error> {
|
|||||||
while parsing_nodes.len() != 1 {
|
while parsing_nodes.len() != 1 {
|
||||||
let last = parsing_nodes.pop().unwrap();
|
let last = parsing_nodes.pop().unwrap();
|
||||||
let mut prev = parsing_nodes.pop().unwrap();
|
let mut prev = parsing_nodes.pop().unwrap();
|
||||||
prev.add_child(last);
|
if prev.operator.can_have_child() {
|
||||||
parsing_nodes.push(prev);
|
prev.add_child(last);
|
||||||
|
parsing_nodes.push(prev);
|
||||||
|
} else {
|
||||||
|
return Err(Error::CanNotAddChild);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(parsing_nodes.pop().unwrap())
|
Ok(parsing_nodes.pop().unwrap())
|
||||||
@ -403,7 +509,14 @@ fn close_bracket(parsing_nodes: &mut Vec<Node>, bracket: Operator) -> Result<(),
|
|||||||
let mut current = parsing_nodes.pop().unwrap();
|
let mut current = parsing_nodes.pop().unwrap();
|
||||||
let mut prev = parsing_nodes.pop().unwrap();
|
let mut prev = parsing_nodes.pop().unwrap();
|
||||||
|
|
||||||
if current.operator == bracket {
|
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() {
|
if prev.is_unclosed_function() {
|
||||||
prev.closed = true;
|
prev.closed = true;
|
||||||
parsing_nodes.push(prev);
|
parsing_nodes.push(prev);
|
||||||
@ -503,9 +616,8 @@ fn rob_to(mut was_robed: Node, mut rober: Node) -> Vec<Node> {
|
|||||||
|
|
||||||
fn find(contexts: &[Context], key: &str) -> Option<Value> {
|
fn find(contexts: &[Context], key: &str) -> Option<Value> {
|
||||||
for context in contexts.iter().rev() {
|
for context in contexts.iter().rev() {
|
||||||
let value = get(context, key);
|
match context.get(key) {
|
||||||
match value {
|
Some(value) => return Some(value.clone()),
|
||||||
Some(_) => return value,
|
|
||||||
None => continue,
|
None => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -513,23 +625,6 @@ fn find(contexts: &[Context], key: &str) -> Option<Value> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(context: &Context, key: &str) -> Option<Value> {
|
|
||||||
let mut keys = key.split('.').collect::<Vec<_>>();
|
|
||||||
let context_key = keys.remove(0);
|
|
||||||
let context_value_option = context.get(context_key);
|
|
||||||
|
|
||||||
if context_value_option.is_none() {
|
|
||||||
None
|
|
||||||
} else if !keys.is_empty() {
|
|
||||||
match context_value_option.unwrap().search(&keys.join(".")) {
|
|
||||||
Some(value) => Some(value.clone()),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(context_value_option.unwrap().clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_range(ident: &str) -> bool {
|
fn is_range(ident: &str) -> bool {
|
||||||
ident.contains("..")
|
ident.contains("..")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user