mirror of
https://github.com/gorules/zen.git
synced 2026-10-04 08:02:28 +00:00
fix: expression date range; (#49)
* fix: expression date range; * restore tests; update v8; * perf: add precomputation for interval; add standard tests;
This commit is contained in:
@@ -18,10 +18,10 @@ async-trait = { workspace = true }
|
||||
bincode = { workspace = true, optional = true }
|
||||
serde_json = { workspace = true, features = ["arbitrary_precision"] }
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
serde_v8 = { version = "0.100.0" }
|
||||
serde_v8 = { version = "0.103.0" }
|
||||
once_cell = { version = "1.17.2" }
|
||||
futures = "0.3.28"
|
||||
v8 = { version = "0.73.0" }
|
||||
v8 = { version = "0.74.0" }
|
||||
zen-expression = { path = "../expression", version = "0.5.2" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -17,25 +17,48 @@ pub(crate) struct ParserIterator<'a, 'b> {
|
||||
position: Cell<usize>,
|
||||
bump: &'b Bump,
|
||||
is_done: Cell<bool>,
|
||||
has_interval: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b> ParserIterator<'a, 'b> {
|
||||
pub fn try_new(tokens: &'a Vec<Token<'a>>, bump: &'b Bump) -> Result<Self, ParserError> {
|
||||
let current = tokens.get(0).ok_or(ParserError::TokenOutOfBounds)?;
|
||||
let has_interval = tokens
|
||||
.iter()
|
||||
.any(|t| t.kind == TokenKind::Operator && t.value == "..");
|
||||
|
||||
Ok(Self {
|
||||
tokens,
|
||||
bump,
|
||||
has_interval,
|
||||
current: Cell::new(current),
|
||||
position: Cell::new(0),
|
||||
is_done: Cell::new(false),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_interval(&self) -> bool {
|
||||
self.has_interval
|
||||
}
|
||||
|
||||
pub fn current(&self) -> &'a Token<'a> {
|
||||
self.current.get()
|
||||
}
|
||||
|
||||
pub fn position(&self) -> usize {
|
||||
self.position.get()
|
||||
}
|
||||
|
||||
pub fn set_position(&self, position: usize) -> ParserResult<()> {
|
||||
let Some(token) = self.tokens.get(position) else {
|
||||
return Err(ParserError::TokenOutOfBounds)
|
||||
};
|
||||
|
||||
self.position.set(position);
|
||||
self.current.set(token);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_done(&self) -> bool {
|
||||
self.is_done.get()
|
||||
}
|
||||
@@ -66,6 +89,7 @@ impl<'a, 'b> ParserIterator<'a, 'b> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn lookup(&self, dx: usize, kind: TokenKind, values: TokenValues<'a>) -> bool {
|
||||
self.token_cmp_at_bool(self.position.get() + dx, kind, values)
|
||||
}
|
||||
|
||||
@@ -178,21 +178,43 @@ where
|
||||
}
|
||||
|
||||
fn parse_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);
|
||||
}
|
||||
|
||||
if self.iterator.current().kind != TokenKind::Bracket {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if !self.iterator.lookup(2, TokenKind::Operator, Some(&[".."])) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let initial_position = self.iterator.position();
|
||||
let left_bracket = self.iterator.current().value;
|
||||
self.iterator.expect(TokenKind::Bracket, None)?;
|
||||
let left = self.parse_primary_expression()?;
|
||||
self.iterator.expect(TokenKind::Operator, Some(&[".."]))?;
|
||||
let right = self.parse_primary_expression()?;
|
||||
if let Err(_) = self.iterator.expect(TokenKind::Bracket, None) {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let Ok(left) = self.parse_primary_expression() else {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
if let Err(_) = self.iterator.expect(TokenKind::Operator, Some(&[".."])) {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let Ok(right) = self.parse_primary_expression() else {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let right_bracket = self.iterator.current().value;
|
||||
self.iterator.expect(TokenKind::Bracket, None)?;
|
||||
|
||||
if let Err(_) = self.iterator.expect(TokenKind::Bracket, None) {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let interval_node = self.iterator.node(Node::Interval {
|
||||
left_bracket: self.iterator.str_value(left_bracket),
|
||||
|
||||
@@ -135,27 +135,49 @@ where
|
||||
}
|
||||
|
||||
fn parse_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);
|
||||
}
|
||||
|
||||
let current_token = self.iterator.current();
|
||||
if current_token.kind != TokenKind::Bracket {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if !self.iterator.lookup(2, TokenKind::Operator, Some(&[".."])) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let initial_position = self.iterator.position();
|
||||
let should_wrap =
|
||||
!self
|
||||
.iterator
|
||||
.lookup_back(1, TokenKind::Operator, Some(&["not in", "in"]));
|
||||
|
||||
let left_bracket = self.iterator.current().value;
|
||||
self.iterator.expect(TokenKind::Bracket, None)?;
|
||||
let left = self.parse_primary()?;
|
||||
self.iterator.expect(TokenKind::Operator, Some(&[".."]))?;
|
||||
let right = self.parse_primary()?;
|
||||
if let Err(_) = self.iterator.expect(TokenKind::Bracket, None) {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Ok(left) = self.parse_primary() else {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
if let Err(_) = self.iterator.expect(TokenKind::Operator, Some(&[".."])) {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Ok(right) = self.parse_primary() else {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let right_bracket = self.iterator.current().value;
|
||||
self.iterator.expect(TokenKind::Bracket, None)?;
|
||||
|
||||
if let Err(_) = self.iterator.expect(TokenKind::Bracket, None) {
|
||||
self.iterator.set_position(initial_position)?;
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let interval_node = self.iterator.node(Node::Interval {
|
||||
left,
|
||||
|
||||
@@ -128,6 +128,14 @@ fn isolate_standard_test() {
|
||||
expr: r#"date("2022-04-04") > date("2022-03-04")"#,
|
||||
result: json!(true),
|
||||
},
|
||||
TestCase {
|
||||
expr: r#"date("2022-04-04") in [date("2022-03-04")..date("2022-04-04")]"#,
|
||||
result: json!(true),
|
||||
},
|
||||
TestCase {
|
||||
expr: r#"date("2022-04-04") in [date("2022-03-04")..date("2022-04-04"))"#,
|
||||
result: json!(false),
|
||||
},
|
||||
TestCase {
|
||||
expr: r#"time("2022-04-04T21:48:30Z") > time("2022-05-04 21:48:20")"#,
|
||||
result: json!(true),
|
||||
@@ -513,6 +521,26 @@ fn isolate_unary_tests() {
|
||||
result: json!(true),
|
||||
}]),
|
||||
},
|
||||
UnaryTestEnv {
|
||||
env: json!({
|
||||
"input": "2023-01-02"
|
||||
}),
|
||||
reference: "date(input)",
|
||||
cases: Vec::from([
|
||||
TestCase {
|
||||
expr: r#"[date("2023-01-01")..date("2023-01-03")]"#,
|
||||
result: json!(true),
|
||||
},
|
||||
TestCase {
|
||||
expr: r#"[date("2023-02-01")..date("2023-01-03")]"#,
|
||||
result: json!(false),
|
||||
},
|
||||
TestCase {
|
||||
expr: r#"$ in [date("2023-01-01")..date("2023-01-03")]"#,
|
||||
result: json!(true),
|
||||
},
|
||||
]),
|
||||
},
|
||||
UnaryTestEnv {
|
||||
env: json!({
|
||||
"customer": {
|
||||
|
||||
Reference in New Issue
Block a user