git.delta.rocks / jrsonnet / refs/commits / 3ee61c42d34d

difftreelog

source

crates/jrsonnet-evaluator/src/function/parse.rs7.1 KiBsourcehistory
1use jrsonnet_gcmodule::Trace;2use jrsonnet_interner::IStr;3use jrsonnet_parser::{LocExpr, ParamsDesc};45use super::{6	arglike::ArgsLike,7	builtin::{BuiltinParam, BuiltinParamName},8};9use crate::{10	destructure::destruct,11	error::{Error::*, Result},12	evaluate_named,13	gc::GcHashMap,14	tb, throw,15	val::ThunkValue,16	Context, Pending, State, Thunk, Val,17};1819#[derive(Trace)]20struct EvaluateNamedThunk {21	ctx: Pending<Context>,22	name: IStr,23	value: LocExpr,24}2526impl ThunkValue for EvaluateNamedThunk {27	type Output = Val;28	fn get(self: Box<Self>, s: State) -> Result<Val> {29		evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)30	}31}3233/// Creates correct [context](Context) for function body evaluation returning error on invalid call.34///35/// ## Parameters36/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)37/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)38/// * `params`: function parameters' definition39/// * `args`: passed function arguments40/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily41pub fn parse_function_call(42	s: State,43	ctx: Context,44	body_ctx: Context,45	params: &ParamsDesc,46	args: &dyn ArgsLike,47	tailstrict: bool,48) -> Result<Context> {49	let mut passed_args = GcHashMap::with_capacity(params.len());50	if args.unnamed_len() > params.len() {51		throw!(TooManyArgsFunctionHas(52			params.len(),53			params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()54		))55	}5657	let mut filled_named = 0;58	let mut filled_positionals = 0;5960	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {61		let name = params[id].0.clone();62		destruct(63			&name,64			arg,65			Pending::new_filled(ctx.clone()),66			&mut passed_args,67		)?;68		filled_positionals += 1;69		Ok(())70	})?;7172	args.named_iter(s, ctx, tailstrict, &mut |name, value| {73		// FIXME: O(n) for arg existence check74		if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {75			throw!(UnknownFunctionParameter((name as &str).to_owned()));76		}77		if passed_args.insert(name.clone(), value).is_some() {78			throw!(BindingParameterASecondTime(name.clone()));79		}80		filled_named += 1;81		Ok(())82	})?;8384	if filled_named + filled_positionals < params.len() {85		// Some args are unset, but maybe we have defaults for them86		// Default values should be created in newly created context87		let fctx = Context::new_future();88		let mut defaults =89			GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);9091		for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {92			if let Some(name) = param.0.name() {93				if passed_args.contains_key(&name) {94					continue;95				}96			} else if idx < filled_positionals {97				continue;98			}99100			destruct(101				&param.0,102				Thunk::new(tb!(EvaluateNamedThunk {103					ctx: fctx.clone(),104					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),105					value: param.1.clone().expect("default exists"),106				})),107				fctx.clone(),108				&mut defaults,109			)?;110			if param.0.name().is_some() {111				filled_named += 1;112			} else {113				filled_positionals += 1;114			}115		}116117		// Some args still weren't filled118		if filled_named + filled_positionals != params.len() {119			for param in params.iter().skip(args.unnamed_len()) {120				let mut found = false;121				args.named_names(&mut |name| {122					if Some(name) == param.0.name().as_ref() {123						found = true;124					}125				});126				if !found {127					throw!(FunctionParameterNotBoundInCall(128						param129							.0130							.clone()131							.name()132							.unwrap_or_else(|| "<destruct>".into()),133						params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()134					));135				}136			}137			unreachable!();138		}139140		Ok(body_ctx141			.extend(passed_args, None, None, None)142			.extend(defaults, None, None, None)143			.into_future(fctx))144	} else {145		let body_ctx = body_ctx.extend(passed_args, None, None, None);146		Ok(body_ctx)147	}148}149150/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead151///152/// ## Parameters153/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)154/// * `params`: function parameters' definition155/// * `args`: passed function arguments156/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily157pub fn parse_builtin_call(158	s: State,159	ctx: Context,160	params: &[BuiltinParam],161	args: &dyn ArgsLike,162	tailstrict: bool,163) -> Result<GcHashMap<BuiltinParamName, Thunk<Val>>> {164	let mut passed_args = GcHashMap::with_capacity(params.len());165	if args.unnamed_len() > params.len() {166		throw!(TooManyArgsFunctionHas(167			params.len(),168			params169				.iter()170				.map(|p| (Some(p.name.as_ref().into()), p.has_default))171				.collect()172		))173	}174175	let mut filled_args = 0;176177	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {178		let name = params[id].name.clone();179		passed_args.insert(name, arg);180		filled_args += 1;181		Ok(())182	})?;183184	args.named_iter(s, ctx, tailstrict, &mut |name, arg| {185		// FIXME: O(n) for arg existence check186		let p = params187			.iter()188			.find(|p| p.name == name as &str)189			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;190		if passed_args.insert(p.name.clone(), arg).is_some() {191			throw!(BindingParameterASecondTime(name.clone()));192		}193		filled_args += 1;194		Ok(())195	})?;196197	if filled_args < params.len() {198		for param in params.iter().filter(|p| p.has_default) {199			if passed_args.contains_key(&param.name) {200				continue;201			}202			filled_args += 1;203		}204205		// Some args still wasn't filled206		if filled_args != params.len() {207			for param in params.iter().skip(args.unnamed_len()) {208				let mut found = false;209				args.named_names(&mut |name| {210					if name as &str == &param.name as &str {211						found = true;212					}213				});214				if !found {215					throw!(FunctionParameterNotBoundInCall(216						param.name.clone().into(),217						params218							.iter()219							.map(|p| (Some(p.name.as_ref().into()), p.has_default))220							.collect()221					));222				}223			}224			unreachable!();225		}226	}227	Ok(passed_args)228}229230/// Creates Context, which has all argument default values applied231/// and with unbound values causing error to be returned232pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {233	#[derive(Trace)]234	struct DependsOnUnbound(IStr, ParamsDesc);235	impl ThunkValue for DependsOnUnbound {236		type Output = Val;237		fn get(self: Box<Self>, _: State) -> Result<Val> {238			Err(FunctionParameterNotBoundInCall(239				self.0.clone(),240				self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),241			)242			.into())243		}244	}245246	let fctx = Context::new_future();247248	let mut bindings = GcHashMap::new();249250	for param in params.iter() {251		if let Some(v) = &param.1 {252			destruct(253				&param.0.clone(),254				Thunk::new(tb!(EvaluateNamedThunk {255					ctx: fctx.clone(),256					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),257					value: v.clone(),258				})),259				fctx.clone(),260				&mut bindings,261			)?;262		} else {263			destruct(264				&param.0,265				Thunk::new(tb!(DependsOnUnbound(266					param.0.name().unwrap_or_else(|| "<destruct>".into()),267					params.clone()268				))),269				fctx.clone(),270				&mut bindings,271			)?;272		}273	}274275	Ok(body_ctx276		.extend(bindings, None, None, None)277		.into_future(fctx))278}