git.delta.rocks / jrsonnet / refs/commits / 75beb619a12f

difftreelog

source

crates/jrsonnet-evaluator/src/function/parse.rs6.5 KiBsourcehistory
1use std::rc::Rc;23use jrsonnet_ir::{4	ArgsDesc, Expr, ExprParams,5	function::{FunctionSignature, ParamName},6};7use rustc_hash::FxHashMap;89use crate::{10	Context, Pending, Thunk, Val, bail,11	destructure::destruct,12	error::{ErrorKind::*, Result},13	evaluate, evaluate_named_param,14	gc::WithCapacityExt as _,15};1617fn eval_arg(ctx: Context, arg: &Rc<Expr>, tailstrict: bool) -> Result<Thunk<Val>> {18	if tailstrict {19		Ok(Thunk::evaluated(evaluate(ctx, arg)?))20	} else {21		let arg = arg.clone();22		Ok(Thunk!(move || evaluate(ctx, &arg)))23	}24}2526/// Creates correct [context](Context) for function body evaluation returning error on invalid call.27///28/// ## Parameters29/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)30/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)31/// * `params`: function parameters' definition32/// * `args`: passed function arguments33/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily34pub(crate) fn parse_function_call(35	ctx: Context,36	body_ctx: Context,37	params: &ExprParams,38	args: &ArgsDesc,39	tailstrict: bool,40) -> Result<Context> {41	let mut passed_args = FxHashMap::with_capacity(params.binds_len());42	if args.unnamed.len() > params.signature.len() {43		bail!(TooManyArgsFunctionHas(44			params.signature.len(),45			params.signature.clone(),46		))47	}4849	let mut filled_named = 0;50	let mut filled_positionals = 0;5152	for (id, arg) in args.unnamed.iter().enumerate() {53		destruct(54			&params.exprs[id].destruct,55			eval_arg(ctx.clone(), arg, tailstrict)?,56			Pending::new_filled(ctx.clone()),57			&mut passed_args,58		)?;59		filled_positionals += 1;60	}6162	for (name, value) in &args.named {63		// FIXME: O(n) for arg existence check64		if !params.exprs.iter().any(|p| &p.destruct.name() == name) {65			bail!(UnknownFunctionParameter(name.clone()));66		}67		if passed_args68			.insert(name.clone(), eval_arg(ctx.clone(), value, tailstrict)?)69			.is_some()70		{71			bail!(BindingParameterASecondTime(name.clone()));72		}73		filled_named += 1;74	}7576	if filled_named + filled_positionals < params.len() {77		// Some args are unset, but maybe we have defaults for them78		// Default values should be created in newly created context79		let fctx = Context::new_future();80		let mut defaults =81			FxHashMap::with_capacity(params.binds_len() - filled_named - filled_positionals);8283		for (idx, into, default) in params84			.exprs85			.iter()86			.enumerate()87			.filter_map(|(i, p)| Some((i, &p.destruct, p.default.as_ref()?)))88		{89			if let ParamName::Named(name) = into.name() {90				if passed_args.contains_key(&name) {91					continue;92				}93			} else if idx < filled_positionals {94				continue;95			}9697			destruct(98				into,99				{100					let ctx = fctx.clone();101					let name = into.name();102					let value = default.clone();103					Thunk!(move || evaluate_named_param(ctx.unwrap(), &value, name))104				},105				fctx.clone(),106				&mut defaults,107			)?;108			if into.name().is_named() {109				filled_named += 1;110			} else {111				filled_positionals += 1;112			}113		}114115		// Some args still weren't filled116		if filled_named + filled_positionals != params.len() {117			for param in params.exprs.iter().skip(args.unnamed.len()) {118				let mut found = false;119				for (name, _) in &args.named {120					if &param.destruct.name() == name {121						found = true;122					}123				}124				if !found {125					bail!(FunctionParameterNotBoundInCall(126						param.destruct.name(),127						params.signature.clone()128					));129				}130			}131			unreachable!();132		}133134		Ok(body_ctx135			.extend_bindings(passed_args)136			.extend_bindings(defaults)137			.into_future(fctx))138	} else {139		let body_ctx = body_ctx.extend_bindings(passed_args);140		Ok(body_ctx)141	}142}143144/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead145///146/// ## Parameters147/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)148/// * `params`: function parameters' definition149/// * `args`: passed function arguments150/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily151pub fn parse_builtin_call(152	ctx: Context,153	params: FunctionSignature,154	args: &ArgsDesc,155	tailstrict: bool,156) -> Result<Vec<Option<Thunk<Val>>>> {157	let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];158	if args.unnamed.len() > params.len() {159		bail!(TooManyArgsFunctionHas(params.len(), params,))160	}161162	let mut filled_args = 0;163164	for (id, arg) in args.unnamed.iter().enumerate() {165		passed_args[id] = Some(eval_arg(ctx.clone(), arg, tailstrict)?);166		filled_args += 1;167	}168169	for (name, arg) in &args.named {170		// FIXME: O(n) for arg existence check171		let id = params172			.iter()173			.position(|p| p.name() == name)174			.ok_or_else(|| UnknownFunctionParameter(name.clone()))?;175		if passed_args[id]176			.replace(eval_arg(ctx.clone(), arg, tailstrict)?)177			.is_some()178		{179			bail!(BindingParameterASecondTime(name.clone()));180		}181		filled_args += 1;182	}183184	if filled_args < params.len() {185		for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {186			if passed_args[id].is_some() {187				continue;188			}189			filled_args += 1;190		}191192		// Some args still wasn't filled193		if filled_args != params.len() {194			for param in params.iter().skip(args.unnamed.len()) {195				let mut found = false;196				for (name, _) in &args.named {197					if param.name() == name {198						found = true;199					}200				}201				if !found {202					bail!(FunctionParameterNotBoundInCall(203						param.name().clone(),204						params,205					));206				}207			}208			unreachable!();209		}210	}211	Ok(passed_args)212}213214/// Creates Context, which has all argument default values applied215/// and with unbound values causing error to be returned216pub fn parse_default_function_call(body_ctx: Context, params: &ExprParams) -> Result<Context> {217	let fctx = Context::new_future();218219	let mut bindings = FxHashMap::with_capacity(params.binds_len());220221	for param in params.exprs.iter() {222		if let Some(v) = &param.default {223			destruct(224				&param.destruct.clone(),225				{226					let ctx = fctx.clone();227					let name = param.destruct.name();228					let value = v.clone();229					Thunk!(move || evaluate_named_param(ctx.unwrap(), &value, name))230				},231				fctx.clone(),232				&mut bindings,233			)?;234		} else {235			destruct(236				&param.destruct,237				{238					let param_name = param.destruct.name();239					let params = params.clone();240					Thunk!(move || Err(FunctionParameterNotBoundInCall(241						param_name,242						params.signature243					)244					.into()))245				},246				fctx.clone(),247				&mut bindings,248			)?;249		}250	}251252	Ok(body_ctx.extend_bindings(bindings).into_future(fctx))253}