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

difftreelog

source

crates/jrsonnet-evaluator/src/function/parse.rs7.2 KiBsourcehistory
1use std::mem::replace;23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{LocExpr, ParamsDesc};67use super::{arglike::ArgsLike, builtin::BuiltinParam};8use crate::{9	destructure::destruct,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(51			params.len(),52			params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()53		))54	}5556	let mut filled_named = 0;57	let mut filled_positionals = 0;5859	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {60		let name = params[id].0.clone();61		destruct(62			&name,63			arg,64			Pending::new_filled(ctx.clone()),65			&mut passed_args,66		)?;67		filled_positionals += 1;68		Ok(())69	})?;7071	args.named_iter(s, ctx, tailstrict, &mut |name, value| {72		// FIXME: O(n) for arg existence check73		if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {74			throw!(UnknownFunctionParameter((name as &str).to_owned()));75		}76		if passed_args.insert(name.clone(), value).is_some() {77			throw!(BindingParameterASecondTime(name.clone()));78		}79		filled_named += 1;80		Ok(())81	})?;8283	if filled_named + filled_positionals < params.len() {84		// Some args are unset, but maybe we have defaults for them85		// Default values should be created in newly created context86		let fctx = Context::new_future();87		let mut defaults =88			GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);8990		for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {91			if let Some(name) = param.0.name() {92				if passed_args.contains_key(&name) {93					continue;94				}95			} else if idx < filled_positionals {96				continue;97			}9899			destruct(100				&param.0,101				Thunk::new(tb!(EvaluateNamedThunk {102					ctx: fctx.clone(),103					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),104					value: param.1.clone().expect("default exists"),105				})),106				fctx.clone(),107				&mut defaults,108			)?;109			if param.0.name().is_some() {110				filled_named += 1;111			} else {112				filled_positionals += 1;113			}114		}115116		// Some args still weren't filled117		if filled_named + filled_positionals != params.len() {118			for param in params.iter().skip(args.unnamed_len()) {119				let mut found = false;120				args.named_names(&mut |name| {121					if Some(name) == param.0.name().as_ref() {122						found = true;123					}124				});125				if !found {126					throw!(FunctionParameterNotBoundInCall(127						param.0.clone().name(),128						params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()129					));130				}131			}132			unreachable!();133		}134135		Ok(body_ctx136			.extend(passed_args, None, None, None)137			.extend(defaults, None, None, None)138			.into_future(fctx))139	} else {140		let body_ctx = body_ctx.extend(passed_args, None, None, None);141		Ok(body_ctx)142	}143}144145/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead146///147/// ## Parameters148/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)149/// * `params`: function parameters' definition150/// * `args`: passed function arguments151/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily152pub fn parse_builtin_call(153	s: State,154	ctx: Context,155	params: &[BuiltinParam],156	args: &dyn ArgsLike,157	tailstrict: bool,158) -> Result<Vec<Option<Thunk<Val>>>> {159	let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];160	if args.unnamed_len() > params.len() {161		throw!(TooManyArgsFunctionHas(162			params.len(),163			params164				.iter()165				.map(|p| (p.name.as_ref().map(|v| v.as_ref().into()), p.has_default))166				.collect()167		))168	}169170	let mut filled_args = 0;171172	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {173		passed_args[id] = Some(arg);174		filled_args += 1;175		Ok(())176	})?;177178	args.named_iter(s, ctx, tailstrict, &mut |name, arg| {179		// FIXME: O(n) for arg existence check180		let id = params181			.iter()182			.position(|p| {183				p.name184					.as_ref()185					.map(|v| &v as &str == name as &str)186					.unwrap_or(false)187			})188			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;189		if replace(&mut passed_args[id], Some(arg)).is_some() {190			throw!(BindingParameterASecondTime(name.clone()));191		}192		filled_args += 1;193		Ok(())194	})?;195196	if filled_args < params.len() {197		for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default) {198			if passed_args[id].is_some() {199				continue;200			}201			filled_args += 1;202		}203204		// Some args still wasn't filled205		if filled_args != params.len() {206			for param in params.iter().skip(args.unnamed_len()) {207				let mut found = false;208				args.named_names(&mut |name| {209					if param210						.name211						.as_ref()212						.map(|v| &v as &str == name as &str)213						.unwrap_or(false)214					{215						found = true;216					}217				});218				if !found {219					throw!(FunctionParameterNotBoundInCall(220						param.name.as_ref().map(|v| v.as_ref().into()),221						params222							.iter()223							.map(|p| (p.name.as_ref().map(|p| p.as_ref().into()), p.has_default))224							.collect()225					));226				}227			}228			unreachable!();229		}230	}231	Ok(passed_args)232}233234/// Creates Context, which has all argument default values applied235/// and with unbound values causing error to be returned236pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {237	#[derive(Trace)]238	struct DependsOnUnbound(IStr, ParamsDesc);239	impl ThunkValue for DependsOnUnbound {240		type Output = Val;241		fn get(self: Box<Self>, _: State) -> Result<Val> {242			Err(FunctionParameterNotBoundInCall(243				Some(self.0.clone()),244				self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),245			)246			.into())247		}248	}249250	let fctx = Context::new_future();251252	let mut bindings = GcHashMap::new();253254	for param in params.iter() {255		if let Some(v) = &param.1 {256			destruct(257				&param.0.clone(),258				Thunk::new(tb!(EvaluateNamedThunk {259					ctx: fctx.clone(),260					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),261					value: v.clone(),262				})),263				fctx.clone(),264				&mut bindings,265			)?;266		} else {267			destruct(268				&param.0,269				Thunk::new(tb!(DependsOnUnbound(270					param.0.name().unwrap_or_else(|| "<destruct>".into()),271					params.clone()272				))),273				fctx.clone(),274				&mut bindings,275			)?;276		}277	}278279	Ok(body_ctx280		.extend(bindings, None, None, None)281		.into_future(fctx))282}