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

difftreelog

source

crates/jrsonnet-evaluator/src/function.rs7.5 KiBsourcehistory
1use crate::{Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val, error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw};2use gcmodule::Trace;3use jrsonnet_interner::IStr;4use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};5use std::collections::HashMap;67const NO_DEFAULT_CONTEXT: &str =8	"no default context set for call with defined default parameter value";910#[derive(Trace)]11struct EvaluateLazyVal {12	context: Context,13	expr: LocExpr,14}15impl LazyValValue for EvaluateLazyVal {16	fn get(self: Box<Self>) -> Result<Val> {17		evaluate(self.context, &self.expr)18	}19}2021/// Creates correct [context](Context) for function body evaluation returning error on invalid call.22///23/// ## Parameters24/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)25/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)26/// * `params`: function parameters' definition27/// * `args`: passed function arguments28/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily29pub fn parse_function_call(30	ctx: Context,31	body_ctx: Context,32	params: &ParamsDesc,33	args: &ArgsDesc,34	tailstrict: bool,35) -> Result<Context> {36	let mut passed_args = GcHashMap::with_capacity(params.len());37	if args.unnamed.len() > params.len() {38		throw!(TooManyArgsFunctionHas(params.len()))39	}4041	let mut filled_args = 0;4243	for (id, arg) in args.unnamed.iter().enumerate() {44		let name = params[id].0.clone();45		passed_args.insert(46			name,47			if tailstrict {48				LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)49			} else {50				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {51					context: ctx.clone(),52					expr: arg.clone(),53				})))54			},55		);56		filled_args += 1;57	}5859	for (name, value) in args.named.iter() {60		// FIXME: O(n) for arg existence check61		if !params.iter().any(|p| &p.0 == name) {62			throw!(UnknownFunctionParameter((name as &str).to_owned()));63		}64		if passed_args65			.insert(66				name.clone(),67				if tailstrict {68					LazyVal::new_resolved(evaluate(ctx.clone(), value)?)69				} else {70					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {71						context: ctx.clone(),72						expr: value.clone(),73					})))74				},75			)76			.is_some()77		{78			throw!(BindingParameterASecondTime(name.clone()));79		}80		filled_args += 1;81	}8283	if filled_args < params.len() {84		// Some args are unset, but maybe we have defaults for them85		// Default values should be created in newly created context86		let future_context = FutureWrapper::<Context>::new();87		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);8889		for param in params.iter().filter(|p| p.1.is_some()) {90			if passed_args.contains_key(&param.0.clone()) {91				continue;92			}93			#[derive(Trace)]94			struct LazyNamedBinding {95				future_context: FutureWrapper<Context>,96				name: IStr,97				value: LocExpr,98			}99			impl LazyValValue for LazyNamedBinding {100				fn get(self: Box<Self>) -> Result<Val> {101					evaluate_named(self.future_context.unwrap(), &self.value, self.name)102				}103			}104			LazyVal::new(TraceBox(Box::new(LazyNamedBinding {105				future_context: future_context.clone(),106				name: param.0.clone(),107				value: param.1.clone().unwrap(),108			})));109110			defaults.insert(111				param.0.clone(),112				LazyVal::new(TraceBox(Box::new(LazyNamedBinding {113					future_context: future_context.clone(),114					name: param.0.clone(),115					value: param.1.clone().unwrap(),116				}))),117			);118			filled_args += 1;119		}120121		// Some args still wasn't filled122		if filled_args != params.len() {123			for param in params.iter().skip(args.unnamed.len()) {124				if !args.named.iter().any(|a| a.0 == param.0) {125					throw!(FunctionParameterNotBoundInCall(param.0.clone()));126				}127			}128			unreachable!();129		}130131		Ok(body_ctx132			.extend(passed_args, None, None, None)133			.extend_bound(defaults)134			.into_future(future_context))135	} else {136		let body_ctx = body_ctx.extend(passed_args, None, None, None);137		Ok(body_ctx)138	}139}140141pub fn parse_function_call_map(142	ctx: Context,143	body_ctx: Option<Context>,144	params: &ParamsDesc,145	args: &HashMap<IStr, Val>,146	tailstrict: bool,147) -> Result<Context> {148	let mut out = GcHashMap::with_capacity(params.len());149	let mut positioned_args = vec![None; params.0.len()];150	for (name, val) in args.iter() {151		let idx = params152			.iter()153			.position(|p| *p.0 == **name)154			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;155156		if idx >= params.len() {157			throw!(TooManyArgsFunctionHas(params.len()));158		}159		if positioned_args[idx].is_some() {160			throw!(BindingParameterASecondTime(params[idx].0.clone()));161		}162		positioned_args[idx] = Some(val.clone());163	}164	// Fill defaults165	for (id, p) in params.iter().enumerate() {166		let val = if let Some(arg) = positioned_args[id].take() {167			LazyVal::new_resolved(arg)168		} else if let Some(default) = &p.1 {169			if tailstrict {170				LazyVal::new_resolved(evaluate(171					body_ctx.clone().expect(NO_DEFAULT_CONTEXT),172					default,173				)?)174			} else {175				let body_ctx = body_ctx.clone();176				let default = default.clone();177				#[derive(Trace)]178				struct EvaluateLazyVal {179					body_ctx: Option<Context>,180					default: LocExpr,181				}182				impl LazyValValue for EvaluateLazyVal {183					fn get(self: Box<Self>) -> Result<Val> {184						evaluate(185							self.body_ctx.clone().expect(NO_DEFAULT_CONTEXT),186							&self.default,187						)188					}189				}190				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {191					body_ctx,192					default,193				})))194			}195		} else {196			throw!(FunctionParameterNotBoundInCall(p.0.clone()));197		};198		out.insert(p.0.clone(), val);199	}200201	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))202}203204pub fn place_args(205	ctx: Context,206	body_ctx: Option<Context>,207	params: &ParamsDesc,208	args: &[Val],209) -> Result<Context> {210	let mut out = GcHashMap::with_capacity(params.len());211	let mut positioned_args = vec![None; params.0.len()];212	for (id, arg) in args.iter().enumerate() {213		if id >= params.len() {214			throw!(TooManyArgsFunctionHas(params.len()));215		}216		positioned_args[id] = Some(arg);217	}218	// Fill defaults219	for (id, p) in params.iter().enumerate() {220		let val = if let Some(arg) = &positioned_args[id] {221			(*arg).clone()222		} else if let Some(default) = &p.1 {223			evaluate(ctx.clone(), default)?224		} else {225			throw!(FunctionParameterNotBoundInCall(p.0.clone()));226		};227		out.insert(p.0.clone(), LazyVal::new_resolved(val));228	}229230	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))231}232233#[macro_export]234macro_rules! parse_args {235	($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [236		$($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?237	], $handler:block) => {{238		use $crate::{error::Error::*, throw, evaluate, push_description_frame, typed::CheckType};239240		let args = $args;241		if args.unnamed.len() + args.named.len() > $total_args {242			throw!(TooManyArgsFunctionHas($total_args));243		}244		$(245			if args.unnamed.len() + args.named.len() <= $id {246				throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));247			}248			// Is named249			let $name = if $id >= $args.unnamed.len() {250				let named = &args.named[$id - $args.unnamed.len()];251				if &named.0 != stringify!($name) {252					throw!(IntrinsicArgumentReorderingIsNotSupportedYet);253				}254				&named.1255			} else {256				&$args.unnamed[$id]257			};258			let $name = push_description_frame(|| format!("evaluating builtin argument {}", stringify!($name)), || {259				let value = evaluate($ctx.clone(), &$name)?;260				$ty.check(&value)?;261				Ok(value)262			})?;263			$(264				let $name = if let $match(v) = $name {265					v266				} else {267					unreachable!();268				};269			)?270		)+271		($handler as crate::Result<_>)272	}};273}