1use hashbrown::HashMap;2use jrsonnet_gcmodule::Trace;3use jrsonnet_interner::IStr;4use jrsonnet_parser::{ArgsDesc, LocExpr};56use crate::{evaluate, gc::GcHashMap, typed::Typed, Context, Result, Thunk, Val};789pub trait OptionalContext {}1011pub trait ArgLike {12 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;13}1415impl 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}2526impl<T> ArgLike for T27where28 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 {}3940#[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}6263pub 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}8081impl 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}109110impl ArgsLike for ArgsDesc {111 fn unnamed_len(&self) -> usize {112 self.unnamed.len()113 }114115 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();129130 Thunk!(move || evaluate(ctx, &arg))131 },132 )?;133 }134 Ok(())135 }136137 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();151152 Thunk!(move || evaluate(ctx, &arg))153 },154 )?;155 }156 Ok(())157 }158159 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {160 for (name, _) in &self.named {161 handler(name);162 }163 }164165 fn is_empty(&self) -> bool {166 self.unnamed.is_empty() && self.named.is_empty()167 }168}169170impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {171 fn unnamed_len(&self) -> usize {172 0173 }174175 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 }183184 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 }195196 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {197 for (name, _) in self {198 handler(name);199 }200 }201202 fn is_empty(&self) -> bool {203 self.is_empty()204 }205}206impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}207208impl<A: ArgLike> ArgsLike for GcHashMap<IStr, A> {209 fn unnamed_len(&self) -> usize {210 self.0.unnamed_len()211 }212213 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 }221222 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 }230231 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {232 self.0.named_names(handler);233 }234235 fn is_empty(&self) -> bool {236 self.0.is_empty()237 }238}239240macro_rules! impl_args_like {241 ($count:expr; $($gen:ident)*) => {242 impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {243 fn unnamed_len(&self) -> usize {244 $count245 }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)) {}270271 fn is_empty(&self) -> bool {272 273 false274 }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 288 1usize; A @ B C D E F G H I J K L289}290291impl ArgsLike for () {292 fn unnamed_len(&self) -> usize {293 0294 }295296 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 }304305 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 }313314 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}315 fn is_empty(&self) -> bool {316 true317 }318}319impl OptionalContext for () {}