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
before · crates/jrsonnet-evaluator/src/function/mod.rs
1use std::fmt::Debug;23pub use arglike::{ArgLike, ArgsLike, TlaArg};4use gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6pub use jrsonnet_macros::builtin;7use jrsonnet_parser::{ExprLocation, LocExpr, ParamsDesc};89use self::{10	builtin::{Builtin, StaticBuiltin},11	native::NativeDesc,12	parse::{parse_default_function_call, parse_function_call},13};14use crate::{evaluate, gc::TraceBox, typed::Any, Context, Result, State, Val};1516pub mod arglike;17pub mod builtin;18pub mod native;19pub mod parse;2021#[derive(Clone, Copy)]22pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);23impl<'l> CallLocation<'l> {24	pub const fn new(loc: &'l ExprLocation) -> Self {25		Self(Some(loc))26	}27}28impl CallLocation<'static> {29	pub const fn native() -> Self {30		Self(None)31	}32}3334/// Function implemented in jsonnet35#[derive(Debug, PartialEq, Trace)]36pub struct FuncDesc {37	/// In expressions like38	/// ```jsonnet39	/// local a = function() ...40	/// local a() ...41	/// { a: function() ... }42	/// { a() = ... }43	/// ```44	///45	/// Deducted to `a`, unspecified otherwise46	pub name: IStr,47	/// Context, in which this function was evaluated48	///49	/// I.e in50	/// ```jsonnet51	/// local a = 2;52	/// function() ...53	/// ```54	/// context will contain `a`55	pub ctx: Context,5657	pub params: ParamsDesc,58	pub body: LocExpr,59}60impl FuncDesc {61	/// Create body context, but fill arguments without defaults with lazy error62	pub fn default_body_context(&self) -> Context {63		parse_default_function_call(self.ctx.clone(), &self.params)64	}6566	/// Create context, with which body code will run67	pub fn call_body_context(68		&self,69		s: State,70		call_ctx: Context,71		args: &dyn ArgsLike,72		tailstrict: bool,73	) -> Result<Context> {74		parse_function_call(75			s,76			call_ctx,77			self.ctx.clone(),78			&self.params,79			args,80			tailstrict,81		)82	}83}8485/// Any possible function value, including plain functions and user-provided builtins86#[allow(clippy::module_name_repetitions)]87#[derive(Trace, Clone)]88pub enum FuncVal {89	/// std.id90	Id,91	/// Plain function implemented in jsonnet92	Normal(Cc<FuncDesc>),93	/// Standard library function94	StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),95	/// User-provided function96	Builtin(Cc<TraceBox<dyn Builtin>>),97}9899impl Debug for FuncVal {100	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {101		match self {102			Self::Id => f.debug_tuple("Id").finish(),103			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),104			Self::StaticBuiltin(arg0) => {105				f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()106			}107			Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),108		}109	}110}111112impl FuncVal {113	pub fn into_native<D: NativeDesc>(self) -> D::Value {114		D::into_native(self)115	}116	pub fn params_len(&self) -> usize {117		match self {118			Self::Id => 1,119			Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),120			Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),121			Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),122		}123	}124	pub fn name(&self) -> IStr {125		match self {126			Self::Id => "id".into(),127			Self::Normal(normal) => normal.name.clone(),128			Self::StaticBuiltin(builtin) => builtin.name().into(),129			Self::Builtin(builtin) => builtin.name().into(),130		}131	}132	pub fn evaluate(133		&self,134		s: State,135		call_ctx: Context,136		loc: CallLocation,137		args: &dyn ArgsLike,138		tailstrict: bool,139	) -> Result<Val> {140		match self {141			Self::Id => {142				#[allow(clippy::unnecessary_wraps)]143				#[builtin]144				const fn builtin_id(v: Any) -> Result<Any> {145					Ok(v)146				}147				static ID: &builtin_id = &builtin_id {};148149				ID.call(s, call_ctx, loc, args)150			}151			Self::Normal(func) => {152				let body_ctx = func.call_body_context(s.clone(), call_ctx, args, tailstrict)?;153				evaluate(s, body_ctx, &func.body)154			}155			Self::StaticBuiltin(b) => b.call(s, call_ctx, loc, args),156			Self::Builtin(b) => b.call(s, call_ctx, loc, args),157		}158	}159	pub fn evaluate_simple(&self, s: State, args: &dyn ArgsLike) -> Result<Val> {160		self.evaluate(s, Context::default(), CallLocation::native(), args, true)161	}162163	pub const fn is_identity(&self) -> bool {164		matches!(self, Self::Id)165	}166	pub const fn identity() -> Self {167		Self::Id168	}169}
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -7,6 +7,7 @@
 	builtin::{BuiltinParam, BuiltinParamName},
 };
 use crate::{
+	destructure::destruct,
 	error::{Error::*, Result},
 	evaluate_named,
 	gc::GcHashMap,
@@ -50,60 +51,77 @@
 		throw!(TooManyArgsFunctionHas(params.len()))
 	}
 
-	let mut filled_args = 0;
+	let mut filled_named = 0;
+	let mut filled_positionals = 0;
 
 	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {
 		let name = params[id].0.clone();
-		passed_args.insert(name, arg);
-		filled_args += 1;
+		destruct(&name, arg, &mut passed_args)?;
+		filled_positionals += 1;
 		Ok(())
 	})?;
 
 	args.named_iter(s, ctx, tailstrict, &mut |name, value| {
 		// FIXME: O(n) for arg existence check
-		if !params.iter().any(|p| &p.0 == name) {
+		if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {
 			throw!(UnknownFunctionParameter((name as &str).to_owned()));
 		}
 		if passed_args.insert(name.clone(), value).is_some() {
 			throw!(BindingParameterASecondTime(name.clone()));
 		}
-		filled_args += 1;
+		filled_named += 1;
 		Ok(())
 	})?;
 
-	if filled_args < params.len() {
+	if filled_named + filled_positionals < params.len() {
 		// Some args are unset, but maybe we have defaults for them
 		// Default values should be created in newly created context
 		let fctx = Context::new_future();
-		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);
+		let mut defaults =
+			GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);
 
-		for param in params.iter().filter(|p| p.1.is_some()) {
-			if passed_args.contains_key(&param.0.clone()) {
+		for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {
+			if let Some(name) = param.0.name() {
+				if passed_args.contains_key(&name) {
+					continue;
+				}
+			} else if idx < filled_positionals {
 				continue;
 			}
 
-			defaults.insert(
-				param.0.clone(),
+			destruct(
+				&param.0,
 				Thunk::new(tb!(EvaluateNamedThunk {
 					ctx: fctx.clone(),
-					name: param.0.clone(),
+					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
 					value: param.1.clone().expect("default exists"),
 				})),
-			);
-			filled_args += 1;
+				&mut defaults,
+			)?;
+			if param.0.name().is_some() {
+				filled_named += 1;
+			} else {
+				filled_positionals += 1;
+			}
 		}
 
-		// Some args still wasn't filled
-		if filled_args != params.len() {
+		// Some args still weren't filled
+		if filled_named + filled_positionals != params.len() {
 			for param in params.iter().skip(args.unnamed_len()) {
 				let mut found = false;
 				args.named_names(&mut |name| {
-					if name == &param.0 {
+					if Some(name) == param.0.name().as_ref() {
 						found = true;
 					}
 				});
 				if !found {
-					throw!(FunctionParameterNotBoundInCall(param.0.clone()));
+					throw!(FunctionParameterNotBoundInCall(
+						param
+							.0
+							.clone()
+							.name()
+							.unwrap_or_else(|| "<destruct>".into())
+					));
 				}
 			}
 			unreachable!();
@@ -189,7 +207,7 @@
 
 /// Creates Context, which has all argument default values applied
 /// and with unbound values causing error to be returned
-pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {
+pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {
 	#[derive(Trace)]
 	struct DependsOnUnbound(IStr);
 	impl ThunkValue for DependsOnUnbound {
@@ -205,23 +223,27 @@
 
 	for param in params.iter() {
 		if let Some(v) = &param.1 {
-			bindings.insert(
-				param.0.clone(),
+			destruct(
+				&param.0.clone(),
 				Thunk::new(tb!(EvaluateNamedThunk {
 					ctx: fctx.clone(),
-					name: param.0.clone(),
+					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
 					value: v.clone(),
 				})),
-			);
+				&mut bindings,
+			)?;
 		} else {
-			bindings.insert(
-				param.0.clone(),
-				Thunk::new(tb!(DependsOnUnbound(param.0.clone()))),
-			);
+			destruct(
+				&param.0,
+				Thunk::new(tb!(DependsOnUnbound(
+					param.0.name().unwrap_or_else(|| "<destruct>".into())
+				))),
+				&mut bindings,
+			)?;
 		}
 	}
 
-	body_ctx
+	Ok(body_ctx
 		.extend(bindings, None, None, None)
-		.into_future(fctx)
+		.into_future(fctx))
 }
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())) }