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.rsdiffbeforeafterboth1#[macro_export]2macro_rules! dynamic_wrapper {3 ($orig: ident, $wrapper: ident) => {4 #[derive(Debug, Clone)]5 pub struct $wrapper(pub std::rc::Rc<dyn $orig>);6 impl std::ops::Deref for $wrapper {7 type Target = dyn $orig;8 fn deref(&self) -> &Self::Target {9 &*self.010 }11 }12 impl std::cmp::PartialEq for $wrapper {13 fn eq(&self, other: &Self) -> bool {14 Rc::ptr_eq(&self.0, &other.0)15 }16 }17 };18}1920#[macro_export]21macro_rules! future_wrapper {22 ($orig: ty, $wrapper: ident) => {23 #[derive(Debug, Clone)]24 pub struct $wrapper(pub std::rc::Rc<std::cell::RefCell<Option<$orig>>>);25 impl $wrapper {26 pub fn unwrap(self) -> $orig {27 self.0.borrow().as_ref().map(|e| e.clone()).unwrap()28 }29 pub fn new() -> Self {30 $wrapper(std::rc::Rc::new(std::cell::RefCell::new(None)))31 }32 pub fn fill(self, val: $orig) -> $orig {33 if self.0.borrow().is_some() {34 panic!("wrapper is filled already");35 }36 {37 self.0.borrow_mut().replace(val);38 }39 self.unwrap()40 }41 }42 };43}4445#[macro_export]46macro_rules! dummy_debug {47 ($struct: ident) => {48 impl std::fmt::Debug for $struct {49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {50 f.debug_struct(std::stringify!($struct))51 .finish_non_exhaustive()52 }53 }54 };55}crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -1,70 +1,53 @@
-use crate::BoxedLazyVal;
use crate::{
- bool_val, ArgsBinding, BoxedBinding, BoxedContextCreator, ConstantContextCreator, Context,
- FuncDesc, FunctionDefault, FunctionRhs, NoArgsBinding, Val,
-};
-use crate::{
- future_wrapper, BoxedFunctionDefault, BoxedFunctionRhs, ContextCreator, ObjMember, ObjValue,
- PlainLazyVal,
+ binding, bool_val, context_creator, function_default, function_rhs, future_wrapper, lazy_val,
+ Binding, Context, ContextCreator, FuncDesc, ObjMember, ObjValue, Val,
};
+use closure::closure;
use jsonnet_parser::{
ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, Member, ObjBody, ParamsDesc,
- Visibility,
+ UnaryOpType, Visibility,
};
use std::{
- cell::RefCell,
collections::{BTreeMap, HashMap},
rc::Rc,
};
-pub fn evaluate_binding<'t>(
- b: &BindSpec,
- context_creator: BoxedContextCreator,
-) -> (String, BoxedBinding) {
+pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, Binding) {
+ let b = b.clone();
if let Some(args) = &b.params {
+ let args = args.clone();
(
b.name.clone(),
- Rc::new(ArgsBinding {
- expr: *b.value.clone(),
- args: args.clone(),
- context_creator: context_creator.clone(),
- }),
+ binding!(move |this, super_obj| Val::Lazy(lazy_val!(
+ closure!(clone b, clone args, clone context_creator, || evaluate_method(
+ context_creator.0(this.clone(), super_obj.clone()),
+ &b.value,
+ args.clone()
+ ))
+ ))),
)
} else {
(
b.name.clone(),
- Rc::new(NoArgsBinding {
- expr: *b.value.clone(),
- context_creator: context_creator.clone(),
- }) as BoxedBinding,
+ binding!(move |this, super_obj| {
+ println!("Evaluating binding");
+ Val::Lazy(lazy_val!(
+ closure!(clone context_creator, clone b, || evaluate(
+ context_creator.0(this.clone(), super_obj.clone()),
+ &b.value
+ ))
+ ))
+ }),
)
}
}
-#[derive(Debug)]
-struct MethodRhs {
- rhs: Expr,
-}
-impl FunctionRhs for MethodRhs {
- fn evaluate(&self, ctx: Context) -> Val {
- evaluate(ctx, &self.rhs)
- }
-}
-
-#[derive(Debug)]
-struct MethodDefault {}
-impl FunctionDefault for MethodDefault {
- fn default(&self, ctx: Context, expr: Expr) -> Val {
- evaluate(ctx, &expr)
- }
-}
-
pub fn evaluate_method(ctx: Context, expr: &Expr, arg_spec: ParamsDesc) -> Val {
Val::Func(FuncDesc {
ctx,
params: arg_spec,
- eval_rhs: BoxedFunctionRhs(Rc::new(MethodRhs { rhs: expr.clone() })),
- eval_default: BoxedFunctionDefault(Rc::new(MethodDefault {})),
+ eval_rhs: function_rhs!(closure!(clone expr, |ctx| evaluate(ctx, &expr))),
+ eval_default: function_default!(|ctx, default| evaluate(ctx, &default)),
})
}
@@ -74,7 +57,7 @@
jsonnet_parser::FieldName::Dyn(expr) => {
let name = evaluate(context, expr).unwrap_if_lazy();
match name {
- Val::Str(n) => n.clone(),
+ Val::Str(n) => n,
_ => panic!(
"dynamic field name can be only evaluated to 'string', got: {:?}",
name
@@ -84,10 +67,19 @@
}
}
+pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val {
+ match (op, b) {
+ (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.0()),
+ (UnaryOpType::Not, Val::Literal(LiteralType::True)) => Val::Literal(LiteralType::False),
+ (UnaryOpType::Not, Val::Literal(LiteralType::False)) => Val::Literal(LiteralType::True),
+ (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),
+ }
+}
+
pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {
match (a, op, b) {
- (Val::Lazy(l), o, r) => evaluate_binary_op(&l.evaluate(), o, r),
- (l, o, Val::Lazy(r)) => evaluate_binary_op(l, o, &r.evaluate()),
+ (Val::Lazy(a), o, b) => evaluate_binary_op(&a.0(), o, b),
+ (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.0()),
(Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),
(Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),
@@ -119,8 +111,8 @@
(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => bool_val(v1 <= v2),
(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => bool_val(v1 >= v2),
- (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val(v1 == v2),
- (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val(v1 != v2),
+ (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val((v1 - v2).abs() < f64::EPSILON),
+ (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val((v1 - v2).abs() > f64::EPSILON),
(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {
Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)
@@ -135,48 +127,44 @@
}
}
-future_wrapper!(HashMap<String, BoxedBinding>, FutureNewBindings);
-
-#[derive(Debug)]
-pub struct ObjectContextCreator {
- original: Context,
- future_bindings: FutureNewBindings,
-}
-
-impl ContextCreator for ObjectContextCreator {
- fn create_context(&self, this: &Option<ObjValue>, super_obj: &Option<ObjValue>) -> Context {
- self.original.extend(
- self.future_bindings.clone().unwrap(),
- self.original.dollar().clone().or_else(|| this.clone()),
- this.clone(),
- super_obj.clone(),
- )
- }
-}
+future_wrapper!(HashMap<String, Binding>, FutureNewBindings);
+future_wrapper!(ObjValue, FutureObjValue);
// TODO: Asserts
pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {
match object {
ObjBody::MemberList(members) => {
let future_bindings = FutureNewBindings::new();
- let binding_context_creator = Rc::new(ObjectContextCreator {
- future_bindings: future_bindings.clone(),
- original: context.clone(),
- });
- let mut bindings: HashMap<String, BoxedBinding> = HashMap::new();
+ let future_this = FutureObjValue::new();
+ let context_creator = context_creator!(
+ closure!(clone context, clone future_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
+ println!("Context created");
+ context.clone().extend(
+ future_bindings.clone().unwrap(),
+ context.clone().dollar().clone().or_else(||this.clone()),
+ this,
+ super_obj
+ )
+ })
+ );
+ let mut bindings: HashMap<String, Binding> = HashMap::new();
for (n, b) in members
.iter()
.filter_map(|m| match m {
Member::BindStmt(b) => Some(b.clone()),
_ => None,
})
- .map(|b| evaluate_binding(&b, binding_context_creator.clone()))
+ .map(|b| {
+ evaluate_binding(&b, context_creator.clone())
+ })
{
bindings.insert(n, b);
}
- let bindings = future_bindings.fill(bindings);
+ future_bindings.fill(bindings);
+
+ println!("Bindings filled");
let mut new_members = BTreeMap::new();
- for member in members.iter() {
+ for member in members.into_iter() {
match member {
Member::Field(FieldMember {
name,
@@ -185,16 +173,22 @@
visibility,
value,
}) => {
- let name = evaluate_field_name(context.clone(), name);
+ let name = evaluate_field_name(context.clone(), &name);
new_members.insert(
name,
ObjMember {
- add: *plus,
+ add: plus,
visibility: visibility.clone(),
- invoke: Rc::new(NoArgsBinding {
- context_creator: binding_context_creator.clone(),
- expr: value.clone(),
- }),
+ invoke: binding!(
+ closure!(clone value, clone context_creator, clone future_this, |this, super_obj| {
+ // FIXME: I should take "this" instead of "future_this" there?
+ // TODO: Assert
+ evaluate(
+ context_creator.0(this, super_obj),
+ &value,
+ )
+ })
+ ),
},
);
}
@@ -204,26 +198,31 @@
value,
..
}) => {
- let name = evaluate_field_name(context.clone(), name);
+ let name = evaluate_field_name(context.clone(), &name);
new_members.insert(
name,
ObjMember {
add: false,
visibility: Visibility::Hidden,
- invoke: Rc::new(ArgsBinding {
- expr: value.clone(),
- args: params.clone(),
- context_creator: binding_context_creator.clone(),
- }),
+ invoke: binding!(
+ closure!(clone value, clone context_creator, clone future_this, |this, super_obj| {
+ // FIXME: I should take "this" instead of "future_this" there?
+ // TODO: Assert
+ evaluate_method(
+ context_creator.0(this, super_obj),
+ &value.clone(),
+ params.clone(),
+ )
+ })
+ ),
},
);
}
Member::BindStmt(_) => {}
Member::AssertStmt(_) => {}
- _ => todo!(),
}
}
- ObjValue::new(None, Rc::new(new_members))
+ future_this.fill(ObjValue::new(None, Rc::new(new_members)))
}
_ => todo!(),
}
@@ -232,6 +231,21 @@
pub fn evaluate(context: Context, expr: &Expr) -> Val {
use Expr::*;
match &*expr {
+ Literal(LiteralType::This) => {
+ println!("{:?}", context.this());
+ Val::Obj(
+ context
+ .this()
+ .clone()
+ .unwrap_or_else(|| panic!("this not found")),
+ )
+ }
+ Literal(LiteralType::Super) => Val::Obj(
+ context
+ .super_obj()
+ .clone()
+ .unwrap_or_else(|| panic!("super not found")),
+ ),
Literal(t) => Val::Literal(t.clone()),
Parened(e) => evaluate(context, e),
Str(v) => Val::Str(v.clone()),
@@ -239,45 +253,34 @@
BinaryOp(v1, o, v2) => {
evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))
}
+ UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)),
Var(name) => {
let variable = context.binding(&name);
- let val = variable.evaluate(None, None);
- val
+ variable.0(None, None).unwrap_if_lazy()
}
Index(box value, box index) => {
match (
evaluate(context.clone(), value).unwrap_if_lazy(),
- evaluate(context.clone(), index),
+ evaluate(context, index),
) {
- (Val::Literal(LiteralType::Super), _idx) => todo!(),
- (Val::Literal(LiteralType::This), idx) => match &idx.unwrap_if_lazy() {
- Val::Str(str) => context
- .this()
- .clone()
- .unwrap_or_else(|| panic!("'this' is not defined in current context"))
- .get_raw(str, None)
- .unwrap_or_else(|| {
- panic!(
- "key {} not found in current context 'this' ({:?})",
- str,
- context.this()
- )
- }),
- _ => panic!("bad index"),
- },
(Val::Obj(v), Val::Str(s)) => v
- .get_raw(&s, None)
+ .get(&s)
.unwrap_or_else(|| panic!("{} not found in {:?}", s, v)),
+ (Val::Arr(v), Val::Num(n)) => v
+ .get(n as usize)
+ .unwrap_or_else(|| panic!("out of bounds"))
+ .clone(),
(v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),
}
}
LocalExpr(bindings, returned) => {
- let mut new_bindings: HashMap<String, BoxedBinding> = HashMap::new();
+ let mut new_bindings: HashMap<String, Binding> = HashMap::new();
let future_context = Context::new_future();
- let context_creator = Rc::new(ConstantContextCreator {
- context: future_context.clone(),
- });
+ let context_creator = context_creator!(
+ closure!(clone future_context, |_, _| future_context.clone().unwrap())
+ );
+
for (k, v) in bindings
.iter()
.map(move |b| evaluate_binding(b, context_creator.clone()))
@@ -299,11 +302,10 @@
.into_iter()
.map(|a| {
(
- a.0,
- Val::Lazy(BoxedLazyVal(Rc::new(PlainLazyVal {
- context: context.clone(),
- expr: *a.1,
- }))),
+ a.clone().0,
+ Val::Lazy(lazy_val!(
+ closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))
+ )),
)
})
.collect(),
@@ -318,9 +320,9 @@
cond_then,
cond_else,
} => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {
- Val::Literal(LiteralType::True) => evaluate(context.clone(), cond_then),
+ Val::Literal(LiteralType::True) => evaluate(context, cond_then),
Val::Literal(LiteralType::False) => match cond_else {
- Some(v) => evaluate(context.clone(), v),
+ Some(v) => evaluate(context, v),
None => Val::Literal(LiteralType::False),
},
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,