git.delta.rocks / jrsonnet / refs/commits / fde4f0975c47

difftreelog

refactor(evaluator) extract shared function call parsing code

Лач2020-06-09parent: #f287f46.patch.diff
in: master

8 files changed

modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
90 let opts: Opts = Opts::parse();90 let opts: Opts = Opts::parse();
91 let evaluator = jsonnet_evaluator::EvaluationState::default();91 let evaluator = jsonnet_evaluator::EvaluationState::default();
92 if !opts.no_stdlib {92 if !opts.no_stdlib {
93 evaluator.add_stdlib();93 evaluator.with_stdlib();
94 }94 }
95 let mut input = current_dir().unwrap();95 let mut input = current_dir().unwrap();
96 input.push(opts.input.clone());96 input.push(opts.input.clone());
105 let v = match opts.format {105 let v = match opts.format {
106 Format::Json => {106 Format::Json => {
107 if opts.no_stdlib {107 if opts.no_stdlib {
108 evaluator.add_stdlib();108 evaluator.with_stdlib();
109 }109 }
110 evaluator.add_global("__tmp__to_json__".to_owned(), v);110 evaluator.add_global("__tmp__to_json__".to_owned(), v);
111 let v = evaluator.parse_evaluate_raw(&format!(111 let v = evaluator.parse_evaluate_raw(&format!(
122 }122 }
123 Format::Yaml => {123 Format::Yaml => {
124 if opts.no_stdlib {124 if opts.no_stdlib {
125 evaluator.add_stdlib();125 evaluator.with_stdlib();
126 }126 }
127 evaluator.add_global("__tmp__to_yaml__".to_owned(), v);127 evaluator.add_global("__tmp__to_yaml__".to_owned(), v);
128 let v = evaluator128 let v = evaluator
modifiedcrates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
1use crate::{1use crate::{
2 future_wrapper, rc_fn_helper, resolved_lazy_val, LazyBinding, LazyVal, ObjValue, Result, Val,2 future_wrapper, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val, LazyBinding, LazyVal,
3 ObjValue, Result, Val,
3};4};
4use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};5use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
1112
12future_wrapper!(Context, FutureContext);13future_wrapper!(Context, FutureContext);
1314
14#[derive(Debug)]
15struct ContextInternals {15struct ContextInternals {
16 dollar: Option<ObjValue>,16 dollar: Option<ObjValue>,
17 this: Option<ObjValue>,17 this: Option<ObjValue>,
18 super_obj: Option<ObjValue>,18 super_obj: Option<ObjValue>,
19 bindings: Rc<HashMap<String, LazyVal>>,19 bindings: LayeredHashMap<String, LazyVal>,
20}20}
21pub struct Context(Rc<ContextInternals>);21pub struct Context(Rc<ContextInternals>);
22impl Debug for Context {22impl Debug for Context {
48 dollar: None,48 dollar: None,
49 this: None,49 this: None,
50 super_obj: None,50 super_obj: None,
51 bindings: Rc::new(HashMap::new()),51 bindings: LayeredHashMap::default(),
52 }))52 }))
53 }53 }
5454
65 }65 }
6666
67 pub fn with_var(&self, name: String, value: Val) -> Result<Context> {67 pub fn with_var(&self, name: String, value: Val) -> Result<Context> {
68 let mut new_bindings: HashMap<_, LazyBinding> = HashMap::new();68 let mut new_bindings = HashMap::new();
69 new_bindings.insert(name, LazyBinding::Bound(resolved_lazy_val!(value.clone())));69 new_bindings.insert(name, resolved_lazy_val!(value));
70 self.extend(new_bindings, None, None, None)70 self.extend(new_bindings, None, None, None)
71 }71 }
7272
73 pub fn extend(73 pub fn extend(
74 &self,74 &self,
75 new_bindings: HashMap<String, LazyBinding>,75 new_bindings: HashMap<String, LazyVal>,
76 new_dollar: Option<ObjValue>,76 new_dollar: Option<ObjValue>,
77 new_this: Option<ObjValue>,77 new_this: Option<ObjValue>,
78 new_super_obj: Option<ObjValue>,78 new_super_obj: Option<ObjValue>,
83 let bindings = if new_bindings.is_empty() {83 let bindings = if new_bindings.is_empty() {
84 self.0.bindings.clone()84 self.0.bindings.clone()
85 } else {85 } else {
86 let mut new = HashMap::new(); // = self.0.bindings.clone();
87 for (k, v) in self.0.bindings.iter() {86 self.0.bindings.extend(new_bindings)
88 new.insert(k.clone(), v.clone());
89 }
90 for (k, v) in new_bindings.into_iter() {
91 new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);
92 }
93 Rc::new(new)
94 };87 };
95 Ok(Context(Rc::new(ContextInternals {88 Ok(Context(Rc::new(ContextInternals {
96 dollar,89 dollar,
99 bindings,92 bindings,
100 })))93 })))
101 }94 }
95 pub fn extend_unbound(
96 &self,
97 new_bindings: HashMap<String, LazyBinding>,
98 new_dollar: Option<ObjValue>,
99 new_this: Option<ObjValue>,
100 new_super_obj: Option<ObjValue>,
101 ) -> Result<Context> {
102 let this = new_this.or_else(|| self.0.this.clone());
103 let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());
104 let mut new = HashMap::new();
105 for (k, v) in new_bindings.into_iter() {
106 new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);
107 }
108 self.extend(new, new_dollar, this, super_obj)
109 }
102}110}
103111
104impl Default for Context {112impl Default for Context {
modifiedcrates/jsonnet-evaluator/src/error.rsdiffbeforeafterboth
7 TypeMismatch(&'static str, Vec<ValType>, ValType),7 TypeMismatch(&'static str, Vec<ValType>, ValType),
8 NoSuchField(String),8 NoSuchField(String),
9
10 UnknownFunctionParameter(String),
11 BindingParameterASecondTime(String),
12 TooManyArgsFunctionHas(usize),
13 FunctionParameterNotBoundInCall(String),
914
10 RuntimeError(String),15 RuntimeError(String),
11 StackOverflow,16 StackOverflow,
modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
1use crate::{1use crate::{
2 binding, context_creator, create_error, function_default, function_rhs, future_wrapper,2 binding, context_creator, create_error, future_wrapper, lazy_val, push, Context,
3 lazy_val, push, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue,3 ContextCreator, FuncDesc, LazyBinding, ObjMember, ObjValue, Result, Val,
4 Result, Val,
5};4};
6use closure::closure;5use closure::closure;
7use jsonnet_parser::{6use jsonnet_parser::{
8 ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember, ForSpecData,7 el, Arg, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember,
9 IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType, Visibility,8 ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,
9 Visibility,
10};10};
1515
16pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, LazyBinding) {16pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, LazyBinding) {
17 let b = b.clone();17 let b = b.clone();
18 if let Some(args) = &b.params {18 if let Some(params) = &b.params {
19 let args = args.clone();19 let params = params.clone();
20 (20 (
21 b.name.clone(),21 b.name.clone(),
22 LazyBinding::Bindable(Rc::new(move |this, super_obj| {22 LazyBinding::Bindable(Rc::new(move |this, super_obj| {
23 Ok(lazy_val!(23 Ok(lazy_val!(
24 closure!(clone b, clone args, clone context_creator, || Ok(evaluate_method(24 closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(
25 context_creator.0(this.clone(), super_obj.clone())?,25 context_creator.0(this.clone(), super_obj.clone())?,
26 params.clone(),
26 &b.value,27 b.value.clone(),
27 args.clone()
28 )))28 )))
29 ))29 ))
30 })),30 })),
46 }46 }
47}47}
4848
49pub fn evaluate_method(ctx: Context, expr: &LocExpr, arg_spec: ParamsDesc) -> Val {49pub fn evaluate_method(ctx: Context, params: ParamsDesc, body: LocExpr) -> Val {
50 Val::Func(FuncDesc {50 Val::Func(FuncDesc { ctx, params, body })
51 ctx,
52 params: arg_spec,
53 eval_rhs: function_rhs!(closure!(clone expr, |ctx| evaluate(ctx, &expr))),
54 eval_default: function_default!(closure!(|ctx, default| evaluate(ctx, &default))),
55 })
56}51}
5752
214 let future_this = FutureObjValue::new();209 let future_this = FutureObjValue::new();
215 let context_creator = context_creator!(210 let context_creator = context_creator!(
216 closure!(clone context, clone new_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {211 closure!(clone context, clone new_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
217 Ok(context.clone().extend(212 Ok(context.clone().extend_unbound(
218 new_bindings.clone().unwrap(),213 new_bindings.clone().unwrap(),
219 context.clone().dollar().clone().or_else(||this.clone()),214 context.clone().dollar().clone().or_else(||this.clone()),
220 Some(this.unwrap()),215 Some(this.unwrap()),
292 // TODO: Assert287 // TODO: Assert
293 Ok(evaluate_method(288 Ok(evaluate_method(
294 context_creator.0(this, super_obj)?,289 context_creator.0(this, super_obj)?,
295 &value.clone(),290 params.clone(),
296 params.clone(),291 value.clone(),
297 ))292 ))
298 })293 })
299 ),294 ),
393 }388 }
394389
395 let context = context390 let context = context
396 .extend(new_bindings, None, None, None)?391 .extend_unbound(new_bindings, None, None, None)?
397 .into_future(future_context);392 .into_future(future_context);
398 evaluate(context, &returned.clone())?393 evaluate(context, &returned.clone())?
399 }394 }
417 &evaluate(context.clone(), s)?,412 &evaluate(context.clone(), s)?,
418 &Val::Obj(evaluate_object(context, t.clone())?),413 &Val::Obj(evaluate_object(context, t.clone())?),
419 )?,414 )?,
420 Apply(value, ArgsDesc(args), tailstrict) => {415 Apply(value, args, tailstrict) => {
421 let value = evaluate(context.clone(), value)?.unwrap_if_lazy()?;416 let value = evaluate(context.clone(), value)?.unwrap_if_lazy()?;
422 match value {417 match value {
423 Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {418 Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {
448 assert!(v >= 0.0);443 assert!(v >= 0.0);
449 let mut out = Vec::with_capacity(v as usize);444 let mut out = Vec::with_capacity(v as usize);
450 for i in 0..v as usize {445 for i in 0..v as usize {
446 let call_ctx =
447 Context::new().with_var("v".to_owned(), Val::Num(i as f64))?;
451 out.push(d.evaluate(vec![(None, Val::Num(i as f64))])?)448 out.push(d.evaluate(
449 call_ctx,
450 &ArgsDesc(vec![Arg(None, el!(Expr::Var("v".to_owned())))]),
451 true,
452 )?)
452 }453 }
536 },537 },
537 Val::Func(f) => {538 Val::Func(f) => {
538 let body = #[inline(always)]539 let body = #[inline(always)]
539 || {540 || f.evaluate(context, args, *tailstrict);
540 f.evaluate(
541 args.clone()
542 .into_iter()
543 .map(
544 #[inline(always)]
545 move |a| {
546 Ok((
547 a.clone().0,
548 if *tailstrict {
549 Val::Lazy(LazyVal::new_resolved(evaluate(
550 context.clone(),
551 &a.1,
552 )?))
553 } else {
554 Val::Lazy(lazy_val!(
555 closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))
556 ))
557 },
558 ))
559 },
560 )
561 .collect::<Result<Vec<_>>>()?,
562 )
563 };
564 if *tailstrict {541 if *tailstrict {
565 body()?542 body()?
566 } else {543 } else {
570 _ => panic!("{:?} is not a function", value),547 _ => panic!("{:?} is not a function", value),
571 }548 }
572 }549 }
573 Function(params, body) => evaluate_method(context, body, params.clone()),550 Function(params, body) => evaluate_method(context, params.clone(), body.clone()),
574 AssertExpr(AssertStmt(value, msg), returned) => {551 AssertExpr(AssertStmt(value, msg), returned) => {
575 let assertion_result = push(value.clone(), "assertion condition".to_owned(), || {552 let assertion_result = push(value.clone(), "assertion condition".to_owned(), || {
576 evaluate(context.clone(), &value)?553 evaluate(context.clone(), &value)?
modifiedcrates/jsonnet-evaluator/src/function.rsdiffbeforeafterboth

no changes

modifiedcrates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth
8mod error;8mod error;
9mod evaluate;9mod evaluate;
10mod function;10mod function;
11mod map;
11mod obj;12mod obj;
12mod val;13mod val;
1314
14pub use ctx::*;15pub use ctx::*;
15pub use dynamic::*;16pub use dynamic::*;
16pub use error::*;17pub use error::*;
17pub use evaluate::*;18pub use evaluate::*;
19pub use function::parse_function_call;
18use jsonnet_parser::*;20use jsonnet_parser::*;
19pub use obj::*;21pub use obj::*;
20use std::{cell::RefCell, collections::HashMap, fmt::Debug, path::PathBuf, rc::Rc};22use std::{cell::RefCell, collections::HashMap, fmt::Debug, path::PathBuf, rc::Rc};
25 binding,27 binding,
26 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<Val>28 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<Val>
27);29);
28rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Result<Val>);30
29rc_fn_helper!(
30 FunctionDefault,
31 function_default,
32 dyn Fn(Context, LocExpr) -> Result<Val>31type BindableFn = dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<LazyVal>;
33);
34
35#[derive(Clone)]32#[derive(Clone)]
36pub enum LazyBinding {33pub enum LazyBinding {
37 Bindable(Rc<dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<LazyVal>>),34 Bindable(Rc<BindableFn>),
38 Bound(LazyVal),35 Bound(LazyVal),
39}36}
4037
59impl Default for EvaluationSettings {56impl Default for EvaluationSettings {
60 fn default() -> Self {57 fn default() -> Self {
61 EvaluationSettings {58 EvaluationSettings {
62 max_stack_frames: 500,59 max_stack_frames: 200,
63 max_stack_trace_size: 20,60 max_stack_trace_size: 20,
64 }61 }
65 }62 }
181 self.0.globals.borrow_mut().insert(name, value);178 self.0.globals.borrow_mut().insert(name, value);
182 }179 }
183180
184 pub fn add_stdlib(&self) {181 pub fn with_stdlib(&self) -> &Self {
185 self.begin_state();182 self.begin_state();
186 use jsonnet_stdlib::STDLIB_STR;183 use jsonnet_stdlib::STDLIB_STR;
187 if cfg!(feature = "serialized-stdlib") {184 if cfg!(feature = "serialized-stdlib") {
199 let val = self.evaluate_file(&PathBuf::from("std.jsonnet")).unwrap();196 let val = self.evaluate_file(&PathBuf::from("std.jsonnet")).unwrap();
200 self.add_global("std".to_owned(), val);197 self.add_global("std".to_owned(), val);
201 self.end_state();198 self.end_state();
199 self
202 }200 }
203201
204 pub fn create_default_context(&self) -> Result<Context> {202 pub fn create_default_context(&self) -> Result<Context> {
210 LazyBinding::Bound(resolved_lazy_val!(value.clone())),208 LazyBinding::Bound(resolved_lazy_val!(value.clone())),
211 );209 );
212 }210 }
213 Context::new().extend(new_bindings, None, None, None)211 Context::new().extend_unbound(new_bindings, None, None, None)
214 }212 }
215213
216 #[inline(always)]214 #[inline(always)]
297 #[test]295 #[test]
298 fn eval_state_standard() {296 fn eval_state_standard() {
299 let state = EvaluationState::default();297 let state = EvaluationState::default();
300 state.add_stdlib();298 state.with_stdlib();
301 assert_eq!(299 assert_eq!(
302 state300 state
303 .parse_evaluate_raw(r#"std.assertEqual(std.base64("test"), "dGVzdA==")"#)301 .parse_evaluate_raw(r#"std.assertEqual(std.base64("test"), "dGVzdA==")"#)
308306
309 macro_rules! eval {307 macro_rules! eval {
310 ($str: expr) => {308 ($str: expr) => {
311 evaluate(
312 Context::new(),
313 EvaluationState::default(),309 EvaluationState::default()
310 .with_stdlib()
314 &parse(311 .parse_evaluate_raw($str)
315 $str,
316 &ParserSettings {
317 loc_data: true,
318 file_name: "test.jsonnet".to_owned(),
319 },
320 )
321 .unwrap(),312 .unwrap()
322 )
323 };313 };
324 }314 }
325
326 macro_rules! eval_stdlib {315 macro_rules! eval_json {
327 ($str: expr) => {{316 ($str: expr) => {{
328 let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";317 let evaluator = EvaluationState::default();
329 evaluate(318 evaluator.with_stdlib();
330 Context::new(),
331 EvaluationState::default(),
332 &parse(
333 &(std + $str),319 let val = evaluator.parse_evaluate_raw($str).unwrap();
334 &ParserSettings {
335 loc_data: true,
336 file_name: "test.jsonnet".to_owned(),320 evaluator.add_global("__tmp__to_yaml__".to_owned(), val);
337 },321 evaluator
338 )322 .parse_evaluate_raw("std.manifestJsonEx(__tmp__to_yaml__, \"\")")
339 .unwrap(),323 .unwrap()
340 )324 .try_cast_str("there should be json string")
325 .unwrap()
326 .clone()
327 .replace("\n", "")
341 }};328 }};
342 }329 }
343330
331 /// Asserts given code returns `true`
344 macro_rules! assert_eval {332 macro_rules! assert_eval {
345 ($str: expr) => {333 ($str: expr) => {
346 assert_eq!(334 assert_eq!(eval!($str), Val::Bool(true))
347 evaluate(
348 Context::new(),
349 EvaluationState::default(),
350 &parse(
351 $str,
352 &ParserSettings {
353 loc_data: true,
354 file_name: "test.jsonnet".to_owned(),
355 }
356 )
357 .unwrap()
358 ),
359 Val::Bool(true)
360 )
361 };335 };
362 }336 }
337
338 /// Asserts given code returns `false`
363 macro_rules! assert_json {339 macro_rules! assert_eval_neg {
364 ($str: expr, $out: expr) => {340 ($str: expr) => {
365 assert_eq!(341 assert_eq!(eval!($str), Val::Bool(false))
366 format!(
367 "{}",
368 evaluate(
369 Context::new(),
370 EvaluationState::default(),
371 &parse(
372 $str,
373 &ParserSettings {
374 loc_data: true,
375 file_name: "test.jsonnet".to_owned(),
376 }
377 )
378 .unwrap()
379 )
380 ),
381 $out
382 )
383 };342 };
384 }343 }
385 macro_rules! assert_json_stdlib {344 macro_rules! assert_json {
386 ($str: expr, $out: expr) => {345 ($str: expr, $out: expr) => {
387 assert_eq!(format!("{}", eval_stdlib!($str)), $out)346 assert_eq!(eval_json!($str), $out.replace("\t", ""))
388 };347 };
389 }348 }
349
350 /// Sanity checking, before trusting to another tests
351 #[test]
352 fn equality_operator() {
353 assert_eval!("2 == 2");
354 assert_eval_neg!("2 != 2");
355 assert_eval!("2 != 3");
356 assert_eval_neg!("2 == 3");
357 assert_eval!("'Hello' == 'Hello'");
358 assert_eval_neg!("'Hello' != 'Hello'");
359 assert_eval!("'Hello' != 'World'");
360 assert_eval_neg!("'Hello' == 'World'");
361 }
362
363 #[test]
364 fn math_evaluation() {
365 assert_eval!("2 + 2 * 2 == 6");
366 assert_eval!("3 + (2 + 2 * 2) == 9");
367 }
368
369 #[test]
370 fn string_concat() {
371 assert_eval!("'Hello' + 'World' == 'HelloWorld'");
372 assert_eval!("'Hello' * 3 == 'HelloHelloHello'");
373 assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");
374 }
375
376 #[test]
377 fn function_contexts() {
378 assert_eval!(
379 r#"
380 local k = {
381 t(name = self.h): [self.h, name],
382 h: 3,
383 };
384 local f = {
385 t: k.t(),
386 h: 4,
387 };
388 f.t[0] == f.t[1]
389 "#
390 );
391 }
392
393 #[test]
394 fn local() {
395 assert_eval!("local a = 2; local b = 3; a + b == 5");
396 assert_eval!("local a = 1, b = a + 1; a + b == 3");
397 assert_eval!("local a = 1; local a = 2; a == 2");
398 }
399
400 #[test]
401 fn object_lazyness() {
402 assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);
403 }
404
405 /// FIXME: This test gets stackoverflow in debug build
406 #[test]
407 fn object_inheritance() {
408 assert_json!("{a: self.b} + {b:3}", r#"{"a": 3,"b": 3}"#);
409 }
410
411 #[test]
412 fn test_object() {
413 assert_json!("{a:2}", r#"{"a": 2}"#);
414 assert_json!("{a:2+2}", r#"{"a": 4}"#);
415 assert_json!("{a:2}+{b:2}", r#"{"a": 2,"b": 2}"#);
416 assert_json!("{b:3}+{b:2}", r#"{"b": 2}"#);
417 assert_json!("{b:3}+{b+:2}", r#"{"b": 5}"#);
418 assert_json!("local test='a'; {[test]:2}", r#"{"a": 2}"#);
419 assert_json!(
420 r#"
421 {
422 name: "Alice",
423 welcome: "Hello " + self.name + "!",
424 }
425 "#,
426 r#"{"name": "Alice","welcome": "Hello Alice!"}"#
427 );
428 assert_json!(
429 r#"
430 {
431 name: "Alice",
432 welcome: "Hello " + self.name + "!",
433 } + {
434 name: "Bob"
435 }
436 "#,
437 r#"{"name": "Bob","welcome": "Hello Bob!"}"#
438 );
439 }
440
441 #[test]
442 fn functions() {
443 assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");
444 assert_json!(
445 r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,
446 r#""HelloDearWorld""#
447 );
448 }
449
450 #[test]
451 fn local_methods() {
452 assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");
453 assert_json!(
454 r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,
455 r#""HelloDearWorld""#
456 );
457 }
458
459 #[test]
460 fn object_locals() {
461 assert_json!(r#"{local a = 3, b: a}"#, r#"{"b": 3}"#);
462 assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b": 3}"#);
463 assert_json!(
464 r#"{local a = function (b) {[b]:4}, test: a("test")}"#,
465 r#"{"test": {"test": 4}}"#
466 );
467 }
468
469 #[test]
470 fn direct_self() {
471 println!(
472 "{:#?}",
473 eval!(
474 r#"
475 {
476 local me = self,
477 a: 3,
478 b(): me.a,
479 }
480 "#
481 )
482 );
483 }
484
485 #[test]
486 fn indirect_self() {
487 // `self` assigned to `me` was lost when being
488 // referenced from field
489 eval!(
490 r#"{
491 local me = self,
492 a: 3,
493 b: me.a,
494 }.b"#
495 );
496 }
497
498 // We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly
499 #[test]
500 fn std_assert_ok() {
501 eval!("std.assertEqual(4.5 << 2, 16)");
502 }
503
504 #[test]
505 #[should_panic]
506 fn std_assert_failure() {
507 eval!("std.assertEqual(4.5 << 2, 15)");
508 }
509
510 #[test]
390 macro_rules! assert_eval_neg {511 fn string_is_string() {
391 ($str: expr) => {
392 assert_eq!(512 assert_eq!(
393 evaluate(
394 Context::new(),
395 EvaluationState::default(),513 eval!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),
396 &parse(
397 $str,
398 &ParserSettings {
399 loc_data: true,
400 file_name: "test.jsonnet".to_owned(),
401 }
402 )
403 .unwrap()
404 ),
405 Val::Bool(false)514 Val::Bool(false)
406 )515 );
407 };
408 }516 }
409517
410 /*518 #[test]
411 /// Sanity checking, before trusting to another tests519 fn base64_works() {
412 #[test]520 assert_json!(r#"std.base64("test")"#, r#""dGVzdA==""#);
413 fn equality_operator() {521 }
414 assert_eval!("2 == 2");522
415 assert_eval_neg!("2 != 2");523 #[test]
416 assert_eval!("2 != 3");524 fn utf8_chars() {
417 assert_eval_neg!("2 == 3");525 assert_json!(
418 assert_eval!("'Hello' == 'Hello'");526 r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,
419 assert_eval_neg!("'Hello' != 'Hello'");527 r#"{"c": 128526,"l": 1}"#
420 assert_eval!("'Hello' != 'World'");528 )
421 assert_eval_neg!("'Hello' == 'World'");529 }
422 }530
423531 #[test]
424 #[test]532 fn json() {
425 fn math_evaluation() {533 assert_json!(
426 assert_eval!("2 + 2 * 2 == 6");534 r#"std.manifestJsonEx({a:3, b:4, c:6},"")"#,
427 assert_eval!("3 + (2 + 2 * 2) == 9");535 r#""{\n\"a\": 3,\n\"b\": 4,\n\"c\": 6\n}""#
428 }536 );
429537 }
430 #[test]538
431 fn string_concat() {539 #[test]
432 assert_eval!("'Hello' + 'World' == 'HelloWorld'");540 fn test() {
433 assert_eval!("'Hello' * 3 == 'HelloHelloHello'");541 assert_json!(
434 assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");542 r#"[[a, b] for a in [1,2,3] for b in [4,5,6]]"#,
435 }543 "[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]"
436544 );
437 #[test]545 }
438 fn local() {546
439 assert_eval!("local a = 2; local b = 3; a + b == 5");547 #[test]
440 assert_eval!("local a = 1, b = a + 1; a + b == 3");548 fn sjsonnet() {
441 assert_eval!("local a = 1; local a = 2; a == 2");549 eval!(
442 }550 r#"
443551 local x0 = {k: 1};
444 #[test]552 local x1 = {k: x0.k + x0.k};
445 fn object_lazyness() {553 local x2 = {k: x1.k + x1.k};
446 assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);554 local x3 = {k: x2.k + x2.k};
447 }555 local x4 = {k: x3.k + x3.k};
448556 local x5 = {k: x4.k + x4.k};
449 #[test]557 local x6 = {k: x5.k + x5.k};
450 fn object_inheritance() {558 local x7 = {k: x6.k + x6.k};
451 assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);559 local x8 = {k: x7.k + x7.k};
452 }560 local x9 = {k: x8.k + x8.k};
453561 local x10 = {k: x9.k + x9.k};
454 #[test]562 local x11 = {k: x10.k + x10.k};
455 fn test_object() {563 local x12 = {k: x11.k + x11.k};
456 assert_json!("{a:2}", r#"{"a":2}"#);564 local x13 = {k: x12.k + x12.k};
457 assert_json!("{a:2+2}", r#"{"a":4}"#);565 local x14 = {k: x13.k + x13.k};
458 assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);566 local x15 = {k: x14.k + x14.k};
459 assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);567 local x16 = {k: x15.k + x15.k};
460 assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);568 local x17 = {k: x16.k + x16.k};
461 assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);569 local x18 = {k: x17.k + x17.k};
462 assert_json!(570 local x19 = {k: x18.k + x18.k};
463 r#"571 local x20 = {k: x19.k + x19.k};
464 {572 local x21 = {k: x20.k + x20.k};
465 name: "Alice",573 x21.k
466 welcome: "Hello " + self.name + "!",574 "#
467 }575 );
468 "#,576 }
469 r#"{"name":"Alice","welcome":"Hello Alice!"}"#
470 );
471 assert_json!(
472 r#"
473 {
474 name: "Alice",
475 welcome: "Hello " + self.name + "!",
476 } + {
477 name: "Bob"
478 }
479 "#,
480 r#"{"name":"Bob","welcome":"Hello Bob!"}"#
481 );
482 }
483
484 #[test]
485 fn functions() {
486 assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");
487 assert_json!(
488 r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,
489 r#""HelloDearWorld""#
490 );
491 }
492
493 #[test]
494 fn local_methods() {
495 assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");
496 assert_json!(
497 r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,
498 r#""HelloDearWorld""#
499 );
500 }
501
502 #[test]
503 fn object_locals() {
504 assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);
505 assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);
506 assert_json!(
507 r#"{local a = function (b) {[b]:4}, test: a("test")}"#,
508 r#"{"test":{"test":4}}"#
509 );
510 }
511
512 #[test]
513 fn direct_self() {
514 println!(
515 "{:#?}",
516 eval!(
517 r#"
518 {
519 local me = self,
520 a: 3,
521 b(): me.a,
522 }
523 "#
524 )
525 );
526 }
527
528 #[test]
529 fn indirect_self() {
530 // `self` assigned to `me` was lost when being
531 // referenced from field
532 eval_stdlib!(
533 r#"{
534 local me = self,
535 a: 3,
536 b: me.a,
537 }.b"#
538 );
539 }
540
541 // We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly
542 #[test]
543 fn std_assert_ok() {
544 eval_stdlib!("std.assertEqual(4.5 << 2, 16)");
545 }
546
547 #[test]
548 #[should_panic]
549 fn std_assert_failure() {
550 eval_stdlib!("std.assertEqual(4.5 << 2, 15)");
551 }
552
553 #[test]
554 fn string_is_string() {
555 assert_eq!(
556 eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),
557 Val::Bool(false)
558 );
559 }
560
561 #[test]
562 fn base64_works() {
563 assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);
564 }
565
566 #[test]
567 fn utf8_chars() {
568 assert_json_stdlib!(
569 r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#,
570 r#"{"c":128526,"l":1}"#
571 )
572 }
573
574 #[test]
575 fn json() {
576 assert_json_stdlib!(
577 r#"std.manifestJsonEx({a:3, b:4, c:6},"")"#,
578 r#""{\n"a": 3,\n"b": 4,\n"c": 6\n}""#
579 );
580 }
581
582 #[test]
583 fn test() {
584 assert_json_stdlib!(
585 r#"[[a, b] for a in [1,2,3] for b in [4,5,6]]"#,
586 "[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]"
587 );
588 }
589
590 #[test]
591 fn sjsonnet() {
592 eval!(
593 r#"
594 local x0 = {k: 1};
595 local x1 = {k: x0.k + x0.k};
596 local x2 = {k: x1.k + x1.k};
597 local x3 = {k: x2.k + x2.k};
598 local x4 = {k: x3.k + x3.k};
599 local x5 = {k: x4.k + x4.k};
600 local x6 = {k: x5.k + x5.k};
601 local x7 = {k: x6.k + x6.k};
602 local x8 = {k: x7.k + x7.k};
603 local x9 = {k: x8.k + x8.k};
604 local x10 = {k: x9.k + x9.k};
605 local x11 = {k: x10.k + x10.k};
606 local x12 = {k: x11.k + x11.k};
607 local x13 = {k: x12.k + x12.k};
608 local x14 = {k: x13.k + x13.k};
609 local x15 = {k: x14.k + x14.k};
610 local x16 = {k: x15.k + x15.k};
611 local x17 = {k: x16.k + x16.k};
612 local x18 = {k: x17.k + x17.k};
613 local x19 = {k: x18.k + x18.k};
614 local x20 = {k: x19.k + x19.k};
615 local x21 = {k: x20.k + x20.k};
616 x21.k
617 "#
618 );
619 }
620 */
621}577}
622578
addedcrates/jsonnet-evaluator/src/map.rsdiffbeforeafterboth

no changes

modifiedcrates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth
1use crate::{1use crate::{
2 create_error, Context, Error, FunctionDefault, FunctionRhs, LazyBinding, ObjValue, Result,2 create_error, evaluate, function::inline_parse_function_call, Context, Error, ObjValue, Result,
3};3};
4use closure::closure;
5use jsonnet_parser::{Param, ParamsDesc};4use jsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};
6use std::{5use std::{
7 cell::RefCell,6 cell::RefCell,
8 collections::HashMap,
9 fmt::{Debug, Display},7 fmt::{Debug, Display},
10 rc::Rc,8 rc::Rc,
11};9};
73pub struct FuncDesc {71pub struct FuncDesc {
74 pub ctx: Context,72 pub ctx: Context,
75 pub params: ParamsDesc,73 pub params: ParamsDesc,
76 pub eval_rhs: FunctionRhs,74 pub body: LocExpr,
77 pub eval_default: FunctionDefault,
78}75}
79impl FuncDesc {76impl FuncDesc {
80 // TODO: Check for unset variables77 // TODO: Check for unset variables
81 /// This function is always inlined to make tailstrict work78 /// This function is always inlined to make tailstrict work
82 #[inline(always)]79 #[inline(always)]
83 pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Result<Val> {80 pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {
84 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();
85 let future_ctx = Context::new_future();
86
87 for Param(name, default) in self.params.with_defaults() {
88 let default = default.unwrap();
89 let eval_default = self.eval_default.clone();
90 new_bindings.insert(
91 name,
92 LazyBinding::Bound(lazy_val!(
93 closure!(clone future_ctx, clone eval_default, clone default, || (eval_default.clone()).0
94 (future_ctx.clone().unwrap(), default.clone()))
95 )),
96 );
97 }
98 for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {
99 new_bindings.insert(
100 name.as_ref().unwrap().clone(),
101 LazyBinding::Bound(resolved_lazy_val!(val.clone())),
102 );
103 }
104 for (i, param) in self.params.0.iter().enumerate() {
105 if let Some((None, val)) = args.get(i) {
106 new_bindings.insert(
107 param.0.clone(),
108 LazyBinding::Bound(resolved_lazy_val!(val.clone())),
109 );
110 }
111 }
112 let ctx = self81 let ctx = inline_parse_function_call(
82 call_ctx,
113 .ctx83 Some(self.ctx.clone()),
114 .extend(new_bindings, None, None, None)?
115 .into_future(future_ctx);84 &self.params,
85 args,
86 tailstrict,
87 )?;
116 self.eval_rhs.0(ctx)88 evaluate(ctx, &self.body)
117 }89 }
118}90}
11991