--- a/cmds/jrsonnet/src/main.rs +++ b/cmds/jrsonnet/src/main.rs @@ -5,8 +5,8 @@ use clap::{CommandFactory, Parser}; use clap_complete::Shell; -use jrsonnet_cli::{ConfigureState, GeneralOpts, ManifestOpts, OutputOpts}; -use jrsonnet_evaluator::{error::LocError, State}; +use jrsonnet_cli::{ConfigureState, GeneralOpts, ManifestOpts, OutputOpts, TraceOpts}; +use jrsonnet_evaluator::{apply_tla, error::LocError, throw, ResultExt, State, Val}; #[cfg(feature = "mimalloc")] #[global_allocator] @@ -121,8 +121,8 @@ } fn main_real(s: &State, opts: Opts) -> Result<(), Error> { - let _guards = opts.general.configure(s)?; - opts.manifest.configure(s)?; + let (_stack_guard, tla, _gc_guard) = opts.general.configure(s)?; + let manifest_format = opts.manifest.configure(s)?; let input = opts.input.input.ok_or(Error::MissingInputArgument)?; let val = if opts.input.exec { @@ -136,7 +136,7 @@ s.import(&input)? }; - let val = s.with_tla(val)?; + let val = apply_tla(s.clone(), &tla, val)?; if let Some(multi) = opts.output.multi { if opts.output.create_output_dirs { --- a/crates/jrsonnet-cli/src/lib.rs +++ b/crates/jrsonnet-cli/src/lib.rs @@ -71,7 +71,7 @@ misc: MiscOpts, #[clap(flatten)] - tla: TLAOpts, + tla: TlaOpts, #[clap(flatten)] std: StdOpts, @@ -85,16 +85,17 @@ impl ConfigureState for GeneralOpts { type Guards = ( ::Guards, + ::Guards, ::Guards, ); fn configure(&self, s: &State) -> Result { // Configure trace first, because tla-code/ext-code can throw self.trace.configure(s)?; let misc_guards = self.misc.configure(s)?; - self.tla.configure(s)?; + let tla_guards = self.tla.configure(s)?; self.std.configure(s)?; let gc_guards = self.gc.configure(s)?; - Ok((misc_guards, gc_guards)) + Ok((misc_guards, tla_guards, gc_guards)) } } --- a/crates/jrsonnet-cli/src/tla.rs +++ b/crates/jrsonnet-cli/src/tla.rs @@ -1,11 +1,17 @@ use clap::Parser; -use jrsonnet_evaluator::{error::Result, State}; +use jrsonnet_evaluator::{ + error::{Error, Result}, + function::TlaArg, + gc::GcHashMap, + IStr, State, +}; +use jrsonnet_parser::{ParserSettings, Source}; use crate::{ConfigureState, ExtFile, ExtStr}; #[derive(Parser)] #[clap(next_help_heading = "TOP LEVEL ARGUMENTS")] -pub struct TLAOpts { +pub struct TlaOpts { /// Add top level string argument. /// Top level arguments will be passed to function before manifestification stage. /// This is preferred to ExtVars method. @@ -25,21 +31,41 @@ #[clap(long, name = "name=tla code path", number_of_values = 1)] tla_code_file: Vec, } -impl ConfigureState for TLAOpts { - type Guards = (); - fn configure(&self, s: &State) -> Result<()> { - for tla in self.tla_str.iter() { - s.add_tla_str((&tla.name as &str).into(), (&tla.value as &str).into()); +impl ConfigureState for TlaOpts { + type Guards = GcHashMap; + fn configure(&self, _s: &State) -> Result { + let mut out = GcHashMap::new(); + for (name, value) in self + .tla_str + .iter() + .map(|c| (&c.name, &c.value)) + .chain(self.tla_str_file.iter().map(|c| (&c.name, &c.value))) + { + out.insert(name.into(), TlaArg::String(value.into())); } - for tla in self.tla_str_file.iter() { - s.add_tla_str((&tla.name as &str).into(), (&tla.value as &str).into()) - } - for tla in self.tla_code.iter() { - s.add_tla_code((&tla.name as &str).into(), &tla.value as &str)?; - } - for tla in self.tla_code_file.iter() { - s.add_tla_code((&tla.name as &str).into(), &tla.value as &str)?; + for (name, code) in self + .tla_code + .iter() + .map(|c| (&c.name, &c.value)) + .chain(self.tla_code_file.iter().map(|c| (&c.name, &c.value))) + { + let source = Source::new_virtual(format!("").into(), code.into()); + out.insert( + (&name as &str).into(), + TlaArg::Code( + jrsonnet_parser::parse( + &code, + &ParserSettings { + source: source.clone(), + }, + ) + .map_err(|e| Error::ImportSyntaxError { + path: source, + error: Box::new(e), + })?, + ), + ); } - Ok(()) + Ok(out) } } --- a/crates/jrsonnet-evaluator/src/function/arglike.rs +++ b/crates/jrsonnet-evaluator/src/function/arglike.rs @@ -1,10 +1,11 @@ -use std::collections::HashMap; - +use hashbrown::HashMap; use jrsonnet_gcmodule::Trace; use jrsonnet_interner::IStr; use jrsonnet_parser::{ArgsDesc, LocExpr}; -use crate::{error::Result, evaluate, tb, typed::Typed, val::ThunkValue, Context, Thunk, Val}; +use crate::{ + error::Result, evaluate, gc::GcHashMap, tb, typed::Typed, val::ThunkValue, Context, Thunk, Val, +}; /// Marker for arguments, which can be evaluated with context set to None pub trait OptionalContext {} @@ -214,6 +215,34 @@ } impl OptionalContext for HashMap where A: ArgLike + OptionalContext {} +impl ArgsLike for GcHashMap { + fn unnamed_len(&self) -> usize { + self.0.unnamed_len() + } + + fn unnamed_iter( + &self, + ctx: Context, + tailstrict: bool, + handler: &mut dyn FnMut(usize, Thunk) -> Result<()>, + ) -> Result<()> { + self.0.unnamed_iter(ctx, tailstrict, handler) + } + + fn named_iter( + &self, + ctx: Context, + tailstrict: bool, + handler: &mut dyn FnMut(&IStr, Thunk) -> Result<()>, + ) -> Result<()> { + self.0.named_iter(ctx, tailstrict, handler) + } + + fn named_names(&self, handler: &mut dyn FnMut(&IStr)) { + self.0.named_names(handler) + } +} + macro_rules! impl_args_like { ($count:expr; $($gen:ident)*) => { impl<$($gen: ArgLike,)*> sealed::Unnamed for ($($gen,)*) {} --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -55,6 +55,7 @@ mod obj; pub mod stack; pub mod stdlib; +mod tla; pub mod trace; pub mod typed; pub mod val; @@ -81,7 +82,7 @@ use jrsonnet_parser::*; pub use obj::*; use stack::check_depth; -use trace::{CompactFormat, TraceFormat}; +pub use tla::apply_tla; pub use val::{ManifestFormat, Thunk, Val}; /// Thunk without bound `super`/`this` @@ -143,10 +144,6 @@ /// Dynamically reconfigurable evaluation settings #[derive(Trace)] pub struct EvaluationSettings { - /// Limits amount of stack trace items preserved - pub max_trace: usize, - /// TLA vars - pub tla_vars: HashMap, /// Context initializer, which will be used for imports and everything /// [`NoopContextInitializer`] is used by default, most likely you want to have `jrsonnet-stdlib` pub context_initializer: TraceBox, @@ -160,19 +157,8 @@ impl Default for EvaluationSettings { fn default() -> Self { Self { - max_trace: 20, context_initializer: tb!(DummyContextInitializer), - tla_vars: HashMap::default(), import_resolver: tb!(DummyImportResolver), - manifest_format: ManifestFormat::Json { - padding: 4, - #[cfg(feature = "exp-preserve-order")] - preserve_order: false, - }, - trace_format: tb!(CompactFormat { - padding: 4, - resolver: trace::PathResolver::Absolute, - }), } } } @@ -404,51 +390,6 @@ let _guard = check_depth()?; f().with_description(frame_desc) - } - - /// # Panics - /// In case of formatting failure - pub fn stringify_err(&self, e: &LocError) -> String { - let mut out = String::new(); - self.settings() - .trace_format - .write_trace(&mut out, self, e) - .unwrap(); - out - } - - pub fn manifest(&self, val: Val) -> Result { - Self::push_description( - || "manifestification".to_string(), - || val.manifest(&self.manifest_format()), - ) - } - pub fn manifest_multi(&self, val: Val) -> Result> { - val.manifest_multi(&self.manifest_format()) - } - pub fn manifest_stream(&self, val: Val) -> Result> { - val.manifest_stream(&self.manifest_format()) - } - - /// If passed value is function then call with set TLA - pub fn with_tla(&self, val: Val) -> Result { - Ok(match val { - Val::Func(func) => State::push_description( - || "during TLA call".to_owned(), - || { - func.evaluate( - self.create_default_context(Source::new_virtual( - "".into(), - IStr::empty(), - )), - CallLocation::native(), - &self.settings().tla_vars, - true, - ) - }, - )?, - v => v, - }) } } @@ -487,35 +428,6 @@ /// Settings utilities impl State { - pub fn add_tla(&self, name: IStr, value: Val) { - self.settings_mut() - .tla_vars - .insert(name, TlaArg::Val(value)); - } - pub fn add_tla_str(&self, name: IStr, value: IStr) { - self.settings_mut() - .tla_vars - .insert(name, TlaArg::String(value)); - } - pub fn add_tla_code(&self, name: IStr, code: &str) -> Result<()> { - let source_name = format!(""); - let source = Source::new_virtual(source_name.into(), code.into()); - let parsed = jrsonnet_parser::parse( - code, - &ParserSettings { - file_name: source.clone(), - }, - ) - .map_err(|e| ImportSyntaxError { - path: source, - error: Box::new(e), - })?; - self.settings_mut() - .tla_vars - .insert(name, TlaArg::Code(parsed)); - Ok(()) - } - // Only panics in case of [`ImportResolver`] contract violation #[allow(clippy::missing_panics_doc)] pub fn resolve_from(&self, from: &SourcePath, path: &str) -> Result { --- /dev/null +++ b/crates/jrsonnet-evaluator/src/tla.rs @@ -0,0 +1,25 @@ +use jrsonnet_interner::IStr; +use jrsonnet_parser::Source; + +use crate::{ + function::{ArgsLike, CallLocation}, + Result, State, Val, +}; + +pub fn apply_tla(s: State, args: &A, val: Val) -> Result { + Ok(if let Val::Func(func) = val { + State::push_description( + || "during TLA call".to_owned(), + || { + func.evaluate( + s.create_default_context(Source::new_virtual("".into(), IStr::empty())), + CallLocation::native(), + args, + false, + ) + }, + )? + } else { + val + }) +}