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

difftreelog

source

crates/jrsonnet-evaluator/src/function.rs7.4 KiBsourcehistory
1use crate::{2	error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw, Context, FutureWrapper,3	GcHashMap, LazyVal, LazyValValue, Result, Val,4};5use gcmodule::Trace;6use jrsonnet_interner::IStr;7use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};8use std::collections::HashMap;910const NO_DEFAULT_CONTEXT: &str =11	"no default context set for call with defined default parameter value";1213#[derive(Trace)]14struct EvaluateLazyVal {15	context: Context,16	expr: LocExpr,17}18impl LazyValValue for EvaluateLazyVal {19	fn get(self: Box<Self>) -> Result<Val> {20		evaluate(self.context, &self.expr)21	}22}2324/// Creates correct [context](Context) for function body evaluation returning error on invalid call.25///26/// ## Parameters27/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)28/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)29/// * `params`: function parameters' definition30/// * `args`: passed function arguments31/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily32pub fn parse_function_call(33	ctx: Context,34	body_ctx: Context,35	params: &ParamsDesc,36	args: &ArgsDesc,37	tailstrict: bool,38) -> Result<Context> {39	let mut passed_args = GcHashMap::with_capacity(params.len());40	if args.unnamed.len() > params.len() {41		throw!(TooManyArgsFunctionHas(params.len()))42	}4344	let mut filled_args = 0;4546	for (id, arg) in args.unnamed.iter().enumerate() {47		let name = params[id].0.clone();48		passed_args.insert(49			name,50			if tailstrict {51				LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)52			} else {53				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {54					context: ctx.clone(),55					expr: arg.clone(),56				})))57			},58		);59		filled_args += 1;60	}6162	for (name, value) in args.named.iter() {63		// FIXME: O(n) for arg existence check64		if !params.iter().any(|p| &p.0 == name) {65			throw!(UnknownFunctionParameter((name as &str).to_owned()));66		}67		if passed_args68			.insert(69				name.clone(),70				if tailstrict {71					LazyVal::new_resolved(evaluate(ctx.clone(), value)?)72				} else {73					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {74						context: ctx.clone(),75						expr: value.clone(),76					})))77				},78			)79			.is_some()80		{81			throw!(BindingParameterASecondTime(name.clone()));82		}83		filled_args += 1;84	}8586	if filled_args < params.len() {87		// Some args are unset, but maybe we have defaults for them88		// Default values should be created in newly created context89		let future_context = FutureWrapper::<Context>::new();90		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);9192		for param in params.iter().filter(|p| p.1.is_some()) {93			if passed_args.contains_key(&param.0.clone()) {94				continue;95			}96			#[derive(Trace)]97			struct LazyNamedBinding {98				future_context: FutureWrapper<Context>,99				name: IStr,100				value: LocExpr,101			}102			impl LazyValValue for LazyNamedBinding {103				fn get(self: Box<Self>) -> Result<Val> {104					evaluate_named(self.future_context.unwrap(), &self.value, self.name)105				}106			}107			LazyVal::new(TraceBox(Box::new(LazyNamedBinding {108				future_context: future_context.clone(),109				name: param.0.clone(),110				value: param.1.clone().unwrap(),111			})));112113			defaults.insert(114				param.0.clone(),115				LazyVal::new(TraceBox(Box::new(LazyNamedBinding {116					future_context: future_context.clone(),117					name: param.0.clone(),118					value: param.1.clone().unwrap(),119				}))),120			);121			filled_args += 1;122		}123124		// Some args still wasn't filled125		if filled_args != params.len() {126			for param in params.iter().skip(args.unnamed.len()) {127				if !args.named.iter().any(|a| a.0 == param.0) {128					throw!(FunctionParameterNotBoundInCall(param.0.clone()));129				}130			}131			unreachable!();132		}133134		Ok(body_ctx135			.extend(passed_args, None, None, None)136			.extend_bound(defaults)137			.into_future(future_context))138	} else {139		let body_ctx = body_ctx.extend(passed_args, None, None, None);140		Ok(body_ctx)141	}142}143144pub fn parse_function_call_map(145	ctx: Context,146	body_ctx: Option<Context>,147	params: &ParamsDesc,148	args: &HashMap<IStr, Val>,149	tailstrict: bool,150) -> Result<Context> {151	let mut out = GcHashMap::with_capacity(params.len());152	let mut positioned_args = vec![None; params.0.len()];153	for (name, val) in args.iter() {154		let idx = params155			.iter()156			.position(|p| *p.0 == **name)157			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;158159		if idx >= params.len() {160			throw!(TooManyArgsFunctionHas(params.len()));161		}162		if positioned_args[idx].is_some() {163			throw!(BindingParameterASecondTime(params[idx].0.clone()));164		}165		positioned_args[idx] = Some(val.clone());166	}167	// Fill defaults168	for (id, p) in params.iter().enumerate() {169		let val = if let Some(arg) = positioned_args[id].take() {170			LazyVal::new_resolved(arg)171		} else if let Some(default) = &p.1 {172			if tailstrict {173				LazyVal::new_resolved(evaluate(174					body_ctx.clone().expect(NO_DEFAULT_CONTEXT),175					default,176				)?)177			} else {178				let body_ctx = body_ctx.clone();179				let default = default.clone();180				#[derive(Trace)]181				struct EvaluateLazyVal {182					body_ctx: Option<Context>,183					default: LocExpr,184				}185				impl LazyValValue for EvaluateLazyVal {186					fn get(self: Box<Self>) -> Result<Val> {187						evaluate(188							self.body_ctx.clone().expect(NO_DEFAULT_CONTEXT),189							&self.default,190						)191					}192				}193				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal { body_ctx, default })))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}