1use std::collections::HashMap;2use std::rc::Rc;34use jrsonnet_gcmodule::Trace;5use jrsonnet_interner::IStr;6use jrsonnet_parser::{ArgsDesc, Expr, SourceFifo, SourcePath, Spanned};78use crate::{evaluate, typed::Typed, with_state, Context, Result, Thunk, Val};91011pub trait OptionalContext {}1213pub trait ArgLike {14 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;15}1617impl ArgLike for &Rc<Spanned<Expr>> {18 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {19 Ok(if tailstrict {20 Thunk::evaluated(evaluate(ctx, self)?)21 } else {22 let expr = (*self).clone();23 Thunk!(move || evaluate(ctx, &expr))24 })25 }26}2728impl<T> ArgLike for T29where30 T: Typed + Clone,31{32 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {33 if T::provides_lazy() && !tailstrict {34 return Ok(T::into_lazy_untyped(self.clone()));35 }36 let val = T::into_untyped(self.clone())?;37 Ok(Thunk::evaluated(val))38 }39}40impl<T> OptionalContext for T where T: Typed + Clone {}4142#[derive(Clone, Trace)]43pub enum TlaArg {44 String(IStr),45 Val(Val),46 Lazy(Thunk<Val>),47 Import(String),48 ImportStr(String),49 InlineCode(String),50}51impl TlaArg {52 pub fn evaluate_tailstrict(&self) -> Result<Val> {53 match self {54 Self::String(s) => Ok(Val::string(s.clone())),55 Self::Val(val) => Ok(val.clone()),56 Self::Lazy(lazy) => Ok(lazy.evaluate()?),57 Self::Import(p) => with_state(|s| {58 let resolved = s.resolve_from_default(&p.as_str())?;59 s.import_resolved(resolved)60 }),61 Self::ImportStr(p) => with_state(|s| {62 let resolved = s.resolve_from_default(&p.as_str())?;63 s.import_resolved_str(resolved).map(Val::string)64 }),65 Self::InlineCode(p) => with_state(|s| {66 let resolved =67 SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));68 s.import_resolved(resolved)69 }),70 }71 }72 pub fn evaluate(&self) -> Result<Thunk<Val>> {73 match self {74 Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),75 Self::Val(val) => Ok(Thunk::evaluated(val.clone())),76 Self::Lazy(lazy) => Ok(lazy.clone()),77 Self::Import(p) => with_state(|s| {78 let resolved = s.resolve_from_default(&p.as_str())?;79 Ok(Thunk!(move || s.import_resolved(resolved)))80 }),81 Self::ImportStr(p) => with_state(|s| {82 let resolved = s.resolve_from_default(&p.as_str())?;83 Ok(Thunk!(move || s84 .import_resolved_str(resolved)85 .map(Val::string)))86 }),87 Self::InlineCode(p) => with_state(|s| {88 let resolved =89 SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));90 Ok(Thunk!(move || s.import_resolved(resolved)))91 }),92 }93 }94}95969798impl ArgLike for TlaArg {99 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {100 if tailstrict {101 self.evaluate_tailstrict().map(Thunk::evaluated)102 } else {103 self.evaluate()104 }105 }106}107108pub trait ArgsLike {109 fn unnamed_len(&self) -> usize;110 fn unnamed_iter(111 &self,112 ctx: Context,113 tailstrict: bool,114 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,115 ) -> Result<()>;116 fn named_iter(117 &self,118 ctx: Context,119 tailstrict: bool,120 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,121 ) -> Result<()>;122 fn named_names(&self, handler: &mut dyn FnMut(&IStr));123 fn is_empty(&self) -> bool;124}125126impl ArgsLike for Vec<Val> {127 fn unnamed_len(&self) -> usize {128 self.len()129 }130 fn unnamed_iter(131 &self,132 _ctx: Context,133 _tailstrict: bool,134 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,135 ) -> Result<()> {136 for (idx, el) in self.iter().enumerate() {137 handler(idx, Thunk::evaluated(el.clone()))?;138 }139 Ok(())140 }141 fn named_iter(142 &self,143 _ctx: Context,144 _tailstrict: bool,145 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,146 ) -> Result<()> {147 Ok(())148 }149 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}150 fn is_empty(&self) -> bool {151 self.is_empty()152 }153}154155impl ArgsLike for ArgsDesc {156 fn unnamed_len(&self) -> usize {157 self.unnamed.len()158 }159160 fn unnamed_iter(161 &self,162 ctx: Context,163 tailstrict: bool,164 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,165 ) -> Result<()> {166 for (id, arg) in self.unnamed.iter().enumerate() {167 handler(168 id,169 if tailstrict {170 Thunk::evaluated(evaluate(ctx.clone(), arg)?)171 } else {172 let ctx = ctx.clone();173 let arg = arg.clone();174175 Thunk!(move || evaluate(ctx, &arg))176 },177 )?;178 }179 Ok(())180 }181182 fn named_iter(183 &self,184 ctx: Context,185 tailstrict: bool,186 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,187 ) -> Result<()> {188 for (name, arg) in &self.named {189 handler(190 name,191 if tailstrict {192 Thunk::evaluated(evaluate(ctx.clone(), arg)?)193 } else {194 let ctx = ctx.clone();195 let arg = arg.clone();196197 Thunk!(move || evaluate(ctx, &arg))198 },199 )?;200 }201 Ok(())202 }203204 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {205 for (name, _) in &self.named {206 handler(name);207 }208 }209210 fn is_empty(&self) -> bool {211 self.unnamed.is_empty() && self.named.is_empty()212 }213}214215impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {216 fn unnamed_len(&self) -> usize {217 0218 }219220 fn unnamed_iter(221 &self,222 _ctx: Context,223 _tailstrict: bool,224 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,225 ) -> Result<()> {226 Ok(())227 }228229 fn named_iter(230 &self,231 ctx: Context,232 tailstrict: bool,233 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,234 ) -> Result<()> {235 for (name, value) in self {236 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;237 }238 Ok(())239 }240241 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {242 for (name, _) in self {243 handler(name);244 }245 }246247 fn is_empty(&self) -> bool {248 self.is_empty()249 }250}251impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}252253macro_rules! impl_args_like {254 ($count:expr; $($gen:ident)*) => {255 impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {256 fn unnamed_len(&self) -> usize {257 $count258 }259 #[allow(non_snake_case, unused_assignments)]260 fn unnamed_iter(261 &self,262 ctx: Context,263 tailstrict: bool,264 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,265 ) -> Result<()> {266 let mut i = 0usize;267 let ($($gen,)*) = self;268 $(269 handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;270 i+=1;271 )*272 Ok(())273 }274 fn named_iter(275 &self,276 _ctx: Context,277 _tailstrict: bool,278 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,279 ) -> Result<()> {280 Ok(())281 }282 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}283284 fn is_empty(&self) -> bool {285 286 false287 }288 }289 impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}290 };291 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {292 impl_args_like!($count; $($cur)*);293 impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);294 };295 ($count:expr; $($cur:ident)* @) => {296 impl_args_like!($count; $($cur)*);297 }298}299impl_args_like! {300 301 1usize; A @ B C D E F G H I J K L302}303304impl ArgsLike for () {305 fn unnamed_len(&self) -> usize {306 0307 }308309 fn unnamed_iter(310 &self,311 _ctx: Context,312 _tailstrict: bool,313 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,314 ) -> Result<()> {315 Ok(())316 }317318 fn named_iter(319 &self,320 _ctx: Context,321 _tailstrict: bool,322 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,323 ) -> Result<()> {324 Ok(())325 }326327 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}328 fn is_empty(&self) -> bool {329 true330 }331}332impl OptionalContext for () {}