git.delta.rocks / jrsonnet / refs/commits / 44f6e2c9e550

difftreelog

source

crates/jrsonnet-evaluator/src/function/parse.rs6.5 KiBsourcehistory
1use std::rc::Rc;23use jrsonnet_ir::{4	function::{FunctionSignature, ParamName},5	ArgsDesc, Expr, ExprParams, Spanned,6};7use rustc_hash::FxHashMap;89use crate::{10	bail,11	destructure::destruct,12	error::{ErrorKind::*, Result},13	evaluate, evaluate_named_param,14	gc::WithCapacityExt as _,15	Context, Pending, Thunk, Val,16};1718fn eval_arg(ctx: Context, arg: &Rc<Spanned<Expr>>, tailstrict: bool) -> Result<Thunk<Val>> {19	if tailstrict {20		Ok(Thunk::evaluated(evaluate(ctx, arg)?))21	} else {22		let arg = arg.clone();23		Ok(Thunk!(move || evaluate(ctx, &arg)))24	}25}2627/// Creates correct [context](Context) for function body evaluation returning error on invalid call.28///29/// ## Parameters30/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)31/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)32/// * `params`: function parameters' definition33/// * `args`: passed function arguments34/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily35pub(crate) fn parse_function_call(36	ctx: Context,37	body_ctx: Context,38	params: &ExprParams,39	args: &ArgsDesc,40	tailstrict: bool,41) -> Result<Context> {42	let mut passed_args = FxHashMap::with_capacity(params.binds_len());43	if args.unnamed.len() > params.signature.len() {44		bail!(TooManyArgsFunctionHas(45			params.signature.len(),46			params.signature.clone(),47		))48	}4950	let mut filled_named = 0;51	let mut filled_positionals = 0;5253	for (id, arg) in args.unnamed.iter().enumerate() {54		destruct(55			&params.exprs[id].destruct,56			eval_arg(ctx.clone(), arg, tailstrict)?,57			Pending::new_filled(ctx.clone()),58			&mut passed_args,59		)?;60		filled_positionals += 1;61	}6263	for (name, value) in &args.named {64		// FIXME: O(n) for arg existence check65		if !params.exprs.iter().any(|p| &p.destruct.name() == name) {66			bail!(UnknownFunctionParameter(name.clone()));67		}68		if passed_args69			.insert(name.clone(), eval_arg(ctx.clone(), value, tailstrict)?)70			.is_some()71		{72			bail!(BindingParameterASecondTime(name.clone()));73		}74		filled_named += 1;75	}7677	if filled_named + filled_positionals < params.len() {78		// Some args are unset, but maybe we have defaults for them79		// Default values should be created in newly created context80		let fctx = Context::new_future();81		let mut defaults =82			FxHashMap::with_capacity(params.binds_len() - filled_named - filled_positionals);8384		for (idx, into, default) in params85			.exprs86			.iter()87			.enumerate()88			.filter_map(|(i, p)| Some((i, &p.destruct, p.default.as_ref()?)))89		{90			if let ParamName::Named(name) = into.name() {91				if passed_args.contains_key(&name) {92					continue;93				}94			} else if idx < filled_positionals {95				continue;96			}9798			destruct(99				into,100				{101					let ctx = fctx.clone();102					let name = into.name();103					let value = default.clone();104					Thunk!(move || evaluate_named_param(ctx.unwrap(), &value, name))105				},106				fctx.clone(),107				&mut defaults,108			)?;109			if into.name().is_named() {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.exprs.iter().skip(args.unnamed.len()) {119				let mut found = false;120				for (name, _) in &args.named {121					if &param.destruct.name() == name {122						found = true;123					}124				}125				if !found {126					bail!(FunctionParameterNotBoundInCall(127						param.destruct.name(),128						params.signature.clone()129					));130				}131			}132			unreachable!();133		}134135		Ok(body_ctx136			.extend_bindings(passed_args)137			.extend_bindings(defaults)138			.into_future(fctx))139	} else {140		let body_ctx = body_ctx.extend_bindings(passed_args);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	ctx: Context,154	params: FunctionSignature,155	args: &ArgsDesc,156	tailstrict: bool,157) -> Result<Vec<Option<Thunk<Val>>>> {158	let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];159	if args.unnamed.len() > params.len() {160		bail!(TooManyArgsFunctionHas(params.len(), params,))161	}162163	let mut filled_args = 0;164165	for (id, arg) in args.unnamed.iter().enumerate() {166		passed_args[id] = Some(eval_arg(ctx.clone(), arg, tailstrict)?);167		filled_args += 1;168	}169170	for (name, arg) in &args.named {171		// FIXME: O(n) for arg existence check172		let id = params173			.iter()174			.position(|p| p.name() == name)175			.ok_or_else(|| UnknownFunctionParameter(name.clone()))?;176		if passed_args[id]177			.replace(eval_arg(ctx.clone(), arg, tailstrict)?)178			.is_some()179		{180			bail!(BindingParameterASecondTime(name.clone()));181		}182		filled_args += 1;183	}184185	if filled_args < params.len() {186		for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {187			if passed_args[id].is_some() {188				continue;189			}190			filled_args += 1;191		}192193		// Some args still wasn't filled194		if filled_args != params.len() {195			for param in params.iter().skip(args.unnamed.len()) {196				let mut found = false;197				for (name, _) in &args.named {198					if param.name() == name {199						found = true;200					}201				}202				if !found {203					bail!(FunctionParameterNotBoundInCall(204						param.name().clone(),205						params,206					));207				}208			}209			unreachable!();210		}211	}212	Ok(passed_args)213}214215/// Creates Context, which has all argument default values applied216/// and with unbound values causing error to be returned217pub fn parse_default_function_call(body_ctx: Context, params: &ExprParams) -> Result<Context> {218	let fctx = Context::new_future();219220	let mut bindings = FxHashMap::with_capacity(params.binds_len());221222	for param in params.exprs.iter() {223		if let Some(v) = &param.default {224			destruct(225				&param.destruct.clone(),226				{227					let ctx = fctx.clone();228					let name = param.destruct.name();229					let value = v.clone();230					Thunk!(move || evaluate_named_param(ctx.unwrap(), &value, name))231				},232				fctx.clone(),233				&mut bindings,234			)?;235		} else {236			destruct(237				&param.destruct,238				{239					let param_name = param.destruct.name();240					let params = params.clone();241					Thunk!(move || Err(FunctionParameterNotBoundInCall(242						param_name,243						params.signature244					)245					.into()))246				},247				fctx.clone(),248				&mut bindings,249			)?;250		}251	}252253	Ok(body_ctx.extend_bindings(bindings).into_future(fctx))254}