git.delta.rocks / jrsonnet / refs/commits / 1c69f1ac7855

difftreelog

source

crates/jrsonnet-evaluator/src/function/arglike.rs7.7 KiBsourcehistory
1use std::collections::HashMap;23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{ArgsDesc, LocExpr, SourceFifo, SourcePath};67use crate::{evaluate, typed::Typed, with_state, Context, Result, Thunk, Val};89/// Marker for arguments, which can be evaluated with context set to None10pub trait OptionalContext {}1112pub trait ArgLike {13	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;14}1516impl ArgLike for &LocExpr {17	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {18		Ok(if tailstrict {19			Thunk::evaluated(evaluate(ctx, self)?)20		} else {21			let expr = (*self).clone();22			Thunk!(move || evaluate(ctx, &expr))23		})24	}25}2627impl<T> ArgLike for T28where29	T: Typed + Clone,30{31	fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {32		if T::provides_lazy() && !tailstrict {33			return Ok(T::into_lazy_untyped(self.clone()));34		}35		let val = T::into_untyped(self.clone())?;36		Ok(Thunk::evaluated(val))37	}38}39impl<T> OptionalContext for T where T: Typed + Clone {}4041#[derive(Clone, Trace)]42pub enum TlaArg {43	String(IStr),44	Val(Val),45	Lazy(Thunk<Val>),46	Import(String),47	ImportStr(String),48	InlineCode(String),49}50impl TlaArg {51	pub fn evaluate_tailstrict(&self) -> Result<Val> {52		match self {53			Self::String(s) => Ok(Val::string(s.clone())),54			Self::Val(val) => Ok(val.clone()),55			Self::Lazy(lazy) => Ok(lazy.evaluate()?),56			Self::Import(p) => with_state(|s| {57				let resolved = s.resolve_from_default(&p.as_str())?;58				s.import_resolved(resolved)59			}),60			Self::ImportStr(p) => with_state(|s| {61				let resolved = s.resolve_from_default(&p.as_str())?;62				s.import_resolved_str(resolved).map(Val::string)63			}),64			Self::InlineCode(p) => with_state(|s| {65				let resolved =66					SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));67				s.import_resolved(resolved)68			}),69		}70	}71	pub fn evaluate(&self) -> Result<Thunk<Val>> {72		match self {73			Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),74			Self::Val(val) => Ok(Thunk::evaluated(val.clone())),75			Self::Lazy(lazy) => Ok(lazy.clone()),76			Self::Import(p) => with_state(|s| {77				let resolved = s.resolve_from_default(&p.as_str())?;78				Ok(Thunk!(move || s.import_resolved(resolved)))79			}),80			Self::ImportStr(p) => with_state(|s| {81				let resolved = s.resolve_from_default(&p.as_str())?;82				Ok(Thunk!(move || s83					.import_resolved_str(resolved)84					.map(Val::string)))85			}),86			Self::InlineCode(p) => with_state(|s| {87				let resolved =88					SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));89				Ok(Thunk!(move || s.import_resolved(resolved)))90			}),91		}92	}93}9495// TODO: Is this implementation really required, as there is no Context to use?96// Maybe something a bit stricter is possible to add, especially with precompiled calls?97impl ArgLike for TlaArg {98	fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {99		if tailstrict {100			self.evaluate_tailstrict().map(Thunk::evaluated)101		} else {102			self.evaluate()103		}104	}105}106107pub trait ArgsLike {108	fn unnamed_len(&self) -> usize;109	fn unnamed_iter(110		&self,111		ctx: Context,112		tailstrict: bool,113		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,114	) -> Result<()>;115	fn named_iter(116		&self,117		ctx: Context,118		tailstrict: bool,119		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,120	) -> Result<()>;121	fn named_names(&self, handler: &mut dyn FnMut(&IStr));122	fn is_empty(&self) -> bool;123}124125impl ArgsLike for Vec<Val> {126	fn unnamed_len(&self) -> usize {127		self.len()128	}129	fn unnamed_iter(130		&self,131		_ctx: Context,132		_tailstrict: bool,133		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,134	) -> Result<()> {135		for (idx, el) in self.iter().enumerate() {136			handler(idx, Thunk::evaluated(el.clone()))?;137		}138		Ok(())139	}140	fn named_iter(141		&self,142		_ctx: Context,143		_tailstrict: bool,144		_handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,145	) -> Result<()> {146		Ok(())147	}148	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}149	fn is_empty(&self) -> bool {150		self.is_empty()151	}152}153154impl ArgsLike for ArgsDesc {155	fn unnamed_len(&self) -> usize {156		self.unnamed.len()157	}158159	fn unnamed_iter(160		&self,161		ctx: Context,162		tailstrict: bool,163		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,164	) -> Result<()> {165		for (id, arg) in self.unnamed.iter().enumerate() {166			handler(167				id,168				if tailstrict {169					Thunk::evaluated(evaluate(ctx.clone(), arg)?)170				} else {171					let ctx = ctx.clone();172					let arg = arg.clone();173174					Thunk!(move || evaluate(ctx, &arg))175				},176			)?;177		}178		Ok(())179	}180181	fn named_iter(182		&self,183		ctx: Context,184		tailstrict: bool,185		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,186	) -> Result<()> {187		for (name, arg) in &self.named {188			handler(189				name,190				if tailstrict {191					Thunk::evaluated(evaluate(ctx.clone(), arg)?)192				} else {193					let ctx = ctx.clone();194					let arg = arg.clone();195196					Thunk!(move || evaluate(ctx, &arg))197				},198			)?;199		}200		Ok(())201	}202203	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {204		for (name, _) in &self.named {205			handler(name);206		}207	}208209	fn is_empty(&self) -> bool {210		self.unnamed.is_empty() && self.named.is_empty()211	}212}213214impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {215	fn unnamed_len(&self) -> usize {216		0217	}218219	fn unnamed_iter(220		&self,221		_ctx: Context,222		_tailstrict: bool,223		_handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,224	) -> Result<()> {225		Ok(())226	}227228	fn named_iter(229		&self,230		ctx: Context,231		tailstrict: bool,232		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,233	) -> Result<()> {234		for (name, value) in self {235			handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;236		}237		Ok(())238	}239240	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {241		for (name, _) in self {242			handler(name);243		}244	}245246	fn is_empty(&self) -> bool {247		self.is_empty()248	}249}250impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}251252macro_rules! impl_args_like {253	($count:expr; $($gen:ident)*) => {254		impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {255			fn unnamed_len(&self) -> usize {256				$count257			}258			#[allow(non_snake_case, unused_assignments)]259			fn unnamed_iter(260				&self,261				ctx: Context,262				tailstrict: bool,263				handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,264			) -> Result<()> {265				let mut i = 0usize;266				let ($($gen,)*) = self;267				$(268					handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;269					i+=1;270				)*271				Ok(())272			}273			fn named_iter(274				&self,275				_ctx: Context,276				_tailstrict: bool,277				_handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,278			) -> Result<()> {279				Ok(())280			}281			fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}282283			fn is_empty(&self) -> bool {284				// impl_args_like only implements non-empty tuples.285				false286			}287		}288		impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}289	};290	($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {291		impl_args_like!($count; $($cur)*);292		impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);293	};294	($count:expr; $($cur:ident)* @) => {295		impl_args_like!($count; $($cur)*);296	}297}298impl_args_like! {299	// First argument is already in position, so count starts from 1300	1usize; A @ B C D E F G H I J K L301}302303impl ArgsLike for () {304	fn unnamed_len(&self) -> usize {305		0306	}307308	fn unnamed_iter(309		&self,310		_ctx: Context,311		_tailstrict: bool,312		_handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,313	) -> Result<()> {314		Ok(())315	}316317	fn named_iter(318		&self,319		_ctx: Context,320		_tailstrict: bool,321		_handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,322	) -> Result<()> {323		Ok(())324	}325326	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}327	fn is_empty(&self) -> bool {328		true329	}330}331impl OptionalContext for () {}