git.delta.rocks / jrsonnet / refs/commits / 562e7b71e322

difftreelog

refactor remove manual ThunkValue implementations

Yaroslav Bolyukin2024-08-30parent: #7cd3ab4.patch.diff
in: master

3 files changed

modifiedcrates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -3,23 +3,11 @@
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{ArgsDesc, LocExpr};
 
-use crate::{evaluate, gc::GcHashMap, typed::Typed, val::ThunkValue, Context, Result, Thunk, Val};
+use crate::{evaluate, gc::GcHashMap, typed::Typed, Context, Result, Thunk, Val};
 
 /// Marker for arguments, which can be evaluated with context set to None
 pub trait OptionalContext {}
 
-#[derive(Trace)]
-struct EvaluateThunk {
-	ctx: Context,
-	expr: LocExpr,
-}
-impl ThunkValue for EvaluateThunk {
-	type Output = Val;
-	fn get(self: Box<Self>) -> Result<Val> {
-		evaluate(self.ctx, &self.expr)
-	}
-}
-
 pub trait ArgLike {
 	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;
 }
@@ -29,10 +17,8 @@
 		Ok(if tailstrict {
 			Thunk::evaluated(evaluate(ctx, self)?)
 		} else {
-			Thunk::new(EvaluateThunk {
-				ctx,
-				expr: (*self).clone(),
-			})
+			let expr = (*self).clone();
+			Thunk!(move || evaluate(ctx, &expr))
 		})
 	}
 }
@@ -65,10 +51,8 @@
 			Self::Code(code) => Ok(if tailstrict {
 				Thunk::evaluated(evaluate(ctx, code)?)
 			} else {
-				Thunk::new(EvaluateThunk {
-					ctx,
-					expr: code.clone(),
-				})
+				let code = code.clone();
+				Thunk!(move || evaluate(ctx, &code))
 			}),
 			Self::Val(val) => Ok(Thunk::evaluated(val.clone())),
 			Self::Lazy(lazy) => Ok(lazy.clone()),
@@ -140,10 +124,10 @@
 				if tailstrict {
 					Thunk::evaluated(evaluate(ctx.clone(), arg)?)
 				} else {
-					Thunk::new(EvaluateThunk {
-						ctx: ctx.clone(),
-						expr: arg.clone(),
-					})
+					let ctx = ctx.clone();
+					let arg = arg.clone();
+
+					Thunk!(move || evaluate(ctx, &arg))
 				},
 			)?;
 		}
@@ -162,10 +146,10 @@
 				if tailstrict {
 					Thunk::evaluated(evaluate(ctx.clone(), arg)?)
 				} else {
-					Thunk::new(EvaluateThunk {
-						ctx: ctx.clone(),
-						expr: arg.clone(),
-					})
+					let ctx = ctx.clone();
+					let arg = arg.clone();
+
+					Thunk!(move || evaluate(ctx, &arg))
 				},
 			)?;
 		}
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/function/parse.rs
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	bail,10	destructure::destruct,11	error::{ErrorKind::*, Result},12	evaluate_named,13	function::builtin::ParamDefault,14	gc::GcHashMap,15	val::ThunkValue,16	Context, Pending, 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>) -> Result<Val> {29		evaluate_named(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	ctx: Context,43	body_ctx: Context,44	params: &ParamsDesc,45	args: &dyn ArgsLike,46	tailstrict: bool,47) -> Result<Context> {48	let mut passed_args =49		GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());50	if args.unnamed_len() > params.len() {51		bail!(TooManyArgsFunctionHas(52			params.len(),53			params54				.iter()55				.map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))56				.collect()57		))58	}5960	let mut filled_named = 0;61	let mut filled_positionals = 0;6263	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {64		let name = params[id].0.clone();65		destruct(66			&name,67			arg,68			Pending::new_filled(ctx.clone()),69			&mut passed_args,70		)?;71		filled_positionals += 1;72		Ok(())73	})?;7475	args.named_iter(ctx, tailstrict, &mut |name, value| {76		// FIXME: O(n) for arg existence check77		if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {78			bail!(UnknownFunctionParameter((name as &str).to_owned()));79		}80		if passed_args.insert(name.clone(), value).is_some() {81			bail!(BindingParameterASecondTime(name.clone()));82		}83		filled_named += 1;84		Ok(())85	})?;8687	if filled_named + filled_positionals < params.len() {88		// Some args are unset, but maybe we have defaults for them89		// Default values should be created in newly created context90		let fctx = Context::new_future();91		let mut defaults = GcHashMap::with_capacity(92			params.iter().map(|p| p.0.capacity_hint()).sum::<usize>()93				- filled_named94				- filled_positionals,95		);9697		for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {98			if let Some(name) = param.0.name() {99				if passed_args.contains_key(&name) {100					continue;101				}102			} else if idx < filled_positionals {103				continue;104			}105106			destruct(107				&param.0,108				Thunk::new(EvaluateNamedThunk {109					ctx: fctx.clone(),110					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),111					value: param.1.clone().expect("default exists"),112				}),113				fctx.clone(),114				&mut defaults,115			)?;116			if param.0.name().is_some() {117				filled_named += 1;118			} else {119				filled_positionals += 1;120			}121		}122123		// Some args still weren't filled124		if filled_named + filled_positionals != params.len() {125			for param in params.iter().skip(args.unnamed_len()) {126				let mut found = false;127				args.named_names(&mut |name| {128					if Some(name) == param.0.name().as_ref() {129						found = true;130					}131				});132				if !found {133					bail!(FunctionParameterNotBoundInCall(134						param.0.clone().name(),135						params136							.iter()137							.map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))138							.collect()139					));140				}141			}142			unreachable!();143		}144145		Ok(body_ctx146			.extend(passed_args, None, None, None)147			.extend(defaults, None, None, None)148			.into_future(fctx))149	} else {150		let body_ctx = body_ctx.extend(passed_args, None, None, None);151		Ok(body_ctx)152	}153}154155/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead156///157/// ## Parameters158/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)159/// * `params`: function parameters' definition160/// * `args`: passed function arguments161/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily162pub fn parse_builtin_call(163	ctx: Context,164	params: &[BuiltinParam],165	args: &dyn ArgsLike,166	tailstrict: bool,167) -> Result<Vec<Option<Thunk<Val>>>> {168	let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];169	if args.unnamed_len() > params.len() {170		bail!(TooManyArgsFunctionHas(171			params.len(),172			params173				.iter()174				.map(|p| (p.name().as_str().map(IStr::from), p.default()))175				.collect()176		))177	}178179	let mut filled_args = 0;180181	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {182		passed_args[id] = Some(arg);183		filled_args += 1;184		Ok(())185	})?;186187	args.named_iter(ctx, tailstrict, &mut |name, arg| {188		// FIXME: O(n) for arg existence check189		let id = params190			.iter()191			.position(|p| p.name() == name)192			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;193		if replace(&mut passed_args[id], Some(arg)).is_some() {194			bail!(BindingParameterASecondTime(name.clone()));195		}196		filled_args += 1;197		Ok(())198	})?;199200	if filled_args < params.len() {201		for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {202			if passed_args[id].is_some() {203				continue;204			}205			filled_args += 1;206		}207208		// Some args still wasn't filled209		if filled_args != params.len() {210			for param in params.iter().skip(args.unnamed_len()) {211				let mut found = false;212				args.named_names(&mut |name| {213					if param.name() == name {214						found = true;215					}216				});217				if !found {218					bail!(FunctionParameterNotBoundInCall(219						param.name().as_str().map(IStr::from),220						params221							.iter()222							.map(|p| (p.name().as_str().map(IStr::from), p.default()))223							.collect()224					));225				}226			}227			unreachable!();228		}229	}230	Ok(passed_args)231}232233/// Creates Context, which has all argument default values applied234/// and with unbound values causing error to be returned235pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {236	let fctx = Context::new_future();237238	let mut bindings = GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());239240	for param in params.iter() {241		if let Some(v) = &param.1 {242			destruct(243				&param.0.clone(),244				Thunk::new(EvaluateNamedThunk {245					ctx: fctx.clone(),246					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),247					value: v.clone(),248				}),249				fctx.clone(),250				&mut bindings,251			)?;252		} else {253			destruct(254				&param.0,255				{256					let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());257					let params = params.clone();258					Thunk!(move || Err(FunctionParameterNotBoundInCall(259						Some(param_name),260						params261							.iter()262							.map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))263							.collect(),264					)265					.into()))266				},267				fctx.clone(),268				&mut bindings,269			)?;270		}271	}272273	Ok(body_ctx274		.extend(bindings, None, None, None)275		.into_future(fctx))276}
after · crates/jrsonnet-evaluator/src/function/parse.rs
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	bail,10	destructure::destruct,11	error::{ErrorKind::*, Result},12	evaluate_named,13	function::builtin::ParamDefault,14	gc::GcHashMap,15	Context, Pending, Thunk, Val,16};1718/// Creates correct [context](Context) for function body evaluation returning error on invalid call.19///20/// ## Parameters21/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)22/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)23/// * `params`: function parameters' definition24/// * `args`: passed function arguments25/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily26pub fn parse_function_call(27	ctx: Context,28	body_ctx: Context,29	params: &ParamsDesc,30	args: &dyn ArgsLike,31	tailstrict: bool,32) -> Result<Context> {33	let mut passed_args =34		GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());35	if args.unnamed_len() > params.len() {36		bail!(TooManyArgsFunctionHas(37			params.len(),38			params39				.iter()40				.map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))41				.collect()42		))43	}4445	let mut filled_named = 0;46	let mut filled_positionals = 0;4748	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {49		let name = params[id].0.clone();50		destruct(51			&name,52			arg,53			Pending::new_filled(ctx.clone()),54			&mut passed_args,55		)?;56		filled_positionals += 1;57		Ok(())58	})?;5960	args.named_iter(ctx, tailstrict, &mut |name, value| {61		// FIXME: O(n) for arg existence check62		if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {63			bail!(UnknownFunctionParameter((name as &str).to_owned()));64		}65		if passed_args.insert(name.clone(), value).is_some() {66			bail!(BindingParameterASecondTime(name.clone()));67		}68		filled_named += 1;69		Ok(())70	})?;7172	if filled_named + filled_positionals < params.len() {73		// Some args are unset, but maybe we have defaults for them74		// Default values should be created in newly created context75		let fctx = Context::new_future();76		let mut defaults = GcHashMap::with_capacity(77			params.iter().map(|p| p.0.capacity_hint()).sum::<usize>()78				- filled_named79				- filled_positionals,80		);8182		for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {83			if let Some(name) = param.0.name() {84				if passed_args.contains_key(&name) {85					continue;86				}87			} else if idx < filled_positionals {88				continue;89			}9091			destruct(92				&param.0,93				{94					let ctx = fctx.clone();95					let name = param.0.name().unwrap_or_else(|| "<destruct>".into());96					let value = param.1.clone().expect("default exists");97					Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))98				},99				fctx.clone(),100				&mut defaults,101			)?;102			if param.0.name().is_some() {103				filled_named += 1;104			} else {105				filled_positionals += 1;106			}107		}108109		// Some args still weren't filled110		if filled_named + filled_positionals != params.len() {111			for param in params.iter().skip(args.unnamed_len()) {112				let mut found = false;113				args.named_names(&mut |name| {114					if Some(name) == param.0.name().as_ref() {115						found = true;116					}117				});118				if !found {119					bail!(FunctionParameterNotBoundInCall(120						param.0.clone().name(),121						params122							.iter()123							.map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))124							.collect()125					));126				}127			}128			unreachable!();129		}130131		Ok(body_ctx132			.extend(passed_args, None, None, None)133			.extend(defaults, None, None, None)134			.into_future(fctx))135	} else {136		let body_ctx = body_ctx.extend(passed_args, None, None, None);137		Ok(body_ctx)138	}139}140141/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead142///143/// ## Parameters144/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)145/// * `params`: function parameters' definition146/// * `args`: passed function arguments147/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily148pub fn parse_builtin_call(149	ctx: Context,150	params: &[BuiltinParam],151	args: &dyn ArgsLike,152	tailstrict: bool,153) -> Result<Vec<Option<Thunk<Val>>>> {154	let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];155	if args.unnamed_len() > params.len() {156		bail!(TooManyArgsFunctionHas(157			params.len(),158			params159				.iter()160				.map(|p| (p.name().as_str().map(IStr::from), p.default()))161				.collect()162		))163	}164165	let mut filled_args = 0;166167	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {168		passed_args[id] = Some(arg);169		filled_args += 1;170		Ok(())171	})?;172173	args.named_iter(ctx, tailstrict, &mut |name, arg| {174		// FIXME: O(n) for arg existence check175		let id = params176			.iter()177			.position(|p| p.name() == name)178			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;179		if replace(&mut passed_args[id], Some(arg)).is_some() {180			bail!(BindingParameterASecondTime(name.clone()));181		}182		filled_args += 1;183		Ok(())184	})?;185186	if filled_args < params.len() {187		for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {188			if passed_args[id].is_some() {189				continue;190			}191			filled_args += 1;192		}193194		// Some args still wasn't filled195		if filled_args != params.len() {196			for param in params.iter().skip(args.unnamed_len()) {197				let mut found = false;198				args.named_names(&mut |name| {199					if param.name() == name {200						found = true;201					}202				});203				if !found {204					bail!(FunctionParameterNotBoundInCall(205						param.name().as_str().map(IStr::from),206						params207							.iter()208							.map(|p| (p.name().as_str().map(IStr::from), p.default()))209							.collect()210					));211				}212			}213			unreachable!();214		}215	}216	Ok(passed_args)217}218219/// Creates Context, which has all argument default values applied220/// and with unbound values causing error to be returned221pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {222	let fctx = Context::new_future();223224	let mut bindings = GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());225226	for param in params.iter() {227		if let Some(v) = &param.1 {228			destruct(229				&param.0.clone(),230				{231					let ctx = fctx.clone();232					let name = param.0.name().unwrap_or_else(|| "<destruct>".into());233					let value = v.clone();234					Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))235				},236				fctx.clone(),237				&mut bindings,238			)?;239		} else {240			destruct(241				&param.0,242				{243					let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());244					let params = params.clone();245					Thunk!(move || Err(FunctionParameterNotBoundInCall(246						Some(param_name),247						params248							.iter()249							.map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))250							.collect(),251					)252					.into()))253				},254				fctx.clone(),255				&mut bindings,256			)?;257		}258	}259260	Ok(body_ctx261		.extend(bindings, None, None, None)262		.into_future(fctx))263}
modifiednix/benchmarks.nixdiffbeforeafterboth
--- a/nix/benchmarks.nix
+++ b/nix/benchmarks.nix
@@ -52,6 +52,7 @@
       mkdir -p $out
       cp -r ${src}/* $out/
       cd $out
+      chmod u+w jsonnetfile.lock.json
       mkdir vendor
       ${jsonnet-bundler}/bin/jb install
     '';
@@ -125,7 +126,7 @@
           go-jsonnet $path > generated.jsonnet
           path=generated.jsonnet
         ''}
-        hyperfine -N -w4 -m20 --output=pipe --style=basic --export-markdown result.md \
+        hyperfine -N -w4 -m20 --output=pipe --style=basic --export-asciidoc result.adoc \
           ${concatStringsSep " " (forEach jrsonnetVariants (
           variant: "\"${variant.drv}/bin/jrsonnet $path${optionalString (vendor != "") " -J${vendor}"}\" -n \"Rust${
             if variant.name != ""
@@ -137,7 +138,7 @@
           ${optionalString (skipGo == "") "\"go-jsonnet $path${optionalString (vendor != "") " -J ${vendor}"}\" -n \"Go\""} \
           ${optionalString (skipScala == "") "\"sjsonnet $path${optionalString (vendor != "") " -J ${vendor}"}\" -n \"Scala\""} \
           ${optionalString (skipCpp == "") "\"jsonnet $path${optionalString (vendor != "") " -J ${vendor}"}\" -n \"C++\""}
-        cat result.md >> $out
+        cat result.adoc >> $out
       '';
     in ''
       set -oux