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

difftreelog

source

crates/jrsonnet-evaluator/src/function/parse.rs6.0 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, TraceBox},13	throw,14	val::LazyValValue,15	Context, FutureWrapper, LazyVal, State, Val,16};1718#[derive(Trace)]19struct EvaluateNamedLazyVal {20	ctx: FutureWrapper<Context>,21	name: IStr,22	value: LocExpr,23}2425impl LazyValValue for EvaluateNamedLazyVal {26	fn get(self: Box<Self>, s: State) -> Result<Val> {27		evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)28	}29}3031/// Creates correct [context](Context) for function body evaluation returning error on invalid call.32///33/// ## Parameters34/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)35/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)36/// * `params`: function parameters' definition37/// * `args`: passed function arguments38/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily39pub fn parse_function_call(40	s: State,41	ctx: Context,42	body_ctx: Context,43	params: &ParamsDesc,44	args: &dyn ArgsLike,45	tailstrict: bool,46) -> Result<Context> {47	let mut passed_args = GcHashMap::with_capacity(params.len());48	if args.unnamed_len() > params.len() {49		throw!(TooManyArgsFunctionHas(params.len()))50	}5152	let mut filled_args = 0;5354	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {55		let name = params[id].0.clone();56		passed_args.insert(name, arg);57		filled_args += 1;58		Ok(())59	})?;6061	args.named_iter(s, ctx, tailstrict, &mut |name, value| {62		// FIXME: O(n) for arg existence check63		if !params.iter().any(|p| &p.0 == name) {64			throw!(UnknownFunctionParameter((name as &str).to_owned()));65		}66		if passed_args.insert(name.clone(), value).is_some() {67			throw!(BindingParameterASecondTime(name.clone()));68		}69		filled_args += 1;70		Ok(())71	})?;7273	if filled_args < params.len() {74		// Some args are unset, but maybe we have defaults for them75		// Default values should be created in newly created context76		let fctx = Context::new_future();77		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);7879		for param in params.iter().filter(|p| p.1.is_some()) {80			if passed_args.contains_key(&param.0.clone()) {81				continue;82			}8384			defaults.insert(85				param.0.clone(),86				LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {87					ctx: fctx.clone(),88					name: param.0.clone(),89					value: param.1.clone().expect("default exists"),90				}))),91			);92			filled_args += 1;93		}9495		// Some args still wasn't filled96		if filled_args != params.len() {97			for param in params.iter().skip(args.unnamed_len()) {98				let mut found = false;99				args.named_names(&mut |name| {100					if name == &param.0 {101						found = true;102					}103				});104				if !found {105					throw!(FunctionParameterNotBoundInCall(param.0.clone()));106				}107			}108			unreachable!();109		}110111		Ok(body_ctx112			.extend(passed_args, None, None, None)113			.extend_bound(defaults)114			.into_future(fctx))115	} else {116		let body_ctx = body_ctx.extend(passed_args, None, None, None);117		Ok(body_ctx)118	}119}120121/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead122///123/// ## Parameters124/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)125/// * `params`: function parameters' definition126/// * `args`: passed function arguments127/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily128pub fn parse_builtin_call(129	s: State,130	ctx: Context,131	params: &[BuiltinParam],132	args: &dyn ArgsLike,133	tailstrict: bool,134) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {135	let mut passed_args = GcHashMap::with_capacity(params.len());136	if args.unnamed_len() > params.len() {137		throw!(TooManyArgsFunctionHas(params.len()))138	}139140	let mut filled_args = 0;141142	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {143		let name = params[id].name.clone();144		passed_args.insert(name, arg);145		filled_args += 1;146		Ok(())147	})?;148149	args.named_iter(s, ctx, tailstrict, &mut |name, arg| {150		// FIXME: O(n) for arg existence check151		let p = params152			.iter()153			.find(|p| p.name == name as &str)154			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;155		if passed_args.insert(p.name.clone(), arg).is_some() {156			throw!(BindingParameterASecondTime(name.clone()));157		}158		filled_args += 1;159		Ok(())160	})?;161162	if filled_args < params.len() {163		for param in params.iter().filter(|p| p.has_default) {164			if passed_args.contains_key(&param.name) {165				continue;166			}167			filled_args += 1;168		}169170		// Some args still wasn't filled171		if filled_args != params.len() {172			for param in params.iter().skip(args.unnamed_len()) {173				let mut found = false;174				args.named_names(&mut |name| {175					if name as &str == &param.name as &str {176						found = true;177					}178				});179				if !found {180					throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));181				}182			}183			unreachable!();184		}185	}186	Ok(passed_args)187}188189/// Creates Context, which has all argument default values applied190/// and with unbound values causing error to be returned191pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {192	#[derive(Trace)]193	struct DependsOnUnbound(IStr);194	impl LazyValValue for DependsOnUnbound {195		fn get(self: Box<Self>, _: State) -> Result<Val> {196			Err(FunctionParameterNotBoundInCall(self.0.clone()).into())197		}198	}199200	let fctx = Context::new_future();201202	let mut bindings = GcHashMap::new();203204	for param in params.iter() {205		if let Some(v) = &param.1 {206			bindings.insert(207				param.0.clone(),208				LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {209					ctx: fctx.clone(),210					name: param.0.clone(),211					value: v.clone(),212				}))),213			);214		} else {215			bindings.insert(216				param.0.clone(),217				LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),218			);219		}220	}221222	body_ctx223		.extend(bindings, None, None, None)224		.into_future(fctx)225}