difftreelog
fix(evaluator) arrays should be lazy
in: master
2 files changed
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -121,6 +121,7 @@
Ok(match (a, op, b) {
(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,
+ (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => Val::Bool(v1 == v2),
(Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => Val::Bool(v1 != v2),
(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),
@@ -146,27 +147,26 @@
)),
)?,
+ // Bool X Bool
+ (Val::Bool(a), BinaryOpType::Eq, Val::Bool(b)) => Val::Bool(a == b),
+ (Val::Bool(a), BinaryOpType::Ne, Val::Bool(b)) => Val::Bool(a != b),
+
(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),
(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),
+ // Str X Str
+ (Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),
+ (Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),
+ (Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),
+ (Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),
+
+ // Num X Num
(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),
(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),
(Val::Num(v1), BinaryOpType::Mod, Val::Num(v2)) => Val::Num(v1 % v2),
(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),
- (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {
- Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)
- }
- (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {
- Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)
- }
-
- (Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),
- (Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),
- (Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),
- (Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),
-
(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),
(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),
(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),
@@ -184,8 +184,35 @@
(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {
Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)
}
- (a, BinaryOpType::Eq, b) => Val::Bool(a == b),
- (a, BinaryOpType::Ne, b) => Val::Bool(a != b),
+ (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {
+ Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)
+ }
+ (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {
+ Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)
+ }
+
+ // Arr X Arr
+ (Val::Arr(a), BinaryOpType::Eq, Val::Arr(b)) => {
+ if a.len() != b.len() {
+ Val::Bool(false)
+ } else {
+ for i in 0..a.len() {
+ if let Val::Bool(v) = evaluate_binary_op_normal(
+ context.clone(),
+ &a[i].clone().unwrap_if_lazy()?,
+ op,
+ &b[i].clone().unwrap_if_lazy()?,
+ )? {
+ if !v {
+ return Ok(Val::Bool(false));
+ }
+ } else {
+ unreachable!()
+ }
+ }
+ return Ok(Val::Bool(true));
+ }
+ }
_ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),
})
}
@@ -212,7 +239,7 @@
Val::Arr(list) => {
let mut out = Vec::new();
for item in list {
- let item = item.clone();
+ let item = item.clone().unwrap_if_lazy()?;
out.push(evaluate_comp(
context.with_var(var.clone(), item)?,
value,
@@ -372,7 +399,8 @@
(Val::Arr(v), Val::Num(n)) => v
.get(n as usize)
.unwrap_or_else(|| panic!("out of bounds"))
- .clone(),
+ .clone()
+ .unwrap_if_lazy()?,
(Val::Str(s), Val::Num(n)) => {
Val::Str(s.chars().skip(n as usize).take(1).collect())
}
@@ -402,7 +430,11 @@
Arr(items) => {
let mut out = Vec::with_capacity(items.len());
for item in items {
- out.push(evaluate(context.clone(), item)?);
+ out.push(Val::Lazy(lazy_val!(
+ closure!(clone context, clone item, || {
+ evaluate(context.clone(), &item)
+ })
+ )));
}
Val::Arr(out)
}
crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth1use crate::{2 create_error, lazy_binding, Context, Error, FunctionDefault, FunctionRhs, LazyBinding,3 ObjValue, Result,4};5use closure::closure;6use jsonnet_parser::ParamsDesc;7use std::{8 cell::RefCell,9 collections::HashMap,10 fmt::{Debug, Display},11 rc::Rc,12};1314struct LazyValInternals {15 pub f: Box<dyn Fn() -> Result<Val>>,16 pub cached: RefCell<Option<Val>>,17}18#[derive(Clone)]19pub struct LazyVal(Rc<LazyValInternals>);20impl LazyVal {21 pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {22 LazyVal(Rc::new(LazyValInternals {23 f,24 cached: RefCell::new(None),25 }))26 }27 pub fn evaluate(&self) -> Result<Val> {28 {29 let cached = self.0.cached.borrow();30 if cached.is_some() {31 return Ok(cached.clone().unwrap());32 }33 }34 let result = (self.0.f)()?;35 self.0.cached.borrow_mut().replace(result.clone());36 Ok(result)37 }38}39#[macro_export]40macro_rules! lazy_val {41 ($f: expr) => {42 $crate::LazyVal::new(Box::new($f))43 };44}45impl Debug for LazyVal {46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {47 write!(f, "Lazy")48 }49}50impl PartialEq for LazyVal {51 fn eq(&self, other: &Self) -> bool {52 Rc::ptr_eq(&self.0, &other.0)53 }54}5556#[derive(Debug, PartialEq, Clone)]57pub struct FuncDesc {58 pub ctx: Context,59 pub params: ParamsDesc,60 pub eval_rhs: FunctionRhs,61 pub eval_default: FunctionDefault,62}63impl FuncDesc {64 // TODO: Check for unset variables65 pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Result<Val> {66 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();67 let future_ctx = Context::new_future();6869 // self.params70 // .with_defaults()71 // .into_iter()72 // .for_each(|Param(name, default)| {73 // let default = Rc::new(*default.unwrap());74 // new_bindings.insert(75 // name,76 // binding!(move |_, _| Val::Lazy(lazy_val!(|| self77 // .eval_default78 // .079 // .default(future_ctx.unwrap(), *default.clone())))),80 // );81 // });82 for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {83 new_bindings.insert(84 name.as_ref().unwrap().clone(),85 lazy_binding!(86 closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone())))))87 ),88 );89 }90 for (i, param) in self.params.0.iter().enumerate() {91 if let Some((None, val)) = args.get(i) {92 new_bindings.insert(93 param.0.clone(),94 lazy_binding!(95 closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone())))))96 ),97 );98 }99 }100 let ctx = self101 .ctx102 .extend(new_bindings, None, None, None)?103 .into_future(future_ctx);104 self.eval_rhs.0(ctx)105 }106}107108#[derive(Debug)]109pub enum ValType {110 Bool,111 Null,112 Str,113 Num,114 Arr,115 Obj,116 Func,117}118impl ValType {119 pub fn name(&self) -> &'static str {120 use ValType::*;121 match self {122 Bool => "boolean",123 Null => "null",124 Str => "string",125 Num => "number",126 Arr => "array",127 Obj => "object",128 Func => "function",129 }130 }131}132impl Display for ValType {133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {134 write!(f, "{}", self.name())135 }136}137138#[derive(Debug, PartialEq, Clone)]139pub enum Val {140 Bool(bool),141 Null,142 Str(String),143 Num(f64),144 Lazy(LazyVal),145 Arr(Vec<Val>),146 Obj(ObjValue),147 Func(FuncDesc),148149 // Library functions implemented in native150 Intristic(String, String),151}152impl Val {153 pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {154 match self.unwrap_if_lazy()? {155 Val::Bool(v) => Ok(v),156 v => create_error(Error::TypeMismatch(157 context,158 vec![ValType::Bool],159 v.value_type()?,160 )),161 }162 }163 pub fn try_cast_str(self, context: &'static str) -> Result<String> {164 match self.unwrap_if_lazy()? {165 Val::Str(v) => Ok(v),166 v => create_error(Error::TypeMismatch(167 context,168 vec![ValType::Str],169 v.value_type()?,170 )),171 }172 }173 pub fn unwrap_if_lazy(self) -> Result<Self> {174 Ok(if let Val::Lazy(v) = self {175 v.evaluate()?.unwrap_if_lazy()?176 } else {177 self178 })179 }180 pub fn value_type(&self) -> Result<ValType> {181 Ok(match self {182 Val::Str(..) => ValType::Str,183 Val::Num(..) => ValType::Num,184 Val::Arr(..) => ValType::Arr,185 Val::Obj(..) => ValType::Obj,186 Val::Func(..) => ValType::Func,187 Val::Bool(_) => ValType::Bool,188 Val::Null => ValType::Null,189 Val::Intristic(_, _) => ValType::Func,190 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,191 })192 }193}1use crate::{2 create_error, lazy_binding, Context, Error, FunctionDefault, FunctionRhs, LazyBinding,3 ObjValue, Result,4};5use closure::closure;6use jsonnet_parser::ParamsDesc;7use std::{8 cell::RefCell,9 collections::HashMap,10 fmt::{Debug, Display},11 rc::Rc,12};1314struct LazyValInternals {15 pub f: Box<dyn Fn() -> Result<Val>>,16 pub cached: RefCell<Option<Val>>,17}18#[derive(Clone)]19pub struct LazyVal(Rc<LazyValInternals>);20impl LazyVal {21 pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {22 LazyVal(Rc::new(LazyValInternals {23 f,24 cached: RefCell::new(None),25 }))26 }27 pub fn evaluate(&self) -> Result<Val> {28 {29 let cached = self.0.cached.borrow();30 if cached.is_some() {31 return Ok(cached.clone().unwrap());32 }33 }34 let result = (self.0.f)()?;35 self.0.cached.borrow_mut().replace(result.clone());36 Ok(result)37 }38}39#[macro_export]40macro_rules! lazy_val {41 ($f: expr) => {42 $crate::LazyVal::new(Box::new($f))43 };44}45impl Debug for LazyVal {46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {47 if self.0.cached.borrow().is_some() {48 write!(f, "{:?}", self.0.cached.borrow().clone().unwrap())49 } else {50 write!(f, "Lazy")51 }52 }53}54impl PartialEq for LazyVal {55 fn eq(&self, other: &Self) -> bool {56 Rc::ptr_eq(&self.0, &other.0)57 }58}5960#[derive(Debug, PartialEq, Clone)]61pub struct FuncDesc {62 pub ctx: Context,63 pub params: ParamsDesc,64 pub eval_rhs: FunctionRhs,65 pub eval_default: FunctionDefault,66}67impl FuncDesc {68 // TODO: Check for unset variables69 pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Result<Val> {70 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();71 let future_ctx = Context::new_future();7273 // self.params74 // .with_defaults()75 // .into_iter()76 // .for_each(|Param(name, default)| {77 // let default = Rc::new(*default.unwrap());78 // new_bindings.insert(79 // name,80 // binding!(move |_, _| Val::Lazy(lazy_val!(|| self81 // .eval_default82 // .083 // .default(future_ctx.unwrap(), *default.clone())))),84 // );85 // });86 for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {87 new_bindings.insert(88 name.as_ref().unwrap().clone(),89 lazy_binding!(90 closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone())))))91 ),92 );93 }94 for (i, param) in self.params.0.iter().enumerate() {95 if let Some((None, val)) = args.get(i) {96 new_bindings.insert(97 param.0.clone(),98 lazy_binding!(99 closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone())))))100 ),101 );102 }103 }104 let ctx = self105 .ctx106 .extend(new_bindings, None, None, None)?107 .into_future(future_ctx);108 self.eval_rhs.0(ctx)109 }110}111112#[derive(Debug)]113pub enum ValType {114 Bool,115 Null,116 Str,117 Num,118 Arr,119 Obj,120 Func,121}122impl ValType {123 pub fn name(&self) -> &'static str {124 use ValType::*;125 match self {126 Bool => "boolean",127 Null => "null",128 Str => "string",129 Num => "number",130 Arr => "array",131 Obj => "object",132 Func => "function",133 }134 }135}136impl Display for ValType {137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {138 write!(f, "{}", self.name())139 }140}141142#[derive(Debug, PartialEq, Clone)]143pub enum Val {144 Bool(bool),145 Null,146 Str(String),147 Num(f64),148 Lazy(LazyVal),149 Arr(Vec<Val>),150 Obj(ObjValue),151 Func(FuncDesc),152153 // Library functions implemented in native154 Intristic(String, String),155}156impl Val {157 pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {158 match self.unwrap_if_lazy()? {159 Val::Bool(v) => Ok(v),160 v => create_error(Error::TypeMismatch(161 context,162 vec![ValType::Bool],163 v.value_type()?,164 )),165 }166 }167 pub fn try_cast_str(self, context: &'static str) -> Result<String> {168 match self.unwrap_if_lazy()? {169 Val::Str(v) => Ok(v),170 v => create_error(Error::TypeMismatch(171 context,172 vec![ValType::Str],173 v.value_type()?,174 )),175 }176 }177 pub fn unwrap_if_lazy(self) -> Result<Self> {178 Ok(if let Val::Lazy(v) = self {179 v.evaluate()?.unwrap_if_lazy()?180 } else {181 self182 })183 }184 pub fn value_type(&self) -> Result<ValType> {185 Ok(match self {186 Val::Str(..) => ValType::Str,187 Val::Num(..) => ValType::Num,188 Val::Arr(..) => ValType::Arr,189 Val::Obj(..) => ValType::Obj,190 Val::Func(..) => ValType::Func,191 Val::Bool(_) => ValType::Bool,192 Val::Null => ValType::Null,193 Val::Intristic(_, _) => ValType::Func,194 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,195 })196 }197}