1use std::collections::HashMap;23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{ArgsDesc, LocExpr, SourceFifo, SourcePath};67use crate::{evaluate, typed::Typed, with_state, Context, Result, Thunk, Val};8910pub trait OptionalContext {}1112pub trait ArgLike {13 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;14}1516impl ArgLike for &LocExpr {17 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {18 Ok(if tailstrict {19 Thunk::evaluated(evaluate(ctx, self)?)20 } else {21 let expr = (*self).clone();22 Thunk!(move || evaluate(ctx, &expr))23 })24 }25}2627impl<T> ArgLike for T28where29 T: Typed + Clone,30{31 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {32 if T::provides_lazy() && !tailstrict {33 return Ok(T::into_lazy_untyped(self.clone()));34 }35 let val = T::into_untyped(self.clone())?;36 Ok(Thunk::evaluated(val))37 }38}39impl<T> OptionalContext for T where T: Typed + Clone {}4041#[derive(Clone, Trace)]42pub enum TlaArg {43 String(IStr),44 Val(Val),45 Lazy(Thunk<Val>),46 Import(String),47 ImportStr(String),48 InlineCode(String),49}50impl TlaArg {51 pub fn evaluate_tailstrict(&self) -> Result<Val> {52 match self {53 Self::String(s) => Ok(Val::string(s.clone())),54 Self::Val(val) => Ok(val.clone()),55 Self::Lazy(lazy) => Ok(lazy.evaluate()?),56 Self::Import(p) => with_state(|s| {57 let resolved = s.resolve_from_default(&p.as_str())?;58 s.import_resolved(resolved)59 }),60 Self::ImportStr(p) => with_state(|s| {61 let resolved = s.resolve_from_default(&p.as_str())?;62 s.import_resolved_str(resolved).map(Val::string)63 }),64 Self::InlineCode(p) => with_state(|s| {65 let resolved =66 SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));67 s.import_resolved(resolved)68 }),69 }70 }71 pub fn evaluate(&self) -> Result<Thunk<Val>> {72 match self {73 Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),74 Self::Val(val) => Ok(Thunk::evaluated(val.clone())),75 Self::Lazy(lazy) => Ok(lazy.clone()),76 Self::Import(p) => with_state(|s| {77 let resolved = s.resolve_from_default(&p.as_str())?;78 Ok(Thunk!(move || s.import_resolved(resolved)))79 }),80 Self::ImportStr(p) => with_state(|s| {81 let resolved = s.resolve_from_default(&p.as_str())?;82 Ok(Thunk!(move || s83 .import_resolved_str(resolved)84 .map(Val::string)))85 }),86 Self::InlineCode(p) => with_state(|s| {87 let resolved =88 SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));89 Ok(Thunk!(move || s.import_resolved(resolved)))90 }),91 }92 }93}94959697impl ArgLike for TlaArg {98 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {99 if tailstrict {100 self.evaluate_tailstrict().map(Thunk::evaluated)101 } else {102 self.evaluate()103 }104 }105}106107pub trait ArgsLike {108 fn unnamed_len(&self) -> usize;109 fn unnamed_iter(110 &self,111 ctx: Context,112 tailstrict: bool,113 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,114 ) -> Result<()>;115 fn named_iter(116 &self,117 ctx: Context,118 tailstrict: bool,119 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,120 ) -> Result<()>;121 fn named_names(&self, handler: &mut dyn FnMut(&IStr));122 fn is_empty(&self) -> bool;123}124125impl ArgsLike for Vec<Val> {126 fn unnamed_len(&self) -> usize {127 self.len()128 }129 fn unnamed_iter(130 &self,131 _ctx: Context,132 _tailstrict: bool,133 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,134 ) -> Result<()> {135 for (idx, el) in self.iter().enumerate() {136 handler(idx, Thunk::evaluated(el.clone()))?;137 }138 Ok(())139 }140 fn named_iter(141 &self,142 _ctx: Context,143 _tailstrict: bool,144 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,145 ) -> Result<()> {146 Ok(())147 }148 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}149 fn is_empty(&self) -> bool {150 self.is_empty()151 }152}153154impl ArgsLike for ArgsDesc {155 fn unnamed_len(&self) -> usize {156 self.unnamed.len()157 }158159 fn unnamed_iter(160 &self,161 ctx: Context,162 tailstrict: bool,163 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,164 ) -> Result<()> {165 for (id, arg) in self.unnamed.iter().enumerate() {166 handler(167 id,168 if tailstrict {169 Thunk::evaluated(evaluate(ctx.clone(), arg)?)170 } else {171 let ctx = ctx.clone();172 let arg = arg.clone();173174 Thunk!(move || evaluate(ctx, &arg))175 },176 )?;177 }178 Ok(())179 }180181 fn named_iter(182 &self,183 ctx: Context,184 tailstrict: bool,185 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,186 ) -> Result<()> {187 for (name, arg) in &self.named {188 handler(189 name,190 if tailstrict {191 Thunk::evaluated(evaluate(ctx.clone(), arg)?)192 } else {193 let ctx = ctx.clone();194 let arg = arg.clone();195196 Thunk!(move || evaluate(ctx, &arg))197 },198 )?;199 }200 Ok(())201 }202203 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {204 for (name, _) in &self.named {205 handler(name);206 }207 }208209 fn is_empty(&self) -> bool {210 self.unnamed.is_empty() && self.named.is_empty()211 }212}213214impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {215 fn unnamed_len(&self) -> usize {216 0217 }218219 fn unnamed_iter(220 &self,221 _ctx: Context,222 _tailstrict: bool,223 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,224 ) -> Result<()> {225 Ok(())226 }227228 fn named_iter(229 &self,230 ctx: Context,231 tailstrict: bool,232 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,233 ) -> Result<()> {234 for (name, value) in self {235 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;236 }237 Ok(())238 }239240 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {241 for (name, _) in self {242 handler(name);243 }244 }245246 fn is_empty(&self) -> bool {247 self.is_empty()248 }249}250impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}251252macro_rules! impl_args_like {253 ($count:expr; $($gen:ident)*) => {254 impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {255 fn unnamed_len(&self) -> usize {256 $count257 }258 #[allow(non_snake_case, unused_assignments)]259 fn unnamed_iter(260 &self,261 ctx: Context,262 tailstrict: bool,263 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,264 ) -> Result<()> {265 let mut i = 0usize;266 let ($($gen,)*) = self;267 $(268 handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;269 i+=1;270 )*271 Ok(())272 }273 fn named_iter(274 &self,275 _ctx: Context,276 _tailstrict: bool,277 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,278 ) -> Result<()> {279 Ok(())280 }281 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}282283 fn is_empty(&self) -> bool {284 285 false286 }287 }288 impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}289 };290 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {291 impl_args_like!($count; $($cur)*);292 impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);293 };294 ($count:expr; $($cur:ident)* @) => {295 impl_args_like!($count; $($cur)*);296 }297}298impl_args_like! {299 300 1usize; A @ B C D E F G H I J K L301}302303impl ArgsLike for () {304 fn unnamed_len(&self) -> usize {305 0306 }307308 fn unnamed_iter(309 &self,310 _ctx: Context,311 _tailstrict: bool,312 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,313 ) -> Result<()> {314 Ok(())315 }316317 fn named_iter(318 &self,319 _ctx: Context,320 _tailstrict: bool,321 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,322 ) -> Result<()> {323 Ok(())324 }325326 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}327 fn is_empty(&self) -> bool {328 true329 }330}331impl OptionalContext for () {}