Skip to content

Commit

Permalink
feat: function calls
Browse files Browse the repository at this point in the history
  • Loading branch information
thesayyn committed Mar 11, 2024
1 parent 81cc1d1 commit 0ff68dc
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 48 deletions.
4 changes: 2 additions & 2 deletions cel_spec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}}
Expand Down
30 changes: 25 additions & 5 deletions program/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
use std::{collections::HashMap, rc::Rc};

use crate::{eval::Bag, Value};

#[derive(Default)]
pub struct Context {

par: Option<Rc<Context>>,
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<Value> {
// TODO: this is probably expensive to do.
self.vars.get(name.as_str()).map(|f| f.to_owned())
}
pub fn parent(&self) -> Option<Rc<Context>> {
self.par.clone()
}
}
76 changes: 43 additions & 33 deletions program/src/eval.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -7,69 +8,72 @@ pub trait Bag {
fn unpack(self) -> Value;
}

/// Indexer permits random access of elements by index ```a[b()]```.
pub trait Indexer<T> 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<Expression>) -> Value {
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).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"),
Expand All @@ -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,
Expand All @@ -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)
},
}
}
}
41 changes: 35 additions & 6 deletions program/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,36 +30,65 @@ 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()
}
}



#[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()))
};
}
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)
});
}

#[test]
fn basic_test() {
assert_eq!(eval_program!(r#"r"""#), string!(""));
}
}

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!(""));
}
}
20 changes: 18 additions & 2 deletions program/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@ pub enum Value {
String(Rc<String>),
Map(Rc<OrderedHashMap<Value, Value>>),
List(Rc<Vec<Value>>),
Function(Rc<FnValue>)
}

#[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<bool> for Value {
Expand All @@ -28,12 +43,12 @@ impl Into<bool> 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<H: std::hash::Hasher>(&self, state: &mut H) {
// TODO: this is not right. hash each arm separately.
Expand Down Expand Up @@ -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),
}
}
}
Expand Down

0 comments on commit 0ff68dc

Please sign in to comment.