1use crate::{2 error::{Error::*, LocError},3 evaluate, evaluate_named,4 gc::TraceBox,5 throw,6 typed::Typed,7 Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val,8};9use gcmodule::Trace;10use jrsonnet_interner::IStr;11pub use jrsonnet_macros::builtin;12use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};13use std::{borrow::Cow, collections::HashMap, convert::TryFrom};1415#[derive(Trace)]16struct EvaluateLazyVal {17 context: Context,18 expr: LocExpr,19}20impl LazyValValue for EvaluateLazyVal {21 fn get(self: Box<Self>) -> Result<Val> {22 evaluate(self.context, &self.expr)23 }24}2526pub trait ArgLike {27 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;28}29impl ArgLike for &LocExpr {30 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {31 Ok(if tailstrict {32 LazyVal::new_resolved(evaluate(ctx, self)?)33 } else {34 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {35 context: ctx,36 expr: (*self).clone(),37 })))38 })39 }40}41impl<T> ArgLike for T42where43 T: Typed + Clone,44 Val: TryFrom<T, Error = LocError>,45{46 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {47 let val: Val = Val::try_from(self.clone())?;48 Ok(LazyVal::new_resolved(val))49 }50}51pub enum TlaArg {52 String(IStr),53 Code(LocExpr),54 Val(Val),55}56impl ArgLike for TlaArg {57 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {58 match self {59 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),60 TlaArg::Code(code) => Ok(if tailstrict {61 LazyVal::new_resolved(evaluate(ctx, code)?)62 } else {63 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {64 context: ctx,65 expr: code.clone(),66 })))67 }),68 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),69 }70 }71}7273pub trait ArgsLike {74 fn unnamed_len(&self) -> usize;75 fn unnamed_iter(76 &self,77 ctx: Context,78 tailstrict: bool,79 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,80 ) -> Result<()>;81 fn named_iter(82 &self,83 ctx: Context,84 tailstrict: bool,85 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,86 ) -> Result<()>;87 fn named_names(&self, handler: &mut dyn FnMut(&IStr));88}8990impl ArgsLike for ArgsDesc {91 fn unnamed_len(&self) -> usize {92 self.unnamed.len()93 }9495 fn unnamed_iter(96 &self,97 ctx: Context,98 tailstrict: bool,99 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,100 ) -> Result<()> {101 for (id, arg) in self.unnamed.iter().enumerate() {102 handler(103 id,104 if tailstrict {105 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)106 } else {107 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {108 context: ctx.clone(),109 expr: arg.clone(),110 })))111 },112 )?;113 }114 Ok(())115 }116117 fn named_iter(118 &self,119 ctx: Context,120 tailstrict: bool,121 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,122 ) -> Result<()> {123 for (name, arg) in self.named.iter() {124 handler(125 name,126 if tailstrict {127 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)128 } else {129 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {130 context: ctx.clone(),131 expr: arg.clone(),132 })))133 },134 )?;135 }136 Ok(())137 }138139 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {140 for (name, _) in self.named.iter() {141 handler(name)142 }143 }144}145146impl<A: ArgLike> ArgsLike for [(IStr, A)] {147 fn unnamed_len(&self) -> usize {148 0149 }150151 fn unnamed_iter(152 &self,153 _ctx: Context,154 _tailstrict: bool,155 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,156 ) -> Result<()> {157 Ok(())158 }159160 fn named_iter(161 &self,162 ctx: Context,163 tailstrict: bool,164 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,165 ) -> Result<()> {166 for (name, val) in self.iter() {167 handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;168 }169 Ok(())170 }171172 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {173 for (name, _) in self.iter() {174 handler(name);175 }176 }177}178179impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {180 fn unnamed_len(&self) -> usize {181 0182 }183184 fn unnamed_iter(185 &self,186 _ctx: Context,187 _tailstrict: bool,188 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,189 ) -> Result<()> {190 Ok(())191 }192193 fn named_iter(194 &self,195 ctx: Context,196 tailstrict: bool,197 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,198 ) -> Result<()> {199 for (name, value) in self.iter() {200 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;201 }202 Ok(())203 }204205 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {206 for (name, _) in self.iter() {207 handler(name);208 }209 }210}211212impl<A: ArgLike> ArgsLike for [A] {213 fn unnamed_len(&self) -> usize {214 self.len()215 }216217 fn unnamed_iter(218 &self,219 ctx: Context,220 tailstrict: bool,221 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,222 ) -> Result<()> {223 for (i, arg) in self.iter().enumerate() {224 handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;225 }226 Ok(())227 }228229 fn named_iter(230 &self,231 _ctx: Context,232 _tailstrict: bool,233 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,234 ) -> Result<()> {235 Ok(())236 }237238 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}239}240impl<A: ArgLike> ArgsLike for &[A] {241 fn unnamed_len(&self) -> usize {242 (*self).unnamed_len()243 }244245 fn unnamed_iter(246 &self,247 ctx: Context,248 tailstrict: bool,249 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,250 ) -> Result<()> {251 (*self).unnamed_iter(ctx, tailstrict, handler)252 }253254 fn named_iter(255 &self,256 ctx: Context,257 tailstrict: bool,258 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,259 ) -> Result<()> {260 (*self).named_iter(ctx, tailstrict, handler)261 }262263 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {264 (*self).named_names(handler)265 }266}267268269270271272273274275276pub fn parse_function_call(277 ctx: Context,278 body_ctx: Context,279 params: &ParamsDesc,280 args: &dyn ArgsLike,281 tailstrict: bool,282) -> Result<Context> {283 let mut passed_args = GcHashMap::with_capacity(params.len());284 if args.unnamed_len() > params.len() {285 throw!(TooManyArgsFunctionHas(params.len()))286 }287288 let mut filled_args = 0;289290 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {291 let name = params[id].0.clone();292 passed_args.insert(name, arg);293 filled_args += 1;294 Ok(())295 })?;296297 args.named_iter(ctx, tailstrict, &mut |name, value| {298 299 if !params.iter().any(|p| &p.0 == name) {300 throw!(UnknownFunctionParameter((name as &str).to_owned()));301 }302 if passed_args.insert(name.clone(), value).is_some() {303 throw!(BindingParameterASecondTime(name.clone()));304 }305 filled_args += 1;306 Ok(())307 })?;308309 if filled_args < params.len() {310 311 312 let future_context = FutureWrapper::<Context>::new();313 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);314315 for param in params.iter().filter(|p| p.1.is_some()) {316 if passed_args.contains_key(¶m.0.clone()) {317 continue;318 }319 #[derive(Trace)]320 struct LazyNamedBinding {321 future_context: FutureWrapper<Context>,322 name: IStr,323 value: LocExpr,324 }325 impl LazyValValue for LazyNamedBinding {326 fn get(self: Box<Self>) -> Result<Val> {327 evaluate_named(self.future_context.unwrap(), &self.value, self.name)328 }329 }330 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {331 future_context: future_context.clone(),332 name: param.0.clone(),333 value: param.1.clone().unwrap(),334 })));335336 defaults.insert(337 param.0.clone(),338 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {339 future_context: future_context.clone(),340 name: param.0.clone(),341 value: param.1.clone().unwrap(),342 }))),343 );344 filled_args += 1;345 }346347 348 if filled_args != params.len() {349 for param in params.iter().skip(args.unnamed_len()) {350 let mut found = false;351 args.named_names(&mut |name| {352 if name == ¶m.0 {353 found = true;354 }355 });356 if !found {357 throw!(FunctionParameterNotBoundInCall(param.0.clone()));358 }359 }360 unreachable!();361 }362363 Ok(body_ctx364 .extend(passed_args, None, None, None)365 .extend_bound(defaults)366 .into_future(future_context))367 } else {368 let body_ctx = body_ctx.extend(passed_args, None, None, None);369 Ok(body_ctx)370 }371}372373type BuiltinParamName = Cow<'static, str>;374375#[derive(Clone, Trace)]376pub struct BuiltinParam {377 pub name: BuiltinParamName,378 pub has_default: bool,379}380381382pub trait Builtin: Trace {383 fn name(&self) -> &str;384 fn params(&self) -> &[BuiltinParam];385 fn call(386 &self,387 context: Context,388 loc: Option<&ExprLocation>,389 args: &dyn ArgsLike,390 ) -> Result<Val>;391}392393pub trait StaticBuiltin: Builtin + Send + Sync394where395 Self: 'static,396{397 398 399}400401402403404405406407408pub fn parse_builtin_call(409 ctx: Context,410 params: &[BuiltinParam],411 args: &dyn ArgsLike,412 tailstrict: bool,413) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {414 let mut passed_args = GcHashMap::with_capacity(params.len());415 if args.unnamed_len() > params.len() {416 throw!(TooManyArgsFunctionHas(params.len()))417 }418419 let mut filled_args = 0;420421 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {422 let name = params[id].name.clone();423 passed_args.insert(name, arg);424 filled_args += 1;425 Ok(())426 })?;427428 args.named_iter(ctx, tailstrict, &mut |name, arg| {429 430 let p = params431 .iter()432 .find(|p| p.name == name as &str)433 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;434 if passed_args.insert(p.name.clone(), arg).is_some() {435 throw!(BindingParameterASecondTime(name.clone()));436 }437 filled_args += 1;438 Ok(())439 })?;440441 if filled_args < params.len() {442 for param in params.iter().filter(|p| p.has_default) {443 if passed_args.contains_key(¶m.name) {444 continue;445 }446 filled_args += 1;447 }448449 450 if filled_args != params.len() {451 for param in params.iter().skip(args.unnamed_len()) {452 let mut found = false;453 args.named_names(&mut |name| {454 if name as &str == ¶m.name as &str {455 found = true;456 }457 });458 if !found {459 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));460 }461 }462 unreachable!();463 }464 }465 Ok(passed_args)466}