From 76f0cfd3dd2f4c52bf799849b9ec75c4d747e76a Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 26 Jul 2023 17:22:53 +0000 Subject: [PATCH] feat: --exp-apply argument --- --- a/cmds/jrsonnet/Cargo.toml +++ b/cmds/jrsonnet/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [features] -experimental = ["exp-preserve-order", "exp-destruct"] +experimental = ["exp-preserve-order", "exp-destruct", "exp-null-coaelse", "exp-object-iteration", "exp-bigint", "exp-apply"] # Use mimalloc as allocator mimalloc = ["mimallocator"] # Experimental feature, which allows to preserve order of object fields @@ -22,6 +22,10 @@ exp-object-iteration = ["jrsonnet-evaluator/exp-object-iteration"] # Bigint type exp-bigint = ["jrsonnet-evaluator/exp-bigint", "jrsonnet-cli/exp-bigint"] +# obj?.field, obj?.['field'] +exp-null-coaelse = ["jrsonnet-evaluator/exp-null-coaelse", "jrsonnet-parser/exp-null-coaelse"] +# --exp-apply +exp-apply = [] # std.thisFile support legacy-this-file = ["jrsonnet-cli/legacy-this-file"] --- 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, + + /// 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, } /// 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( + "".to_owned(), + &apply, + InitialUnderscore(Thunk::evaluated(val)), + )?; + } let manifest_format = opts.manifest.manifest_format(); if let Some(multi) = opts.output.multi { --- 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( e: CallLocation<'_>, @@ -428,25 +476,34 @@ } let mut settings = self.settings_mut(); let initializer = &mut settings.context_initializer; - match initializer.as_any().downcast_ref::() { - 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::() { + 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); +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, + code: impl Into, + context_initializer: impl ContextInitializer, + ) -> Result { + 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 -- gitstuff