jrsonnet_evaluator/function/
arglike.rs

1use hashbrown::HashMap;
2use jrsonnet_gcmodule::Trace;
3use jrsonnet_interner::IStr;
4use jrsonnet_parser::{ArgsDesc, LocExpr};
5
6use crate::{evaluate, gc::GcHashMap, typed::Typed, Context, Result, Thunk, Val};
7
8/// Marker for arguments, which can be evaluated with context set to None
9pub trait OptionalContext {}
10
11pub trait ArgLike {
12	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;
13}
14
15impl ArgLike for &LocExpr {
16	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
17		Ok(if tailstrict {
18			Thunk::evaluated(evaluate(ctx, self)?)
19		} else {
20			let expr = (*self).clone();
21			Thunk!(move || evaluate(ctx, &expr))
22		})
23	}
24}
25
26impl<T> ArgLike for T
27where
28	T: Typed + Clone,
29{
30	fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
31		if T::provides_lazy() && !tailstrict {
32			return Ok(T::into_lazy_untyped(self.clone()));
33		}
34		let val = T::into_untyped(self.clone())?;
35		Ok(Thunk::evaluated(val))
36	}
37}
38impl<T> OptionalContext for T where T: Typed + Clone {}
39
40#[derive(Clone, Trace)]
41pub enum TlaArg {
42	String(IStr),
43	Code(LocExpr),
44	Val(Val),
45	Lazy(Thunk<Val>),
46}
47impl ArgLike for TlaArg {
48	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
49		match self {
50			Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),
51			Self::Code(code) => Ok(if tailstrict {
52				Thunk::evaluated(evaluate(ctx, code)?)
53			} else {
54				let code = code.clone();
55				Thunk!(move || evaluate(ctx, &code))
56			}),
57			Self::Val(val) => Ok(Thunk::evaluated(val.clone())),
58			Self::Lazy(lazy) => Ok(lazy.clone()),
59		}
60	}
61}
62
63pub trait ArgsLike {
64	fn unnamed_len(&self) -> usize;
65	fn unnamed_iter(
66		&self,
67		ctx: Context,
68		tailstrict: bool,
69		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
70	) -> Result<()>;
71	fn named_iter(
72		&self,
73		ctx: Context,
74		tailstrict: bool,
75		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
76	) -> Result<()>;
77	fn named_names(&self, handler: &mut dyn FnMut(&IStr));
78	fn is_empty(&self) -> bool;
79}
80
81impl ArgsLike for Vec<Val> {
82	fn unnamed_len(&self) -> usize {
83		self.len()
84	}
85	fn unnamed_iter(
86		&self,
87		_ctx: Context,
88		_tailstrict: bool,
89		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
90	) -> Result<()> {
91		for (idx, el) in self.iter().enumerate() {
92			handler(idx, Thunk::evaluated(el.clone()))?;
93		}
94		Ok(())
95	}
96	fn named_iter(
97		&self,
98		_ctx: Context,
99		_tailstrict: bool,
100		_handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
101	) -> Result<()> {
102		Ok(())
103	}
104	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}
105	fn is_empty(&self) -> bool {
106		self.is_empty()
107	}
108}
109
110impl ArgsLike for ArgsDesc {
111	fn unnamed_len(&self) -> usize {
112		self.unnamed.len()
113	}
114
115	fn unnamed_iter(
116		&self,
117		ctx: Context,
118		tailstrict: bool,
119		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
120	) -> Result<()> {
121		for (id, arg) in self.unnamed.iter().enumerate() {
122			handler(
123				id,
124				if tailstrict {
125					Thunk::evaluated(evaluate(ctx.clone(), arg)?)
126				} else {
127					let ctx = ctx.clone();
128					let arg = arg.clone();
129
130					Thunk!(move || evaluate(ctx, &arg))
131				},
132			)?;
133		}
134		Ok(())
135	}
136
137	fn named_iter(
138		&self,
139		ctx: Context,
140		tailstrict: bool,
141		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
142	) -> Result<()> {
143		for (name, arg) in &self.named {
144			handler(
145				name,
146				if tailstrict {
147					Thunk::evaluated(evaluate(ctx.clone(), arg)?)
148				} else {
149					let ctx = ctx.clone();
150					let arg = arg.clone();
151
152					Thunk!(move || evaluate(ctx, &arg))
153				},
154			)?;
155		}
156		Ok(())
157	}
158
159	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {
160		for (name, _) in &self.named {
161			handler(name);
162		}
163	}
164
165	fn is_empty(&self) -> bool {
166		self.unnamed.is_empty() && self.named.is_empty()
167	}
168}
169
170impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {
171	fn unnamed_len(&self) -> usize {
172		0
173	}
174
175	fn unnamed_iter(
176		&self,
177		_ctx: Context,
178		_tailstrict: bool,
179		_handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
180	) -> Result<()> {
181		Ok(())
182	}
183
184	fn named_iter(
185		&self,
186		ctx: Context,
187		tailstrict: bool,
188		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
189	) -> Result<()> {
190		for (name, value) in self {
191			handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;
192		}
193		Ok(())
194	}
195
196	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {
197		for (name, _) in self {
198			handler(name);
199		}
200	}
201
202	fn is_empty(&self) -> bool {
203		self.is_empty()
204	}
205}
206impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}
207
208impl<A: ArgLike> ArgsLike for GcHashMap<IStr, A> {
209	fn unnamed_len(&self) -> usize {
210		self.0.unnamed_len()
211	}
212
213	fn unnamed_iter(
214		&self,
215		ctx: Context,
216		tailstrict: bool,
217		handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
218	) -> Result<()> {
219		self.0.unnamed_iter(ctx, tailstrict, handler)
220	}
221
222	fn named_iter(
223		&self,
224		ctx: Context,
225		tailstrict: bool,
226		handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
227	) -> Result<()> {
228		self.0.named_iter(ctx, tailstrict, handler)
229	}
230
231	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {
232		self.0.named_names(handler);
233	}
234
235	fn is_empty(&self) -> bool {
236		self.0.is_empty()
237	}
238}
239
240macro_rules! impl_args_like {
241	($count:expr; $($gen:ident)*) => {
242		impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {
243			fn unnamed_len(&self) -> usize {
244				$count
245			}
246			#[allow(non_snake_case, unused_assignments)]
247			fn unnamed_iter(
248				&self,
249				ctx: Context,
250				tailstrict: bool,
251				handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
252			) -> Result<()> {
253				let mut i = 0usize;
254				let ($($gen,)*) = self;
255				$(
256					handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;
257					i+=1;
258				)*
259				Ok(())
260			}
261			fn named_iter(
262				&self,
263				_ctx: Context,
264				_tailstrict: bool,
265				_handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
266			) -> Result<()> {
267				Ok(())
268			}
269			fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}
270
271			fn is_empty(&self) -> bool {
272				// impl_args_like only implements non-empty tuples.
273				false
274			}
275		}
276		impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}
277	};
278	($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {
279		impl_args_like!($count; $($cur)*);
280		impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);
281	};
282	($count:expr; $($cur:ident)* @) => {
283		impl_args_like!($count; $($cur)*);
284	}
285}
286impl_args_like! {
287	// First argument is already in position, so count starts from 1
288	1usize; A @ B C D E F G H I J K L
289}
290
291impl ArgsLike for () {
292	fn unnamed_len(&self) -> usize {
293		0
294	}
295
296	fn unnamed_iter(
297		&self,
298		_ctx: Context,
299		_tailstrict: bool,
300		_handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
301	) -> Result<()> {
302		Ok(())
303	}
304
305	fn named_iter(
306		&self,
307		_ctx: Context,
308		_tailstrict: bool,
309		_handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
310	) -> Result<()> {
311		Ok(())
312	}
313
314	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}
315	fn is_empty(&self) -> bool {
316		true
317	}
318}
319impl OptionalContext for () {}