difftreelog
fix(evaluator) instristics
in: master
6 files changed
crates/jsonnet-evaluator/README.mddiffbeforeafterboth--- /dev/null
+++ b/crates/jsonnet-evaluator/README.md
@@ -0,0 +1,11 @@
+# jsonnet-evaluator
+
+Interpreter for parsed jsonnet tree
+
+## Intristics
+
+Some functions from stdlib are implemented as intristics
+
+### Intristic handling
+
+If indexed jsonnet object has field '__intristic_namespace__' of type 'string', then any not found field/method is resolved as `Val::Intristic(__intristic_namespace__, name)`
crates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/ctx.rs
+++ b/crates/jsonnet-evaluator/src/ctx.rs
@@ -1,4 +1,4 @@
-use crate::{future_wrapper, rc_fn_helper, Binding, ObjValue};
+use crate::{future_wrapper, rc_fn_helper, LazyBinding, ObjValue, LazyVal, Val};
use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
rc_fn_helper!(
@@ -14,7 +14,7 @@
dollar: Option<ObjValue>,
this: Option<ObjValue>,
super_obj: Option<ObjValue>,
- bindings: Rc<RefCell<HashMap<String, Binding>>>,
+ bindings: Rc<HashMap<String, LazyVal>>,
}
pub struct Context(Rc<ContextInternals>);
impl Debug for Context {
@@ -46,14 +46,13 @@
dollar: None,
this: None,
super_obj: None,
- bindings: Rc::new(RefCell::new(HashMap::new())),
+ bindings: Rc::new(HashMap::new()),
}))
}
- pub fn binding(&self, name: &str) -> Binding {
+ pub fn binding(&self, name: &str) -> LazyVal {
self.0
.bindings
- .borrow()
.get(name)
.cloned()
.unwrap_or_else(|| {
@@ -69,7 +68,7 @@
pub fn extend(
&self,
- new_bindings: HashMap<String, Binding>,
+ new_bindings: HashMap<String, LazyBinding>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
@@ -80,11 +79,14 @@
let bindings = if new_bindings.is_empty() {
self.0.bindings.clone()
} else {
- let new = self.0.bindings.clone();
+ let mut new = HashMap::new(); // = self.0.bindings.clone();
+ for (k, v) in self.0.bindings.iter() {
+ new.insert(k.clone(), v.clone());
+ }
for (k, v) in new_bindings.into_iter() {
- new.borrow_mut().insert(k, v);
+ new.insert(k, v.0(this.clone(), super_obj.clone()));
}
- new
+ Rc::new(new)
};
Context(Rc::new(ContextInternals {
dollar,
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -1,6 +1,7 @@
use crate::{
- binding, bool_val, context_creator, function_default, function_rhs, future_wrapper, lazy_val,
- Binding, Context, ContextCreator, FuncDesc, ObjMember, ObjValue, Val,
+ binding, bool_val, context_creator, function_default, function_rhs, future_wrapper,
+ lazy_binding, lazy_val, Binding, Context, ContextCreator, FuncDesc, LazyBinding, ObjMember,
+ ObjValue, Val,
};
use closure::closure;
use jsonnet_parser::{
@@ -12,30 +13,28 @@
rc::Rc,
};
-pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, Binding) {
+pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, LazyBinding) {
let b = b.clone();
if let Some(args) = &b.params {
let args = args.clone();
(
b.name.clone(),
- binding!(move |this, super_obj| Val::Lazy(lazy_val!(
+ lazy_binding!(move |this, super_obj| 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(),
- binding!(move |this, super_obj| {
- Val::Lazy(lazy_val!(
- closure!(clone context_creator, clone b, || evaluate(
- context_creator.0(this.clone(), super_obj.clone()),
- &b.value
- ))
- ))
+ lazy_binding!(move |this, super_obj| {
+ lazy_val!(closure!(clone context_creator, clone b, || evaluate(
+ context_creator.0(this.clone(), super_obj.clone()),
+ &b.value
+ )))
}),
)
}
@@ -87,6 +86,19 @@
(Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),
(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),
+ (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::False)) => {
+ bool_val(false)
+ }
+ (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
+ bool_val(false)
+ }
+ (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::False)) => {
+ bool_val(false)
+ }
+ (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
+ bool_val(true)
+ }
+
(Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),
(Val::Arr(a), BinaryOpType::Add, Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),
@@ -126,37 +138,39 @@
}
}
-future_wrapper!(HashMap<String, Binding>, FutureNewBindings);
+future_wrapper!(HashMap<String, LazyBinding>, 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 new_bindings = FutureNewBindings::new();
let future_this = FutureObjValue::new();
let context_creator = context_creator!(
- closure!(clone context, clone future_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
+ closure!(clone context, clone new_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
context.clone().extend(
- future_bindings.clone().unwrap(),
+ new_bindings.clone().unwrap(),
context.clone().dollar().clone().or_else(||this.clone()),
- Some(future_this.clone().unwrap()),
+ Some(this.clone().unwrap()),
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, context_creator.clone()))
{
- bindings.insert(n, b);
+ let mut bindings: HashMap<String, LazyBinding> = 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, context_creator.clone()))
+ {
+ bindings.insert(n, b);
+ }
+ new_bindings.fill(bindings);
}
- future_bindings.fill(bindings);
let mut new_members = BTreeMap::new();
for member in members.into_iter() {
@@ -176,11 +190,12 @@
visibility: visibility.clone(),
invoke: binding!(
closure!(clone value, clone context_creator, |this, super_obj| {
+ let context = context_creator.0(this, super_obj);
// TODO: Assert
evaluate(
- context_creator.0(this, super_obj),
+ context,
&value,
- )
+ ).unwrap_if_lazy()
})
),
},
@@ -244,27 +259,39 @@
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);
- variable.0(None, None).unwrap_if_lazy()
- }
+ Var(name) => Val::Lazy(context.binding(&name)).unwrap_if_lazy(),
Index(box value, box index) => {
match (
evaluate(context.clone(), value).unwrap_if_lazy(),
- evaluate(context, index),
+ evaluate(context.clone(), index),
) {
(Val::Obj(v), Val::Str(s)) => v
.get(&s)
- .unwrap_or_else(|| panic!("{} not found in {:?}", s, v)),
+ .unwrap_or_else(closure!(clone context, || {
+ if let Some(n) = v.get("__intristic_namespace__") {
+ if let Val::Str(n) = n.unwrap_if_lazy() {
+ Val::Intristic(n, s)
+ } else {
+ panic!("__intristic_namespace__ should be string");
+ }
+ } else {
+ panic!("{} not found in {:?}", s, v)
+ }
+ }))
+ .unwrap_if_lazy(),
(Val::Arr(v), Val::Num(n)) => v
.get(n as usize)
.unwrap_or_else(|| panic!("out of bounds"))
.clone(),
+ (Val::Str(s), Val::Num(n)) => {
+ // FIXME: Only works for ASCII
+ Val::Str(String::from_utf8(vec![s.as_bytes()[n as usize]]).unwrap())
+ }
(v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),
}
}
LocalExpr(bindings, returned) => {
- let mut new_bindings: HashMap<String, Binding> = HashMap::new();
+ let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();
let future_context = Context::new_future();
let context_creator = context_creator!(
@@ -287,6 +314,49 @@
Apply(box value, ArgsDesc(args)) => {
let value = evaluate(context.clone(), value).unwrap_if_lazy();
match value {
+ // TODO: Capture context of application
+ Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {
+ ("std", "length") => {
+ assert_eq!(args.len(), 1);
+ let expr = &args.get(0).unwrap().1;
+ match evaluate(context, expr) {
+ Val::Str(n) => Val::Num(n.len() as f64),
+ Val::Arr(i) => Val::Num(i.len() as f64),
+ v => panic!("can't get length of {:?}", v),
+ }
+ }
+ ("std", "type") => {
+ assert_eq!(args.len(), 1);
+ let expr = &args.get(0).unwrap().1;
+ Val::Str(evaluate(context, expr).type_of().to_owned())
+ }
+ ("std", "makeArray") => {
+ assert_eq!(args.len(), 2);
+ if let (Val::Num(v), Val::Func(d)) = (
+ evaluate(context.clone(), &args[0].1),
+ evaluate(context, &args[1].1),
+ ) {
+ assert!(v > 0.0);
+ let mut out = Vec::with_capacity(v as usize);
+ for i in 0..v as usize {
+ out.push(d.evaluate(vec![(None, Val::Num(i as f64))]))
+ }
+ Val::Arr(out)
+ } else {
+ panic!("bad makeArray call");
+ }
+ }
+ ("std", "codepoint") => {
+ assert_eq!(args.len(), 1);
+ if let Val::Str(s) = evaluate(context.clone(), &args[0].1) {
+ // FIXME: this is not a codepoint
+ Val::Num(s.as_bytes()[0] as f64)
+ } else {
+ panic!("bad codepoint call");
+ }
+ }
+ (ns, name) => panic!("Intristic not found: {}.{}", ns, name),
+ },
Val::Func(f) => f.evaluate(
args.clone()
.into_iter()
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5mod ctx;6mod dynamic;7mod evaluate;8mod obj;9mod val;1011pub use ctx::*;12pub use dynamic::*;13pub use evaluate::*;14use jsonnet_parser::*;15pub use obj::*;16pub use val::*;1718rc_fn_helper!(19 Binding,20 binding,21 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val22);23rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);24rc_fn_helper!(25 FunctionDefault,26 function_default,27 dyn Fn(Context, Expr) -> Val28);2930#[cfg(test)]31pub mod tests {32 use super::{evaluate, Context, Val};33 use jsonnet_parser::*;3435 macro_rules! eval {36 ($str: expr) => {37 evaluate(Context::new(), &parse($str).unwrap())38 };39 }4041 macro_rules! eval_stdlib {42 ($str: expr) => {43 let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";44 evaluate(Context::new(), &parse(&(std + $str)).unwrap())45 };46 }4748 macro_rules! assert_eval {49 ($str: expr) => {50 assert_eq!(51 evaluate(Context::new(), &parse($str).unwrap()),52 Val::Literal(LiteralType::True)53 )54 };55 }56 macro_rules! assert_json {57 ($str: expr, $out: expr) => {58 assert_eq!(59 format!("{}", evaluate(Context::new(), &parse($str).unwrap())),60 $out61 )62 };63 }64 macro_rules! assert_eval_neg {65 ($str: expr) => {66 assert_eq!(67 evaluate(Context::new(), &parse($str).unwrap()),68 Val::Literal(LiteralType::False)69 )70 };71 }7273 /// Sanity checking, before trusting to another tests74 #[test]75 fn equality_operator() {76 assert_eval!("2 == 2");77 assert_eval_neg!("2 != 2");78 assert_eval!("2 != 3");79 assert_eval_neg!("2 == 3");80 assert_eval!("'Hello' == 'Hello'");81 assert_eval_neg!("'Hello' != 'Hello'");82 assert_eval!("'Hello' != 'World'");83 assert_eval_neg!("'Hello' == 'World'");84 }8586 #[test]87 fn math_evaluation() {88 assert_eval!("2 + 2 * 2 == 6");89 assert_eval!("3 + (2 + 2 * 2) == 9");90 }9192 #[test]93 fn string_concat() {94 assert_eval!("'Hello' + 'World' == 'HelloWorld'");95 assert_eval!("'Hello' * 3 == 'HelloHelloHello'");96 assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");97 }9899 #[test]100 fn local() {101 assert_eval!("local a = 2; local b = 3; a + b == 5");102 assert_eval!("local a = 1, b = a + 1; a + b == 3");103 assert_eval!("local a = 1; local a = 2; a == 2");104 }105106 #[test]107 fn object_lazyness() {108 assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);109 }110111 #[test]112 fn object_inheritance() {113 assert_json!("{a:self.b} + {b:3}", r#"{"a":3,"b":3}"#);114 }115116 #[test]117 fn test_object() {118 assert_json!("{a:2}", r#"{"a":2}"#);119 assert_json!("{a:2+2}", r#"{"a":4}"#);120 assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);121 assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);122 assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);123 assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);124 assert_json!(125 r#"126 {127 name: "Alice",128 welcome: "Hello " + self.name + "!",129 }130 "#,131 r#"{"name":"Alice","welcome":"Hello Alice!"}"#132 );133 assert_json!(134 r#"135 {136 name: "Alice",137 welcome: "Hello " + self.name + "!",138 } + {139 name: "Bob"140 }141 "#,142 r#"{"name":"Bob","welcome":"Hello Bob!"}"#143 );144 }145146 #[test]147 fn functions() {148 assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");149 assert_json!(150 r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,151 r#""HelloDearWorld""#152 );153 }154155 #[test]156 fn local_methods() {157 assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");158 assert_json!(159 r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,160 r#""HelloDearWorld""#161 );162 }163164 #[test]165 fn object_locals() {166 assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);167 assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);168 assert_json!(169 r#"{local a = function (b) {[b]:4}, test: a("test")}"#,170 r#"{"test":{"test":4}}"#171 );172 }173174 #[test]175 fn direct_self() {176 println!(177 "{:#?}",178 eval!(179 r#"180 {181 local me = self,182 a: 3,183 b(): me.a,184 }185 "#186 )187 );188 }189190 #[test]191 fn indirect_self() {192 // `self` assigned to `me` was lost when being193 // referenced from field194 eval_stdlib!(195 r#"{196 local me = self,197 b: me,198 }.b"#199 );200 }201202 // We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly203 #[test]204 fn std_assert_ok() {205 eval_stdlib!("std.assertEqual(4.5 << 2, 16)");206 }207208 #[test]209 #[should_panic]210 fn std_assert_failure() {211 eval_stdlib!("std.assertEqual(4.5 << 2, 15)");212 }213214 #[test]215 fn base64_works() {216 eval_stdlib!(r#"std.base64("test")"#);217 }218}1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5mod ctx;6mod dynamic;7mod evaluate;8mod obj;9mod val;1011pub use ctx::*;12pub use dynamic::*;13pub use evaluate::*;14use jsonnet_parser::*;15pub use obj::*;16pub use val::*;1718rc_fn_helper!(19 Binding,20 binding,21 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val22);23rc_fn_helper!(24 LazyBinding,25 lazy_binding,26 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> LazyVal27);28rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);29rc_fn_helper!(30 FunctionDefault,31 function_default,32 dyn Fn(Context, Expr) -> Val33);3435#[cfg(test)]36pub mod tests {37 use super::{evaluate, Context, Val};38 use jsonnet_parser::*;3940 macro_rules! eval {41 ($str: expr) => {42 evaluate(Context::new(), &parse($str).unwrap())43 };44 }4546 macro_rules! eval_stdlib {47 ($str: expr) => {{48 let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";49 evaluate(Context::new(), &parse(&(std + $str)).unwrap())50 }};51 }5253 macro_rules! assert_eval {54 ($str: expr) => {55 assert_eq!(56 evaluate(Context::new(), &parse($str).unwrap()),57 Val::Literal(LiteralType::True)58 )59 };60 }61 macro_rules! assert_json {62 ($str: expr, $out: expr) => {63 assert_eq!(64 format!("{}", evaluate(Context::new(), &parse($str).unwrap())),65 $out66 )67 };68 }69 macro_rules! assert_json_stdlib {70 ($str: expr, $out: expr) => {71 assert_eq!(72 format!("{}", eval_stdlib!($str)),73 $out74 )75 };76 }77 macro_rules! assert_eval_neg {78 ($str: expr) => {79 assert_eq!(80 evaluate(Context::new(), &parse($str).unwrap()),81 Val::Literal(LiteralType::False)82 )83 };84 }8586 /// Sanity checking, before trusting to another tests87 #[test]88 fn equality_operator() {89 assert_eval!("2 == 2");90 assert_eval_neg!("2 != 2");91 assert_eval!("2 != 3");92 assert_eval_neg!("2 == 3");93 assert_eval!("'Hello' == 'Hello'");94 assert_eval_neg!("'Hello' != 'Hello'");95 assert_eval!("'Hello' != 'World'");96 assert_eval_neg!("'Hello' == 'World'");97 }9899 #[test]100 fn math_evaluation() {101 assert_eval!("2 + 2 * 2 == 6");102 assert_eval!("3 + (2 + 2 * 2) == 9");103 }104105 #[test]106 fn string_concat() {107 assert_eval!("'Hello' + 'World' == 'HelloWorld'");108 assert_eval!("'Hello' * 3 == 'HelloHelloHello'");109 assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");110 }111112 #[test]113 fn local() {114 assert_eval!("local a = 2; local b = 3; a + b == 5");115 assert_eval!("local a = 1, b = a + 1; a + b == 3");116 assert_eval!("local a = 1; local a = 2; a == 2");117 }118119 #[test]120 fn object_lazyness() {121 assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);122 }123124 #[test]125 fn object_inheritance() {126 assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);127 }128129 #[test]130 fn test_object() {131 assert_json!("{a:2}", r#"{"a":2}"#);132 assert_json!("{a:2+2}", r#"{"a":4}"#);133 assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);134 assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);135 assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);136 assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);137 assert_json!(138 r#"139 {140 name: "Alice",141 welcome: "Hello " + self.name + "!",142 }143 "#,144 r#"{"name":"Alice","welcome":"Hello Alice!"}"#145 );146 assert_json!(147 r#"148 {149 name: "Alice",150 welcome: "Hello " + self.name + "!",151 } + {152 name: "Bob"153 }154 "#,155 r#"{"name":"Bob","welcome":"Hello Bob!"}"#156 );157 }158159 #[test]160 fn functions() {161 assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");162 assert_json!(163 r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,164 r#""HelloDearWorld""#165 );166 }167168 #[test]169 fn local_methods() {170 assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");171 assert_json!(172 r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,173 r#""HelloDearWorld""#174 );175 }176177 #[test]178 fn object_locals() {179 assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);180 assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);181 assert_json!(182 r#"{local a = function (b) {[b]:4}, test: a("test")}"#,183 r#"{"test":{"test":4}}"#184 );185 }186187 #[test]188 fn direct_self() {189 println!(190 "{:#?}",191 eval!(192 r#"193 {194 local me = self,195 a: 3,196 b(): me.a,197 }198 "#199 )200 );201 }202203 #[test]204 fn indirect_self() {205 // `self` assigned to `me` was lost when being206 // referenced from field207 eval_stdlib!(208 r#"{209 local me = self,210 a: 3,211 b: me.a,212 }.b"#213 );214 }215216 // We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly217 #[test]218 fn std_assert_ok() {219 eval_stdlib!("std.assertEqual(4.5 << 2, 16)");220 }221222 #[test]223 #[should_panic]224 fn std_assert_failure() {225 eval_stdlib!("std.assertEqual(4.5 << 2, 15)");226 }227228 #[test]229 fn string_is_string() {230 assert_eq!(231 eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),232 Val::Literal(LiteralType::False)233 );234 }235236 #[test]237 fn base64_works() {238 assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);239 }240}crates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/obj.rs
+++ b/crates/jsonnet-evaluator/src/obj.rs
@@ -70,17 +70,17 @@
}
pub fn get(&self, key: &str) -> Option<Val> {
// TODO: Cache get_raw result
- self.get_raw(key, Some(self))
+ self.get_raw(key, self)
}
- fn get_raw(&self, key: &str, real_this: Option<&ObjValue>) -> Option<Val> {
+ fn get_raw(&self, key: &str, real_this: &ObjValue) -> Option<Val> {
match (self.0.this_entries.get(key), &self.0.super_obj) {
(Some(k), None) => Some(k.invoke.0(
- real_this.as_ref().map(|e| (*e).clone()),
+ Some(real_this.clone()),
self.0.super_obj.clone(),
)),
(Some(k), Some(s)) => {
let our = k.invoke.0(
- real_this.as_ref().map(|e| (*e).clone()),
+ Some(real_this.clone()),
self.0.super_obj.clone(),
);
if k.add {
crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/val.rs
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -1,4 +1,6 @@
-use crate::{binding, rc_fn_helper, Binding, Context, FunctionDefault, FunctionRhs, ObjValue};
+use crate::{
+ lazy_binding, rc_fn_helper, Context, FunctionDefault, FunctionRhs, LazyBinding, ObjValue,
+};
use closure::closure;
use jsonnet_parser::{LiteralType, ParamsDesc};
use std::{
@@ -18,7 +20,7 @@
impl FuncDesc {
// TODO: Check for unset variables
pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Val {
- let mut new_bindings: HashMap<String, Binding> = HashMap::new();
+ let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();
let future_ctx = Context::new_future();
// self.params
@@ -37,8 +39,8 @@
for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {
new_bindings.insert(
name.as_ref().unwrap().clone(),
- binding!(
- closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone val, || val.clone()))))
+ lazy_binding!(
+ closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))
),
);
}
@@ -46,8 +48,8 @@
if let Some((None, val)) = args.get(i) {
new_bindings.insert(
param.0.clone(),
- binding!(
- closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone val, || val.clone()))))
+ lazy_binding!(
+ closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))
),
);
}