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

difftreelog

feat FuncDesc helper for default context

Yaroslav Bolyukin2022-04-17parent: #efea837.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth
23 }23 }
24}24}
25
26#[derive(Trace)]
27struct EvaluateNamedLazyVal {
28 future_context: FutureWrapper<Context>,
29 name: IStr,
30 value: LocExpr,
31}
32impl LazyValValue for EvaluateNamedLazyVal {
33 fn get(self: Box<Self>) -> Result<Val> {
34 evaluate_named(self.future_context.unwrap(), &self.value, self.name)
35 }
36}
2537
26pub trait ArgLike {38pub trait ArgLike {
27 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;39 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;
309 if filled_args < params.len() {321 if filled_args < params.len() {
310 // Some args are unset, but maybe we have defaults for them322 // Some args are unset, but maybe we have defaults for them
311 // Default values should be created in newly created context323 // Default values should be created in newly created context
312 let future_context = FutureWrapper::<Context>::new();324 let future_context = Context::new_future();
313 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);325 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);
314326
315 for param in params.iter().filter(|p| p.1.is_some()) {327 for param in params.iter().filter(|p| p.1.is_some()) {
316 if passed_args.contains_key(&param.0.clone()) {328 if passed_args.contains_key(&param.0.clone()) {
317 continue;329 continue;
318 }330 }
319 #[derive(Trace)]
320 struct LazyNamedBinding {
321 future_context: FutureWrapper<Context>,
322 name: IStr,
323 value: LocExpr,
324 }
325 impl LazyValValue for LazyNamedBinding {
326 fn get(self: Box<Self>) -> Result<Val> {
327 evaluate_named(self.future_context.unwrap(), &self.value, self.name)
328 }
329 }
330 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {331 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {
331 future_context: future_context.clone(),332 future_context: future_context.clone(),
332 name: param.0.clone(),333 name: param.0.clone(),
333 value: param.1.clone().unwrap(),334 value: param.1.clone().unwrap(),
334 })));335 })));
335336
336 defaults.insert(337 defaults.insert(
337 param.0.clone(),338 param.0.clone(),
338 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {339 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {
339 future_context: future_context.clone(),340 future_context: future_context.clone(),
340 name: param.0.clone(),341 name: param.0.clone(),
341 value: param.1.clone().unwrap(),342 value: param.1.clone().unwrap(),
465 Ok(passed_args)466 Ok(passed_args)
466}467}
468
469/// Creates Context, which has all argument default values applied
470/// and with unbound values causing error to be returned
471pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {
472 let ctx = Context::new_future();
473
474 let mut bindings = GcHashMap::new();
475
476 #[derive(Trace)]
477 struct DependsOnUnbound(IStr);
478 impl LazyValValue for DependsOnUnbound {
479 fn get(self: Box<Self>) -> Result<Val> {
480 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())
481 }
482 }
483
484 for param in params.iter() {
485 if let Some(v) = &param.1 {
486 bindings.insert(
487 param.0.clone(),
488 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {
489 future_context: ctx.clone(),
490 name: param.0.clone(),
491 value: v.clone(),
492 }))),
493 );
494 } else {
495 bindings.insert(
496 param.0.clone(),
497 LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),
498 );
499 }
500 }
501
502 body_ctx.extend(bindings, None, None, None).into_future(ctx)
503}
467504
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -5,7 +5,9 @@
 	cc_ptr_eq,
 	error::{Error::*, LocError},
 	evaluate,
-	function::{parse_function_call, ArgsLike, Builtin, StaticBuiltin},
+	function::{
+		parse_default_function_call, parse_function_call, ArgsLike, Builtin, StaticBuiltin,
+	},
 	gc::TraceBox,
 	throw, Context, ObjValue, Result,
 };
@@ -84,6 +86,22 @@
 	pub params: ParamsDesc,
 	pub body: LocExpr,
 }
+impl FuncDesc {
+	/// Create body context, but fill arguments without defaults with lazy error
+	pub fn default_body_context(&self) -> Context {
+		parse_default_function_call(self.ctx.clone(), &self.params)
+	}
+
+	/// Create context, with which body code will run
+	pub fn call_body_context(
+		&self,
+		call_ctx: Context,
+		args: &dyn ArgsLike,
+		tailstrict: bool,
+	) -> Result<Context> {
+		parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)
+	}
+}
 
 #[derive(Trace, Clone)]
 pub enum FuncVal {
@@ -140,16 +158,10 @@
 	) -> Result<Val> {
 		match self {
 			Self::Normal(func) => {
-				let ctx = parse_function_call(
-					call_ctx,
-					func.ctx.clone(),
-					&func.params,
-					args,
-					tailstrict,
-				)?;
-				evaluate(ctx, &func.body)
+				let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;
+				evaluate(body_ctx, &func.body)
 			}
-			Self::StaticBuiltin(name) => name.call(call_ctx, loc, args),
+			Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),
 			Self::Builtin(b) => b.call(call_ctx, loc, args),
 		}
 	}