Skip to content

Commit

Permalink
Implement negation
Browse files Browse the repository at this point in the history
Signed-off-by: Igor Pashev <[email protected]>
  • Loading branch information
ip1981 committed May 25, 2023
1 parent 2dff4e7 commit d57fae4
Show file tree
Hide file tree
Showing 5 changed files with 489 additions and 385 deletions.
43 changes: 42 additions & 1 deletion jexl-eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
//!
use jexl_parser::{
ast::{Expression, OpCode},
ast::{Expression, OpCode, UnOpCode},
Parser,
};
use serde_json::{json as value, Value};
Expand Down Expand Up @@ -215,6 +215,9 @@ impl<'a> Evaluator<'a> {
}
}

Expression::UnaryOperation { operation, right } => {
self.apply_unary_op(operation, right, context)
}
Expression::BinaryOperation {
left,
right,
Expand Down Expand Up @@ -264,6 +267,19 @@ impl<'a> Evaluator<'a> {
}
}

fn apply_unary_op<'b>(
&self,
operation: UnOpCode,
right: Box<Expression>,
context: &Context,
) -> Result<'b, Value> {
let right = self.eval_ast(*right, context)?;

match operation {
UnOpCode::Negate => Ok(value!(!right.is_truthy())),
}
}

fn eval_op<'b>(
&self,
operation: OpCode,
Expand Down Expand Up @@ -968,4 +984,29 @@ mod tests {
assert!(res.is_ok());
assert_eq!(res.unwrap(), value!(42.0));
}

#[test]
fn test_negation() {
let evaluator = Evaluator::new();

let res = evaluator.eval("!true");
assert!(res.is_ok());
assert_eq!(res.unwrap(), value!(false));

let res = evaluator.eval("!true == !(!null)");
assert!(res.is_ok());
assert_eq!(res.unwrap(), value!(true));

let res = evaluator.eval(r#"!(!"") != true"#);
assert!(res.is_ok());
assert_eq!(res.unwrap(), value!(true));

let res = evaluator.eval("!0.0 in [true]");
assert!(res.is_ok());
assert_eq!(res.unwrap(), value!(true));

let res = evaluator.eval("! (2 in [3, 2, 1])");
assert!(res.is_ok());
assert_eq!(res.unwrap(), value!(false));
}
}
10 changes: 10 additions & 0 deletions jexl-parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ pub enum Expression {
Identifier(String),
Null,

UnaryOperation {
operation: UnOpCode,
right: Box<Expression>,
},

BinaryOperation {
operation: OpCode,
left: Box<Expression>,
Expand Down Expand Up @@ -44,6 +49,11 @@ pub enum Expression {
},
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum UnOpCode {
Negate,
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum OpCode {
Add,
Expand Down
32 changes: 31 additions & 1 deletion jexl-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl Parser {
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{Expression, OpCode};
use crate::ast::{Expression, OpCode, UnOpCode};

#[test]
fn literal() {
Expand Down Expand Up @@ -169,6 +169,36 @@ mod tests {
);
}

#[test]
fn test_simple_negation() {
let exp = "!false";
let parsed = Parser::parse(exp).unwrap();
assert_eq!(
parsed,
Expression::UnaryOperation {
operation: UnOpCode::Negate,
right: Box::new(Expression::Boolean(false)),
}
);
}

#[test]
fn test_negation() {
let exp = "! a && b";
let parsed = Parser::parse(exp).unwrap();
assert_eq!(
parsed,
Expression::BinaryOperation {
operation: OpCode::And,
left: Box::new(Expression::UnaryOperation {
operation: UnOpCode::Negate,
right: Box::new(Expression::Identifier("a".to_string())),
}),
right: Box::new(Expression::Identifier("b".to_string())),
}
);
}

#[test]
fn test_parsing_null() {
assert_eq!(Parser::parse("null"), Ok(Expression::Null));
Expand Down
11 changes: 8 additions & 3 deletions jexl-parser/src/parser.lalrpop
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::str::FromStr;

use crate::ast::{Expression, OpCode};
use crate::ast::{Expression, OpCode, UnOpCode};

grammar;

Expand Down Expand Up @@ -49,7 +49,7 @@ Expr60: Box<Expression> = {
/// - Or an `index` operation, taking an expression on the left hand side, and another expression inside square ("[]") brackets.
///
/// # Examples:
///
///
/// Assume our context is the following
/// ```
///{
Expand All @@ -67,6 +67,7 @@ Expr60: Box<Expression> = {
Expr70: Box<Expression> = {
<subject: Expr70> <index: Index> => Box::new(Expression::IndexOperation{subject, index}),
<subject: Expr70> "." <ident: Identifier> => Box::new(Expression::DotOperation{subject, ident}),
<operation: Op60> <right: Expr80> => Box::new(Expression::UnaryOperation { operation, right }),
Expr80
};

Expand Down Expand Up @@ -116,6 +117,10 @@ Op50: OpCode = {
"^" => OpCode::Exponent,
};

Op60: UnOpCode = {
"!" => UnOpCode::Negate,
};

Number: f64 = {
r"[0-9]+" => f64::from_str(<>).unwrap(),
r"[0-9]+\.[0-9]*" => f64::from_str(<>).unwrap(),
Expand Down Expand Up @@ -167,6 +172,6 @@ Object: Vec<(String, Box<Expression>)> = {
}

ObjectIdentifier: String = {
String,
String,
Identifier
}
Loading

0 comments on commit d57fae4

Please sign in to comment.