Skip to content

Commit

Permalink
rework test spec
Browse files Browse the repository at this point in the history
  • Loading branch information
thesayyn committed May 26, 2024
1 parent a5f0aca commit eb4b2e3
Show file tree
Hide file tree
Showing 16 changed files with 384 additions and 148 deletions.
1 change: 1 addition & 0 deletions cel-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ordered_hash_map = "0.4.0"
regex = "1.4.2"
lalrpop-util = "0.19.1"
lazy_static = "1.4.0"
unescape = "0.1.0"

[dev-dependencies]
cel-spec = {path = "../cel-spec"}
Expand Down
46 changes: 26 additions & 20 deletions cel-rs/src/eval.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::collections::HashMap;
use std::rc::Rc;

use crate::parser::{Atom, Expression, RelationOp};
use crate::value::value::{Val};
use crate::value::value::Val;
use crate::Context;

#[derive(Default)]
Expand Down Expand Up @@ -47,24 +50,7 @@ impl Eval {
// crate::parser::Member::Fields(_) => todo!("Fields"),
// }
// }
// fn eval_map(&self, entries: Vec<(Expression, Expression)>, ctx: &mut Context) -> Value {
// let mut map = OrderedHashMap::with_capacity(entries.len());
// for (kexpr, vexpr) in entries {
// let k = self.eval(kexpr, ctx).unpack();
// let v = self.eval(vexpr, ctx).unpack();
// map.insert(k, v);
// }
// Value::Map(Rc::new(map))
// }

// fn eval_list(&self, elems: Vec<Expression>, ctx: &mut Context) -> Value {
// let mut list = Vec::with_capacity(elems.len());
// for expr in elems {
// let v = self.eval(expr, ctx).unpack();
// list.push(v);
// }
// Value::List(Rc::new(list))
// }

pub fn eval(&self, expr: Expression, ctx: &mut Context) -> Val {
match expr {
Expand All @@ -88,8 +74,8 @@ impl Eval {
Expression::Unary(_, _) => todo!(),
Expression::Member(_, _) => todo!(),
Expression::FunctionCall(_) => todo!(),
Expression::List(_) => todo!(),
Expression::Map(_) => todo!(),
Expression::List(values) => self.eval_list(values, ctx),
Expression::Map(entries) => self.eval_map(entries, ctx),
Expression::Atom(atom) => self.eval_atom(atom, ctx),
Expression::Ident(ident) => ctx
.resolve(&ident)
Expand All @@ -98,6 +84,26 @@ impl Eval {
}
}

fn eval_map(&self, entries: Vec<(Expression, Expression)>, ctx: &mut Context) -> Val {
let mut map = HashMap::with_capacity(entries.len());
for (kexpr, vexpr) in entries {
let k = self.eval(kexpr, ctx);
let v = self.eval(vexpr, ctx);
map.insert(k, v);
}
Val::new_map(Rc::new(map))
}


fn eval_list(&self, elems: Vec<Expression>, ctx: &mut Context) -> Val {
let mut list = Vec::with_capacity(elems.len());
for expr in elems {
let v = self.eval(expr, ctx);
list.push(v);
}
Val::new_list(Rc::new(list))
}

pub fn eval_atom(&self, atom: Atom, ctx: &mut Context) -> Val {
match atom {
Atom::Int(i) => Val::new_int(i),
Expand Down
3 changes: 2 additions & 1 deletion cel-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ mod parser;

// public api
pub use crate::program::Program;
pub use crate::context::Context;
pub use crate::context::Context;
pub use value::value::{Val, Value};
23 changes: 14 additions & 9 deletions cel-rs/src/parser/cel.lalrpop
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::parser::{RelationOp, ArithmeticOp, Expression, UnaryOp, Member, Atom};
use crate::parser::parse;
use std::rc::Rc;

grammar;
Expand Down Expand Up @@ -90,33 +91,37 @@ RelationOp: RelationOp = {

Atom: Atom = {
// Integer literals
r"-?[0-9]+" => Atom::Int(<>.parse().unwrap()),
r"-?0[xX]([0-9a-fA-F]+)" => Atom::Int(i64::from_str_radix(<>, 16).unwrap()),

r"-?[0-9]+" => Atom::Int(<>.parse().expect("failed to parse int")),

r"0[xX]([0-9a-fA-F]+)" => Atom::Int(i64::from_str_radix(<>.trim_start_matches("0x").trim_start_matches("0X"), 16).unwrap()),
r"-0[xX]([0-9a-fA-F]+)" => Atom::Int(-i64::from_str_radix(<>.trim_start_matches("-0x").trim_start_matches("-0x"), 16).unwrap()),

// LALRPOP does not support regex capture groups. https://github.com/lalrpop/lalrpop/issues/575
r"-?[0-9]+[uU]" => Atom::UInt(<>.trim_end_matches(|c| c == 'u' || c == 'U').parse().unwrap()),
r"-?0[xX]([0-9a-fA-F]+)[uU]" => Atom::UInt(u64::from_str_radix(<>.trim_end_matches(|c| c == 'u' || c == 'U'), 16).unwrap()),
r"0[xX]([0-9a-fA-F]+)[uU]" => Atom::UInt(u64::from_str_radix(<>.trim_start_matches("0x").trim_start_matches("0X").trim_end_matches(|c| c == 'u' || c == 'U'), 16).expect("heyo")),

// Float with decimals and optional exponent
r"([-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?)" => Atom::Float(<>.parse().unwrap()),
// Float with no decimals and required exponent
r"[-+]?[0-9]+[eE][-+]?[0-9]+" => Atom::Float(<>.parse().unwrap()),

// Double quoted string
"r"? <s:r#""(\\.|[^"\n])*""#> => Atom::String(Rc::new(s[1..s.len()-1].into())),
"r"? <s:r#""""(\\.|[^"{3}])*""""#> => Atom::String(Rc::new(s[3..s.len()-3].into())),
"r"? <s:r#""(\\.|[^"\n])*""#> => Atom::String(Rc::new(parse::parse_str(&s[1..s.len()-1]))),
"r"? <s:r#""""(\\.|[^"{3}])*""""#> => Atom::String(Rc::new(parse::parse_str(&s[3..s.len()-3]))),

// Single quoted string
"r"? <s:r#"'(\\.|[^'\n])*'"#> => Atom::String(Rc::new(s[1..s.len()-1].into())),
"r"? <s:r#"'''(\\.|[^'{3}])*'''"#> => Atom::String(Rc::new(s[3..s.len()-3].into())),
"r"? <s:r#"'(\\.|[^'\n])*'"#> => Atom::String(Rc::new(parse::parse_str(&s[1..s.len()-1]))),
"r"? <s:r#"'''(\\.|[^'{3}])*'''"#> => Atom::String(Rc::new(parse::parse_str(&s[3..s.len()-3]))),


// Double quoted bytes
r#"[bB]"(\\.|[^"\n])*""# => Atom::Bytes(Vec::from(<>[2..<>.len()-1].as_bytes()).into()),
r#"[bB]"""(\\.|[^"{3}])*""""# => Atom::Bytes(Vec::from(<>[4..<>.len()-3].as_bytes()).into()),

// Single quoted bytes
r#"[bB]'(\\.|[^'\n])*'"# =>Atom::Bytes(Vec::from(<>[2..<>.len()-1].as_bytes()).into()),
r#"[bB]'''(\\.|[^'{3}])*'''"# => Atom::Bytes(Vec::from(<>[4..<>.len()-3].as_bytes()).into()),
r#"[bB]'(\\.|[^'\n])*'"# =>Atom::Bytes(Vec::from(parse::parse_str(&<>[2..<>.len()-1]).as_bytes()).into()),
r#"[bB]'''(\\.|[^'{3}])*'''"# => Atom::Bytes(Vec::from(parse::parse_str(&<>[4..<>.len()-3]).as_bytes()).into()),

"true" => Atom::Bool(true),
"false" => Atom::Bool(false),
Expand Down
1 change: 1 addition & 0 deletions cel-rs/src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use lalrpop_util::lalrpop_mod;

pub mod ast;
pub mod parse;
pub use ast::*;

lalrpop_mod!(
Expand Down
5 changes: 5 additions & 0 deletions cel-rs/src/parser/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use unescape::unescape;

pub fn parse_str(str: &str) -> String {
unescape(str).unwrap_or(String::new())
}
12 changes: 12 additions & 0 deletions cel-rs/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ pub mod tests {
assert_eq!(eval_program!(r#"2 == 2"#), Val::new_bool(true));
}

#[test]
fn self_eval_int_hex_negative() {
let expr = r#"-0x55555555"#;
let program = crate::Program::new(expr);
assert!(program.is_ok(), "failed to parse '{}'", expr);
let program = program.unwrap();
let mut ctx = crate::Context::default();
let value = program.eval(&mut ctx);
let expected_value = crate::Val::new_int(-1431655765);
assert_eq!(value, expected_value, r#""{:?}" did not match "{:?}""#, value, expected_value);
}


// fn calc_string_string(args: Vec<Value>) -> Value {
// println!("{:?}", args);
Expand Down
11 changes: 6 additions & 5 deletions cel-rs/src/value/bool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ impl Value for Bool {
}

fn compare(&self, other: &Val) -> Option<Val> {
other.as_bool().map(|ob| {
(&self.0).cmp(ob).into()
})
other.as_bool().map(|ob| (&self.0).cmp(ob).into())
}

fn equals(&self, other: &Val) -> Option<Val> {
other.as_bool().map(|f| Val::new_bool(&self.0 == f))
fn equals(&self, other: &Val) -> Val {
other
.as_bool()
.map(|f| Val::new_bool(&self.0 == f))
.unwrap_or(Val::new_bool(false))
}
}
8 changes: 8 additions & 0 deletions cel-rs/src/value/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::fmt;

use super::{value::{Value, Val}, ty::Ty};

#[derive(Eq, PartialEq)]
Expand All @@ -6,6 +8,12 @@ pub struct Error {
error: String,
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "error(id = {:?}, message = {})", self.id, self.error)
}
}

impl Error {
pub fn new(error: String) -> Val {
return Val::new(Self { id: None, error });
Expand Down
47 changes: 47 additions & 0 deletions cel-rs/src/value/map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use super::ty::Ty;
use crate::{Val, Value};
use std::{collections::HashMap, rc::Rc};

pub struct Map(Rc<HashMap<Val, Val>>);

impl Map {
pub fn new(h: Rc<HashMap<Val, Val>>) -> Self {
Self(h)
}
}

impl Value for Map {
fn ty(&self) -> super::ty::Ty {
Ty::Map
}

fn native_value(&self) -> &dyn std::any::Any {
&self.0
}

fn equals(&self, other: &Val) -> Val {
other
.native_value()
.downcast_ref::<Rc<HashMap<Val, Val>>>()
.map(|other| {
if other.len() != self.0.len() {
return Val::new_bool(false);
}

for (k, v) in self.0.iter() {
let ov = other.get(k);
if let Some(ov) = ov {
// TODO: use value.equals once all types support it.
if !ov.compare(v).is_some_and(|o| o.as_int() == Some(&0)) {
return Val::new_bool(false);
}
} else {
return Val::new_bool(false);
}
}

Val::new_bool(true)
})
.unwrap_or(Val::new_bool(false))
}
}
1 change: 1 addition & 0 deletions cel-rs/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ pub mod bytes;
pub mod double;
pub mod uint;
pub mod int;
pub mod map;
Loading

0 comments on commit eb4b2e3

Please sign in to comment.