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::{2 binding, bool_val, context_creator, function_default, function_rhs, future_wrapper, lazy_val,3 Binding, Context, ContextCreator, FuncDesc, ObjMember, ObjValue, Val,4};5use closure::closure;6use jsonnet_parser::{7 ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, Member, ObjBody, ParamsDesc,8 UnaryOpType, Visibility,9};10use std::{11 collections::{BTreeMap, HashMap},12 rc::Rc,13};1415pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, Binding) {16 let b = b.clone();17 if let Some(args) = &b.params {18 let args = args.clone();19 (20 b.name.clone(),21 binding!(move |this, super_obj| Val::Lazy(lazy_val!(22 closure!(clone b, clone args, clone context_creator, || evaluate_method(23 context_creator.0(this.clone(), super_obj.clone()),24 &b.value,25 args.clone()26 ))27 ))),28 )29 } else {30 (31 b.name.clone(),32 binding!(move |this, super_obj| {33 Val::Lazy(lazy_val!(34 closure!(clone context_creator, clone b, || evaluate(35 context_creator.0(this.clone(), super_obj.clone()),36 &b.value37 ))38 ))39 }),40 )41 }42}4344pub fn evaluate_method(ctx: Context, expr: &Expr, arg_spec: ParamsDesc) -> Val {45 Val::Func(FuncDesc {46 ctx,47 params: arg_spec,48 eval_rhs: function_rhs!(closure!(clone expr, |ctx| evaluate(ctx, &expr))),49 eval_default: function_default!(|ctx, default| evaluate(ctx, &default)),50 })51}5253pub fn evaluate_field_name(context: Context, field_name: &jsonnet_parser::FieldName) -> String {54 match field_name {55 jsonnet_parser::FieldName::Fixed(n) => n.clone(),56 jsonnet_parser::FieldName::Dyn(expr) => {57 let name = evaluate(context, expr).unwrap_if_lazy();58 match name {59 Val::Str(n) => n,60 _ => panic!(61 "dynamic field name can be only evaluated to 'string', got: {:?}",62 name63 ),64 }65 }66 }67}6869pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val {70 match (op, b) {71 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.0()),72 (UnaryOpType::Not, Val::Literal(LiteralType::True)) => Val::Literal(LiteralType::False),73 (UnaryOpType::Not, Val::Literal(LiteralType::False)) => Val::Literal(LiteralType::True),74 (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),75 }76}7778pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {79 match (a, op, b) {80 (Val::Lazy(a), o, b) => evaluate_binary_op(&a.0(), o, b),81 (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.0()),8283 (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),84 (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),85 (Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),8687 (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)),8990 (Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),9192 (Val::Arr(a), BinaryOpType::Add, Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),9394 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),95 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),96 (Val::Num(v1), BinaryOpType::Mod, Val::Num(v2)) => Val::Num(v1 % v2),9798 (Val::Num(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Num(v1 + v2),99 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),100101 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {102 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)103 }104 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {105 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)106 }107108 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => bool_val(v1 < v2),109 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => bool_val(v1 > v2),110 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => bool_val(v1 <= v2),111 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => bool_val(v1 >= v2),112113 (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val((v1 - v2).abs() < f64::EPSILON),114 (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val((v1 - v2).abs() > f64::EPSILON),115116 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {117 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)118 }119 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {120 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)121 }122 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {123 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)124 }125 _ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),126 }127}128129future_wrapper!(HashMap<String, Binding>, FutureNewBindings);130future_wrapper!(ObjValue, FutureObjValue);131132// TODO: Asserts133pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {134 match object {135 ObjBody::MemberList(members) => {136 let future_bindings = FutureNewBindings::new();137 let future_this = FutureObjValue::new();138 let context_creator = context_creator!(139 closure!(clone context, clone future_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {140 context.clone().extend(141 future_bindings.clone().unwrap(),142 context.clone().dollar().clone().or_else(||this.clone()),143 Some(future_this.clone().unwrap()),144 super_obj145 )146 })147 );148 let mut bindings: HashMap<String, Binding> = HashMap::new();149 for (n, b) in members150 .iter()151 .filter_map(|m| match m {152 Member::BindStmt(b) => Some(b.clone()),153 _ => None,154 })155 .map(|b| evaluate_binding(&b, context_creator.clone()))156 {157 bindings.insert(n, b);158 }159 future_bindings.fill(bindings);160161 let mut new_members = BTreeMap::new();162 for member in members.into_iter() {163 match member {164 Member::Field(FieldMember {165 name,166 plus,167 params: None,168 visibility,169 value,170 }) => {171 let name = evaluate_field_name(context.clone(), &name);172 new_members.insert(173 name,174 ObjMember {175 add: plus,176 visibility: visibility.clone(),177 invoke: binding!(178 closure!(clone value, clone context_creator, |this, super_obj| {179 // TODO: Assert180 evaluate(181 context_creator.0(this, super_obj),182 &value,183 )184 })185 ),186 },187 );188 }189 Member::Field(FieldMember {190 name,191 params: Some(params),192 value,193 ..194 }) => {195 let name = evaluate_field_name(context.clone(), &name);196 new_members.insert(197 name,198 ObjMember {199 add: false,200 visibility: Visibility::Hidden,201 invoke: binding!(202 closure!(clone value, clone context_creator, |this, super_obj| {203 // TODO: Assert204 evaluate_method(205 context_creator.0(this, super_obj),206 &value.clone(),207 params.clone(),208 )209 })210 ),211 },212 );213 }214 Member::BindStmt(_) => {}215 Member::AssertStmt(_) => {}216 }217 }218 future_this.fill(ObjValue::new(None, Rc::new(new_members)))219 }220 _ => todo!(),221 }222}223224pub fn evaluate(context: Context, expr: &Expr) -> Val {225 use Expr::*;226 match &*expr {227 Literal(LiteralType::This) => Val::Obj(228 context229 .this()230 .clone()231 .unwrap_or_else(|| panic!("this not found")),232 ),233 Literal(LiteralType::Super) => Val::Obj(234 context235 .super_obj()236 .clone()237 .unwrap_or_else(|| panic!("super not found")),238 ),239 Literal(t) => Val::Literal(t.clone()),240 Parened(e) => evaluate(context, e),241 Str(v) => Val::Str(v.clone()),242 Num(v) => Val::Num(*v),243 BinaryOp(v1, o, v2) => {244 evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))245 }246 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)),247 Var(name) => {248 let variable = context.binding(&name);249 variable.0(None, None).unwrap_if_lazy()250 }251 Index(box value, box index) => {252 match (253 evaluate(context.clone(), value).unwrap_if_lazy(),254 evaluate(context, index),255 ) {256 (Val::Obj(v), Val::Str(s)) => v257 .get(&s)258 .unwrap_or_else(|| panic!("{} not found in {:?}", s, v)),259 (Val::Arr(v), Val::Num(n)) => v260 .get(n as usize)261 .unwrap_or_else(|| panic!("out of bounds"))262 .clone(),263 (v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),264 }265 }266 LocalExpr(bindings, returned) => {267 let mut new_bindings: HashMap<String, Binding> = HashMap::new();268 let future_context = Context::new_future();269270 let context_creator = context_creator!(271 closure!(clone future_context, |_, _| future_context.clone().unwrap())272 );273274 for (k, v) in bindings275 .iter()276 .map(move |b| evaluate_binding(b, context_creator.clone()))277 {278 new_bindings.insert(k, v);279 }280281 let context = context282 .extend(new_bindings, None, None, None)283 .into_future(future_context);284 evaluate(context, &*returned.clone())285 }286 Obj(body) => Val::Obj(evaluate_object(context, body.clone())),287 Apply(box value, ArgsDesc(args)) => {288 let value = evaluate(context.clone(), value).unwrap_if_lazy();289 match value {290 Val::Func(f) => f.evaluate(291 args.clone()292 .into_iter()293 .map(|a| {294 (295 a.clone().0,296 Val::Lazy(lazy_val!(297 closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))298 )),299 )300 })301 .collect(),302 ),303 _ => panic!("{:?} is not a function", value),304 }305 }306 Function(params, body) => evaluate_method(context, body, params.clone()),307 Error(e) => panic!("error: {}", evaluate(context, e)),308 IfElse {309 cond,310 cond_then,311 cond_else,312 } => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {313 Val::Literal(LiteralType::True) => evaluate(context, cond_then),314 Val::Literal(LiteralType::False) => match cond_else {315 Some(v) => evaluate(context, v),316 None => Val::Literal(LiteralType::False),317 },318 v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),319 },320 _ => panic!("evaluation not implemented: {:?}", expr),321 }322}1use crate::{2 binding, bool_val, context_creator, function_default, function_rhs, future_wrapper,3 lazy_binding, lazy_val, Binding, Context, ContextCreator, FuncDesc, LazyBinding, ObjMember,4 ObjValue, Val,5};6use closure::closure;7use jsonnet_parser::{8 ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, Member, ObjBody, ParamsDesc,9 UnaryOpType, Visibility,10};11use std::{12 collections::{BTreeMap, HashMap},13 rc::Rc,14};1516pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, LazyBinding) {17 let b = b.clone();18 if let Some(args) = &b.params {19 let args = args.clone();20 (21 b.name.clone(),22 lazy_binding!(move |this, super_obj| lazy_val!(23 closure!(clone b, clone args, clone context_creator, || evaluate_method(24 context_creator.0(this.clone(), super_obj.clone()),25 &b.value,26 args.clone()27 ))28 )),29 )30 } else {31 (32 b.name.clone(),33 lazy_binding!(move |this, super_obj| {34 lazy_val!(closure!(clone context_creator, clone b, || evaluate(35 context_creator.0(this.clone(), super_obj.clone()),36 &b.value37 )))38 }),39 )40 }41}4243pub fn evaluate_method(ctx: Context, expr: &Expr, arg_spec: ParamsDesc) -> Val {44 Val::Func(FuncDesc {45 ctx,46 params: arg_spec,47 eval_rhs: function_rhs!(closure!(clone expr, |ctx| evaluate(ctx, &expr))),48 eval_default: function_default!(|ctx, default| evaluate(ctx, &default)),49 })50}5152pub fn evaluate_field_name(context: Context, field_name: &jsonnet_parser::FieldName) -> String {53 match field_name {54 jsonnet_parser::FieldName::Fixed(n) => n.clone(),55 jsonnet_parser::FieldName::Dyn(expr) => {56 let name = evaluate(context, expr).unwrap_if_lazy();57 match name {58 Val::Str(n) => n,59 _ => panic!(60 "dynamic field name can be only evaluated to 'string', got: {:?}",61 name62 ),63 }64 }65 }66}6768pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val {69 match (op, b) {70 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.0()),71 (UnaryOpType::Not, Val::Literal(LiteralType::True)) => Val::Literal(LiteralType::False),72 (UnaryOpType::Not, Val::Literal(LiteralType::False)) => Val::Literal(LiteralType::True),73 (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),74 }75}7677pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {78 match (a, op, b) {79 (Val::Lazy(a), o, b) => evaluate_binary_op(&a.0(), o, b),80 (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.0()),8182 (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),83 (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),84 (Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),8586 (Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),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 }101102 (Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),103104 (Val::Arr(a), BinaryOpType::Add, Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),105106 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),107 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),108 (Val::Num(v1), BinaryOpType::Mod, Val::Num(v2)) => Val::Num(v1 % v2),109110 (Val::Num(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Num(v1 + v2),111 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),112113 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {114 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)115 }116 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {117 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)118 }119120 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => bool_val(v1 < v2),121 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => bool_val(v1 > v2),122 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => bool_val(v1 <= v2),123 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => bool_val(v1 >= v2),124125 (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val((v1 - v2).abs() < f64::EPSILON),126 (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val((v1 - v2).abs() > f64::EPSILON),127128 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {129 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)130 }131 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {132 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)133 }134 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {135 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)136 }137 _ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),138 }139}140141future_wrapper!(HashMap<String, LazyBinding>, FutureNewBindings);142future_wrapper!(ObjValue, FutureObjValue);143144// TODO: Asserts145pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {146 match object {147 ObjBody::MemberList(members) => {148 let new_bindings = FutureNewBindings::new();149 let future_this = FutureObjValue::new();150 let context_creator = context_creator!(151 closure!(clone context, clone new_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {152 context.clone().extend(153 new_bindings.clone().unwrap(),154 context.clone().dollar().clone().or_else(||this.clone()),155 Some(this.clone().unwrap()),156 super_obj157 )158 })159 );160 {161 let mut bindings: HashMap<String, LazyBinding> = HashMap::new();162 for (n, b) in members163 .iter()164 .filter_map(|m| match m {165 Member::BindStmt(b) => Some(b.clone()),166 _ => None,167 })168 .map(|b| evaluate_binding(&b, context_creator.clone()))169 {170 bindings.insert(n, b);171 }172 new_bindings.fill(bindings);173 }174175 let mut new_members = BTreeMap::new();176 for member in members.into_iter() {177 match member {178 Member::Field(FieldMember {179 name,180 plus,181 params: None,182 visibility,183 value,184 }) => {185 let name = evaluate_field_name(context.clone(), &name);186 new_members.insert(187 name,188 ObjMember {189 add: plus,190 visibility: visibility.clone(),191 invoke: binding!(192 closure!(clone value, clone context_creator, |this, super_obj| {193 let context = context_creator.0(this, super_obj);194 // TODO: Assert195 evaluate(196 context,197 &value,198 ).unwrap_if_lazy()199 })200 ),201 },202 );203 }204 Member::Field(FieldMember {205 name,206 params: Some(params),207 value,208 ..209 }) => {210 let name = evaluate_field_name(context.clone(), &name);211 new_members.insert(212 name,213 ObjMember {214 add: false,215 visibility: Visibility::Hidden,216 invoke: binding!(217 closure!(clone value, clone context_creator, |this, super_obj| {218 // TODO: Assert219 evaluate_method(220 context_creator.0(this, super_obj),221 &value.clone(),222 params.clone(),223 )224 })225 ),226 },227 );228 }229 Member::BindStmt(_) => {}230 Member::AssertStmt(_) => {}231 }232 }233 future_this.fill(ObjValue::new(None, Rc::new(new_members)))234 }235 _ => todo!(),236 }237}238239pub fn evaluate(context: Context, expr: &Expr) -> Val {240 use Expr::*;241 match &*expr {242 Literal(LiteralType::This) => Val::Obj(243 context244 .this()245 .clone()246 .unwrap_or_else(|| panic!("this not found")),247 ),248 Literal(LiteralType::Super) => Val::Obj(249 context250 .super_obj()251 .clone()252 .unwrap_or_else(|| panic!("super not found")),253 ),254 Literal(t) => Val::Literal(t.clone()),255 Parened(e) => evaluate(context, e),256 Str(v) => Val::Str(v.clone()),257 Num(v) => Val::Num(*v),258 BinaryOp(v1, o, v2) => {259 evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))260 }261 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)),262 Var(name) => Val::Lazy(context.binding(&name)).unwrap_if_lazy(),263 Index(box value, box index) => {264 match (265 evaluate(context.clone(), value).unwrap_if_lazy(),266 evaluate(context.clone(), index),267 ) {268 (Val::Obj(v), Val::Str(s)) => v269 .get(&s)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(),282 (Val::Arr(v), Val::Num(n)) => v283 .get(n as usize)284 .unwrap_or_else(|| panic!("out of bounds"))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 }290 (v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),291 }292 }293 LocalExpr(bindings, returned) => {294 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();295 let future_context = Context::new_future();296297 let context_creator = context_creator!(298 closure!(clone future_context, |_, _| future_context.clone().unwrap())299 );300301 for (k, v) in bindings302 .iter()303 .map(move |b| evaluate_binding(b, context_creator.clone()))304 {305 new_bindings.insert(k, v);306 }307308 let context = context309 .extend(new_bindings, None, None, None)310 .into_future(future_context);311 evaluate(context, &*returned.clone())312 }313 Obj(body) => Val::Obj(evaluate_object(context, body.clone())),314 Apply(box value, ArgsDesc(args)) => {315 let value = evaluate(context.clone(), value).unwrap_if_lazy();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 },360 Val::Func(f) => f.evaluate(361 args.clone()362 .into_iter()363 .map(|a| {364 (365 a.clone().0,366 Val::Lazy(lazy_val!(367 closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))368 )),369 )370 })371 .collect(),372 ),373 _ => panic!("{:?} is not a function", value),374 }375 }376 Function(params, body) => evaluate_method(context, body, params.clone()),377 Error(e) => panic!("error: {}", evaluate(context, e)),378 IfElse {379 cond,380 cond_then,381 cond_else,382 } => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {383 Val::Literal(LiteralType::True) => evaluate(context, cond_then),384 Val::Literal(LiteralType::False) => match cond_else {385 Some(v) => evaluate(context, v),386 None => Val::Literal(LiteralType::False),387 },388 v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),389 },390 _ => panic!("evaluation not implemented: {:?}", expr),391 }392}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())))
),
);
}