1use hashbrown::HashMap;2use jrsonnet_gcmodule::Trace;3use jrsonnet_interner::IStr;4use jrsonnet_parser::{ArgsDesc, LocExpr};56use crate::{7 error::Result,8 evaluate,9 gc::GcHashMap,10 typed::Typed,11 val::{StrValue, ThunkValue},12 Context, Thunk, Val,13};141516pub trait OptionalContext {}1718#[derive(Trace)]19struct EvaluateThunk {20 ctx: Context,21 expr: LocExpr,22}23impl ThunkValue for EvaluateThunk {24 type Output = Val;25 fn get(self: Box<Self>) -> Result<Val> {26 evaluate(self.ctx, &self.expr)27 }28}2930pub trait ArgLike {31 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;32}3334impl ArgLike for &LocExpr {35 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {36 Ok(if tailstrict {37 Thunk::evaluated(evaluate(ctx, self)?)38 } else {39 Thunk::new(EvaluateThunk {40 ctx,41 expr: (*self).clone(),42 })43 })44 }45}4647impl<T> ArgLike for T48where49 T: Typed + Clone,50{51 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {52 let val = T::into_untyped(self.clone())?;53 Ok(Thunk::evaluated(val))54 }55}56impl<T> OptionalContext for T where T: Typed + Clone {}5758impl ArgLike for Thunk<Val> {59 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {60 if tailstrict {61 self.force()?;62 }63 Ok(self.clone())64 }65}66impl OptionalContext for Thunk<Val> {}6768#[derive(Clone, Trace)]69pub enum TlaArg {70 String(IStr),71 Code(LocExpr),72 Val(Val),73}74impl ArgLike for TlaArg {75 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {76 match self {77 TlaArg::String(s) => Ok(Thunk::evaluated(Val::Str(StrValue::Flat(s.clone())))),78 TlaArg::Code(code) => Ok(if tailstrict {79 Thunk::evaluated(evaluate(ctx, code)?)80 } else {81 Thunk::new(EvaluateThunk {82 ctx,83 expr: code.clone(),84 })85 }),86 TlaArg::Val(val) => Ok(Thunk::evaluated(val.clone())),87 }88 }89}9091mod sealed {92 93 pub trait Unnamed {}94 95 pub trait Named {}96}9798pub trait ArgsLike {99 fn unnamed_len(&self) -> usize;100 fn unnamed_iter(101 &self,102 ctx: Context,103 tailstrict: bool,104 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,105 ) -> Result<()>;106 fn named_iter(107 &self,108 ctx: Context,109 tailstrict: bool,110 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,111 ) -> Result<()>;112 fn named_names(&self, handler: &mut dyn FnMut(&IStr));113}114115impl ArgsLike for Vec<Val> {116 fn unnamed_len(&self) -> usize {117 self.len()118 }119 fn unnamed_iter(120 &self,121 _ctx: Context,122 _tailstrict: bool,123 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,124 ) -> Result<()> {125 for (idx, el) in self.iter().enumerate() {126 handler(idx, Thunk::evaluated(el.clone()))?;127 }128 Ok(())129 }130 fn named_iter(131 &self,132 _ctx: Context,133 _tailstrict: bool,134 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,135 ) -> Result<()> {136 Ok(())137 }138 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}139}140141impl ArgsLike for ArgsDesc {142 fn unnamed_len(&self) -> usize {143 self.unnamed.len()144 }145146 fn unnamed_iter(147 &self,148 ctx: Context,149 tailstrict: bool,150 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,151 ) -> Result<()> {152 for (id, arg) in self.unnamed.iter().enumerate() {153 handler(154 id,155 if tailstrict {156 Thunk::evaluated(evaluate(ctx.clone(), arg)?)157 } else {158 Thunk::new(EvaluateThunk {159 ctx: ctx.clone(),160 expr: arg.clone(),161 })162 },163 )?;164 }165 Ok(())166 }167168 fn named_iter(169 &self,170 ctx: Context,171 tailstrict: bool,172 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,173 ) -> Result<()> {174 for (name, arg) in &self.named {175 handler(176 name,177 if tailstrict {178 Thunk::evaluated(evaluate(ctx.clone(), arg)?)179 } else {180 Thunk::new(EvaluateThunk {181 ctx: ctx.clone(),182 expr: arg.clone(),183 })184 },185 )?;186 }187 Ok(())188 }189190 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {191 for (name, _) in &self.named {192 handler(name);193 }194 }195}196197impl<V: ArgLike, S> sealed::Named for HashMap<IStr, V, S> {}198impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {199 fn unnamed_len(&self) -> usize {200 0201 }202203 fn unnamed_iter(204 &self,205 _ctx: Context,206 _tailstrict: bool,207 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,208 ) -> Result<()> {209 Ok(())210 }211212 fn named_iter(213 &self,214 ctx: Context,215 tailstrict: bool,216 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,217 ) -> Result<()> {218 for (name, value) in self.iter() {219 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;220 }221 Ok(())222 }223224 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {225 for (name, _) in self.iter() {226 handler(name);227 }228 }229}230impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}231232impl<A: ArgLike> ArgsLike for GcHashMap<IStr, A> {233 fn unnamed_len(&self) -> usize {234 self.0.unnamed_len()235 }236237 fn unnamed_iter(238 &self,239 ctx: Context,240 tailstrict: bool,241 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,242 ) -> Result<()> {243 self.0.unnamed_iter(ctx, tailstrict, handler)244 }245246 fn named_iter(247 &self,248 ctx: Context,249 tailstrict: bool,250 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,251 ) -> Result<()> {252 self.0.named_iter(ctx, tailstrict, handler)253 }254255 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {256 self.0.named_names(handler);257 }258}259260macro_rules! impl_args_like {261 ($count:expr; $($gen:ident)*) => {262 impl<$($gen: ArgLike,)*> sealed::Unnamed for ($($gen,)*) {}263 impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {264 fn unnamed_len(&self) -> usize {265 $count266 }267 #[allow(non_snake_case, unused_assignments)]268 fn unnamed_iter(269 &self,270 ctx: Context,271 tailstrict: bool,272 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,273 ) -> Result<()> {274 let mut i = 0usize;275 let ($($gen,)*) = self;276 $(277 handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;278 i+=1;279 )*280 Ok(())281 }282 fn named_iter(283 &self,284 _ctx: Context,285 _tailstrict: bool,286 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,287 ) -> Result<()> {288 Ok(())289 }290 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}291 }292 impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}293294 impl<$($gen: ArgLike,)*> sealed::Named for ($((IStr, $gen),)*) {}295 impl<$($gen: ArgLike,)*> ArgsLike for ($((IStr, $gen),)*) {296 fn unnamed_len(&self) -> usize {297 0298 }299 fn unnamed_iter(300 &self,301 _ctx: Context,302 _tailstrict: bool,303 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,304 ) -> Result<()> {305 Ok(())306 }307 #[allow(non_snake_case)]308 fn named_iter(309 &self,310 ctx: Context,311 tailstrict: bool,312 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,313 ) -> Result<()> {314 let ($($gen,)*) = self;315 $(316 handler(&$gen.0, $gen.1.evaluate_arg(ctx.clone(), tailstrict)?)?;317 )*318 Ok(())319 }320 #[allow(non_snake_case)]321 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {322 let ($($gen,)*) = self;323 $(324 handler(&$gen.0);325 )*326 }327 }328 impl<$($gen: ArgLike,)*> OptionalContext for ($((IStr, $gen),)*) where $($gen: OptionalContext),* {}329 };330 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {331 impl_args_like!($count; $($cur)*);332 impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);333 };334 ($count:expr; $($cur:ident)* @) => {335 impl_args_like!($count; $($cur)*);336 }337}338impl_args_like! {339 340 1usize; A @ B C D E F G H I J K L341}342343impl ArgsLike for () {344 fn unnamed_len(&self) -> usize {345 0346 }347348 fn unnamed_iter(349 &self,350 _ctx: Context,351 _tailstrict: bool,352 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,353 ) -> Result<()> {354 Ok(())355 }356357 fn named_iter(358 &self,359 _ctx: Context,360 _tailstrict: bool,361 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,362 ) -> Result<()> {363 Ok(())364 }365366 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}367}368impl OptionalContext for () {}