1use hashbrown::HashMap;2use jrsonnet_gcmodule::Trace;3use jrsonnet_interner::IStr;4use jrsonnet_parser::{ArgsDesc, LocExpr};56use crate::{evaluate, gc::GcHashMap, typed::Typed, val::ThunkValue, Context, Result, Thunk, Val};789pub trait OptionalContext {}1011#[derive(Trace)]12struct EvaluateThunk {13 ctx: Context,14 expr: LocExpr,15}16impl ThunkValue for EvaluateThunk {17 type Output = Val;18 fn get(self: Box<Self>) -> Result<Val> {19 evaluate(self.ctx, &self.expr)20 }21}2223pub trait ArgLike {24 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;25}2627impl ArgLike for &LocExpr {28 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {29 Ok(if tailstrict {30 Thunk::evaluated(evaluate(ctx, self)?)31 } else {32 Thunk::new(EvaluateThunk {33 ctx,34 expr: (*self).clone(),35 })36 })37 }38}3940impl<T> ArgLike for T41where42 T: Typed + Clone,43{44 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {45 if T::provides_lazy() && !tailstrict {46 return Ok(T::into_lazy_untyped(self.clone()));47 }48 let val = T::into_untyped(self.clone())?;49 Ok(Thunk::evaluated(val))50 }51}52impl<T> OptionalContext for T where T: Typed + Clone {}5354#[derive(Clone, Trace)]55pub enum TlaArg {56 String(IStr),57 Code(LocExpr),58 Val(Val),59 Lazy(Thunk<Val>),60}61impl ArgLike for TlaArg {62 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {63 match self {64 Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),65 Self::Code(code) => Ok(if tailstrict {66 Thunk::evaluated(evaluate(ctx, code)?)67 } else {68 Thunk::new(EvaluateThunk {69 ctx,70 expr: code.clone(),71 })72 }),73 Self::Val(val) => Ok(Thunk::evaluated(val.clone())),74 Self::Lazy(lazy) => Ok(lazy.clone()),75 }76 }77}7879pub trait ArgsLike {80 fn unnamed_len(&self) -> usize;81 fn unnamed_iter(82 &self,83 ctx: Context,84 tailstrict: bool,85 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,86 ) -> Result<()>;87 fn named_iter(88 &self,89 ctx: Context,90 tailstrict: bool,91 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,92 ) -> Result<()>;93 fn named_names(&self, handler: &mut dyn FnMut(&IStr));94 fn is_empty(&self) -> bool;95}9697impl ArgsLike for Vec<Val> {98 fn unnamed_len(&self) -> usize {99 self.len()100 }101 fn unnamed_iter(102 &self,103 _ctx: Context,104 _tailstrict: bool,105 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,106 ) -> Result<()> {107 for (idx, el) in self.iter().enumerate() {108 handler(idx, Thunk::evaluated(el.clone()))?;109 }110 Ok(())111 }112 fn named_iter(113 &self,114 _ctx: Context,115 _tailstrict: bool,116 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,117 ) -> Result<()> {118 Ok(())119 }120 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}121 fn is_empty(&self) -> bool {122 self.is_empty()123 }124}125126impl ArgsLike for ArgsDesc {127 fn unnamed_len(&self) -> usize {128 self.unnamed.len()129 }130131 fn unnamed_iter(132 &self,133 ctx: Context,134 tailstrict: bool,135 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,136 ) -> Result<()> {137 for (id, arg) in self.unnamed.iter().enumerate() {138 handler(139 id,140 if tailstrict {141 Thunk::evaluated(evaluate(ctx.clone(), arg)?)142 } else {143 Thunk::new(EvaluateThunk {144 ctx: ctx.clone(),145 expr: arg.clone(),146 })147 },148 )?;149 }150 Ok(())151 }152153 fn named_iter(154 &self,155 ctx: Context,156 tailstrict: bool,157 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,158 ) -> Result<()> {159 for (name, arg) in &self.named {160 handler(161 name,162 if tailstrict {163 Thunk::evaluated(evaluate(ctx.clone(), arg)?)164 } else {165 Thunk::new(EvaluateThunk {166 ctx: ctx.clone(),167 expr: arg.clone(),168 })169 },170 )?;171 }172 Ok(())173 }174175 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {176 for (name, _) in &self.named {177 handler(name);178 }179 }180181 fn is_empty(&self) -> bool {182 self.unnamed.is_empty() && self.named.is_empty()183 }184}185186impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {187 fn unnamed_len(&self) -> usize {188 0189 }190191 fn unnamed_iter(192 &self,193 _ctx: Context,194 _tailstrict: bool,195 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,196 ) -> Result<()> {197 Ok(())198 }199200 fn named_iter(201 &self,202 ctx: Context,203 tailstrict: bool,204 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,205 ) -> Result<()> {206 for (name, value) in self {207 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;208 }209 Ok(())210 }211212 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {213 for (name, _) in self {214 handler(name);215 }216 }217218 fn is_empty(&self) -> bool {219 self.is_empty()220 }221}222impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}223224impl<A: ArgLike> ArgsLike for GcHashMap<IStr, A> {225 fn unnamed_len(&self) -> usize {226 self.0.unnamed_len()227 }228229 fn unnamed_iter(230 &self,231 ctx: Context,232 tailstrict: bool,233 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,234 ) -> Result<()> {235 self.0.unnamed_iter(ctx, tailstrict, handler)236 }237238 fn named_iter(239 &self,240 ctx: Context,241 tailstrict: bool,242 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,243 ) -> Result<()> {244 self.0.named_iter(ctx, tailstrict, handler)245 }246247 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {248 self.0.named_names(handler);249 }250251 fn is_empty(&self) -> bool {252 self.0.is_empty()253 }254}255256macro_rules! impl_args_like {257 ($count:expr; $($gen:ident)*) => {258 impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {259 fn unnamed_len(&self) -> usize {260 $count261 }262 #[allow(non_snake_case, unused_assignments)]263 fn unnamed_iter(264 &self,265 ctx: Context,266 tailstrict: bool,267 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,268 ) -> Result<()> {269 let mut i = 0usize;270 let ($($gen,)*) = self;271 $(272 handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;273 i+=1;274 )*275 Ok(())276 }277 fn named_iter(278 &self,279 _ctx: Context,280 _tailstrict: bool,281 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,282 ) -> Result<()> {283 Ok(())284 }285 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}286287 fn is_empty(&self) -> bool {288 289 false290 }291 }292 impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}293 };294 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {295 impl_args_like!($count; $($cur)*);296 impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);297 };298 ($count:expr; $($cur:ident)* @) => {299 impl_args_like!($count; $($cur)*);300 }301}302impl_args_like! {303 304 1usize; A @ B C D E F G H I J K L305}306307impl ArgsLike for () {308 fn unnamed_len(&self) -> usize {309 0310 }311312 fn unnamed_iter(313 &self,314 _ctx: Context,315 _tailstrict: bool,316 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,317 ) -> Result<()> {318 Ok(())319 }320321 fn named_iter(322 &self,323 _ctx: Context,324 _tailstrict: bool,325 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,326 ) -> Result<()> {327 Ok(())328 }329330 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}331 fn is_empty(&self) -> bool {332 true333 }334}335impl OptionalContext for () {}