mirror of
https://github.com/gorules/zen.git
synced 2026-10-04 08:02:28 +00:00
fix: negative value checks (#89)
* fix: negative value checks; * offset unary fix; * disable unary csv test;
This commit is contained in:
@@ -25,7 +25,10 @@ rust_decimal = { workspace = true, features = ["maths-nopanic"] }
|
||||
rust_decimal_macros = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
criterion = { workspace = true }
|
||||
csv = "1.2.2"
|
||||
serde_json5 = "0.1.0"
|
||||
|
||||
[features]
|
||||
default = ["regex-deprecated"]
|
||||
|
||||
@@ -37,7 +37,7 @@ where
|
||||
}
|
||||
|
||||
pub fn parse(&self) -> ParserResult<&'b Node<'b>> {
|
||||
let result = self.parse_expression(0)?;
|
||||
let result = self.expression(0)?;
|
||||
if !self.iterator.is_done() {
|
||||
let token = self.iterator.current();
|
||||
return Err(FailedToParse {
|
||||
@@ -48,8 +48,8 @@ where
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
fn parse_expression(&self, precedence: u8) -> ParserResult<&'b Node<'b>> {
|
||||
let mut node_left = self.parse_primary()?;
|
||||
fn expression(&self, precedence: u8) -> ParserResult<&'b Node<'b>> {
|
||||
let mut node_left = self.primary_expression()?;
|
||||
let mut token = self.iterator.current();
|
||||
|
||||
while !self.iterator.is_done() {
|
||||
@@ -58,8 +58,8 @@ where
|
||||
if op.precedence >= precedence {
|
||||
self.iterator.next()?;
|
||||
let node_right = match op.associativity {
|
||||
Associativity::Left => self.parse_expression(op.precedence + 1)?,
|
||||
_ => self.parse_expression(op.precedence)?,
|
||||
Associativity::Left => self.expression(op.precedence + 1)?,
|
||||
_ => self.expression(op.precedence)?,
|
||||
};
|
||||
|
||||
node_left = self.iterator.node(Node::Binary {
|
||||
@@ -77,36 +77,36 @@ where
|
||||
}
|
||||
|
||||
if precedence == 0 {
|
||||
node_left = self.parse_conditional(node_left)?;
|
||||
node_left = self.conditional(node_left)?;
|
||||
}
|
||||
|
||||
Ok(node_left)
|
||||
}
|
||||
|
||||
fn parse_primary(&self) -> ParserResult<&'b Node<'b>> {
|
||||
fn primary_expression(&self) -> ParserResult<&'b Node<'b>> {
|
||||
let token = self.iterator.current();
|
||||
if token.kind == TokenKind::Operator {
|
||||
if let Some(op) = UNARY_OPERATORS.get(token.value) {
|
||||
self.iterator.next()?;
|
||||
let expr = self.parse_expression(op.precedence)?;
|
||||
let expr = self.expression(op.precedence)?;
|
||||
let node = self.iterator.node(Node::Unary {
|
||||
operator: self.iterator.str_value(token.value),
|
||||
node: expr,
|
||||
})?;
|
||||
|
||||
return self.parse_postfix(node);
|
||||
return self.postfix(node);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(interval_node) = self.parse_interval()? {
|
||||
return self.parse_postfix(interval_node);
|
||||
if let Some(interval_node) = self.interval()? {
|
||||
return self.postfix(interval_node);
|
||||
}
|
||||
|
||||
if token.kind == TokenKind::Bracket && token.value == "(" {
|
||||
self.iterator.next()?;
|
||||
let expr = self.parse_expression(0)?;
|
||||
let expr = self.expression(0)?;
|
||||
self.iterator.expect(TokenKind::Bracket, Some(&[")"]))?;
|
||||
return self.parse_postfix(expr);
|
||||
return self.postfix(expr);
|
||||
}
|
||||
|
||||
if self.depth.get() > 0 {
|
||||
@@ -115,7 +115,7 @@ where
|
||||
self.iterator.next()?;
|
||||
}
|
||||
let node = self.iterator.node(Node::Pointer)?;
|
||||
return self.parse_postfix(node);
|
||||
return self.postfix(node);
|
||||
}
|
||||
} else if token.kind == TokenKind::Operator && (token.value == "#" || token.value == ".") {
|
||||
return Err(UnexpectedToken {
|
||||
@@ -124,10 +124,10 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
self.parse_primary_expression()
|
||||
self.literal()
|
||||
}
|
||||
|
||||
fn parse_conditional(&self, node: &'b Node<'b>) -> ParserResult<&'b Node<'b>> {
|
||||
fn conditional(&self, node: &'b Node<'b>) -> ParserResult<&'b Node<'b>> {
|
||||
let mut nd = self.iterator.node(node.clone())?;
|
||||
let mut expr1: &'b Node;
|
||||
let mut expr2: &'b Node;
|
||||
@@ -139,13 +139,13 @@ where
|
||||
|
||||
let token = self.iterator.current();
|
||||
if token.kind != TokenKind::Operator && token.value != ":" {
|
||||
expr1 = self.parse_expression(0)?;
|
||||
expr1 = self.expression(0)?;
|
||||
self.iterator.expect(TokenKind::Operator, Some(&[":"]))?;
|
||||
expr2 = self.parse_expression(0)?;
|
||||
expr2 = self.expression(0)?;
|
||||
} else {
|
||||
self.iterator.next()?;
|
||||
expr1 = node;
|
||||
expr2 = self.parse_expression(0)?;
|
||||
expr2 = self.expression(0)?;
|
||||
}
|
||||
|
||||
nd = self.iterator.node(Node::Conditional {
|
||||
@@ -158,7 +158,7 @@ where
|
||||
Ok(nd)
|
||||
}
|
||||
|
||||
fn parse_primary_expression(&self) -> ParserResult<&'b Node<'b>> {
|
||||
fn literal(&self) -> ParserResult<&'b Node<'b>> {
|
||||
let node: &'b Node;
|
||||
let token = self.iterator.current();
|
||||
|
||||
@@ -168,14 +168,14 @@ where
|
||||
match token.value {
|
||||
"true" | "false" => return self.iterator.bool(token),
|
||||
"null" => return self.iterator.null(token),
|
||||
_ => node = self.parse_identifier_expression(token)?,
|
||||
_ => node = self.identifier_expression(token)?,
|
||||
}
|
||||
}
|
||||
TokenKind::Number => return self.iterator.number(token),
|
||||
TokenKind::String => return self.iterator.string(token),
|
||||
_ => {
|
||||
if token.kind == TokenKind::Bracket && token.value == "[" {
|
||||
node = self.parse_array(token)?;
|
||||
node = self.array(token)?;
|
||||
} else {
|
||||
return Err(UnexpectedToken {
|
||||
expected: "identifier, string, number or opening bracket".to_string(),
|
||||
@@ -185,10 +185,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
self.parse_postfix(node)
|
||||
self.postfix(node)
|
||||
}
|
||||
|
||||
fn parse_interval(&self) -> ParserResult<Option<&'b Node<'b>>> {
|
||||
fn interval(&self) -> ParserResult<Option<&'b Node<'b>>> {
|
||||
// Performance optimisation: skip if expression does not contain an interval for faster evaluation
|
||||
if !self.iterator.has_interval() {
|
||||
return Ok(None);
|
||||
@@ -205,7 +205,7 @@ where
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let Ok(left) = self.parse_primary_expression() else {
|
||||
let Ok(left) = self.primary_expression() else {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -215,7 +215,7 @@ where
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let Ok(right) = self.parse_primary_expression() else {
|
||||
let Ok(right) = self.primary_expression() else {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -236,7 +236,8 @@ where
|
||||
|
||||
Ok(Some(interval_node))
|
||||
}
|
||||
fn parse_identifier_expression(&self, token: &Token) -> ParserResult<&'b Node<'b>> {
|
||||
|
||||
fn identifier_expression(&self, token: &Token) -> ParserResult<&'b Node<'b>> {
|
||||
if self.iterator.current().kind != TokenKind::Bracket
|
||||
|| self.iterator.current().value != "("
|
||||
{
|
||||
@@ -253,7 +254,7 @@ where
|
||||
|
||||
return match builtin.arity {
|
||||
Arity::Single => {
|
||||
let arg = self.parse_expression(0)?;
|
||||
let arg = self.expression(0)?;
|
||||
self.iterator.expect(TokenKind::Bracket, Some(&[")"]))?;
|
||||
|
||||
self.iterator.node(Node::BuiltIn {
|
||||
@@ -262,9 +263,9 @@ where
|
||||
})
|
||||
}
|
||||
Arity::Dual => {
|
||||
let arg1 = self.parse_expression(0)?;
|
||||
let arg1 = self.expression(0)?;
|
||||
self.iterator.expect(TokenKind::Operator, Some(&[","]))?;
|
||||
let arg2 = self.parse_expression(0)?;
|
||||
let arg2 = self.expression(0)?;
|
||||
self.iterator.expect(TokenKind::Bracket, Some(&[")"]))?;
|
||||
|
||||
self.iterator.node(Node::BuiltIn {
|
||||
@@ -273,9 +274,9 @@ where
|
||||
})
|
||||
}
|
||||
Arity::Closure => {
|
||||
let arg1 = self.parse_expression(0)?;
|
||||
let arg1 = self.expression(0)?;
|
||||
self.iterator.expect(TokenKind::Operator, Some(&[","]))?;
|
||||
let arg2 = self.parse_closure()?;
|
||||
let arg2 = self.closure()?;
|
||||
self.iterator.expect(TokenKind::Bracket, Some(&[")"]))?;
|
||||
|
||||
self.iterator.node(Node::BuiltIn {
|
||||
@@ -286,12 +287,12 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
fn parse_array(&self, _token: &Token) -> ParserResult<&'b Node<'b>> {
|
||||
fn array(&self, _token: &Token) -> ParserResult<&'b Node<'b>> {
|
||||
let mut nodes = Vec::new();
|
||||
|
||||
self.iterator.expect(TokenKind::Bracket, Some(&["["]))?;
|
||||
while self.iterator.current().kind != TokenKind::Bracket
|
||||
&& self.iterator.current().value != "]"
|
||||
|
||||
while !(self.iterator.current().kind == TokenKind::Bracket
|
||||
&& self.iterator.current().value == "]")
|
||||
{
|
||||
if !nodes.is_empty() {
|
||||
self.iterator.expect(TokenKind::Operator, Some(&[","]))?;
|
||||
@@ -300,7 +301,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
nodes.push(self.parse_primary()?);
|
||||
nodes.push(self.expression(0)?);
|
||||
}
|
||||
|
||||
self.iterator.expect(TokenKind::Bracket, Some(&["]"]))?;
|
||||
@@ -309,15 +310,15 @@ where
|
||||
self.iterator.node(node)
|
||||
}
|
||||
|
||||
fn parse_closure(&self) -> ParserResult<&'b Node<'b>> {
|
||||
fn closure(&self) -> ParserResult<&'b Node<'b>> {
|
||||
self.depth.set(self.depth.get() + 1);
|
||||
let node = self.parse_expression(0)?;
|
||||
let node = self.expression(0)?;
|
||||
self.depth.set(self.depth.get() - 1);
|
||||
|
||||
return self.iterator.node(Node::Closure(node));
|
||||
}
|
||||
|
||||
fn parse_postfix(&self, node: &'b Node<'b>) -> ParserResult<&'b Node<'b>> {
|
||||
fn postfix(&self, node: &'b Node<'b>) -> ParserResult<&'b Node<'b>> {
|
||||
let mut postfix_token = self.iterator.current();
|
||||
let mut nd = self.iterator.node(node.clone())?;
|
||||
|
||||
@@ -353,13 +354,13 @@ where
|
||||
c = self.iterator.current();
|
||||
|
||||
if c.kind != TokenKind::Bracket && c.value != "]" {
|
||||
to = Some(self.parse_expression(0)?);
|
||||
to = Some(self.expression(0)?);
|
||||
}
|
||||
|
||||
nd = self.iterator.node(Node::Slice { node: nd, to, from })?;
|
||||
self.iterator.expect(TokenKind::Bracket, Some(&["]"]))?;
|
||||
} else {
|
||||
from = Some(self.parse_expression(0)?);
|
||||
from = Some(self.expression(0)?);
|
||||
c = self.iterator.current();
|
||||
|
||||
if c.kind == TokenKind::Operator && c.value == ":" {
|
||||
@@ -367,7 +368,7 @@ where
|
||||
c = self.iterator.current();
|
||||
|
||||
if c.kind != TokenKind::Bracket && c.value != "]" {
|
||||
to = Some(self.parse_expression(0)?);
|
||||
to = Some(self.expression(0)?);
|
||||
}
|
||||
|
||||
nd = self.iterator.node(Node::Slice { node: nd, from, to })?;
|
||||
|
||||
@@ -36,7 +36,7 @@ pub(crate) const BUILT_INS: Map<&'static str, BuiltIn> = phf_map! {
|
||||
"weekdayString" => BuiltIn { arity: Arity::Single },
|
||||
};
|
||||
|
||||
pub(crate) const OPERATORS: Map<&'static str, Operator> = phf_map! {
|
||||
pub(crate) const STANDARD_OPERATORS: Map<&'static str, Operator> = phf_map! {
|
||||
"or" => Operator { precedence: 10, associativity: Left },
|
||||
"and" => Operator { precedence: 15, associativity: Left },
|
||||
"==" => Operator { precedence: 20, associativity: Left },
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::lexer::token::{Token, TokenKind};
|
||||
use crate::parser::definitions::{Arity, Associativity};
|
||||
use crate::parser::error::{ParserError, ParserResult};
|
||||
use crate::parser::iter::ParserIterator;
|
||||
use crate::parser::unary::constants::{BUILT_INS, OPERATORS};
|
||||
use crate::parser::unary::constants::{BUILT_INS, STANDARD_OPERATORS};
|
||||
|
||||
mod constants;
|
||||
|
||||
@@ -31,10 +31,10 @@ where
|
||||
}
|
||||
|
||||
pub fn parse(&self) -> ParserResult<&'b Node<'b>> {
|
||||
self.parse_expression(0, true)
|
||||
self.expression(0, true)
|
||||
}
|
||||
|
||||
fn parse_expression(&self, precedence: u8, root: bool) -> ParserResult<&'b Node<'b>> {
|
||||
fn expression(&self, precedence: u8, root: bool) -> ParserResult<&'b Node<'b>> {
|
||||
let mut node_left: &'b Node<'b> = self.iterator.node(MAIN_NODE)?;
|
||||
let mut token = self.iterator.current();
|
||||
|
||||
@@ -43,7 +43,7 @@ where
|
||||
TokenKind::Operator => {
|
||||
if token.value == "," {
|
||||
self.iterator.next()?;
|
||||
let node_right: &'b Node<'b> = self.parse_expression(0, true)?;
|
||||
let node_right: &'b Node<'b> = self.expression(0, true)?;
|
||||
|
||||
token = self.iterator.current();
|
||||
node_left = self.iterator.node(Node::Binary {
|
||||
@@ -51,17 +51,12 @@ where
|
||||
operator: "or",
|
||||
right: node_right,
|
||||
})?;
|
||||
continue;
|
||||
} else if let Some(op) = OPERATORS.get(token.value) {
|
||||
} else if let Some(op) = STANDARD_OPERATORS.get(token.value) {
|
||||
if op.precedence >= precedence {
|
||||
self.iterator.next()?;
|
||||
let node_right = match op.associativity {
|
||||
Associativity::Left => {
|
||||
self.parse_expression(op.precedence + 1, false)?
|
||||
}
|
||||
Associativity::Right => {
|
||||
self.parse_expression(op.precedence, false)?
|
||||
}
|
||||
Associativity::Left => self.expression(op.precedence + 1, false)?,
|
||||
Associativity::Right => self.expression(op.precedence, false)?,
|
||||
};
|
||||
|
||||
node_left = self.iterator.node(Node::Binary {
|
||||
@@ -70,7 +65,6 @@ where
|
||||
right: node_right,
|
||||
})?;
|
||||
token = self.iterator.current();
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
return Err(ParserError::FailedToParse {
|
||||
@@ -82,7 +76,7 @@ where
|
||||
}
|
||||
}
|
||||
TokenKind::Identifier | TokenKind::String | TokenKind::Number => {
|
||||
let node_right = self.parse_primary()?;
|
||||
let node_right = self.literal()?;
|
||||
if !root {
|
||||
return Ok(node_right);
|
||||
}
|
||||
@@ -93,12 +87,11 @@ where
|
||||
operator: "==",
|
||||
right: node_right,
|
||||
})?;
|
||||
continue;
|
||||
}
|
||||
TokenKind::Bracket => {
|
||||
let node_right: &Node;
|
||||
|
||||
if let Some(interval) = self.parse_interval(node_left)? {
|
||||
if let Some(interval) = self.interval(node_left)? {
|
||||
node_left = interval;
|
||||
} else if token.value == "[" {
|
||||
let should_wrap = !self.iterator.lookup_back(
|
||||
@@ -106,7 +99,7 @@ where
|
||||
TokenKind::Operator,
|
||||
Some(&["not in", "in"]),
|
||||
);
|
||||
node_right = self.parse_array(token)?;
|
||||
node_right = self.array(token)?;
|
||||
|
||||
if should_wrap {
|
||||
node_left = self.iterator.node(Node::Binary {
|
||||
@@ -125,8 +118,6 @@ where
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +125,7 @@ where
|
||||
Ok(node_left)
|
||||
}
|
||||
|
||||
fn parse_interval(&self, node: &'b Node<'b>) -> ParserResult<Option<&'b Node<'b>>> {
|
||||
fn interval(&self, node: &'b Node<'b>) -> ParserResult<Option<&'b Node<'b>>> {
|
||||
// Performance optimisation: skip if expression does not contain an interval for faster evaluation
|
||||
if !self.iterator.has_interval() {
|
||||
return Ok(None);
|
||||
@@ -157,7 +148,7 @@ where
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Ok(left) = self.parse_primary() else {
|
||||
let Ok(left) = self.literal() else {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -167,7 +158,7 @@ where
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Ok(right) = self.parse_primary() else {
|
||||
let Ok(right) = self.literal() else {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -197,7 +188,7 @@ where
|
||||
Ok(Some(interval_node))
|
||||
}
|
||||
|
||||
fn parse_array(&self, _token: &Token) -> ParserResult<&'b Node<'b>> {
|
||||
fn array(&self, _token: &Token) -> ParserResult<&'b Node<'b>> {
|
||||
let mut nodes = Vec::new();
|
||||
|
||||
self.iterator.expect(TokenKind::Bracket, Some(&["["]))?;
|
||||
@@ -211,7 +202,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
nodes.push(self.parse_primary()?);
|
||||
nodes.push(self.literal()?);
|
||||
}
|
||||
|
||||
self.iterator.expect(TokenKind::Bracket, Some(&["]"]))?;
|
||||
@@ -219,7 +210,7 @@ where
|
||||
self.iterator.node(node)
|
||||
}
|
||||
|
||||
fn parse_primary(&self) -> ParserResult<&'b Node<'b>> {
|
||||
fn literal(&self) -> ParserResult<&'b Node<'b>> {
|
||||
let token = self.iterator.current();
|
||||
|
||||
match token.kind {
|
||||
@@ -228,7 +219,7 @@ where
|
||||
match token.value {
|
||||
"true" | "false" => self.iterator.bool(token),
|
||||
"null" => self.iterator.null(token),
|
||||
_ => self.parse_prebuilt(token),
|
||||
_ => self.builtin(token),
|
||||
}
|
||||
}
|
||||
TokenKind::Number => self.iterator.number(token),
|
||||
@@ -240,7 +231,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_prebuilt(&self, token: &Token<'a>) -> ParserResult<&'b Node<'b>> {
|
||||
fn builtin(&self, token: &Token<'a>) -> ParserResult<&'b Node<'b>> {
|
||||
let current_token = self.iterator.current();
|
||||
let valid_token = current_token.kind == TokenKind::Bracket && current_token.value == "(";
|
||||
|
||||
@@ -260,7 +251,7 @@ where
|
||||
|
||||
match built_in.arity {
|
||||
Arity::Single => {
|
||||
let arg = self.parse_primary()?;
|
||||
let arg = self.literal()?;
|
||||
let node = self.iterator.node(Node::BuiltIn {
|
||||
name: self.iterator.str_value(token.value),
|
||||
arguments: self.bump.alloc_slice_copy(&[arg]),
|
||||
|
||||
@@ -421,7 +421,7 @@ impl<'a> VM<'a> {
|
||||
let var = self.pop()?;
|
||||
|
||||
match var {
|
||||
Number(a) => self.stack.push(self.bump.alloc(Number(a.round()))),
|
||||
Number(a) => self.stack.push(self.bump.alloc(Number(a.ceil()))),
|
||||
_ => {
|
||||
return Err(OpcodeErr {
|
||||
opcode: "Ceil".into(),
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
expression (string);input (json 5);output (json 5)
|
||||
|
||||
# Boolean
|
||||
true and true;;true
|
||||
true and false;;false
|
||||
false and true;;false
|
||||
false and false;;false
|
||||
true or true;;true
|
||||
true or false;;true
|
||||
false or true;;true
|
||||
false or false;;false
|
||||
not true;;false
|
||||
not false;;true
|
||||
x == true;{ "x": true };true
|
||||
x == true;{ "x": false };false
|
||||
x == false;{ "x": true };false
|
||||
x == false;{ "x": false };true
|
||||
x and true;{ "x": true };true
|
||||
x and true;{ "x": false };false
|
||||
x or true;{ "x": true };true
|
||||
x or true;{ "x": false };true
|
||||
not x;{ "x": true };false
|
||||
not x;{ "x": false };true
|
||||
true and x;{ "x": true };true
|
||||
true and x;{ "x": false };false
|
||||
true or x;{ "x": true };true
|
||||
true or x;{ "x": false };true
|
||||
true and not x;{ "x": true };false
|
||||
true and not x;{ "x": false };true
|
||||
false or x;{ "x": true };true
|
||||
false or x;{ "x": false };false
|
||||
false and x;{ "x": true };false
|
||||
false and x;{ "x": false };false
|
||||
false or not x;{ "x": true };false
|
||||
false or not x;{ "x": false };true
|
||||
not x and true;{ "x": true };false
|
||||
not x and true;{ "x": false };true
|
||||
x or not true;{ "x": true };true
|
||||
x or not true;{ "x": false };false
|
||||
x or not false;{ "x": true };true
|
||||
x or not false;{ "x": false };true
|
||||
|
||||
# Numbers
|
||||
1 == 1;;true
|
||||
1 == 2;;false
|
||||
1 != 2;;true
|
||||
1 != 1;;false
|
||||
1 < 2;;true
|
||||
1 < 1;;false
|
||||
1 <= 1;;true
|
||||
2 <= 1;;false
|
||||
2 > 1;;true
|
||||
1 > 1;;false
|
||||
1 >= 1;;true
|
||||
1 >= 2;;false
|
||||
-1 == -1;;true
|
||||
-1 == 1;;false
|
||||
-1 != 1;;true
|
||||
-1 != -1;;false
|
||||
-1 < 1;;true
|
||||
-1 < -1;;false
|
||||
-1 <= -1;;true
|
||||
1 <= -1;;false
|
||||
1 > -1;;true
|
||||
-1 > -1;;false
|
||||
-1 >= -1;;true
|
||||
-1 >= 1;;false
|
||||
1 + 2 == 3;;true
|
||||
1 + 2 == 4;;false
|
||||
1 - 2 == -1;;true
|
||||
1 - 2 == 0;;false
|
||||
2 * 3 == 6;;true
|
||||
2 * 3 == 7;;false
|
||||
6 / 3 == 2;;true
|
||||
6 / 3 == 2.5;;false
|
||||
5 % 2 == 1;;true
|
||||
6 % 2 == 0;;true
|
||||
-5 + 5 == 0;;true
|
||||
-5 * 5 == -25;;true
|
||||
-5 * -5 == 25;;true
|
||||
abs(-5) == 5;;true
|
||||
abs(5) == 5;;true
|
||||
2 ^ 3 == 8;;true
|
||||
2 ^ -1 == 0.5;;true
|
||||
max([3, 5]) == 5;;true
|
||||
max([-3, -5]) == -3;;true
|
||||
min([3, 5]) == 3;;true
|
||||
min([-3, -5]) == -5;;true
|
||||
3 + 4 * 2;;11
|
||||
(3 + 4) * 2;;14
|
||||
(10 - 4) / 2;;3
|
||||
abs(-5);;5
|
||||
sum([1, 2, 3, 4, 5]);;15
|
||||
avg([10, 20, 30]);;20
|
||||
min([5, 8, 2, 11, 7]);;2
|
||||
max([5, 8, 2, 11, 7]);;11
|
||||
floor(4.8);;4
|
||||
ceil(4.1);;5
|
||||
round(3.5);;4
|
||||
rand(100) >= 0 and rand(100) <= 100;;true
|
||||
median([4, 2, 7, 5, 3]);;4
|
||||
mode([1, 2, 2, 3, 3, 3, 4, 4, 4, 4]);;4
|
||||
5 % 2 == 1;;true
|
||||
10 % 4 == 2;;true
|
||||
true ? 10 == 10 : 20 == 30;;true
|
||||
false ? 10 == 10 : 20 == 30;;false
|
||||
true ? 10 == 20 : false ? 30 == 40 : true ? 50 == 60 : 70 == 80;;false
|
||||
|
||||
# String
|
||||
'hello' + " " + "world";; 'hello world'
|
||||
len("Hello, World!");; 13
|
||||
lower("Hello, World!");; "hello, world!"
|
||||
upper("Hello, World!");; "HELLO, WORLD!"
|
||||
startsWith("Hello, World!", "Hello");; true
|
||||
startsWith("Hello, World!", "World");; false
|
||||
endsWith("Hello, World!", "World!");; true
|
||||
endsWith("Hello, World!", "Hello!");; false
|
||||
contains("Hello, World!", "lo");; true
|
||||
contains("Hello, World!", "foo");; false
|
||||
matches("Hello, World!", "H[a-z]+, W[a-z]+!");; true
|
||||
matches("Hello, World!", "[0-9]+");; false
|
||||
extract("2022-09-18", "(\d{4})-(\d{2})-(\d{2})");; ["2022-09-18", "2022", "09", "18"]
|
||||
|
||||
# Array
|
||||
len([1, 2, 3, 4, 5]);; 5
|
||||
sum([1, 2, 3, 4, 5]);; 15
|
||||
avg([10, 20, 30]);; 20
|
||||
min([5, 8, 2, 11, 7]);; 2
|
||||
max([5, 8, 2, 11, 7]);; 11
|
||||
contains([1, 2, 3, 4, 5], 3);; true
|
||||
contains([1, 2, 3, 4, 5], 6);; false
|
||||
count([1, 2, 3, 4, 5, 2], # == 2);; 2
|
||||
filter([1, 2, 3, 4, 5], # > 3);; [4, 5]
|
||||
map([1, 2, 3, 4, 5], # * 2);; [2, 4, 6, 8, 10]
|
||||
one([1, 2, 3, 4, 5], # == 3);; true
|
||||
one([1, 2, 3, 4, 5], # == 6);; false
|
||||
none([1, 2, 3, 4, 5], # > 5);; true
|
||||
some([1, 2, 3, 4, 5], # > 3);; true
|
||||
flatMap([[1, 2], [3, 4], [5, 6]], #);; [1, 2, 3, 4, 5, 6]
|
||||
|
||||
# Dates
|
||||
date("2023-09-18T12:00:00Z");; 1695038400
|
||||
date("2023-09-18T00:00:00Z");; 1694995200
|
||||
time("2023-09-18T12:00:00Z");; 43200
|
||||
time("2023-09-18T00:00:00Z");; 0
|
||||
duration("1h 30m");; 5400
|
||||
year("2023-09-18");; 2023
|
||||
monthOfYear("2023-09-18");; 9
|
||||
monthString("2023-09-18");; "Sep"
|
||||
dayOfMonth("2023-09-18");; 18
|
||||
dayOfWeek("2023-09-18");; 1
|
||||
dayOfYear("2023-09-18");; 261
|
||||
weekOfYear("2023-09-18");; 38
|
||||
weekdayString("2023-09-18");; "Mon"
|
||||
|
||||
# Decimal precision
|
||||
0.1 + 0.2 == 0.3;; true
|
||||
0.1 + 0.2;; 0.3
|
||||
0.1 * 0.2 == 0.02;; true
|
||||
0.1 * 0.2;; 0.02
|
||||
0.3 - 0.1 == 0.2;; true
|
||||
0.3 - 0.1;; 0.2
|
||||
0.3 / 0.1 == 3.0;; true
|
||||
0.3 / 0.1;; 3
|
||||
|
||||
# More complex formulas
|
||||
(10 + 5) * 3 / 2 > 15;; true
|
||||
(100 - 25) / (5 * 2) < 10;; true
|
||||
1000 / (10 - 2) + 50 == 175;; true
|
||||
(3 + 4) * (8 - 6) == 14;; true
|
||||
10^3 != 999;; true
|
||||
1000 / 10^3 <= 1.1;; true
|
||||
abs(-20) > 10;; true
|
||||
floor(8.9) == 8;; true
|
||||
ceil(8.1) == 9;; true
|
||||
round(7.6) == 8;; true
|
||||
10 % 3 != 0;; true
|
||||
-8 + 2^3 == 0;; true
|
||||
2 * -(-5) == 10;; true
|
||||
20 / (5 / 2) == 8.0;; true
|
||||
(4 + 2) * 3 - (5 / 2) + 1 < 18;; true
|
||||
5^2 + 3 * 4 - 6 / 2 > 20;; true
|
||||
4^3 - 2 * 5 + 7 / 2 == 57.5;; true
|
||||
abs(-7 + 4) * (8 - 6^2) < 34;; true
|
||||
5 in [1..10];; true
|
||||
5 in (1..10);; true
|
||||
5 not in [1..10);; false
|
||||
5 not in (1..10);; false
|
||||
-5 in [-10..-1];; true
|
||||
-5 in [-10..-1);; true
|
||||
-5 not in [-10..-1];; false
|
||||
-5 not in [-10..-1);; false
|
||||
-5 in [-5..5];; true
|
||||
-5 in (-5..5);; false
|
||||
-5 not in [-5..5);; false
|
||||
-5 not in (-5..5);; true
|
||||
10 > 5;; true
|
||||
5 < 10;; true
|
||||
10 >= 10;; true
|
||||
5 <= 5;; true
|
||||
(1 + 2) == 3;; true
|
||||
(1 + 2) != 4;; true
|
||||
(1 + 2) > 2;; true
|
||||
(1 + 2) < 5;; true
|
||||
|
Can't render this file because it contains an unexpected character in line 14 and column 13.
|
@@ -0,0 +1,42 @@
|
||||
expression (string);input (json 5);output (json 5)
|
||||
|
||||
# Boolean
|
||||
true;{ "$": true }; true
|
||||
true;{ "$": false }; false
|
||||
false;{ "$": true }; false
|
||||
false;{ "$": false }; true
|
||||
|
||||
# Number
|
||||
> 5;{$: 10};true
|
||||
< 10;{$: 5};true
|
||||
>= 10;{$: 10};true
|
||||
<= 5;{$: 5};true
|
||||
[-10..0];{$: 5};true
|
||||
(-10..0];{$: 5};false
|
||||
[-10..0);{$: 5};true
|
||||
(-10..0);{$: 5};false
|
||||
[-15..-5];{$: 5};false
|
||||
[-5..5];{$: 5};true
|
||||
[-6..6];{$: 5};false
|
||||
[-5..5];{$: 5};true
|
||||
[0..0];{$: 5};true
|
||||
> 5 and < 10;{$: 5};true
|
||||
<= 10 and > 5;{$: 5};true
|
||||
>= 10 or < 5;{$: 10};true
|
||||
<= 5 or >= 10;{$: 10};true
|
||||
[-10..0] and > -5;{$: -5};true
|
||||
(-10..0] or > 0;{$: 5};true
|
||||
[-10..0) and > -5;{$: -5};false
|
||||
(-10..0) or > 0;{$: 5};true
|
||||
[-15..-5] and <= -10;{$: -5};false
|
||||
[-5..5] or > 0;{$: 5};true
|
||||
[-5..5] or < 0;{$: 5};false
|
||||
[-5..5] and > 0;{$: 5};true
|
||||
[0..0] and < 5;{$: 5};true
|
||||
> 10 or < -5;{$: 5};true
|
||||
> 10, < -5;{$: 5};true
|
||||
<= -10 and >= -15;{$: -5};true
|
||||
|
||||
# String
|
||||
'GB','US';{$: 'US'};true
|
||||
'GB','US';{$: 'FR'};false
|
||||
|
Can't render this file because it contains an unexpected character in line 4 and column 8.
|
@@ -1,4 +1,6 @@
|
||||
use anyhow::Context;
|
||||
use bumpalo::Bump;
|
||||
use std::ops::Index;
|
||||
|
||||
use serde_json::{json, Value};
|
||||
|
||||
@@ -706,3 +708,40 @@ fn isolate_test_decimals() {
|
||||
|
||||
assert_eq!(result, Value::from(9223372036854775807i64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standard_csv() {
|
||||
let csv_data = include_str!("data/standard.csv");
|
||||
let mut r = csv::ReaderBuilder::new()
|
||||
.delimiter(b';')
|
||||
.from_reader(csv_data.as_bytes());
|
||||
|
||||
while let Some(maybe_row) = r.records().next() {
|
||||
let Ok(row) = maybe_row else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let (expression, input_str, output_str) = (row.index(0), row.index(1), row.index(2));
|
||||
if expression.starts_with("#") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let output: Value = serde_json5::from_str(output_str).unwrap();
|
||||
|
||||
let isolate = Isolate::default();
|
||||
if !input_str.is_empty() {
|
||||
let input: Value = serde_json5::from_str(input_str).unwrap();
|
||||
isolate.inject_env(&input);
|
||||
}
|
||||
|
||||
let result = isolate
|
||||
.run_standard(expression)
|
||||
.context(format!("Expression: {expression}"))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
result, output,
|
||||
"Expression {expression}. Expected: {output}, got: {result}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user