git.delta.rocks / jrsonnet / refs/commits / 3961a530b796

difftreelog

feat destruct function arguments

Yaroslav Bolyukin2022-06-03parent: #c35e2bd.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -12,7 +12,7 @@
 };
 
 #[allow(clippy::too_many_lines)]
-fn destruct(
+pub fn destruct(
 	d: &Destruct,
 	parent: Thunk<Val>,
 	new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -59,7 +59,7 @@
 }
 impl FuncDesc {
 	/// Create body context, but fill arguments without defaults with lazy error
-	pub fn default_body_context(&self) -> Context {
+	pub fn default_body_context(&self) -> Result<Context> {
 		parse_default_function_call(self.ctx.clone(), &self.params)
 	}
 
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
after · crates/jrsonnet-evaluator/src/function/parse.rs
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	destructure::destruct,11	error::{Error::*, Result},12	evaluate_named,13	gc::GcHashMap,14	tb, throw,15	val::ThunkValue,16	Context, Pending, State, Thunk, Val,17};1819#[derive(Trace)]20struct EvaluateNamedThunk {21	ctx: Pending<Context>,22	name: IStr,23	value: LocExpr,24}2526impl ThunkValue for EvaluateNamedThunk {27	type Output = Val;28	fn get(self: Box<Self>, s: State) -> Result<Val> {29		evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)30	}31}3233/// Creates correct [context](Context) for function body evaluation returning error on invalid call.34///35/// ## Parameters36/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)37/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)38/// * `params`: function parameters' definition39/// * `args`: passed function arguments40/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily41pub fn parse_function_call(42	s: State,43	ctx: Context,44	body_ctx: Context,45	params: &ParamsDesc,46	args: &dyn ArgsLike,47	tailstrict: bool,48) -> Result<Context> {49	let mut passed_args = GcHashMap::with_capacity(params.len());50	if args.unnamed_len() > params.len() {51		throw!(TooManyArgsFunctionHas(params.len()))52	}5354	let mut filled_named = 0;55	let mut filled_positionals = 0;5657	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {58		let name = params[id].0.clone();59		destruct(&name, arg, &mut passed_args)?;60		filled_positionals += 1;61		Ok(())62	})?;6364	args.named_iter(s, ctx, tailstrict, &mut |name, value| {65		// FIXME: O(n) for arg existence check66		if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {67			throw!(UnknownFunctionParameter((name as &str).to_owned()));68		}69		if passed_args.insert(name.clone(), value).is_some() {70			throw!(BindingParameterASecondTime(name.clone()));71		}72		filled_named += 1;73		Ok(())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			GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);8283		for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {84			if let Some(name) = param.0.name() {85				if passed_args.contains_key(&name) {86					continue;87				}88			} else if idx < filled_positionals {89				continue;90			}9192			destruct(93				&param.0,94				Thunk::new(tb!(EvaluateNamedThunk {95					ctx: fctx.clone(),96					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),97					value: param.1.clone().expect("default exists"),98				})),99				&mut defaults,100			)?;101			if param.0.name().is_some() {102				filled_named += 1;103			} else {104				filled_positionals += 1;105			}106		}107108		// Some args still weren't filled109		if filled_named + filled_positionals != params.len() {110			for param in params.iter().skip(args.unnamed_len()) {111				let mut found = false;112				args.named_names(&mut |name| {113					if Some(name) == param.0.name().as_ref() {114						found = true;115					}116				});117				if !found {118					throw!(FunctionParameterNotBoundInCall(119						param120							.0121							.clone()122							.name()123							.unwrap_or_else(|| "<destruct>".into())124					));125				}126			}127			unreachable!();128		}129130		Ok(body_ctx131			.extend(passed_args, None, None, None)132			.extend(defaults, None, None, None)133			.into_future(fctx))134	} else {135		let body_ctx = body_ctx.extend(passed_args, None, None, None);136		Ok(body_ctx)137	}138}139140/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead141///142/// ## Parameters143/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)144/// * `params`: function parameters' definition145/// * `args`: passed function arguments146/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily147pub fn parse_builtin_call(148	s: State,149	ctx: Context,150	params: &[BuiltinParam],151	args: &dyn ArgsLike,152	tailstrict: bool,153) -> Result<GcHashMap<BuiltinParamName, Thunk<Val>>> {154	let mut passed_args = GcHashMap::with_capacity(params.len());155	if args.unnamed_len() > params.len() {156		throw!(TooManyArgsFunctionHas(params.len()))157	}158159	let mut filled_args = 0;160161	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {162		let name = params[id].name.clone();163		passed_args.insert(name, arg);164		filled_args += 1;165		Ok(())166	})?;167168	args.named_iter(s, ctx, tailstrict, &mut |name, arg| {169		// FIXME: O(n) for arg existence check170		let p = params171			.iter()172			.find(|p| p.name == name as &str)173			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;174		if passed_args.insert(p.name.clone(), arg).is_some() {175			throw!(BindingParameterASecondTime(name.clone()));176		}177		filled_args += 1;178		Ok(())179	})?;180181	if filled_args < params.len() {182		for param in params.iter().filter(|p| p.has_default) {183			if passed_args.contains_key(&param.name) {184				continue;185			}186			filled_args += 1;187		}188189		// Some args still wasn't filled190		if filled_args != params.len() {191			for param in params.iter().skip(args.unnamed_len()) {192				let mut found = false;193				args.named_names(&mut |name| {194					if name as &str == &param.name as &str {195						found = true;196					}197				});198				if !found {199					throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));200				}201			}202			unreachable!();203		}204	}205	Ok(passed_args)206}207208/// Creates Context, which has all argument default values applied209/// and with unbound values causing error to be returned210pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {211	#[derive(Trace)]212	struct DependsOnUnbound(IStr);213	impl ThunkValue for DependsOnUnbound {214		type Output = Val;215		fn get(self: Box<Self>, _: State) -> Result<Val> {216			Err(FunctionParameterNotBoundInCall(self.0.clone()).into())217		}218	}219220	let fctx = Context::new_future();221222	let mut bindings = GcHashMap::new();223224	for param in params.iter() {225		if let Some(v) = &param.1 {226			destruct(227				&param.0.clone(),228				Thunk::new(tb!(EvaluateNamedThunk {229					ctx: fctx.clone(),230					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),231					value: v.clone(),232				})),233				&mut bindings,234			)?;235		} else {236			destruct(237				&param.0,238				Thunk::new(tb!(DependsOnUnbound(239					param.0.name().unwrap_or_else(|| "<destruct>".into())240				))),241				&mut bindings,242			)?;243		}244	}245246	Ok(body_ctx247		.extend(bindings, None, None, None)248		.into_future(fctx))249}
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -152,7 +152,7 @@
 /// name, default value
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-pub struct Param(pub IStr, pub Option<LocExpr>);
+pub struct Param(pub Destruct, pub Option<LocExpr>);
 
 /// Defined function parameters
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -206,6 +206,7 @@
 	},
 }
 impl Destruct {
+	/// Name of destructure, used for function parameter names
 	pub fn name(&self) -> Option<IStr> {
 		match self {
 			Self::Full(name) => Some(name.clone()),
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -59,7 +59,7 @@
 		rule keyword(id: &'static str) -> ()
 			= ##parse_string_literal(id) end_of_ident()
 
-		pub rule param(s: &ParserSettings) -> expr::Param = name:id() expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
+		pub rule param(s: &ParserSettings) -> expr::Param = name:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
 		pub rule params(s: &ParserSettings) -> expr::ParamsDesc
 			= params:param(s) ** comma() comma()? { expr::ParamsDesc(Rc::new(params)) }
 			/ { expr::ParamsDesc(Rc::new(Vec::new())) }