fix: negative value checks (#89)

* fix: negative value checks;

* offset unary fix;

* disable unary csv test;
This commit is contained in:
stefan-gorules
2023-09-19 11:23:31 +02:00
committed by GitHub
parent 929e9baaa5
commit 7b17575a66
8 changed files with 353 additions and 73 deletions
+3
View File
@@ -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"]
+44 -43
View File
@@ -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 },
+19 -28
View File
@@ -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]),
+1 -1
View File
@@ -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(),
+204
View File
@@ -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.
+42
View File
@@ -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.
+39
View File
@@ -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}"
);
}
}