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};910pub trait ArgLike {11 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;12}1314impl ArgLike for &Rc<Spanned<Expr>> {15 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {16 Ok(if tailstrict {17 Thunk::evaluated(evaluate(ctx, self)?)18 } else {19 let expr = (*self).clone();20 Thunk!(move || evaluate(ctx, &expr))21 })22 }23}2425impl<T> ArgLike for T26where27 T: Typed + Clone,28{29 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {30 if T::provides_lazy() && !tailstrict {31 return Ok(T::into_lazy_untyped(self.clone()));32 }33 let val = T::into_untyped(self.clone())?;34 Ok(Thunk::evaluated(val))35 }36}3738#[derive(Clone, Trace)]39pub enum TlaArg {40 String(IStr),41 Val(Val),42 Lazy(Thunk<Val>),43 Import(String),44 ImportStr(String),45 InlineCode(String),46}47impl TlaArg {48 pub fn evaluate_tailstrict(&self) -> Result<Val> {49 match self {50 Self::String(s) => Ok(Val::string(s.clone())),51 Self::Val(val) => Ok(val.clone()),52 Self::Lazy(lazy) => Ok(lazy.evaluate()?),53 Self::Import(p) => with_state(|s| {54 let resolved = s.resolve_from_default(&p.as_str())?;55 s.import_resolved(resolved)56 }),57 Self::ImportStr(p) => with_state(|s| {58 let resolved = s.resolve_from_default(&p.as_str())?;59 s.import_resolved_str(resolved).map(Val::string)60 }),61 Self::InlineCode(p) => with_state(|s| {62 let resolved =63 SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));64 s.import_resolved(resolved)65 }),66 }67 }68 pub fn evaluate(&self) -> Result<Thunk<Val>> {69 match self {70 Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),71 Self::Val(val) => Ok(Thunk::evaluated(val.clone())),72 Self::Lazy(lazy) => Ok(lazy.clone()),73 Self::Import(p) => with_state(|s| {74 let resolved = s.resolve_from_default(&p.as_str())?;75 Ok(Thunk!(move || s.import_resolved(resolved)))76 }),77 Self::ImportStr(p) => with_state(|s| {78 let resolved = s.resolve_from_default(&p.as_str())?;79 Ok(Thunk!(move || s80 .import_resolved_str(resolved)81 .map(Val::string)))82 }),83 Self::InlineCode(p) => with_state(|s| {84 let resolved =85 SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));86 Ok(Thunk!(move || s.import_resolved(resolved)))87 }),88 }89 }90}9192pub trait ArgsLike {93 fn unnamed_len(&self) -> usize;94 fn unnamed_iter(95 &self,96 ctx: Context,97 tailstrict: bool,98 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,99 ) -> Result<()>;100 fn named_iter(101 &self,102 ctx: Context,103 tailstrict: bool,104 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,105 ) -> Result<()>;106 fn named_names(&self, handler: &mut dyn FnMut(&IStr));107 fn is_empty(&self) -> bool;108}109110impl ArgsLike for Vec<Val> {111 fn unnamed_len(&self) -> usize {112 self.len()113 }114 fn unnamed_iter(115 &self,116 _ctx: Context,117 _tailstrict: bool,118 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,119 ) -> Result<()> {120 for (idx, el) in self.iter().enumerate() {121 handler(idx, Thunk::evaluated(el.clone()))?;122 }123 Ok(())124 }125 fn named_iter(126 &self,127 _ctx: Context,128 _tailstrict: bool,129 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,130 ) -> Result<()> {131 Ok(())132 }133 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}134 fn is_empty(&self) -> bool {135 self.is_empty()136 }137}138139impl ArgsLike for ArgsDesc {140 fn unnamed_len(&self) -> usize {141 self.unnamed.len()142 }143144 fn unnamed_iter(145 &self,146 ctx: Context,147 tailstrict: bool,148 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,149 ) -> Result<()> {150 for (id, arg) in self.unnamed.iter().enumerate() {151 handler(152 id,153 if tailstrict {154 Thunk::evaluated(evaluate(ctx.clone(), arg)?)155 } else {156 let ctx = ctx.clone();157 let arg = arg.clone();158159 Thunk!(move || evaluate(ctx, &arg))160 },161 )?;162 }163 Ok(())164 }165166 fn named_iter(167 &self,168 ctx: Context,169 tailstrict: bool,170 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,171 ) -> Result<()> {172 for (name, arg) in &self.named {173 handler(174 name,175 if tailstrict {176 Thunk::evaluated(evaluate(ctx.clone(), arg)?)177 } else {178 let ctx = ctx.clone();179 let arg = arg.clone();180181 Thunk!(move || evaluate(ctx, &arg))182 },183 )?;184 }185 Ok(())186 }187188 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {189 for (name, _) in &self.named {190 handler(name);191 }192 }193194 fn is_empty(&self) -> bool {195 self.unnamed.is_empty() && self.named.is_empty()196 }197}