git.delta.rocks / jrsonnet / refs/commits / 32f6ee5b9541

difftreelog

source

crates/jrsonnet-evaluator/src/function/parse.rs5.9 KiBsourcehistory
1use gcmodule::Trace;2use jrsonnet_interner::IStr;3use jrsonnet_parser::{LocExpr, ParamsDesc};45use super::{6	arglike::ArgsLike,7	builtin::{BuiltinParam, BuiltinParamName},8};9use crate::{10	error::{Error::*, Result},11	evaluate_named,12	gc::GcHashMap,13	tb, throw,14	val::ThunkValue,15	Context, Pending, State, Thunk, Val,16};1718#[derive(Trace)]19struct EvaluateNamedThunk {20	ctx: Pending<Context>,21	name: IStr,22	value: LocExpr,23}2425impl ThunkValue for EvaluateNamedThunk {26	type Output = Val;27	fn get(self: Box<Self>, s: State) -> Result<Val> {28		evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)29	}30}3132/// Creates correct [context](Context) for function body evaluation returning error on invalid call.33///34/// ## Parameters35/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)36/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)37/// * `params`: function parameters' definition38/// * `args`: passed function arguments39/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily40pub fn parse_function_call(41	s: State,42	ctx: Context,43	body_ctx: Context,44	params: &ParamsDesc,45	args: &dyn ArgsLike,46	tailstrict: bool,47) -> Result<Context> {48	let mut passed_args = GcHashMap::with_capacity(params.len());49	if args.unnamed_len() > params.len() {50		throw!(TooManyArgsFunctionHas(params.len()))51	}5253	let mut filled_args = 0;5455	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {56		let name = params[id].0.clone();57		passed_args.insert(name, arg);58		filled_args += 1;59		Ok(())60	})?;6162	args.named_iter(s, ctx, tailstrict, &mut |name, value| {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_args.insert(name.clone(), value).is_some() {68			throw!(BindingParameterASecondTime(name.clone()));69		}70		filled_args += 1;71		Ok(())72	})?;7374	if filled_args < params.len() {75		// Some args are unset, but maybe we have defaults for them76		// Default values should be created in newly created context77		let fctx = Context::new_future();78		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);7980		for param in params.iter().filter(|p| p.1.is_some()) {81			if passed_args.contains_key(&param.0.clone()) {82				continue;83			}8485			defaults.insert(86				param.0.clone(),87				Thunk::new(tb!(EvaluateNamedThunk {88					ctx: fctx.clone(),89					name: param.0.clone(),90					value: param.1.clone().expect("default exists"),91				})),92			);93			filled_args += 1;94		}9596		// Some args still wasn't filled97		if filled_args != params.len() {98			for param in params.iter().skip(args.unnamed_len()) {99				let mut found = false;100				args.named_names(&mut |name| {101					if name == &param.0 {102						found = true;103					}104				});105				if !found {106					throw!(FunctionParameterNotBoundInCall(param.0.clone()));107				}108			}109			unreachable!();110		}111112		Ok(body_ctx113			.extend(passed_args, None, None, None)114			.extend(defaults, None, None, None)115			.into_future(fctx))116	} else {117		let body_ctx = body_ctx.extend(passed_args, None, None, None);118		Ok(body_ctx)119	}120}121122/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead123///124/// ## Parameters125/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)126/// * `params`: function parameters' definition127/// * `args`: passed function arguments128/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily129pub fn parse_builtin_call(130	s: State,131	ctx: Context,132	params: &[BuiltinParam],133	args: &dyn ArgsLike,134	tailstrict: bool,135) -> Result<GcHashMap<BuiltinParamName, Thunk<Val>>> {136	let mut passed_args = GcHashMap::with_capacity(params.len());137	if args.unnamed_len() > params.len() {138		throw!(TooManyArgsFunctionHas(params.len()))139	}140141	let mut filled_args = 0;142143	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {144		let name = params[id].name.clone();145		passed_args.insert(name, arg);146		filled_args += 1;147		Ok(())148	})?;149150	args.named_iter(s, ctx, tailstrict, &mut |name, arg| {151		// FIXME: O(n) for arg existence check152		let p = params153			.iter()154			.find(|p| p.name == name as &str)155			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;156		if passed_args.insert(p.name.clone(), arg).is_some() {157			throw!(BindingParameterASecondTime(name.clone()));158		}159		filled_args += 1;160		Ok(())161	})?;162163	if filled_args < params.len() {164		for param in params.iter().filter(|p| p.has_default) {165			if passed_args.contains_key(&param.name) {166				continue;167			}168			filled_args += 1;169		}170171		// Some args still wasn't filled172		if filled_args != params.len() {173			for param in params.iter().skip(args.unnamed_len()) {174				let mut found = false;175				args.named_names(&mut |name| {176					if name as &str == &param.name as &str {177						found = true;178					}179				});180				if !found {181					throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));182				}183			}184			unreachable!();185		}186	}187	Ok(passed_args)188}189190/// Creates Context, which has all argument default values applied191/// and with unbound values causing error to be returned192pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {193	#[derive(Trace)]194	struct DependsOnUnbound(IStr);195	impl ThunkValue for DependsOnUnbound {196		type Output = Val;197		fn get(self: Box<Self>, _: State) -> Result<Val> {198			Err(FunctionParameterNotBoundInCall(self.0.clone()).into())199		}200	}201202	let fctx = Context::new_future();203204	let mut bindings = GcHashMap::new();205206	for param in params.iter() {207		if let Some(v) = &param.1 {208			bindings.insert(209				param.0.clone(),210				Thunk::new(tb!(EvaluateNamedThunk {211					ctx: fctx.clone(),212					name: param.0.clone(),213					value: v.clone(),214				})),215			);216		} else {217			bindings.insert(218				param.0.clone(),219				Thunk::new(tb!(DependsOnUnbound(param.0.clone()))),220			);221		}222	}223224	body_ctx225		.extend(bindings, None, None, None)226		.into_future(fctx)227}