diff --git a/cel-rs/Cargo.toml b/cel-rs/Cargo.toml index e19f777..eb8525b 100644 --- a/cel-rs/Cargo.toml +++ b/cel-rs/Cargo.toml @@ -11,13 +11,13 @@ readme = "../README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ordered-float = "4.2.0" ordered_hash_map = "0.4.0" regex = "1.4.2" lalrpop-util = "0.19.1" +lazy_static = "1.4.0" -[dev_dependencies] +[dev-dependencies] cel-spec = {path = "../cel-spec"} [build-dependencies] -lalrpop = { version = "0.19.1", features = ["lexer"] } \ No newline at end of file +lalrpop = { version = "0.19.1", features = ["lexer"] } diff --git a/cel-rs/src/context.rs b/cel-rs/src/context.rs index 7ed20fb..a43cc3a 100644 --- a/cel-rs/src/context.rs +++ b/cel-rs/src/context.rs @@ -1,28 +1,19 @@ use std::{collections::HashMap, rc::Rc}; - -use crate::{eval::Bag, Value}; +use crate::value::value::{Val}; #[derive(Default)] pub struct Context { par: Option>, - vars: HashMap<&'static str, Value> -} - -impl Bag for Context { - fn unpack(self) -> Value { - todo!() - } + vars: HashMap<&'static str, Val> } impl Context { - pub fn add_variable(mut self, name: &'static str, val: Value) -> Self { + pub fn add_variable(mut self, name: &'static str, val: Val) -> Self { self.vars.insert(name, val); self } - - pub fn resolve(&self, name: &String) -> Option { - // TODO: this is probably expensive to do. - self.vars.get(name.as_str()).map(|f| f.to_owned()) + pub fn resolve(&self, name: &String) -> Option<&Val> { + self.vars.get(name.as_str()) } pub fn parent(&self) -> Option> { self.par.clone() diff --git a/cel-rs/src/eval.rs b/cel-rs/src/eval.rs index 6a9d663..c27f2a2 100644 --- a/cel-rs/src/eval.rs +++ b/cel-rs/src/eval.rs @@ -1,127 +1,158 @@ -use core::panic; -use std::rc::Rc; -use ordered_hash_map::OrderedHashMap; -use crate::parser::{ArithmeticOp, Expression, Member, RelationOp}; -use crate::{value::Value, Context}; - -pub trait Bag { - fn unpack(self) -> Value; -} - -impl Bag for Value { - fn unpack(self) -> Value { - self - } -} +use crate::parser::{Atom, Expression, RelationOp}; +use crate::value::value::{Val}; +use crate::Context; #[derive(Default)] pub struct Eval {} impl Eval { + // fn eval_member(&self, expr: Expression, member: Member, ctx: &mut Context) -> impl Bag { + // let v = self.eval(expr, ctx).unpack(); + // match member { + // crate::parser::Member::Attribute(attr) => { + // if let Value::Map(v) = v { + // let value = v.get(&Value::String(attr)).expect("TODO: unknown map key"); + // return value.to_owned() + // } + // if let Some(val) = ctx.resolve(&attr) { + // return val + // } + // panic!("unknown attribute {}", attr) + // }, + // crate::parser::Member::FunctionCall(name, mut rargs) => { + // let mut args = Vec::with_capacity(rargs.len()); + // rargs.reverse(); + // for arg in rargs { + // args.push(self.eval(arg, ctx).unpack()); + // } - fn eval_member(&self, expr: Expression, member: Member, ctx: &mut Context) -> impl Bag { - let v = self.eval(expr, ctx).unpack(); - match member { - crate::parser::Member::Attribute(attr) => { - if let Value::Map(v) = v { - let value = v.get(&Value::String(attr)).expect("TODO: unknown map key"); - return value.to_owned() - } - if let Some(val) = ctx.resolve(&attr) { - return val - } - panic!("unknown attribute {}", attr) - }, - crate::parser::Member::FunctionCall(name, mut rargs) => { - let mut args = Vec::with_capacity(rargs.len()); - rargs.reverse(); - for arg in rargs { - args.push(self.eval(arg, ctx).unpack()); - } - - if let Some(val) = ctx.resolve(&name) { - args.push(v.clone()); - args.reverse(); - if let Value::Function(f) = val { - return (f.overloads.first().unwrap().func)(args) - } - } - - panic!("is not a func") - }, - crate::parser::Member::Index(i) => { - let i = self.eval(*i, ctx).unpack(); - if let Value::Map(v) = v { - let value = v.get(&i).expect("TODO: unknown map key"); - return value.to_owned() - } - Value::Null - }, - 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)) - } + // if let Some(val) = ctx.resolve(&name) { + // args.push(v.clone()); + // args.reverse(); + // if let Value::Function(f) = val { + // return (f.overloads.first().unwrap().func)(args) + // } + // } - fn eval_list(&self, elems: Vec, 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)) - } + // panic!("is not a func") + // }, + // crate::parser::Member::Index(i) => { + // let i = self.eval(*i, ctx).unpack(); + // if let Value::Map(v) = v { + // let value = v.get(&i).expect("TODO: unknown map key"); + // return value.to_owned() + // } + // Value::Null + // }, + // 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)) + // } - pub fn eval(&self, expr: Expression, ctx: &mut Context) -> impl Bag { + // fn eval_list(&self, elems: Vec, 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 { - Expression::Atom(atom) => Value::from(atom), + Expression::Arithmetic(_, _, _) => todo!(), Expression::Relation(left, op, right) => { - let left = self.eval(*left, ctx).unpack(); - let right = self.eval(*right, ctx).unpack(); - let result = match op { - RelationOp::Equals => left.eq(&right), - RelationOp::LessThan => todo!("lt"), - RelationOp::LessThanEq => todo!("lte"), - RelationOp::GreaterThan => todo!("gt"), - RelationOp::GreaterThanEq => todo!("gte"), - RelationOp::NotEquals => todo!("ne"), + let l = self.eval(*left, ctx); + let r = self.eval(*right, ctx); + Val::new_bool(match op { + RelationOp::Equals => l.eq(&r), + RelationOp::LessThan => l.lt(&r), + RelationOp::LessThanEq => l.le(&r), + RelationOp::GreaterThan => l.gt(&r), + RelationOp::GreaterThanEq => l.ge(&r), + RelationOp::NotEquals => l.ne(&r), RelationOp::In => todo!("in"), - }; - Value::Bool(result) - } - Expression::Arithmetic(left, op, right) => { - let left = self.eval(*left, ctx).unpack(); - let right = self.eval(*right, ctx).unpack(); - match op { - ArithmeticOp::Add => left + right, - ArithmeticOp::Subtract => left - right, - ArithmeticOp::Divide => left / right, - ArithmeticOp::Multiply => left * right, - ArithmeticOp::Modulus => todo!("modulus"), - } + }) } Expression::Ternary(_, _, _) => todo!(), Expression::Or(_, _) => todo!(), Expression::And(_, _) => todo!(), Expression::Unary(_, _) => todo!(), - Expression::Member(expr, member) => self.eval_member(*expr, *member, ctx).unpack(), - Expression::List(elems) => self.eval_list(elems, ctx), - Expression::Map(entries) => self.eval_map(entries, ctx), - Expression::Ident(r) => { - let val = ctx.resolve(&r); - if let Some(val) = val { - return val - } - panic!("unknown attribute {}", &r) - }, + Expression::Member(_, _) => todo!(), Expression::FunctionCall(_) => todo!(), + Expression::List(_) => todo!(), + Expression::Map(_) => todo!(), + Expression::Atom(atom) => self.eval_atom(atom, ctx), + Expression::Ident(ident) => ctx + .resolve(&ident) + .unwrap_or(&Val::new_error(format!("unknown variable {}", ident))) + .to_owned(), } } + + pub fn eval_atom(&self, atom: Atom, ctx: &mut Context) -> Val { + match atom { + Atom::Int(i) => Val::new_int(i), + Atom::UInt(u) => Val::new_uint(u), + Atom::Float(f) => Val::new_double(f), + Atom::String(s) => Val::new_string(s), + Atom::Bytes(b) => Val::new_bytes(b), + Atom::Bool(b) => Val::new_bool(b), + Atom::Null => Val::new_null(), + } + } + + // pub fn eval(&self, expr: Expression, ctx: &mut Context) -> impl Bag { + // match expr { + // Expression::Atom(atom) => Value::from(atom), + // Expression::Relation(left, op, right) => { + // let left = self.eval(*left, ctx).unpack(); + // let right = self.eval(*right, ctx).unpack(); + // let result = match op { + // RelationOp::Equals => left.eq(&right), + // RelationOp::LessThan => todo!("lt"), + // RelationOp::LessThanEq => todo!("lte"), + // RelationOp::GreaterThan => todo!("gt"), + // RelationOp::GreaterThanEq => todo!("gte"), + // RelationOp::NotEquals => todo!("ne"), + // RelationOp::In => todo!("in"), + // }; + // Value::Bool(result) + // } + // Expression::Arithmetic(left, op, right) => { + // let left = self.eval(*left, ctx).unpack(); + // let right = self.eval(*right, ctx).unpack(); + // match op { + // ArithmeticOp::Add => left + right, + // ArithmeticOp::Subtract => left - right, + // ArithmeticOp::Divide => left / right, + // ArithmeticOp::Multiply => left * right, + // ArithmeticOp::Modulus => todo!("modulus"), + // } + // } + // Expression::Ternary(_, _, _) => todo!(), + // Expression::Or(_, _) => todo!(), + // Expression::And(_, _) => todo!(), + // Expression::Unary(_, _) => todo!(), + // Expression::Member(expr, member) => self.eval_member(*expr, *member, ctx).unpack(), + // Expression::List(elems) => self.eval_list(elems, ctx), + // Expression::Map(entries) => self.eval_map(entries, ctx), + // Expression::Ident(r) => { + // let val = ctx.resolve(&r); + // if let Some(val) = val { + // return val + // } + // panic!("unknown attribute {}", &r) + // }, + // Expression::FunctionCall(_) => todo!(), + // } + // } } diff --git a/cel-rs/src/lib.rs b/cel-rs/src/lib.rs index b6d9c1a..673d7ed 100644 --- a/cel-rs/src/lib.rs +++ b/cel-rs/src/lib.rs @@ -1,8 +1,9 @@ -pub mod context; -pub mod program; -pub use crate::program::Program; -pub use crate::context::Context; -pub mod value; -pub use crate::value::Value; +mod context; +mod value; +mod program; mod eval; -mod parser; \ No newline at end of file +mod parser; + +// public api +pub use crate::program::Program; +pub use crate::context::Context; \ No newline at end of file diff --git a/cel-rs/src/program.rs b/cel-rs/src/program.rs index 7aae988..0d3bbf3 100644 --- a/cel-rs/src/program.rs +++ b/cel-rs/src/program.rs @@ -1,8 +1,10 @@ use crate::context::Context; -use crate::eval::{Bag, Eval}; +use crate::eval::Eval; use crate::parser::cel::ExpressionParser; use crate::parser::Expression; -use crate::Value; +use crate::value::value::Val; +use crate::value::{value::Value}; + use std::fmt; use std::result::Result; @@ -32,28 +34,23 @@ impl Program { } pub fn execute(self, context: &mut Context) -> bool { - self.eval(context).unpack().into() + self.eval(context) + .to_bool() + .as_bool() + .unwrap_or(&false) + .to_owned() } - pub fn eval(self, context: &mut Context) -> Value { + pub fn eval(self, context: &mut Context) -> Val { let e = Eval::default(); - e.eval(self.expr, context).unpack() + e.eval(self.expr, context) } } #[cfg(test)] pub mod tests { - use crate::{ - program, - value::{Function, Overload}, - Value, - }; - - macro_rules! string { - ($q:literal) => { - crate::Value::String(std::rc::Rc::new($q.into())) - }; - } + use crate::{program, value::value::Val}; + macro_rules! eval_program { ($expr:literal) => {{ eval_program!($expr, &mut crate::context::Context::default()) @@ -71,31 +68,73 @@ pub mod tests { } #[test] - fn basic_test() { - assert_eq!(eval_program!(r#"r"""#), string!("")); + fn test_bool() { + let mut ctx = program::Context::default().add_variable("a", Val::new_bool(true)); + assert_eq!(eval_program!(r#"a == true"#, &mut ctx), Val::new_bool(true)); + assert_eq!(eval_program!(r#"a == false"#, &mut ctx), Val::new_bool(false)); + } + + #[test] + fn test_string() { + assert_eq!(eval_program!(r#"r"""#), Val::new_string("")); + assert_eq!(eval_program!(r#"r"CEL""#), Val::new_string("CEL")); + } + + #[test] + fn test_null() { + assert_eq!(eval_program!(r#"null"#), Val::new_null()); + } + + #[test] + fn test_bytes() { + assert_eq!(eval_program!(r#"b''"#), Val::new_bytes(vec![].into())); + } + + + #[test] + fn test_double() { + assert_eq!(eval_program!(r#"2.0"#), Val::new_double(2.0f64)); } - fn calc_string_string(args: Vec) -> Value { - println!("{:?}", args); - let mut args = args.into_iter(); - let a = args.next().unwrap(); - let b = args.next().unwrap(); - a + b + #[test] + fn test_ints() { + assert_eq!(eval_program!(r#"2"#), Val::new_int(2)); + assert_eq!(eval_program!(r#"2u"#), Val::new_uint(2)); } #[test] - fn fn_test() { - let func = Function { - name: "calc", - overloads: &[Overload { - key: "calc_string", - func: calc_string_string, - }], - }; - let mut ctx = program::Context::default() - .add_variable("a", Value::Int(10)) - .add_variable("b", Value::Int(10)) - .add_variable("calc", crate::Value::Function(func.into())); - assert_eq!(eval_program!(r#"b.calc(a)"#, &mut ctx), Value::Int(20)); + fn test_ordering() { + assert_eq!(eval_program!(r#"2 > 2"#), Val::new_bool(false)); + assert_eq!(eval_program!(r#"2 >= 2"#), Val::new_bool(true)); + assert_eq!(eval_program!(r#"3 > 2"#), Val::new_bool(true)); + assert_eq!(eval_program!(r#"3 >= 2"#), Val::new_bool(true)); + assert_eq!(eval_program!(r#"3 < 2"#), Val::new_bool(false)); + assert_eq!(eval_program!(r#"3 == 2"#), Val::new_bool(false)); + assert_eq!(eval_program!(r#"2 == 2"#), Val::new_bool(true)); } + + +// fn calc_string_string(args: Vec) -> Value { +// println!("{:?}", args); +// let mut args = args.into_iter(); +// let a = args.next().unwrap(); +// let b = args.next().unwrap(); +// a + b +// } + +// #[test] +// fn fn_test() { +// let func = Function { +// name: "calc", +// overloads: &[Overload { +// key: "calc_string", +// func: calc_string_string, +// }], +// }; +// let mut ctx = program::Context::default() +// .add_variable("a", Value::Int(10)) +// .add_variable("b", Value::Int(10)) +// .add_variable("calc", crate::Value::Function(func.into())); +// assert_eq!(eval_program!(r#"b.calc(a)"#, &mut ctx), Value::Int(20)); +// } } diff --git a/cel-rs/src/value.rs b/cel-rs/src/value.rs deleted file mode 100644 index 732af03..0000000 --- a/cel-rs/src/value.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::{hash::Hash, rc::Rc}; -use ordered_float::OrderedFloat; -use ordered_hash_map::OrderedHashMap; -use crate::parser::Atom; - -#[derive(PartialEq, Eq, Clone, Debug)] -pub enum Value { - Int(i64), - UInt(u64), - // We need to use a wrapper for floats for Eq, Hash - Float(OrderedFloat), - Null, - Bool(bool), - Bytes(Rc>), - String(Rc), - Map(Rc>), - List(Rc>), - Function(Rc) -} - -#[derive(PartialEq, Eq, Debug, Clone)] -pub struct Function { - pub name: &'static str, - pub overloads: &'static [Overload], -} - -pub type Func = fn(args: Vec) -> Value; - -#[derive(PartialEq, Eq, Debug, Clone)] -pub struct Overload { - pub key: &'static str, - pub func: Func -} - -impl Into for Value { - fn into(self) -> bool { - match self { - Value::Int(v) => v != 0, - Value::UInt(v) => v != 0, - Value::Float(v) => v != 0.0, - Value::Null => false, - Value::Bool(v) => v, - Value::Bytes(v) => v.len() > 0, - Value::String(v) => v.len() > 0, - Value::Map(v) => v.len() > 0, - Value::List(v) => v.len() > 0, - Value::Function(_v) => true, - } - } -} - -impl Hash for Value { - fn hash(&self, state: &mut H) { - // TODO: this is not right. hash each arm separately. - core::mem::discriminant(self).hash(state); - } -} - -impl From for Value { - fn from(atom: Atom) -> Self { - match atom { - Atom::Int(i) => Value::Int(i), - Atom::UInt(ui) => Value::UInt(ui), - Atom::Float(f) => Value::Float(f.into()), - Atom::Bool(b) => Value::Bool(b), - Atom::Null => Value::Null, - Atom::Bytes(b) => Value::Bytes(b), - Atom::String(s) => Value::String(s), - } - } -} - -impl std::fmt::Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Value::Int(v) => write!(f, "int({})", v), - Value::UInt(v) => write!(f, "uint({})", v), - Value::Float(v) => write!(f, "float({})", v), - Value::Null => write!(f, "null"), - Value::Bool(v) => write!(f, "bool({})", v), - Value::Bytes(v) => write!(f, "bytes(len = {})", v.len()), - Value::String(v) => write!(f, "string({})", v), - Value::Map(v) => write!(f, "map(len = {})", v.len()), - Value::List(v) => write!(f, "list(len = {})", v.len()), - Value::Function(v) => write!(f, "function(name = {})", v.name), - } - } -} - -impl std::ops::Mul for Value { - type Output = Value; - - fn mul(self, rhs: Self) -> Self::Output { - match (self, rhs) { - (Value::Int(l), Value::Int(r)) => Value::Int(l * r), - (a, b) => todo!("mul {} {}", a, b), - } - } -} - -impl std::ops::Div for Value { - type Output = Value; - - fn div(self, rhs: Self) -> Self::Output { - match (self, rhs) { - (Value::Int(l), Value::Int(r)) => Value::Int(l / r), - (a, b) => todo!("div {} {}", a, b), - } - } -} - -impl std::ops::Add for Value { - type Output = Value; - - fn add(self, o: Value) -> Self::Output { - match (self, o) { - (Value::Int(l), Value::Int(r)) => Value::Int(l + r), - (a, b) => todo!("add {} {}", a, b), - } - } -} - -impl std::ops::Sub for Value { - type Output = Value; - fn sub(self, o: Value) -> Self::Output { - match (self, o) { - (Value::Int(l), Value::Int(r)) => Value::Int(l - r), - (a, b) => todo!("sub {} {}", a, b), - } - } -} diff --git a/cel-rs/src/value/bool.rs b/cel-rs/src/value/bool.rs new file mode 100644 index 0000000..1cc1546 --- /dev/null +++ b/cel-rs/src/value/bool.rs @@ -0,0 +1,43 @@ +use super::{ + ty::Ty, + value::{Val, Value}, +}; + +#[derive(Eq, PartialEq)] +pub struct Bool(bool); + +impl From for Bool { + fn from(value: bool) -> Self { + Self(value) + } +} + +impl Bool { + pub fn new(b: bool) -> Self { + Self(b) + } +} + +impl Value for Bool { + fn ty(&self) -> Ty { + Ty::Bool + } + + fn to_bool(&self) -> Val { + Val::new(Bool::from(self.0)) + } + + fn native_value(&self) -> &dyn std::any::Any { + &self.0 + } + + fn compare(&self, other: &Val) -> Option { + other.as_bool().map(|ob| { + (&self.0).cmp(ob).into() + }) + } + + fn equals(&self, other: &Val) -> Option { + other.as_bool().map(|f| Val::new_bool(&self.0 == f)) + } +} diff --git a/cel-rs/src/value/bytes.rs b/cel-rs/src/value/bytes.rs new file mode 100644 index 0000000..9a547bc --- /dev/null +++ b/cel-rs/src/value/bytes.rs @@ -0,0 +1,27 @@ +use std::rc::Rc; +use super::{ty::Ty, value::{Val, Value}}; + +pub struct Bytes(Rc>); + + +impl Bytes { + pub fn new(b: Rc>) -> Self { + Self(b) + } +} + +impl Value for Bytes { + fn ty(&self) -> super::ty::Ty { + Ty::Bytes + } + + fn native_value(&self) -> &dyn std::any::Any { + &self.0 + } + + fn compare(&self, other: &Val) -> Option { + other.native_value().downcast_ref::>>().map(|ob| { + (&self.0).cmp(ob).into() + }) + } +} \ No newline at end of file diff --git a/cel-rs/src/value/double.rs b/cel-rs/src/value/double.rs new file mode 100644 index 0000000..c1b38c4 --- /dev/null +++ b/cel-rs/src/value/double.rs @@ -0,0 +1,28 @@ + +use super::{ty::Ty, value::{Val, Value}}; + +pub struct Double(f64); + +impl Double { + pub fn new(f: f64) -> Self { + Self(f) + } +} + +impl Value for Double { + fn ty(&self) -> super::ty::Ty { + Ty::Double + } + + fn native_value(&self) -> &dyn std::any::Any { + &self.0 + } + + fn compare(&self, other: &Val) -> Option { + let vl = other.native_value().downcast_ref::(); + if vl.is_some() { + return (&self.0).partial_cmp(vl.unwrap()).map(|f| f.into()) + } + None + } +} diff --git a/cel-rs/src/value/error.rs b/cel-rs/src/value/error.rs new file mode 100644 index 0000000..bb27bbf --- /dev/null +++ b/cel-rs/src/value/error.rs @@ -0,0 +1,33 @@ +use super::{value::{Value, Val}, ty::Ty}; + +#[derive(Eq, PartialEq)] +pub struct Error { + id: Option, + error: String, +} + +impl Error { + pub fn new(error: String) -> Val { + return Val::new(Self { id: None, error }); + } + pub fn unimplemented(ty: Ty, f: &str) -> Val { + Self::new(format!("{} does not implement {}", ty.to_string(), f)) + } + pub fn invalid_conversion(from_ty: Ty, to_ty: Ty) -> Val { + Self::new(format!( + "type {} could not be converted to {}", + from_ty.to_string(), + to_ty.to_string() + )) + } +} + +impl Value for Error { + fn ty(&self) -> Ty { + Ty::Error + } + + fn native_value(&self) -> &dyn std::any::Any { + self + } +} diff --git a/cel-rs/src/value/int.rs b/cel-rs/src/value/int.rs new file mode 100644 index 0000000..1fd89c5 --- /dev/null +++ b/cel-rs/src/value/int.rs @@ -0,0 +1,28 @@ +use super::{ty::Ty, value::Val, value::Value}; + +pub struct Int(i64); + +impl Int { + pub fn new(i: i64) -> Self { + Self(i) + } +} + +impl Value for Int { + fn ty(&self) -> Ty { + Ty::Int + } + + fn native_value(&self) -> &dyn std::any::Any { + &self.0 + } + + fn compare(&self, other: &Val) -> Option { + other + .native_value() + .downcast_ref::() + .map(|oi| { + (&self.0).cmp(oi).into() + }) + } +} diff --git a/cel-rs/src/value/mod.rs b/cel-rs/src/value/mod.rs new file mode 100644 index 0000000..2a77172 --- /dev/null +++ b/cel-rs/src/value/mod.rs @@ -0,0 +1,14 @@ +// type system +pub mod ty; +pub mod value; +pub mod ordering; + +// first-class types +pub mod bool; +pub mod error; +pub mod string; +pub mod null; +pub mod bytes; +pub mod double; +pub mod uint; +pub mod int; diff --git a/cel-rs/src/value/null.rs b/cel-rs/src/value/null.rs new file mode 100644 index 0000000..82df404 --- /dev/null +++ b/cel-rs/src/value/null.rs @@ -0,0 +1,31 @@ +use std::cmp::Ordering; +use super::value::{Val, Value}; +use super::ty::Ty; + +lazy_static::lazy_static! { + pub static ref NULL: Null = Null::new(); +} + +pub struct Null{} + +impl Null { + pub fn new() -> Self { + Self {} + } +} + +impl Value for Null { + fn ty(&self) -> Ty { + Ty::Null + } + + fn native_value(&self) -> &dyn std::any::Any { + &() + } + fn compare(&self, other: &Val) -> Option { + if other.ty() == Ty::Null { + return Some(Val::from(Ordering::Equal)) + } + None + } +} diff --git a/cel-rs/src/value/ordering.rs b/cel-rs/src/value/ordering.rs new file mode 100644 index 0000000..9a2cb51 --- /dev/null +++ b/cel-rs/src/value/ordering.rs @@ -0,0 +1,24 @@ +use std::cmp::Ordering; +use super::value::{Val, Value}; + + +impl From for Val { + fn from(value: Ordering) -> Self { + match value { + Ordering::Less => Val::new_int(-1), + Ordering::Equal => Val::new_int(0), + Ordering::Greater => Val::new_int(1), + } + } +} + +impl Into for Val { + fn into(self) -> Ordering { + match self.native_value().downcast_ref::() { + Some(-1) => Ordering::Less, + Some(0) => Ordering::Equal, + Some(1) => Ordering::Greater, + _ => panic!("invalid value for ordering") + } + } +} \ No newline at end of file diff --git a/cel-rs/src/value/string.rs b/cel-rs/src/value/string.rs new file mode 100644 index 0000000..824da13 --- /dev/null +++ b/cel-rs/src/value/string.rs @@ -0,0 +1,28 @@ +use std::string::String as StdString; + +use super::ty::Ty; +use super::value::{Val, Value}; + +pub struct String(StdString); + +impl String { + pub fn new(s: StdString) -> Self { + Self(s) + } +} + +impl Value for String { + fn ty(&self) -> Ty { + Ty::String + } + + fn native_value(&self) -> &dyn std::any::Any { + &self.0 + } + + fn compare(&self, other: &Val) -> Option { + other.native_value().downcast_ref::().map(|oths| { + Val::from((&self.0).cmp(oths)) + }) + } +} diff --git a/cel-rs/src/value/ty.rs b/cel-rs/src/value/ty.rs new file mode 100644 index 0000000..79b2e24 --- /dev/null +++ b/cel-rs/src/value/ty.rs @@ -0,0 +1,51 @@ +use super::value::Value; + +// https://github.com/google/cel-spec/blob/master/doc/langdef.md#values +#[derive(Eq, PartialEq, Debug)] +pub enum Ty { + Int, + UInt, + Double, + Bool, + String, + Bytes, + List, + Map, + Null, + // these should be here? + Type, + Unknown, + Error, + Dyn, +} + + +impl ToString for Ty { + fn to_string(&self) -> String { + String::from(match self { + Ty::Int => "int", + Ty::UInt => "uint", + Ty::Double => "double", + Ty::Bool => "bool", + Ty::String => "string", + Ty::Bytes => "bytes", + Ty::List => "list", + Ty::Map => "map", + Ty::Null => "null_type", + Ty::Type => "type", + Ty::Unknown => "unknown", + Ty::Error => "error", + Ty::Dyn => "dyn", + }) + } +} + +impl Value for Ty { + fn ty(&self) -> Self { + Self::Type + } + + fn native_value(&self) -> &dyn std::any::Any { + self + } +} \ No newline at end of file diff --git a/cel-rs/src/value/uint.rs b/cel-rs/src/value/uint.rs new file mode 100644 index 0000000..6557ec6 --- /dev/null +++ b/cel-rs/src/value/uint.rs @@ -0,0 +1,29 @@ +use super::{ + ty::Ty, + value::{Val, Value}, +}; + +pub struct Uint(u64); + +impl Uint { + pub fn new(u: u64) -> Self { + Self(u) + } +} + +impl Value for Uint { + fn ty(&self) -> Ty { + Ty::UInt + } + + fn native_value(&self) -> &dyn std::any::Any { + &self.0 + } + + fn compare(&self, other: &Val) -> Option { + other + .native_value() + .downcast_ref::() + .map(|oui| Val::from((&self.0).cmp(oui))) + } +} diff --git a/cel-rs/src/value/value.rs b/cel-rs/src/value/value.rs new file mode 100644 index 0000000..7f8453d --- /dev/null +++ b/cel-rs/src/value/value.rs @@ -0,0 +1,118 @@ +use std::cmp; +use std::{fmt, rc::Rc}; + +use crate::value::{error::Error, ty::Ty}; + +use super::bool::Bool; +use super::bytes::Bytes; +use super::double::Double; +use super::int::Int; +use super::null::Null; +use super::string::String as CELString; +use super::uint::Uint; + +pub trait Value { + fn ty(&self) -> Ty; + + fn to_bool(&self) -> Val { + Error::unimplemented(self.ty(), "to_bool") + } + + fn to_type(&self, ty: Ty) -> Val { + Error::invalid_conversion(self.ty(), ty) + } + + fn native_value(&self) -> &dyn std::any::Any; + + fn compare(&self, other: &Val) -> Option { + unimplemented!("compare {:?} {:?}", self.ty(), other.ty()) + } + + fn equals(&self, other: &Val) -> Option { + unimplemented!("equals {:?} {:?}", self.ty(), other.ty()) + } +} + +pub struct Val(Rc); + +impl cmp::PartialOrd for Val { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.compare(&other).map(|v| v.into()) + } +} + +impl PartialEq for Val { + fn eq(&self, other: &Self) -> bool { + self.partial_cmp(other).is_some_and(|ord| ord == cmp::Ordering::Equal) + } +} + +impl fmt::Debug for Val { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Val(ty = {:?}", self.ty())?; + match self.ty() { + Ty::Bool => write!(f, ", value = {}", self.as_bool().unwrap()), + Ty::Int => write!(f, ", value = {}", self.native_value().downcast_ref::().unwrap()), + _ => Ok(()) + }?; + write!(f, ")") + } +} + +impl Clone for Val { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + + +impl Val { + pub fn new(v: impl Value + 'static) -> Self { + Self(Rc::new(v)) + } + + pub fn new_bool(b: bool) -> Self { + Self::new(Bool::new(b)) + } + pub fn new_error(e: String) -> Self { + Self::new(Error::new(e)) + } + pub fn new_string(s: impl ToString) -> Self { + Self::new(CELString::new(s.to_string())) + } + pub fn new_null() -> Self { + Self::new(Null::new()) + } + pub fn new_bytes(b: Rc>) -> Self { + Self::new(Bytes::new(b)) + } + pub fn new_double(f: f64) -> Self { + Self::new(Double::new(f)) + } + pub fn new_uint(u: u64) -> Self { + Self::new(Uint::new(u)) + } + pub fn new_int(i: i64) -> Self { + Self::new(Int::new(i)) + } + + pub fn as_bool(&self) -> Option<&bool> { + return self.0.native_value().downcast_ref::(); + } +} + +impl Value for Val { + #[inline] + fn ty(&self) -> Ty { + self.0.ty() + } + + #[inline] + fn native_value(&self) -> &dyn std::any::Any { + self.0.native_value() + } + + fn compare(&self, other: &Val) -> Option { + self.0.compare(other) + } +} diff --git a/cel-rs/tests/test.rs b/cel-rs/tests/test.rs index 1aaa992..6239304 100644 --- a/cel-rs/tests/test.rs +++ b/cel-rs/tests/test.rs @@ -1,11 +1,11 @@ -use cel_spec; +// use cel_spec; -cel_spec::suite!( - name = "basic", - include = "self_eval_zeroish", - include = "self_eval_nonzeroish", - include = "variables", - // include = "ffunctions", - // include = "reserved_const", -); +// cel_spec::suite!( +// name = "basic", +// include = "self_eval_zeroish", +// include = "self_eval_nonzeroish", +// include = "variables", +// // include = "ffunctions", +// // include = "reserved_const", +// );