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.rsdiffbeforeafterboth1use crate::{1use crate::{2 binding, bool_val, context_creator, function_default, function_rhs, future_wrapper, lazy_val,2 binding, bool_val, context_creator, function_default, function_rhs, future_wrapper,3 Binding, Context, ContextCreator, FuncDesc, ObjMember, ObjValue, Val,3 lazy_binding, lazy_val, Binding, Context, ContextCreator, FuncDesc, LazyBinding, ObjMember,4 ObjValue, Val,4};5};5use closure::closure;6use closure::closure;12 rc::Rc,13 rc::Rc,13};14};141515pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, Binding) {16pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, LazyBinding) {16 let b = b.clone();17 let b = b.clone();17 if let Some(args) = &b.params {18 if let Some(args) = &b.params {18 let args = args.clone();19 let args = args.clone();19 (20 (20 b.name.clone(),21 b.name.clone(),21 binding!(move |this, super_obj| Val::Lazy(lazy_val!(22 lazy_binding!(move |this, super_obj| lazy_val!(22 closure!(clone b, clone args, clone context_creator, || evaluate_method(23 closure!(clone b, clone args, clone context_creator, || evaluate_method(23 context_creator.0(this.clone(), super_obj.clone()),24 context_creator.0(this.clone(), super_obj.clone()),24 &b.value,25 &b.value,25 args.clone()26 args.clone()26 ))27 ))27 ))),28 )),28 )29 )29 } else {30 } else {30 (31 (31 b.name.clone(),32 b.name.clone(),32 binding!(move |this, super_obj| {33 lazy_binding!(move |this, super_obj| {33 Val::Lazy(lazy_val!(34 lazy_val!(closure!(clone context_creator, clone b, || evaluate(34 closure!(clone context_creator, clone b, || evaluate(35 context_creator.0(this.clone(), super_obj.clone()),35 context_creator.0(this.clone(), super_obj.clone()),36 &b.value36 &b.value37 ))37 )))38 ))39 }),38 }),40 )39 )41 }40 }87 (Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),86 (Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),88 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),87 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),8889 (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::False)) => {90 bool_val(false)91 }92 (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::True)) => {93 bool_val(false)94 }95 (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::False)) => {96 bool_val(false)97 }98 (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::True)) => {99 bool_val(true)100 }8910190 (Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),102 (Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),91103126 }138 }127}139}128140129future_wrapper!(HashMap<String, Binding>, FutureNewBindings);141future_wrapper!(HashMap<String, LazyBinding>, FutureNewBindings);130future_wrapper!(ObjValue, FutureObjValue);142future_wrapper!(ObjValue, FutureObjValue);131143132// TODO: Asserts144// TODO: Asserts133pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {145pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {134 match object {146 match object {135 ObjBody::MemberList(members) => {147 ObjBody::MemberList(members) => {136 let future_bindings = FutureNewBindings::new();148 let new_bindings = FutureNewBindings::new();137 let future_this = FutureObjValue::new();149 let future_this = FutureObjValue::new();138 let context_creator = context_creator!(150 let context_creator = context_creator!(139 closure!(clone context, clone future_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {151 closure!(clone context, clone new_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {140 context.clone().extend(152 context.clone().extend(141 future_bindings.clone().unwrap(),153 new_bindings.clone().unwrap(),142 context.clone().dollar().clone().or_else(||this.clone()),154 context.clone().dollar().clone().or_else(||this.clone()),143 Some(future_this.clone().unwrap()),155 Some(this.clone().unwrap()),144 super_obj156 super_obj145 )157 )146 })158 })147 );159 );160 {148 let mut bindings: HashMap<String, Binding> = HashMap::new();161 let mut bindings: HashMap<String, LazyBinding> = HashMap::new();149 for (n, b) in members162 for (n, b) in members150 .iter()163 .iter()151 .filter_map(|m| match m {164 .filter_map(|m| match m {156 {169 {157 bindings.insert(n, b);170 bindings.insert(n, b);158 }171 }159 future_bindings.fill(bindings);172 new_bindings.fill(bindings);173 }160174161 let mut new_members = BTreeMap::new();175 let mut new_members = BTreeMap::new();162 for member in members.into_iter() {176 for member in members.into_iter() {176 visibility: visibility.clone(),190 visibility: visibility.clone(),177 invoke: binding!(191 invoke: binding!(178 closure!(clone value, clone context_creator, |this, super_obj| {192 closure!(clone value, clone context_creator, |this, super_obj| {179 // TODO: Assert180 evaluate(181 context_creator.0(this, super_obj),193 let context = context_creator.0(this, super_obj);194 // TODO: Assert195 evaluate(196 context,182 &value,197 &value,183 )198 ).unwrap_if_lazy()184 })199 })185 ),200 ),186 },201 },244 evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))259 evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))245 }260 }246 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)),261 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)),247 Var(name) => {262 Var(name) => Val::Lazy(context.binding(&name)).unwrap_if_lazy(),248 let variable = context.binding(&name);249 variable.0(None, None).unwrap_if_lazy()250 }251 Index(box value, box index) => {263 Index(box value, box index) => {252 match (264 match (253 evaluate(context.clone(), value).unwrap_if_lazy(),265 evaluate(context.clone(), value).unwrap_if_lazy(),254 evaluate(context, index),266 evaluate(context.clone(), index),255 ) {267 ) {256 (Val::Obj(v), Val::Str(s)) => v268 (Val::Obj(v), Val::Str(s)) => v257 .get(&s)269 .get(&s)258 .unwrap_or_else(|| panic!("{} not found in {:?}", s, v)),270 .unwrap_or_else(closure!(clone context, || {271 if let Some(n) = v.get("__intristic_namespace__") {272 if let Val::Str(n) = n.unwrap_if_lazy() {273 Val::Intristic(n, s)274 } else {275 panic!("__intristic_namespace__ should be string");276 }277 } else {278 panic!("{} not found in {:?}", s, v)279 }280 }))281 .unwrap_if_lazy(),259 (Val::Arr(v), Val::Num(n)) => v282 (Val::Arr(v), Val::Num(n)) => v260 .get(n as usize)283 .get(n as usize)261 .unwrap_or_else(|| panic!("out of bounds"))284 .unwrap_or_else(|| panic!("out of bounds"))262 .clone(),285 .clone(),286 (Val::Str(s), Val::Num(n)) => {287 // FIXME: Only works for ASCII288 Val::Str(String::from_utf8(vec![s.as_bytes()[n as usize]]).unwrap())289 }263 (v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),290 (v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),264 }291 }265 }292 }266 LocalExpr(bindings, returned) => {293 LocalExpr(bindings, returned) => {267 let mut new_bindings: HashMap<String, Binding> = HashMap::new();294 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();268 let future_context = Context::new_future();295 let future_context = Context::new_future();269296270 let context_creator = context_creator!(297 let context_creator = context_creator!(287 Apply(box value, ArgsDesc(args)) => {314 Apply(box value, ArgsDesc(args)) => {288 let value = evaluate(context.clone(), value).unwrap_if_lazy();315 let value = evaluate(context.clone(), value).unwrap_if_lazy();289 match value {316 match value {317 // TODO: Capture context of application318 Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {319 ("std", "length") => {320 assert_eq!(args.len(), 1);321 let expr = &args.get(0).unwrap().1;322 match evaluate(context, expr) {323 Val::Str(n) => Val::Num(n.len() as f64),324 Val::Arr(i) => Val::Num(i.len() as f64),325 v => panic!("can't get length of {:?}", v),326 }327 }328 ("std", "type") => {329 assert_eq!(args.len(), 1);330 let expr = &args.get(0).unwrap().1;331 Val::Str(evaluate(context, expr).type_of().to_owned())332 }333 ("std", "makeArray") => {334 assert_eq!(args.len(), 2);335 if let (Val::Num(v), Val::Func(d)) = (336 evaluate(context.clone(), &args[0].1),337 evaluate(context, &args[1].1),338 ) {339 assert!(v > 0.0);340 let mut out = Vec::with_capacity(v as usize);341 for i in 0..v as usize {342 out.push(d.evaluate(vec![(None, Val::Num(i as f64))]))343 }344 Val::Arr(out)345 } else {346 panic!("bad makeArray call");347 }348 }349 ("std", "codepoint") => {350 assert_eq!(args.len(), 1);351 if let Val::Str(s) = evaluate(context.clone(), &args[0].1) {352 // FIXME: this is not a codepoint353 Val::Num(s.as_bytes()[0] as f64)354 } else {355 panic!("bad codepoint call");356 }357 }358 (ns, name) => panic!("Intristic not found: {}.{}", ns, name),359 },290 Val::Func(f) => f.evaluate(360 Val::Func(f) => f.evaluate(291 args.clone()361 args.clone()292 .into_iter()362 .into_iter()crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -20,6 +20,11 @@
binding,
dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val
);
+rc_fn_helper!(
+ LazyBinding,
+ lazy_binding,
+ dyn Fn(Option<ObjValue>, Option<ObjValue>) -> LazyVal
+);
rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);
rc_fn_helper!(
FunctionDefault,
@@ -39,10 +44,10 @@
}
macro_rules! eval_stdlib {
- ($str: expr) => {
+ ($str: expr) => {{
let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";
evaluate(Context::new(), &parse(&(std + $str)).unwrap())
- };
+ }};
}
macro_rules! assert_eval {
@@ -61,6 +66,14 @@
)
};
}
+ macro_rules! assert_json_stdlib {
+ ($str: expr, $out: expr) => {
+ assert_eq!(
+ format!("{}", eval_stdlib!($str)),
+ $out
+ )
+ };
+ }
macro_rules! assert_eval_neg {
($str: expr) => {
assert_eq!(
@@ -110,7 +123,7 @@
#[test]
fn object_inheritance() {
- assert_json!("{a:self.b} + {b:3}", r#"{"a":3,"b":3}"#);
+ assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);
}
#[test]
@@ -194,7 +207,8 @@
eval_stdlib!(
r#"{
local me = self,
- b: me,
+ a: 3,
+ b: me.a,
}.b"#
);
}
@@ -212,7 +226,15 @@
}
#[test]
+ fn string_is_string() {
+ assert_eq!(
+ eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),
+ Val::Literal(LiteralType::False)
+ );
+ }
+
+ #[test]
fn base64_works() {
- eval_stdlib!(r#"std.base64("test")"#);
+ assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);
}
}
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())))
),
);
}