git.delta.rocks / jrsonnet / refs/commits / b3f009bb7b2e

difftreelog

source

crates/jrsonnet-evaluator/src/function/arglike.rs7.7 KiBsourcehistory
1use std::collections::HashMap;2use std::rc::Rc;34use jrsonnet_gcmodule::Trace;5use jrsonnet_interner::IStr;6use jrsonnet_parser::{ArgsDesc, Expr, SourceFifo, SourcePath, Spanned};78use crate::{evaluate, typed::Typed, with_state, Context, Result, Thunk, Val};910/// Marker for arguments, which can be evaluated with context set to None11pub trait OptionalContext {}1213pub trait ArgLike {14	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;15}1617impl ArgLike for &Rc<Spanned<Expr>> {18	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {19		Ok(if tailstrict {20			Thunk::evaluated(evaluate(ctx, self)?)21		} else {22			let expr = (*self).clone();23			Thunk!(move || evaluate(ctx, &expr))24		})25	}26}2728impl<T> ArgLike for T29where30	T: Typed + Clone,31{32	fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {33		if T::provides_lazy() && !tailstrict {34			return Ok(T::into_lazy_untyped(self.clone()));35		}36		let val = T::into_untyped(self.clone())?;37		Ok(Thunk::evaluated(val))38	}39}40impl<T> OptionalContext for T where T: Typed + Clone {}4142#[derive(Clone, Trace)]43pub enum TlaArg {44	String(IStr),45	Val(Val),46	Lazy(Thunk<Val>),47	Import(String),48	ImportStr(String),49	InlineCode(String),50}51impl TlaArg {52	pub fn evaluate_tailstrict(&self) -> Result<Val> {53		match self {54			Self::String(s) => Ok(Val::string(s.clone())),55			Self::Val(val) => Ok(val.clone()),56			Self::Lazy(lazy) => Ok(lazy.evaluate()?),57			Self::Import(p) => with_state(|s| {58				let resolved = s.resolve_from_default(&p.as_str())?;59				s.import_resolved(resolved)60			}),61			Self::ImportStr(p) => with_state(|s| {62				let resolved = s.resolve_from_default(&p.as_str())?;63				s.import_resolved_str(resolved).map(Val::string)64			}),65			Self::InlineCode(p) => with_state(|s| {66				let resolved =67					SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));68				s.import_resolved(resolved)69			}),70		}71	}72	pub fn evaluate(&self) -> Result<Thunk<Val>> {73		match self {74			Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),75			Self::Val(val) => Ok(Thunk::evaluated(val.clone())),76			Self::Lazy(lazy) => Ok(lazy.clone()),77			Self::Import(p) => with_state(|s| {78				let resolved = s.resolve_from_default(&p.as_str())?;79				Ok(Thunk!(move || s.import_resolved(resolved)))80			}),81			Self::ImportStr(p) => with_state(|s| {82				let resolved = s.resolve_from_default(&p.as_str())?;83				Ok(Thunk!(move || s84					.import_resolved_str(resolved)85					.map(Val::string)))86			}),87			Self::InlineCode(p) => with_state(|s| {88				let resolved =89					SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));90				Ok(Thunk!(move || s.import_resolved(resolved)))91			}),92		}93	}94}9596// TODO: Is this implementation really required, as there is no Context to use?97// Maybe something a bit stricter is possible to add, especially with precompiled calls?98impl ArgLike for TlaArg {99	fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {100		if tailstrict {101			self.evaluate_tailstrict().map(Thunk::evaluated)102		} else {103			self.evaluate()104		}105	}106}107108pub trait ArgsLike {109	fn unnamed_len(&self) -> usize;110	fn unnamed_iter(111		&self,112		ctx: Context,113		tailstrict: bool,114		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,115	) -> Result<()>;116	fn named_iter(117		&self,118		ctx: Context,119		tailstrict: bool,120		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,121	) -> Result<()>;122	fn named_names(&self, handler: &mut dyn FnMut(&IStr));123	fn is_empty(&self) -> bool;124}125126impl ArgsLike for Vec<Val> {127	fn unnamed_len(&self) -> usize {128		self.len()129	}130	fn unnamed_iter(131		&self,132		_ctx: Context,133		_tailstrict: bool,134		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,135	) -> Result<()> {136		for (idx, el) in self.iter().enumerate() {137			handler(idx, Thunk::evaluated(el.clone()))?;138		}139		Ok(())140	}141	fn named_iter(142		&self,143		_ctx: Context,144		_tailstrict: bool,145		_handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,146	) -> Result<()> {147		Ok(())148	}149	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}150	fn is_empty(&self) -> bool {151		self.is_empty()152	}153}154155impl ArgsLike for ArgsDesc {156	fn unnamed_len(&self) -> usize {157		self.unnamed.len()158	}159160	fn unnamed_iter(161		&self,162		ctx: Context,163		tailstrict: bool,164		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,165	) -> Result<()> {166		for (id, arg) in self.unnamed.iter().enumerate() {167			handler(168				id,169				if tailstrict {170					Thunk::evaluated(evaluate(ctx.clone(), arg)?)171				} else {172					let ctx = ctx.clone();173					let arg = arg.clone();174175					Thunk!(move || evaluate(ctx, &arg))176				},177			)?;178		}179		Ok(())180	}181182	fn named_iter(183		&self,184		ctx: Context,185		tailstrict: bool,186		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,187	) -> Result<()> {188		for (name, arg) in &self.named {189			handler(190				name,191				if tailstrict {192					Thunk::evaluated(evaluate(ctx.clone(), arg)?)193				} else {194					let ctx = ctx.clone();195					let arg = arg.clone();196197					Thunk!(move || evaluate(ctx, &arg))198				},199			)?;200		}201		Ok(())202	}203204	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {205		for (name, _) in &self.named {206			handler(name);207		}208	}209210	fn is_empty(&self) -> bool {211		self.unnamed.is_empty() && self.named.is_empty()212	}213}214215impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {216	fn unnamed_len(&self) -> usize {217		0218	}219220	fn unnamed_iter(221		&self,222		_ctx: Context,223		_tailstrict: bool,224		_handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,225	) -> Result<()> {226		Ok(())227	}228229	fn named_iter(230		&self,231		ctx: Context,232		tailstrict: bool,233		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,234	) -> Result<()> {235		for (name, value) in self {236			handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;237		}238		Ok(())239	}240241	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {242		for (name, _) in self {243			handler(name);244		}245	}246247	fn is_empty(&self) -> bool {248		self.is_empty()249	}250}251impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}252253macro_rules! impl_args_like {254	($count:expr; $($gen:ident)*) => {255		impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {256			fn unnamed_len(&self) -> usize {257				$count258			}259			#[allow(non_snake_case, unused_assignments)]260			fn unnamed_iter(261				&self,262				ctx: Context,263				tailstrict: bool,264				handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,265			) -> Result<()> {266				let mut i = 0usize;267				let ($($gen,)*) = self;268				$(269					handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;270					i+=1;271				)*272				Ok(())273			}274			fn named_iter(275				&self,276				_ctx: Context,277				_tailstrict: bool,278				_handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,279			) -> Result<()> {280				Ok(())281			}282			fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}283284			fn is_empty(&self) -> bool {285				// impl_args_like only implements non-empty tuples.286				false287			}288		}289		impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}290	};291	($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {292		impl_args_like!($count; $($cur)*);293		impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);294	};295	($count:expr; $($cur:ident)* @) => {296		impl_args_like!($count; $($cur)*);297	}298}299impl_args_like! {300	// First argument is already in position, so count starts from 1301	1usize; A @ B C D E F G H I J K L302}303304impl ArgsLike for () {305	fn unnamed_len(&self) -> usize {306		0307	}308309	fn unnamed_iter(310		&self,311		_ctx: Context,312		_tailstrict: bool,313		_handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,314	) -> Result<()> {315		Ok(())316	}317318	fn named_iter(319		&self,320		_ctx: Context,321		_tailstrict: bool,322		_handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,323	) -> Result<()> {324		Ok(())325	}326327	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}328	fn is_empty(&self) -> bool {329		true330	}331}332impl OptionalContext for () {}