difftreelog
refactor(evaluator) use closures where possible
in: master
9 files changed
crates/jsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jsonnet-evaluator/Cargo.toml
+++ b/crates/jsonnet-evaluator/Cargo.toml
@@ -8,6 +8,7 @@
[dependencies]
jsonnet-parser = { path = "../jsonnet-parser" }
+closure = "0.3.0"
[dev-dependencies]
jsonnet-stdlib = { version = "0.1.0", path = "../jsonnet-stdlib" }
crates/jsonnet-evaluator/src/binding.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/binding.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-use crate::{
- ArgsBindingLazyVal, BoxedContextCreator, BoxedLazyVal, NoArgsBindingLazyVal, ObjValue, Val,
-};
-use jsonnet_parser::{Expr, ParamsDesc};
-use std::{fmt::Debug, rc::Rc};
-
-pub trait Binding: Debug {
- fn evaluate(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Val;
-}
-pub type BoxedBinding = Rc<dyn Binding>;
-
-#[derive(Debug)]
-pub struct NoArgsBinding {
- pub expr: Expr,
- pub context_creator: BoxedContextCreator,
-}
-impl Binding for NoArgsBinding {
- fn evaluate(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Val {
- Val::Lazy(BoxedLazyVal(Rc::new(NoArgsBindingLazyVal {
- context_creator: self.context_creator.clone(),
- expr: self.expr.clone(),
- this,
- super_obj,
- })))
- }
-}
-#[derive(Debug)]
-pub struct ArgsBinding {
- pub expr: Expr,
- pub args: ParamsDesc,
- pub context_creator: BoxedContextCreator,
-}
-impl Binding for ArgsBinding {
- fn evaluate(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Val {
- Val::Lazy(BoxedLazyVal(Rc::new(ArgsBindingLazyVal {
- context_creator: self.context_creator.clone(),
- expr: self.expr.clone(),
- args: self.args.clone(),
- this,
- super_obj,
- })))
- }
-}
crates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/ctx.rs
+++ b/crates/jsonnet-evaluator/src/ctx.rs
@@ -1,20 +1,11 @@
-use crate::{dummy_debug, future_wrapper, BoxedBinding, ObjValue};
+use crate::{future_wrapper, rc_fn_helper, Binding, ObjValue};
use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
-
-pub trait ContextCreator: Debug {
- fn create_context(&self, this: &Option<ObjValue>, super_obj: &Option<ObjValue>) -> Context;
-}
-pub type BoxedContextCreator = Rc<dyn ContextCreator>;
-#[derive(Debug)]
-pub struct ConstantContextCreator {
- pub context: FutureContext,
-}
-impl ContextCreator for ConstantContextCreator {
- fn create_context(&self, _this: &Option<ObjValue>, _super_obj: &Option<ObjValue>) -> Context {
- self.context.clone().unwrap()
- }
-}
+rc_fn_helper!(
+ ContextCreator,
+ context_creator,
+ dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Context
+);
future_wrapper!(Context, FutureContext);
@@ -23,10 +14,16 @@
dollar: Option<ObjValue>,
this: Option<ObjValue>,
super_obj: Option<ObjValue>,
- bindings: Rc<RefCell<HashMap<String, BoxedBinding>>>,
+ bindings: Rc<RefCell<HashMap<String, Binding>>>,
}
pub struct Context(Rc<ContextInternals>);
-dummy_debug!(Context);
+impl Debug for Context {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Context")
+ .field("this", &self.0.this.clone().map(|e| Rc::as_ptr(&e.0)))
+ .finish()
+ }
+}
impl Context {
pub fn new_future() -> FutureContext {
FutureContext(Rc::new(RefCell::new(None)))
@@ -53,12 +50,12 @@
}))
}
- pub fn binding(&self, name: &str) -> BoxedBinding {
+ pub fn binding(&self, name: &str) -> Binding {
self.0
.bindings
.borrow()
.get(name)
- .map(|e| e.clone())
+ .cloned()
.unwrap_or_else(|| {
panic!("can't find {} in {:?}", name, self);
})
@@ -72,14 +69,15 @@
pub fn extend(
&self,
- new_bindings: HashMap<String, BoxedBinding>,
+ new_bindings: HashMap<String, Binding>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
) -> Context {
- let dollar = new_dollar.or(self.0.dollar.clone());
- let this = new_this.or(self.0.this.clone());
- let super_obj = new_super_obj.or(self.0.super_obj.clone());
+ println!("Extend with {:?} {:?}", new_dollar, new_this);
+ let dollar = new_dollar.or_else(|| self.0.dollar.clone());
+ let this = new_this.or_else(|| self.0.this.clone());
+ let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());
let bindings = if new_bindings.is_empty() {
self.0.bindings.clone()
} else {
@@ -97,6 +95,13 @@
}))
}
}
+
+impl Default for Context {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
impl PartialEq for Context {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
crates/jsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/dynamic.rs
+++ b/crates/jsonnet-evaluator/src/dynamic.rs
@@ -1,23 +1,4 @@
#[macro_export]
-macro_rules! dynamic_wrapper {
- ($orig: ident, $wrapper: ident) => {
- #[derive(Debug, Clone)]
- pub struct $wrapper(pub std::rc::Rc<dyn $orig>);
- impl std::ops::Deref for $wrapper {
- type Target = dyn $orig;
- fn deref(&self) -> &Self::Target {
- &*self.0
- }
- }
- impl std::cmp::PartialEq for $wrapper {
- fn eq(&self, other: &Self) -> bool {
- Rc::ptr_eq(&self.0, &other.0)
- }
- }
- };
-}
-
-#[macro_export]
macro_rules! future_wrapper {
($orig: ty, $wrapper: ident) => {
#[derive(Debug, Clone)]
@@ -39,17 +20,36 @@
self.unwrap()
}
}
+ impl Default for $wrapper {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
};
}
#[macro_export]
-macro_rules! dummy_debug {
- ($struct: ident) => {
- impl std::fmt::Debug for $struct {
+macro_rules! rc_fn_helper {
+ ($name: ident, $macro_name: ident, $fn: ty) => {
+ #[derive(Clone)]
+ #[doc = "Function wrapper"]
+ pub struct $name(pub std::rc::Rc<$fn>);
+ impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct(std::stringify!($struct))
- .finish_non_exhaustive()
+ f.debug_struct(std::stringify!($name)).finish()
+ }
+ }
+ impl std::cmp::PartialEq for $name {
+ fn eq(&self, other: &$name) -> bool {
+ std::ptr::eq(&self.0, &other.0)
}
}
+ #[doc = "Macro to ease wrapper creation"]
+ #[macro_export]
+ macro_rules! $macro_name {
+ ($val: expr) => {
+ $crate::$name(std::rc::Rc::new($val))
+ };
+ }
};
}
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth1use crate::BoxedLazyVal;2use crate::{1use crate::{3 bool_val, ArgsBinding, BoxedBinding, BoxedContextCreator, ConstantContextCreator, Context,2 binding, bool_val, context_creator, function_default, function_rhs, future_wrapper, lazy_val,4 FuncDesc, FunctionDefault, FunctionRhs, NoArgsBinding, Val,3 Binding, Context, ContextCreator, FuncDesc, ObjMember, ObjValue, Val,5};4};6use crate::{5use closure::closure;7 future_wrapper, BoxedFunctionDefault, BoxedFunctionRhs, ContextCreator, ObjMember, ObjValue,8 PlainLazyVal,9};10use jsonnet_parser::{6use jsonnet_parser::{11 ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, Member, ObjBody, ParamsDesc,7 ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, Member, ObjBody, ParamsDesc,12 Visibility,8 UnaryOpType, Visibility,13};9};14use std::{10use std::{15 cell::RefCell,16 collections::{BTreeMap, HashMap},11 collections::{BTreeMap, HashMap},17 rc::Rc,12 rc::Rc,18};13};191420pub fn evaluate_binding<'t>(15pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, Binding) {21 b: &BindSpec,16 let b = b.clone();22 context_creator: BoxedContextCreator,23) -> (String, BoxedBinding) {24 if let Some(args) = &b.params {17 if let Some(args) = &b.params {18 let args = args.clone();25 (19 (26 b.name.clone(),20 b.name.clone(),27 Rc::new(ArgsBinding {21 binding!(move |this, super_obj| Val::Lazy(lazy_val!(28 expr: *b.value.clone(),22 closure!(clone b, clone args, clone context_creator, || evaluate_method(29 args: args.clone(),23 context_creator.0(this.clone(), super_obj.clone()),24 &b.value,30 context_creator: context_creator.clone(),25 args.clone()26 ))31 }),27 ))),32 )28 )33 } else {29 } else {34 (30 (35 b.name.clone(),31 b.name.clone(),32 binding!(move |this, super_obj| {33 println!("Evaluating binding");36 Rc::new(NoArgsBinding {34 Val::Lazy(lazy_val!(37 expr: *b.value.clone(),35 closure!(clone context_creator, clone b, || evaluate(38 context_creator: context_creator.clone(),36 context_creator.0(this.clone(), super_obj.clone()),37 &b.value38 ))39 }) as BoxedBinding,39 ))40 }),40 )41 )41 }42 }42}43}4344#[derive(Debug)]45struct MethodRhs {46 rhs: Expr,47}48impl FunctionRhs for MethodRhs {49 fn evaluate(&self, ctx: Context) -> Val {50 evaluate(ctx, &self.rhs)51 }52}5354#[derive(Debug)]55struct MethodDefault {}56impl FunctionDefault for MethodDefault {57 fn default(&self, ctx: Context, expr: Expr) -> Val {58 evaluate(ctx, &expr)59 }60}614462pub fn evaluate_method(ctx: Context, expr: &Expr, arg_spec: ParamsDesc) -> Val {45pub fn evaluate_method(ctx: Context, expr: &Expr, arg_spec: ParamsDesc) -> Val {63 Val::Func(FuncDesc {46 Val::Func(FuncDesc {64 ctx,47 ctx,65 params: arg_spec,48 params: arg_spec,66 eval_rhs: BoxedFunctionRhs(Rc::new(MethodRhs { rhs: expr.clone() })),49 eval_rhs: function_rhs!(closure!(clone expr, |ctx| evaluate(ctx, &expr))),67 eval_default: BoxedFunctionDefault(Rc::new(MethodDefault {})),50 eval_default: function_default!(|ctx, default| evaluate(ctx, &default)),68 })51 })69}52}705374 jsonnet_parser::FieldName::Dyn(expr) => {57 jsonnet_parser::FieldName::Dyn(expr) => {75 let name = evaluate(context, expr).unwrap_if_lazy();58 let name = evaluate(context, expr).unwrap_if_lazy();76 match name {59 match name {77 Val::Str(n) => n.clone(),60 Val::Str(n) => n,78 _ => panic!(61 _ => panic!(79 "dynamic field name can be only evaluated to 'string', got: {:?}",62 "dynamic field name can be only evaluated to 'string', got: {:?}",80 name63 name84 }67 }85}68}6970pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val {71 match (op, b) {72 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.0()),73 (UnaryOpType::Not, Val::Literal(LiteralType::True)) => Val::Literal(LiteralType::False),74 (UnaryOpType::Not, Val::Literal(LiteralType::False)) => Val::Literal(LiteralType::True),75 (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),76 }77}867887pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {79pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {88 match (a, op, b) {80 match (a, op, b) {89 (Val::Lazy(l), o, r) => evaluate_binary_op(&l.evaluate(), o, r),81 (Val::Lazy(a), o, b) => evaluate_binary_op(&a.0(), o, b),90 (l, o, Val::Lazy(r)) => evaluate_binary_op(l, o, &r.evaluate()),82 (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.0()),918392 (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),84 (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),93 (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),85 (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),119 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => bool_val(v1 <= v2),111 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => bool_val(v1 <= v2),120 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => bool_val(v1 >= v2),112 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => bool_val(v1 >= v2),121113122 (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val(v1 == v2),114 (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val((v1 - v2).abs() < f64::EPSILON),123 (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val(v1 != v2),115 (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val((v1 - v2).abs() > f64::EPSILON),124116125 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {117 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {126 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)118 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)135 }127 }136}128}137129138future_wrapper!(HashMap<String, BoxedBinding>, FutureNewBindings);130future_wrapper!(HashMap<String, Binding>, FutureNewBindings);139140#[derive(Debug)]141pub struct ObjectContextCreator {142 original: Context,143 future_bindings: FutureNewBindings,144}145146impl ContextCreator for ObjectContextCreator {147 fn create_context(&self, this: &Option<ObjValue>, super_obj: &Option<ObjValue>) -> Context {131future_wrapper!(ObjValue, FutureObjValue);148 self.original.extend(149 self.future_bindings.clone().unwrap(),150 self.original.dollar().clone().or_else(|| this.clone()),151 this.clone(),152 super_obj.clone(),153 )154 }155}156132157// TODO: Asserts133// TODO: Asserts158pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {134pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {159 match object {135 match object {160 ObjBody::MemberList(members) => {136 ObjBody::MemberList(members) => {161 let future_bindings = FutureNewBindings::new();137 let future_bindings = FutureNewBindings::new();138 let future_this = FutureObjValue::new();162 let binding_context_creator = Rc::new(ObjectContextCreator {139 let context_creator = context_creator!(163 future_bindings: future_bindings.clone(),140 closure!(clone context, clone future_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {141 println!("Context created");142 context.clone().extend(143 future_bindings.clone().unwrap(),164 original: context.clone(),144 context.clone().dollar().clone().or_else(||this.clone()),145 this,146 super_obj147 )148 })165 });149 );166 let mut bindings: HashMap<String, BoxedBinding> = HashMap::new();150 let mut bindings: HashMap<String, Binding> = HashMap::new();167 for (n, b) in members151 for (n, b) in members168 .iter()152 .iter()169 .filter_map(|m| match m {153 .filter_map(|m| match m {170 Member::BindStmt(b) => Some(b.clone()),154 Member::BindStmt(b) => Some(b.clone()),171 _ => None,155 _ => None,172 })156 })173 .map(|b| evaluate_binding(&b, binding_context_creator.clone()))157 .map(|b| {158 evaluate_binding(&b, context_creator.clone())159 })174 {160 {175 bindings.insert(n, b);161 bindings.insert(n, b);176 }162 }177 let bindings = future_bindings.fill(bindings);163 future_bindings.fill(bindings);164165 println!("Bindings filled");178 let mut new_members = BTreeMap::new();166 let mut new_members = BTreeMap::new();179 for member in members.iter() {167 for member in members.into_iter() {180 match member {168 match member {181 Member::Field(FieldMember {169 Member::Field(FieldMember {182 name,170 name,185 visibility,173 visibility,186 value,174 value,187 }) => {175 }) => {188 let name = evaluate_field_name(context.clone(), name);176 let name = evaluate_field_name(context.clone(), &name);189 new_members.insert(177 new_members.insert(190 name,178 name,191 ObjMember {179 ObjMember {192 add: *plus,180 add: plus,193 visibility: visibility.clone(),181 visibility: visibility.clone(),194 invoke: Rc::new(NoArgsBinding {182 invoke: binding!(183 closure!(clone value, clone context_creator, clone future_this, |this, super_obj| {184 // FIXME: I should take "this" instead of "future_this" there?185 // TODO: Assert186 evaluate(195 context_creator: binding_context_creator.clone(),187 context_creator.0(this, super_obj),196 expr: value.clone(),188 &value,189 )197 }),190 })191 ),198 },192 },199 );193 );204 value,198 value,205 ..199 ..206 }) => {200 }) => {207 let name = evaluate_field_name(context.clone(), name);201 let name = evaluate_field_name(context.clone(), &name);208 new_members.insert(202 new_members.insert(209 name,203 name,210 ObjMember {204 ObjMember {211 add: false,205 add: false,212 visibility: Visibility::Hidden,206 visibility: Visibility::Hidden,213 invoke: Rc::new(ArgsBinding {207 invoke: binding!(214 expr: value.clone(),208 closure!(clone value, clone context_creator, clone future_this, |this, super_obj| {209 // FIXME: I should take "this" instead of "future_this" there?210 // TODO: Assert211 evaluate_method(212 context_creator.0(this, super_obj),215 args: params.clone(),213 &value.clone(),216 context_creator: binding_context_creator.clone(),214 params.clone(),215 )216 })217 }),217 ),218 },218 },219 );219 );220 }220 }221 Member::BindStmt(_) => {}221 Member::BindStmt(_) => {}222 Member::AssertStmt(_) => {}222 Member::AssertStmt(_) => {}223 _ => todo!(),224 }223 }225 }224 }226 ObjValue::new(None, Rc::new(new_members))225 future_this.fill(ObjValue::new(None, Rc::new(new_members)))227 }226 }228 _ => todo!(),227 _ => todo!(),229 }228 }232pub fn evaluate(context: Context, expr: &Expr) -> Val {231pub fn evaluate(context: Context, expr: &Expr) -> Val {233 use Expr::*;232 use Expr::*;234 match &*expr {233 match &*expr {234 Literal(LiteralType::This) => {235 println!("{:?}", context.this());236 Val::Obj(237 context238 .this()239 .clone()240 .unwrap_or_else(|| panic!("this not found")),241 )242 }243 Literal(LiteralType::Super) => Val::Obj(244 context245 .super_obj()246 .clone()247 .unwrap_or_else(|| panic!("super not found")),248 ),235 Literal(t) => Val::Literal(t.clone()),249 Literal(t) => Val::Literal(t.clone()),236 Parened(e) => evaluate(context, e),250 Parened(e) => evaluate(context, e),237 Str(v) => Val::Str(v.clone()),251 Str(v) => Val::Str(v.clone()),238 Num(v) => Val::Num(*v),252 Num(v) => Val::Num(*v),239 BinaryOp(v1, o, v2) => {253 BinaryOp(v1, o, v2) => {240 evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))254 evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))241 }255 }256 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)),242 Var(name) => {257 Var(name) => {243 let variable = context.binding(&name);258 let variable = context.binding(&name);244 let val = variable.evaluate(None, None);259 variable.0(None, None).unwrap_if_lazy()245 val246 }260 }247 Index(box value, box index) => {261 Index(box value, box index) => {248 match (262 match (249 evaluate(context.clone(), value).unwrap_if_lazy(),263 evaluate(context.clone(), value).unwrap_if_lazy(),250 evaluate(context.clone(), index),264 evaluate(context, index),251 ) {265 ) {252 (Val::Literal(LiteralType::Super), _idx) => todo!(),253 (Val::Literal(LiteralType::This), idx) => match &idx.unwrap_if_lazy() {266 (Val::Obj(v), Val::Str(s)) => v254 Val::Str(str) => context255 .this()267 .get(&s)256 .clone()257 .unwrap_or_else(|| panic!("'this' is not defined in current context"))258 .get_raw(str, None)259 .unwrap_or_else(|| {268 .unwrap_or_else(|| panic!("{} not found in {:?}", s, v)),260 panic!(261 "key {} not found in current context 'this' ({:?})",262 str,263 context.this()264 )265 }),266 _ => panic!("bad index"),267 },268 (Val::Obj(v), Val::Str(s)) => v269 (Val::Arr(v), Val::Num(n)) => v270 .get(n as usize)269 .get_raw(&s, None)271 .unwrap_or_else(|| panic!("out of bounds"))270 .unwrap_or_else(|| panic!("{} not found in {:?}", s, v)),272 .clone(),271 (v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),273 (v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),272 }274 }273 }275 }274 LocalExpr(bindings, returned) => {276 LocalExpr(bindings, returned) => {275 let mut new_bindings: HashMap<String, BoxedBinding> = HashMap::new();277 let mut new_bindings: HashMap<String, Binding> = HashMap::new();276 let future_context = Context::new_future();278 let future_context = Context::new_future();277279278 let context_creator = Rc::new(ConstantContextCreator {280 let context_creator = context_creator!(279 context: future_context.clone(),281 closure!(clone future_context, |_, _| future_context.clone().unwrap())280 });282 );283281 for (k, v) in bindings284 for (k, v) in bindings282 .iter()285 .iter()299 .into_iter()302 .into_iter()300 .map(|a| {303 .map(|a| {301 (304 (302 a.0,305 a.clone().0,303 Val::Lazy(BoxedLazyVal(Rc::new(PlainLazyVal {306 Val::Lazy(lazy_val!(304 context: context.clone(),307 closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))305 expr: *a.1,306 }))),308 )),307 )309 )308 })310 })309 .collect(),311 .collect(),318 cond_then,320 cond_then,319 cond_else,321 cond_else,320 } => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {322 } => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {321 Val::Literal(LiteralType::True) => evaluate(context.clone(), cond_then),323 Val::Literal(LiteralType::True) => evaluate(context, cond_then),322 Val::Literal(LiteralType::False) => match cond_else {324 Val::Literal(LiteralType::False) => match cond_else {323 Some(v) => evaluate(context.clone(), v),325 Some(v) => evaluate(context, v),324 None => Val::Literal(LiteralType::False),326 None => Val::Literal(LiteralType::False),325 },327 },326 v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),328 v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -1,44 +1,50 @@
#![feature(box_syntax, box_patterns)]
#![feature(type_alias_impl_trait)]
#![feature(debug_non_exhaustive)]
-
-mod binding;
+#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]
mod ctx;
mod dynamic;
mod evaluate;
mod obj;
mod val;
-pub use binding::*;
pub use ctx::*;
pub use dynamic::*;
pub use evaluate::*;
use jsonnet_parser::*;
pub use obj::*;
-use std::fmt::Debug;
-use std::rc::Rc;
pub use val::*;
-pub trait FunctionRhs: Debug {
- fn evaluate(&self, ctx: Context) -> Val;
-}
-dynamic_wrapper!(FunctionRhs, BoxedFunctionRhs);
-
-pub trait FunctionDefault: Debug {
- fn default(&self, ctx: Context, expr: Expr) -> Val;
-}
-dynamic_wrapper!(FunctionDefault, BoxedFunctionDefault);
+rc_fn_helper!(
+ Binding,
+ binding,
+ dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val
+);
+rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);
+rc_fn_helper!(
+ FunctionDefault,
+ function_default,
+ dyn Fn(Context, Expr) -> Val
+);
#[cfg(test)]
pub mod tests {
use super::{evaluate, Context, Val};
use jsonnet_parser::*;
- // macro_rules! eval {
- // ($str: expr) => {
- // evaluate(Context::new(), &parse($str).unwrap())
- // };
- // }
+ macro_rules! eval {
+ ($str: expr) => {
+ evaluate(Context::new(), &parse($str).unwrap())
+ };
+ }
+
+ macro_rules! eval_stdlib {
+ ($str: expr) => {
+ let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";
+ evaluate(Context::new(), &parse(&(std + $str)).unwrap())
+ };
+ }
+
macro_rules! assert_eval {
($str: expr) => {
assert_eq!(
@@ -165,23 +171,48 @@
);
}
+ #[test]
+ fn direct_self() {
+ println!(
+ "{:#?}",
+ eval!(
+ r#"
+ {
+ local me = self,
+ a: 3,
+ b(): me.a,
+ }
+ "#
+ )
+ );
+ }
+
+ #[test]
+ fn indirect_self() {
+ // `self` assigned to `me` was lost when being
+ // referenced from field
+ eval_stdlib!(
+ r#"{
+ local me = std.trace("test", self),
+ b: me,
+ }.b"#
+ );
+ }
+
// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly
#[test]
fn std_assert_ok() {
- let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";
- evaluate(
- Context::new(),
- &parse(&(std + "std.assertEqual(4.5 << 2, 16,)")).unwrap(),
- );
+ eval_stdlib!("std.assertEqual(4.5 << 2, 16)");
}
#[test]
#[should_panic]
fn std_assert_failure() {
- let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";
- evaluate(
- Context::new(),
- &parse(&(std + "std.assertEqual(4.5 << 2, 15,)")).unwrap(),
- );
+ eval_stdlib!("std.assertEqual(4.5 << 2, 15)");
+ }
+
+ #[test]
+ fn base64_works() {
+ eval_stdlib!(r#"std.base64("test")"#);
}
}
crates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/obj.rs
+++ b/crates/jsonnet-evaluator/src/obj.rs
@@ -1,7 +1,8 @@
-use crate::{dummy_debug, evaluate_binary_op, BoxedBinding, Val};
+use crate::{evaluate_binary_op, Binding, Val};
use jsonnet_parser::{BinaryOpType, Visibility};
use std::{
collections::{BTreeMap, BTreeSet},
+ fmt::Debug,
rc::Rc,
};
@@ -9,7 +10,7 @@
pub struct ObjMember {
pub add: bool,
pub visibility: Visibility,
- pub invoke: BoxedBinding,
+ pub invoke: Binding,
}
#[derive(Debug)]
@@ -17,8 +18,28 @@
super_obj: Option<ObjValue>,
this_entries: Rc<BTreeMap<String, ObjMember>>,
}
-pub struct ObjValue(Rc<ObjValueInternals>);
-dummy_debug!(ObjValue);
+pub struct ObjValue(pub(crate) Rc<ObjValueInternals>);
+impl Debug for ObjValue {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ if let Some(super_obj) = self.0.super_obj.as_ref() {
+ if f.alternate() {
+ write!(f, "{:#?}", super_obj)?;
+ } else {
+ write!(f, "{:?}", super_obj)?;
+ }
+ write!(f, " + ")?;
+ }
+ let mut debug = f.debug_struct("ObjValue");
+ debug.field("$ptr", &Rc::as_ptr(&self.0));
+ for (name, member) in self.0.this_entries.iter() {
+ debug.field(name, member);
+ }
+ debug.finish_non_exhaustive()
+ // .field("fields", &self.fields())
+ // .finish_non_exhaustive()
+ }
+}
+
impl ObjValue {
pub fn new(
super_obj: Option<ObjValue>,
@@ -47,26 +68,30 @@
}
fields
}
- pub fn get_raw(&self, key: &str, real_this: Option<ObjValue>) -> Option<Val> {
+ pub fn get(&self, key: &str) -> Option<Val> {
+ // TODO: Cache get_raw result
+ self.get_raw(key, Some(self))
+ }
+ fn get_raw(&self, key: &str, real_this: Option<&ObjValue>) -> Option<Val> {
match (self.0.this_entries.get(key), &self.0.super_obj) {
- (Some(k), None) => Some(k.invoke.evaluate(
- real_this.or_else(|| Some(self.clone())),
- self.0.super_obj.clone().map(|e| e.clone()),
+ (Some(k), None) => Some(k.invoke.0(
+ real_this.as_ref().map(|e| (*e).clone()),
+ self.0.super_obj.clone(),
)),
(Some(k), Some(s)) => {
- let our = k
- .invoke
- .evaluate(Some(self.clone()), self.0.super_obj.clone());
+ let our = k.invoke.0(
+ real_this.as_ref().map(|e| (*e).clone()),
+ self.0.super_obj.clone(),
+ );
if k.add {
- s.get_raw(key, Some(self.clone()))
- .map_or(Some(our.clone()), |v| {
- Some(evaluate_binary_op(&v, BinaryOpType::Add, &our))
- })
+ s.get_raw(key, real_this).map_or(Some(our.clone()), |v| {
+ Some(evaluate_binary_op(&v, BinaryOpType::Add, &our))
+ })
} else {
Some(our)
}
}
- (None, Some(s)) => s.get_raw(key, Some(self.clone())),
+ (None, Some(s)) => s.get_raw(key, real_this),
(None, None) => None,
}
}
crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/val.rs
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -1,135 +1,62 @@
-use crate::{
- dynamic_wrapper, evaluate, evaluate_method, BoxedContextCreator, Context, FunctionDefault,
- FunctionRhs, ObjValue,
-};
-use crate::{Binding, BoxedBinding, BoxedFunctionDefault, BoxedFunctionRhs, FutureContext};
-use jsonnet_parser::{ArgsDesc, Expr, LiteralType, Param, ParamsDesc};
+use crate::{binding, rc_fn_helper, Binding, Context, FunctionDefault, FunctionRhs, ObjValue};
+use closure::closure;
+use jsonnet_parser::{LiteralType, ParamsDesc};
use std::{
collections::HashMap,
fmt::{Debug, Display},
- ops::Deref,
- rc::Rc,
};
-pub trait LazyVal: Debug {
- fn evaluate(&self) -> Val;
-}
-dynamic_wrapper!(LazyVal, BoxedLazyVal);
-
-#[derive(Debug)]
-pub struct PlainLazyVal {
- pub expr: Expr,
- pub context: Context,
-}
-impl LazyVal for PlainLazyVal {
- fn evaluate(&self) -> Val {
- evaluate(self.context.clone(), &self.expr)
- }
-}
-
-#[derive(Debug)]
-pub struct NoArgsBindingLazyVal {
- pub expr: Expr,
- pub context_creator: BoxedContextCreator,
-
- pub this: Option<ObjValue>,
- pub super_obj: Option<ObjValue>,
-}
-impl LazyVal for NoArgsBindingLazyVal {
- fn evaluate(&self) -> Val {
- evaluate(
- self.context_creator
- .create_context(&self.this, &self.super_obj),
- &self.expr,
- )
- }
-}
-
-#[derive(Debug)]
-pub struct ArgsBindingLazyVal {
- pub expr: Expr,
- pub args: ParamsDesc,
- pub context_creator: BoxedContextCreator,
-
- pub this: Option<ObjValue>,
- pub super_obj: Option<ObjValue>,
-}
-impl LazyVal for ArgsBindingLazyVal {
- fn evaluate(&self) -> Val {
- evaluate_method(
- self.context_creator
- .create_context(&self.this, &self.super_obj),
- &self.expr,
- self.args.clone(),
- )
- }
-}
-
-#[derive(Debug)]
-pub struct FunctionDefaultBinding {
- eval: BoxedFunctionDefault,
- default: Expr,
- ctx: FutureContext,
-}
-impl Binding for FunctionDefaultBinding {
- fn evaluate(&self, _this: Option<ObjValue>, _super_obj: Option<ObjValue>) -> Val {
- self.eval
- .default(self.ctx.clone().unwrap(), self.default.clone())
- }
-}
+rc_fn_helper!(LazyVal, lazy_val, dyn Fn() -> Val);
-#[derive(Debug)]
-pub struct ValBinding {
- val: Val,
-}
-impl Binding for ValBinding {
- fn evaluate(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Val {
- self.val.clone()
- }
-}
-
#[derive(Debug, PartialEq, Clone)]
pub struct FuncDesc {
pub ctx: Context,
pub params: ParamsDesc,
- pub eval_rhs: BoxedFunctionRhs,
- pub eval_default: BoxedFunctionDefault,
+ pub eval_rhs: FunctionRhs,
+ pub eval_default: FunctionDefault,
}
impl FuncDesc {
// TODO: Check for unset variables
pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Val {
- let mut new_bindings: HashMap<String, BoxedBinding> = HashMap::new();
+ let mut new_bindings: HashMap<String, Binding> = HashMap::new();
let future_ctx = Context::new_future();
- self.params
- .with_defaults()
- .into_iter()
- .for_each(|Param(name, default)| {
- new_bindings.insert(
- name,
- Rc::new(FunctionDefaultBinding {
- eval: self.eval_default.clone(),
- default: *default.unwrap().clone(),
- ctx: future_ctx.clone(),
- }),
- );
- });
- for (name, val) in args.iter().filter(|e| e.0.is_some()) {
+ // self.params
+ // .with_defaults()
+ // .into_iter()
+ // .for_each(|Param(name, default)| {
+ // let default = Rc::new(*default.unwrap());
+ // new_bindings.insert(
+ // name,
+ // binding!(move |_, _| Val::Lazy(lazy_val!(|| self
+ // .eval_default
+ // .0
+ // .default(future_ctx.unwrap(), *default.clone())))),
+ // );
+ // });
+ for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {
new_bindings.insert(
name.as_ref().unwrap().clone(),
- Rc::new(ValBinding { val: val.clone() }),
+ binding!(
+ closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone val, || val.clone()))))
+ ),
);
}
for (i, param) in self.params.0.iter().enumerate() {
if let Some((None, val)) = args.get(i) {
- new_bindings.insert(param.0.clone(), Rc::new(ValBinding { val: val.clone() }));
+ new_bindings.insert(
+ param.0.clone(),
+ binding!(
+ closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone val, || val.clone()))))
+ ),
+ );
}
}
let ctx = self
.ctx
.extend(new_bindings, None, None, None)
.into_future(future_ctx);
- self.eval_rhs.evaluate(ctx)
+ self.eval_rhs.0(ctx)
}
}
@@ -138,15 +65,18 @@
Literal(LiteralType),
Str(String),
Num(f64),
- Lazy(BoxedLazyVal),
+ Lazy(LazyVal),
Arr(Vec<Val>),
Obj(ObjValue),
Func(FuncDesc),
+
+ // Library functions implemented in native
+ Intristic(String, String),
}
impl Val {
pub fn unwrap_if_lazy(self) -> Self {
if let Val::Lazy(v) = self {
- v.evaluate().unwrap_if_lazy()
+ v.0().unwrap_if_lazy()
} else {
self
}
@@ -191,12 +121,12 @@
write!(f, ",")?;
}
write!(f, "\"{}\":", field)?;
- write!(f, "{}", value.get_raw(&field, None).unwrap())?;
+ write!(f, "{}", value.get(&field).unwrap())?;
}
write!(f, "}}")?;
}
Val::Lazy(lazy) => {
- write!(f, "{}", lazy.evaluate())?;
+ write!(f, "{}", lazy.0())?;
}
Val::Func(_) => {
write!(f, "<<FUNC>>")?;
crates/jsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jsonnet-parser/src/expr.rs
+++ b/crates/jsonnet-parser/src/expr.rs
@@ -37,7 +37,7 @@
AssertStmt(AssertStmt),
}
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq)]
pub enum UnaryOpType {
Plus,
Minus,