git.delta.rocks / jrsonnet / refs/commits / 76f0cfd3dd2f

difftreelog

feat --exp-apply argument

Yaroslav Bolyukin2023-07-26parent: #0f5ef44.patch.diff
in: master

3 files changed

modifiedcmds/jrsonnet/Cargo.tomldiffbeforeafterboth
before · cmds/jrsonnet/Cargo.toml
1[package]2name = "jrsonnet"3description = "Rust jsonnet implementation"4version.workspace = true5repository.workspace = true6authors = ["Yaroslav Bolyukin <iam@lach.pw>"]7license = "MIT"8edition = "2021"910[features]11experimental = ["exp-preserve-order", "exp-destruct"]12# Use mimalloc as allocator13mimalloc = ["mimallocator"]14# Experimental feature, which allows to preserve order of object fields15exp-preserve-order = [16    "jrsonnet-evaluator/exp-preserve-order",17    "jrsonnet-cli/exp-preserve-order",18]19# Destructuring of locals20exp-destruct = ["jrsonnet-evaluator/exp-destruct"]21# Iteration over objects yields [key, value] elements22exp-object-iteration = ["jrsonnet-evaluator/exp-object-iteration"]23# Bigint type24exp-bigint = ["jrsonnet-evaluator/exp-bigint", "jrsonnet-cli/exp-bigint"]2526# std.thisFile support27legacy-this-file = ["jrsonnet-cli/legacy-this-file"]2829nightly = ["jrsonnet-evaluator/nightly"]3031[dependencies]32jrsonnet-evaluator.workspace = true33jrsonnet-parser.workspace = true34jrsonnet-cli.workspace = true35jrsonnet-gcmodule.workspace = true3637mimallocator = { version = "0.1.3", optional = true }38thiserror = "1.0"39clap = { version = "4.1", features = ["derive"] }40clap_complete = { version = "4.1" }
after · cmds/jrsonnet/Cargo.toml
1[package]2name = "jrsonnet"3description = "Rust jsonnet implementation"4version.workspace = true5repository.workspace = true6authors = ["Yaroslav Bolyukin <iam@lach.pw>"]7license = "MIT"8edition = "2021"910[features]11experimental = ["exp-preserve-order", "exp-destruct", "exp-null-coaelse", "exp-object-iteration", "exp-bigint", "exp-apply"]12# Use mimalloc as allocator13mimalloc = ["mimallocator"]14# Experimental feature, which allows to preserve order of object fields15exp-preserve-order = [16    "jrsonnet-evaluator/exp-preserve-order",17    "jrsonnet-cli/exp-preserve-order",18]19# Destructuring of locals20exp-destruct = ["jrsonnet-evaluator/exp-destruct"]21# Iteration over objects yields [key, value] elements22exp-object-iteration = ["jrsonnet-evaluator/exp-object-iteration"]23# Bigint type24exp-bigint = ["jrsonnet-evaluator/exp-bigint", "jrsonnet-cli/exp-bigint"]25# obj?.field, obj?.['field']26exp-null-coaelse = ["jrsonnet-evaluator/exp-null-coaelse", "jrsonnet-parser/exp-null-coaelse"]27# --exp-apply28exp-apply = []2930# std.thisFile support31legacy-this-file = ["jrsonnet-cli/legacy-this-file"]3233nightly = ["jrsonnet-evaluator/nightly"]3435[dependencies]36jrsonnet-evaluator.workspace = true37jrsonnet-parser.workspace = true38jrsonnet-cli.workspace = true39jrsonnet-gcmodule.workspace = true4041mimallocator = { version = "0.1.3", optional = true }42thiserror = "1.0"43clap = { version = "4.1", features = ["derive"] }44clap_complete = { version = "4.1" }
modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -43,6 +43,12 @@
 
 	/// Path to the file to be compiled if `--evaluate` is unset, otherwise code itself
 	pub input: Option<String>,
+
+	/// After executing input, apply specified code.
+	/// Output of the initial input will be accessible using `$`
+	#[cfg(feature = "exp-apply")]
+	#[clap(long)]
+	pub exp_apply: Vec<String>,
 }
 
 /// Jsonnet commandline interpreter (Rust implementation)
@@ -181,7 +187,18 @@
 	};
 
 	let tla = opts.tla.tla_opts()?;
-	let val = apply_tla(s.clone(), &tla, val)?;
+	#[allow(unused_mut)]
+	let mut val = apply_tla(s.clone(), &tla, val)?;
+
+	#[cfg(feature = "exp-apply")]
+	for apply in opts.input.exp_apply {
+		use jrsonnet_evaluator::{InitialUnderscore, Thunk};
+		val = s.evaluate_snippet_with(
+			"<exp_apply>".to_owned(),
+			&apply,
+			InitialUnderscore(Thunk::evaluated(val)),
+		)?;
+	}
 
 	let manifest_format = opts.manifest.manifest_format();
 	if let Some(multi) = opts.output.multi {
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -154,6 +154,37 @@
 	}
 }
 
+macro_rules! impl_context_initializer {
+	($($gen:ident)*) => {
+		#[allow(non_snake_case)]
+		impl<$($gen: ContextInitializer + Trace,)*> ContextInitializer for ($($gen,)*) {
+			fn reserve_vars(&self) -> usize {
+				let mut out = 0;
+				let ($($gen,)*) = self;
+				$(out += $gen.reserve_vars();)*
+				out
+			}
+			fn populate(&self, for_file: Source, builder: &mut ContextBuilder) {
+				let ($($gen,)*) = self;
+				$($gen.populate(for_file.clone(), builder);)*
+			}
+			fn as_any(&self) -> &dyn Any {
+				self
+			}
+		}
+	};
+	($($cur:ident)* @ $c:ident $($rest:ident)*) => {
+		impl_context_initializer!($($cur)*);
+		impl_context_initializer!($($cur)* $c @ $($rest)*);
+	};
+	($($cur:ident)* @) => {
+		impl_context_initializer!($($cur)*);
+	}
+}
+impl_context_initializer! {
+	A B @ C D E
+}
+
 /// Dynamically reconfigurable evaluation settings
 #[derive(Trace)]
 pub struct EvaluationSettings {
@@ -361,6 +392,23 @@
 		context_initializer.initialize(self.clone(), source)
 	}
 
+	/// Creates context with all passed global variables, calling custom modifier
+	pub fn create_default_context_with(
+		&self,
+		source: Source,
+		context_initializer: impl ContextInitializer,
+	) -> Context {
+		let default_initializer = &self.settings().context_initializer;
+		let mut builder = ContextBuilder::with_capacity(
+			self.clone(),
+			default_initializer.reserve_vars() + context_initializer.reserve_vars(),
+		);
+		default_initializer.populate(source.clone(), &mut builder);
+		context_initializer.populate(source, &mut builder);
+
+		builder.build()
+	}
+
 	/// Executes code creating a new stack frame
 	pub fn push<T>(
 		e: CallLocation<'_>,
@@ -428,25 +476,34 @@
 		}
 		let mut settings = self.settings_mut();
 		let initializer = &mut settings.context_initializer;
-		match initializer.as_any().downcast_ref::<GlobalsCtx>() {
-			Some(glob) => {
-				glob.globals.borrow_mut().insert(name, value);
-			}
-			None => {
-				let inner = std::mem::replace(&mut settings.context_initializer, tb!(()));
-				settings.context_initializer = tb!(GlobalsCtx {
-					globals: {
-						let mut out = GcHashMap::with_capacity(1);
-						out.insert(name, value);
-						RefCell::new(out)
-					},
-					inner
-				})
-			}
+		if let Some(global) = initializer.as_any().downcast_ref::<GlobalsCtx>() {
+			global.globals.borrow_mut().insert(name, value);
+		} else {
+			let inner = std::mem::replace(&mut settings.context_initializer, tb!(()));
+			settings.context_initializer = tb!(GlobalsCtx {
+				globals: {
+					let mut out = GcHashMap::with_capacity(1);
+					out.insert(name, value);
+					RefCell::new(out)
+				},
+				inner
+			});
 		}
 	}
 }
 
+#[derive(Trace)]
+pub struct InitialUnderscore(pub Thunk<Val>);
+impl ContextInitializer for InitialUnderscore {
+	fn populate(&self, _for_file: Source, builder: &mut ContextBuilder) {
+		builder.bind("_".into(), self.0.clone());
+	}
+
+	fn as_any(&self) -> &dyn Any {
+		self
+	}
+}
+
 /// Raw methods evaluate passed values but don't perform TLA execution
 impl State {
 	/// Parses and evaluates the given snippet
@@ -465,6 +522,30 @@
 		})?;
 		evaluate(self.create_default_context(source), &parsed)
 	}
+	/// Parses and evaluates the given snippet with custom context modifier
+	pub fn evaluate_snippet_with(
+		&self,
+		name: impl Into<IStr>,
+		code: impl Into<IStr>,
+		context_initializer: impl ContextInitializer,
+	) -> Result<Val> {
+		let code = code.into();
+		let source = Source::new_virtual(name.into(), code.clone());
+		let parsed = jrsonnet_parser::parse(
+			&code,
+			&ParserSettings {
+				source: source.clone(),
+			},
+		)
+		.map_err(|e| ImportSyntaxError {
+			path: source.clone(),
+			error: Box::new(e),
+		})?;
+		evaluate(
+			self.create_default_context_with(source, context_initializer),
+			&parsed,
+		)
+	}
 }
 
 /// Settings utilities