difftreelog
fix(evaluator) instristics
in: master
6 files changed
crates/jsonnet-evaluator/README.mddiffbeforeafterbothno changes
crates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth1use crate::{future_wrapper, rc_fn_helper, Binding, ObjValue};1use crate::{future_wrapper, rc_fn_helper, LazyBinding, ObjValue, LazyVal, Val};2use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};2use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};334rc_fn_helper!(4rc_fn_helper!(14 dollar: Option<ObjValue>,14 dollar: Option<ObjValue>,15 this: Option<ObjValue>,15 this: Option<ObjValue>,16 super_obj: Option<ObjValue>,16 super_obj: Option<ObjValue>,17 bindings: Rc<RefCell<HashMap<String, Binding>>>,17 bindings: Rc<HashMap<String, LazyVal>>,18}18}19pub struct Context(Rc<ContextInternals>);19pub struct Context(Rc<ContextInternals>);20impl Debug for Context {20impl Debug for Context {46 dollar: None,46 dollar: None,47 this: None,47 this: None,48 super_obj: None,48 super_obj: None,49 bindings: Rc::new(RefCell::new(HashMap::new())),49 bindings: Rc::new(HashMap::new()),50 }))50 }))51 }51 }525253 pub fn binding(&self, name: &str) -> Binding {53 pub fn binding(&self, name: &str) -> LazyVal {54 self.054 self.055 .bindings55 .bindings56 .borrow()57 .get(name)56 .get(name)58 .cloned()57 .cloned()59 .unwrap_or_else(|| {58 .unwrap_or_else(|| {696870 pub fn extend(69 pub fn extend(71 &self,70 &self,72 new_bindings: HashMap<String, Binding>,71 new_bindings: HashMap<String, LazyBinding>,73 new_dollar: Option<ObjValue>,72 new_dollar: Option<ObjValue>,74 new_this: Option<ObjValue>,73 new_this: Option<ObjValue>,75 new_super_obj: Option<ObjValue>,74 new_super_obj: Option<ObjValue>,80 let bindings = if new_bindings.is_empty() {79 let bindings = if new_bindings.is_empty() {81 self.0.bindings.clone()80 self.0.bindings.clone()82 } else {81 } else {82 let mut new = HashMap::new(); // = self.0.bindings.clone();83 let new = self.0.bindings.clone();83 for (k, v) in self.0.bindings.iter() {84 new.insert(k.clone(), v.clone());85 }84 for (k, v) in new_bindings.into_iter() {86 for (k, v) in new_bindings.into_iter() {85 new.borrow_mut().insert(k, v);87 new.insert(k, v.0(this.clone(), super_obj.clone()));86 }88 }87 new89 Rc::new(new)88 };90 };89 Context(Rc::new(ContextInternals {91 Context(Rc::new(ContextInternals {90 dollar,92 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.rsdiffbeforeafterboth20 binding,20 binding,21 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val21 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val22);22);23rc_fn_helper!(24 LazyBinding,25 lazy_binding,26 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> LazyVal27);23rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);28rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);24rc_fn_helper!(29rc_fn_helper!(25 FunctionDefault,30 FunctionDefault,39 }44 }404541 macro_rules! eval_stdlib {46 macro_rules! eval_stdlib {42 ($str: expr) => {47 ($str: expr) => {{43 let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";48 let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";44 evaluate(Context::new(), &parse(&(std + $str)).unwrap())49 evaluate(Context::new(), &parse(&(std + $str)).unwrap())45 };50 }};46 }51 }475248 macro_rules! assert_eval {53 macro_rules! assert_eval {61 )66 )62 };67 };63 }68 }69 macro_rules! assert_json_stdlib {70 ($str: expr, $out: expr) => {71 assert_eq!(72 format!("{}", eval_stdlib!($str)),73 $out74 )75 };76 }64 macro_rules! assert_eval_neg {77 macro_rules! assert_eval_neg {65 ($str: expr) => {78 ($str: expr) => {66 assert_eq!(79 assert_eq!(110123111 #[test]124 #[test]112 fn object_inheritance() {125 fn object_inheritance() {113 assert_json!("{a:self.b} + {b:3}", r#"{"a":3,"b":3}"#);126 assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);114 }127 }115128116 #[test]129 #[test]192 // `self` assigned to `me` was lost when being205 // `self` assigned to `me` was lost when being193 // referenced from field206 // referenced from field194 eval_stdlib!(207 eval_stdlib!(195 r#"{208 r#"{196 local me = self,209 local me = self,197 b: me,210 a: 3,211 b: me.a,198 }.b"#212 }.b"#199 );213 );200 }214 }201215211 eval_stdlib!("std.assertEqual(4.5 << 2, 15)");225 eval_stdlib!("std.assertEqual(4.5 << 2, 15)");212 }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 }213235214 #[test]236 #[test]215 fn base64_works() {237 fn base64_works() {216 eval_stdlib!(r#"std.base64("test")"#);238 assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);217 }239 }218}240}219241crates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth70 }70 }71 pub fn get(&self, key: &str) -> Option<Val> {71 pub fn get(&self, key: &str) -> Option<Val> {72 // TODO: Cache get_raw result72 // TODO: Cache get_raw result73 self.get_raw(key, Some(self))73 self.get_raw(key, self)74 }74 }75 fn get_raw(&self, key: &str, real_this: Option<&ObjValue>) -> Option<Val> {75 fn get_raw(&self, key: &str, real_this: &ObjValue) -> Option<Val> {76 match (self.0.this_entries.get(key), &self.0.super_obj) {76 match (self.0.this_entries.get(key), &self.0.super_obj) {77 (Some(k), None) => Some(k.invoke.0(77 (Some(k), None) => Some(k.invoke.0(78 real_this.as_ref().map(|e| (*e).clone()),78 Some(real_this.clone()),79 self.0.super_obj.clone(),79 self.0.super_obj.clone(),80 )),80 )),81 (Some(k), Some(s)) => {81 (Some(k), Some(s)) => {82 let our = k.invoke.0(82 let our = k.invoke.0(83 real_this.as_ref().map(|e| (*e).clone()),83 Some(real_this.clone()),84 self.0.super_obj.clone(),84 self.0.super_obj.clone(),85 );85 );86 if k.add {86 if k.add {crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth1use crate::{binding, rc_fn_helper, Binding, Context, FunctionDefault, FunctionRhs, ObjValue};1use crate::{2 lazy_binding, rc_fn_helper, Context, FunctionDefault, FunctionRhs, LazyBinding, ObjValue,3};2use closure::closure;4use closure::closure;3use jsonnet_parser::{LiteralType, ParamsDesc};5use jsonnet_parser::{LiteralType, ParamsDesc};18impl FuncDesc {20impl FuncDesc {19 // TODO: Check for unset variables21 // TODO: Check for unset variables20 pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Val {22 pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Val {21 let mut new_bindings: HashMap<String, Binding> = HashMap::new();23 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();22 let future_ctx = Context::new_future();24 let future_ctx = Context::new_future();232524 // self.params26 // self.params37 for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {39 for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {38 new_bindings.insert(40 new_bindings.insert(39 name.as_ref().unwrap().clone(),41 name.as_ref().unwrap().clone(),40 binding!(42 lazy_binding!(41 closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone val, || val.clone()))))43 closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))42 ),44 ),43 );45 );44 }46 }45 for (i, param) in self.params.0.iter().enumerate() {47 for (i, param) in self.params.0.iter().enumerate() {46 if let Some((None, val)) = args.get(i) {48 if let Some((None, val)) = args.get(i) {47 new_bindings.insert(49 new_bindings.insert(48 param.0.clone(),50 param.0.clone(),49 binding!(51 lazy_binding!(50 closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone val, || val.clone()))))52 closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))51 ),53 ),52 );54 );53 }55 }