From 0ff68dc0916baf9c052463418af26e99cf55bc0b Mon Sep 17 00:00:00 2001 From: thesayyn Date: Mon, 11 Mar 2024 11:01:23 -0700 Subject: [PATCH] feat: function calls --- cel_spec/src/lib.rs | 4 +-- program/src/context.rs | 30 ++++++++++++++--- program/src/eval.rs | 76 ++++++++++++++++++++++++------------------ program/src/program.rs | 41 +++++++++++++++++++---- program/src/value.rs | 20 +++++++++-- 5 files changed, 123 insertions(+), 48 deletions(-) diff --git a/cel_spec/src/lib.rs b/cel_spec/src/lib.rs index f63a351..a81da7d 100644 --- a/cel_spec/src/lib.rs +++ b/cel_spec/src/lib.rs @@ -64,8 +64,8 @@ pub fn suite(attr: TokenStream) -> TokenStream { let program = program::Program::new(r#"{expr}"#); assert!(program.is_ok(), "failed to parse '{{}}'", r#"{expr}"#); let program = program.unwrap(); - let ctx = program::context::Context::default(); - let value = program.eval(ctx); + let mut ctx = program::context::Context::default(); + let value = program.eval(&mut ctx); let expected_value = {expected_value}; assert_eq!(value, expected_value, r#""{{}}" did not match "{{}}""#, value, expected_value); }} diff --git a/program/src/context.rs b/program/src/context.rs index e44457b..7ed20fb 100644 --- a/program/src/context.rs +++ b/program/src/context.rs @@ -1,10 +1,30 @@ +use std::{collections::HashMap, rc::Rc}; + +use crate::{eval::Bag, Value}; + +#[derive(Default)] pub struct Context { - + par: Option>, + vars: HashMap<&'static str, Value> } -impl Default for Context { - fn default() -> Self { - let ctx = Context {}; - ctx +impl Bag for Context { + fn unpack(self) -> Value { + todo!() } } + +impl Context { + pub fn add_variable(mut self, name: &'static str, val: Value) -> 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 parent(&self) -> Option> { + self.par.clone() + } +} \ No newline at end of file diff --git a/program/src/eval.rs b/program/src/eval.rs index a904f8d..7eddd2f 100644 --- a/program/src/eval.rs +++ b/program/src/eval.rs @@ -1,4 +1,5 @@ -use std::rc::Rc; +use core::panic; +use std::{mem, rc::Rc}; use ordered_hash_map::OrderedHashMap; use parser::{ArithmeticOp, Expression, Member, RelationOp}; use crate::{value::Value, Context}; @@ -7,69 +8,72 @@ pub trait Bag { fn unpack(self) -> Value; } -/// Indexer permits random access of elements by index ```a[b()]```. -pub trait Indexer where T: Bag { - fn get(val: T) -> T; -} - impl Bag for Value { fn unpack(self) -> Value { self } } -pub struct Eval { - #[allow(dead_code)] - ctx: Context, -} +#[derive(Default)] +pub struct Eval {} impl Eval { - pub fn new(ctx: Context) -> Self { - Eval { ctx } - } - fn eval_member(&self, expr: Expression, member: Member) -> impl Bag { - let v = self.eval(expr).unpack(); + fn eval_member(&self, expr: Expression, member: Member, ctx: &mut Context) -> impl Bag { + let v = self.eval(expr.clone(), ctx).unpack(); match member { - parser::Member::Attribute(_) => todo!("Attribute"), - parser::Member::FunctionCall(_) => todo!("FunctionCall"), + 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) { + println!("{}", &attr); + return val + } + + panic!("unknown attribute {}", attr) + }, + parser::Member::FunctionCall(args) => { + println!("call args = {:?} v = {:?}, expr = {:?}", args, v, expr); + Value::Null + }, parser::Member::Index(i) => { - let i = self.eval(*i).unpack(); + 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 - }, parser::Member::Fields(_) => todo!("Fields"), } } - fn eval_map(&self, entries: Vec<(Expression, Expression)>) -> Value { + 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).unpack(); - let v = self.eval(vexpr).unpack(); + 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) -> Value { + 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).unpack(); + let v = self.eval(expr, ctx).unpack(); list.push(v); } Value::List(Rc::new(list)) } - pub fn eval(&self, expr: Expression) -> impl Bag { + 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).unpack(); - let right = self.eval(*right).unpack(); + 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"), @@ -82,8 +86,8 @@ impl Eval { Value::Bool(result) } Expression::Arithmetic(left, op, right) => { - let left = self.eval(*left).unpack(); - let right = self.eval(*right).unpack(); + let left = self.eval(*left, ctx).unpack(); + let right = self.eval(*right, ctx).unpack(); match op { ArithmeticOp::Add => left + right, ArithmeticOp::Subtract => left - right, @@ -96,10 +100,16 @@ impl Eval { Expression::Or(_, _) => todo!(), Expression::And(_, _) => todo!(), Expression::Unary(_, _) => todo!(), - Expression::Member(expr, member) => self.eval_member(*expr, *member).unpack(), - Expression::List(elems) => self.eval_list(elems), - Expression::Map(entries) => self.eval_map(entries), - Expression::Ident(r) => Value::String(r), + 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) + }, } } } diff --git a/program/src/program.rs b/program/src/program.rs index 951a734..3cd5039 100644 --- a/program/src/program.rs +++ b/program/src/program.rs @@ -30,13 +30,13 @@ impl Program { } } - pub fn execute(self, context: Context) -> bool { + pub fn execute(self, context: &mut Context) -> bool { self.eval(context).unpack().into() } - pub fn eval(self, context: Context) -> Value { - let e = Eval::new(context); - e.eval(self.expr).unpack() + pub fn eval(self, context: &mut Context) -> Value { + let e = Eval::default(); + e.eval(self.expr, context).unpack() } } @@ -44,6 +44,10 @@ impl Program { #[cfg(test)] pub mod tests { + use std::{default, rc::Rc, task::Context}; + + use crate::{program, value::{FnValue, Overload}, Value}; + macro_rules! string { ($q:literal) => { crate::Value::String(std::rc::Rc::new($q.into())) @@ -51,10 +55,13 @@ pub mod tests { } macro_rules! eval_program { ($expr:literal) => ({ + eval_program!($expr, &mut crate::context::Context::default()) + }); + ($expr:literal, $ctx:expr) => ({ let program = crate::program::Program::new($expr); assert!(program.is_ok(), "failed to create the program {:?}", program.err()); let program = program.unwrap(); - program.eval(crate::context::Context::default()) + program.eval($ctx) }); } @@ -62,4 +69,26 @@ pub mod tests { fn basic_test() { assert_eq!(eval_program!(r#"r"""#), string!("")); } -} \ No newline at end of file + + fn calc_string_string(lhs: &Value, rhs: &Value) -> Value { + Value::Null + } + + #[test] + fn fn_test() { + let func = FnValue { + name: "calc", + overloads: &[ + Overload { + key: "calc_string", + func: calc_string_string + } + ], + }; + let mut ctx = program::Context::default() + .add_variable("a", Value::String(Rc::new("".into()))) + .add_variable("b", Value::Int(0)) + .add_variable("calc", crate::Value::Function(Rc::new(func))); + assert_eq!(eval_program!(r#"b.calc(a)"#, &mut ctx), string!("")); + } +} diff --git a/program/src/value.rs b/program/src/value.rs index 46278ae..2b64269 100644 --- a/program/src/value.rs +++ b/program/src/value.rs @@ -15,6 +15,21 @@ pub enum Value { String(Rc), Map(Rc>), List(Rc>), + Function(Rc) +} + +#[derive(PartialEq, Eq, Debug)] +pub struct FnValue { + pub name: &'static str, + pub overloads: &'static [Overload], +} + +pub type Func = fn(lhs: &Value, rhs: &Value) -> Value; + +#[derive(PartialEq, Eq, Debug)] +pub struct Overload { + pub key: &'static str, + pub func: Func } impl Into for Value { @@ -28,12 +43,12 @@ impl Into for Value { 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::List(v) => v.len() > 0, + Value::Function(_) => true, } } } - impl Hash for Value { fn hash(&self, state: &mut H) { // TODO: this is not right. hash each arm separately. @@ -67,6 +82,7 @@ impl std::fmt::Display for Value { 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), } } }