git.delta.rocks / jrsonnet / refs/commits / 09a4bd7b68d6

difftreelog

source

crates/jrsonnet-evaluator/src/function.rs7.6 KiBsourcehistory
1use crate::{2	error::Error::*, evaluate, evaluate_named, throw, Context, FutureWrapper, LazyVal,3	LazyValValue, Result, Val,4};5use jrsonnet_gc::Trace;6use jrsonnet_interner::IStr;7use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};8use rustc_hash::FxHashMap;9use std::{collections::HashMap, hash::BuildHasherDefault};1011const NO_DEFAULT_CONTEXT: &str =12	"no default context set for call with defined default parameter value";1314#[derive(Trace)]15#[trivially_drop]16struct EvaluateLazyVal {17	context: Context,18	expr: LocExpr,19}20impl LazyValValue for EvaluateLazyVal {21	fn get(self: Box<Self>) -> Result<Val> {22		evaluate(self.context, &self.expr)23	}24}2526/// Creates correct [context](Context) for function body evaluation returning error on invalid call.27///28/// ## Parameters29/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)30/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)31/// * `params`: function parameters' definition32/// * `args`: passed function arguments33/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily34pub fn parse_function_call(35	ctx: Context,36	body_ctx: Context,37	params: &ParamsDesc,38	args: &ArgsDesc,39	tailstrict: bool,40) -> Result<Context> {41	let mut passed_args =42		HashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());43	if args.unnamed.len() > params.len() {44		throw!(TooManyArgsFunctionHas(params.len()))45	}4647	let mut filled_args = 0;4849	for (id, arg) in args.unnamed.iter().enumerate() {50		let name = params[id].0.clone();51		passed_args.insert(52			name,53			if tailstrict {54				LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)55			} else {56				LazyVal::new(Box::new(EvaluateLazyVal {57					context: ctx.clone(),58					expr: arg.clone(),59				}))60			},61		);62		filled_args += 1;63	}6465	for (name, value) in args.named.iter() {66		// FIXME: O(n) for arg existence check67		if !params.iter().any(|p| &p.0 == name) {68			throw!(UnknownFunctionParameter((name as &str).to_owned()));69		}70		if passed_args71			.insert(72				name.clone(),73				if tailstrict {74					LazyVal::new_resolved(evaluate(ctx.clone(), value)?)75				} else {76					LazyVal::new(Box::new(EvaluateLazyVal {77						context: ctx.clone(),78						expr: value.clone(),79					}))80				},81			)82			.is_some()83		{84			throw!(BindingParameterASecondTime(name.clone()));85		}86		filled_args += 1;87	}8889	if filled_args < params.len() {90		// Some args are unset, but maybe we have defaults for them91		// Default values should be created in newly created context92		let future_context = FutureWrapper::<Context>::new();93		let mut defaults = HashMap::with_capacity_and_hasher(94			params.len() - filled_args,95			BuildHasherDefault::default(),96		);9798		for param in params.iter().filter(|p| p.1.is_some()) {99			if passed_args.contains_key(&param.0.clone()) {100				continue;101			}102			#[derive(Trace)]103			#[trivially_drop]104			struct LazyNamedBinding {105				future_context: FutureWrapper<Context>,106				name: IStr,107				value: LocExpr,108			}109			impl LazyValValue for LazyNamedBinding {110				fn get(self: Box<Self>) -> Result<Val> {111					evaluate_named(self.future_context.unwrap(), &self.value, self.name)112				}113			}114			LazyVal::new(Box::new(LazyNamedBinding {115				future_context: future_context.clone(),116				name: param.0.clone(),117				value: param.1.clone().unwrap(),118			}));119120			defaults.insert(121				param.0.clone(),122				LazyVal::new(Box::new(LazyNamedBinding {123					future_context: future_context.clone(),124					name: param.0.clone(),125					value: param.1.clone().unwrap(),126				})),127			);128			filled_args += 1;129		}130131		// Some args still wasn't filled132		if filled_args != params.len() {133			for param in params.iter().skip(args.unnamed.len()) {134				if !args.named.iter().any(|a| a.0 == param.0) {135					throw!(FunctionParameterNotBoundInCall(param.0.clone()));136				}137			}138			unreachable!();139		}140141		Ok(body_ctx142			.extend(passed_args, None, None, None)143			.extend_bound(defaults)144			.into_future(future_context))145	} else {146		let body_ctx = body_ctx.extend(passed_args, None, None, None);147		Ok(body_ctx)148	}149}150151pub fn parse_function_call_map(152	ctx: Context,153	body_ctx: Option<Context>,154	params: &ParamsDesc,155	args: &HashMap<IStr, Val>,156	tailstrict: bool,157) -> Result<Context> {158	let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());159	let mut positioned_args = vec![None; params.0.len()];160	for (name, val) in args.iter() {161		let idx = params162			.iter()163			.position(|p| *p.0 == **name)164			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;165166		if idx >= params.len() {167			throw!(TooManyArgsFunctionHas(params.len()));168		}169		if positioned_args[idx].is_some() {170			throw!(BindingParameterASecondTime(params[idx].0.clone()));171		}172		positioned_args[idx] = Some(val.clone());173	}174	// Fill defaults175	for (id, p) in params.iter().enumerate() {176		let val = if let Some(arg) = positioned_args[id].take() {177			LazyVal::new_resolved(arg)178		} else if let Some(default) = &p.1 {179			if tailstrict {180				LazyVal::new_resolved(evaluate(181					body_ctx.clone().expect(NO_DEFAULT_CONTEXT),182					default,183				)?)184			} else {185				let body_ctx = body_ctx.clone();186				let default = default.clone();187				#[derive(Trace)]188				#[trivially_drop]189				struct EvaluateLazyVal {190					body_ctx: Option<Context>,191					default: LocExpr,192				}193				impl LazyValValue for EvaluateLazyVal {194					fn get(self: Box<Self>) -> Result<Val> {195						evaluate(196							self.body_ctx.clone().expect(NO_DEFAULT_CONTEXT),197							&self.default,198						)199					}200				}201				LazyVal::new(Box::new(EvaluateLazyVal { body_ctx, default }))202			}203		} else {204			throw!(FunctionParameterNotBoundInCall(p.0.clone()));205		};206		out.insert(p.0.clone(), val);207	}208209	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))210}211212pub fn place_args(213	ctx: Context,214	body_ctx: Option<Context>,215	params: &ParamsDesc,216	args: &[Val],217) -> Result<Context> {218	let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());219	let mut positioned_args = vec![None; params.0.len()];220	for (id, arg) in args.iter().enumerate() {221		if id >= params.len() {222			throw!(TooManyArgsFunctionHas(params.len()));223		}224		positioned_args[id] = Some(arg);225	}226	// Fill defaults227	for (id, p) in params.iter().enumerate() {228		let val = if let Some(arg) = &positioned_args[id] {229			(*arg).clone()230		} else if let Some(default) = &p.1 {231			evaluate(ctx.clone(), default)?232		} else {233			throw!(FunctionParameterNotBoundInCall(p.0.clone()));234		};235		out.insert(p.0.clone(), LazyVal::new_resolved(val));236	}237238	Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))239}240241#[macro_export]242macro_rules! parse_args {243	($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [244		$($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?245	], $handler:block) => {{246		use $crate::{error::Error::*, throw, evaluate, push_frame, typed::CheckType};247248		let args = $args;249		if args.unnamed.len() + args.named.len() > $total_args {250			throw!(TooManyArgsFunctionHas($total_args));251		}252		$(253			if args.unnamed.len() + args.named.len() <= $id {254				throw!(FunctionParameterNotBoundInCall(stringify!($name).into()));255			}256			// Is named257			let $name = if $id >= $args.unnamed.len() {258				let named = &args.named[$id - $args.unnamed.len()];259				if &named.0 != stringify!($name) {260					throw!(IntrinsicArgumentReorderingIsNotSupportedYet);261				}262				&named.1263			} else {264				&$args.unnamed[$id]265			};266			let $name = push_frame(None, || format!("evaluating argument"), || {267				let value = evaluate($ctx.clone(), &$name)?;268				$ty.check(&value)?;269				Ok(value)270			})?;271			$(272				let $name = if let $match(v) = $name {273					v274				} else {275					unreachable!();276				};277			)?278		)+279		($handler as crate::Result<_>)280	}};281}