jrsonnet_evaluator/function/
arglike.rs1use 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
8pub 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 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 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 () {}