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}2526#[derive(Trace)]27struct EvaluateNamedLazyVal {28 future_context: FutureWrapper<Context>,29 name: IStr,30 value: LocExpr,31}32impl LazyValValue for EvaluateNamedLazyVal {33 fn get(self: Box<Self>) -> Result<Val> {34 evaluate_named(self.future_context.unwrap(), &self.value, self.name)35 }36}3738pub trait ArgLike {39 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;40}41impl ArgLike for &LocExpr {42 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {43 Ok(if tailstrict {44 LazyVal::new_resolved(evaluate(ctx, self)?)45 } else {46 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {47 context: ctx,48 expr: (*self).clone(),49 })))50 })51 }52}53impl<T> ArgLike for T54where55 T: Typed + Clone,56 Val: TryFrom<T, Error = LocError>,57{58 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {59 let val: Val = Val::try_from(self.clone())?;60 Ok(LazyVal::new_resolved(val))61 }62}63pub enum TlaArg {64 String(IStr),65 Code(LocExpr),66 Val(Val),67}68impl ArgLike for TlaArg {69 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {70 match self {71 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),72 TlaArg::Code(code) => Ok(if tailstrict {73 LazyVal::new_resolved(evaluate(ctx, code)?)74 } else {75 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {76 context: ctx,77 expr: code.clone(),78 })))79 }),80 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),81 }82 }83}8485pub trait ArgsLike {86 fn unnamed_len(&self) -> usize;87 fn unnamed_iter(88 &self,89 ctx: Context,90 tailstrict: bool,91 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,92 ) -> Result<()>;93 fn named_iter(94 &self,95 ctx: Context,96 tailstrict: bool,97 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,98 ) -> Result<()>;99 fn named_names(&self, handler: &mut dyn FnMut(&IStr));100}101102impl ArgsLike for ArgsDesc {103 fn unnamed_len(&self) -> usize {104 self.unnamed.len()105 }106107 fn unnamed_iter(108 &self,109 ctx: Context,110 tailstrict: bool,111 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,112 ) -> Result<()> {113 for (id, arg) in self.unnamed.iter().enumerate() {114 handler(115 id,116 if tailstrict {117 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)118 } else {119 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {120 context: ctx.clone(),121 expr: arg.clone(),122 })))123 },124 )?;125 }126 Ok(())127 }128129 fn named_iter(130 &self,131 ctx: Context,132 tailstrict: bool,133 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,134 ) -> Result<()> {135 for (name, arg) in self.named.iter() {136 handler(137 name,138 if tailstrict {139 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)140 } else {141 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {142 context: ctx.clone(),143 expr: arg.clone(),144 })))145 },146 )?;147 }148 Ok(())149 }150151 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {152 for (name, _) in self.named.iter() {153 handler(name)154 }155 }156}157158impl<A: ArgLike> ArgsLike for [(IStr, A)] {159 fn unnamed_len(&self) -> usize {160 0161 }162163 fn unnamed_iter(164 &self,165 _ctx: Context,166 _tailstrict: bool,167 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,168 ) -> Result<()> {169 Ok(())170 }171172 fn named_iter(173 &self,174 ctx: Context,175 tailstrict: bool,176 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,177 ) -> Result<()> {178 for (name, val) in self.iter() {179 handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;180 }181 Ok(())182 }183184 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {185 for (name, _) in self.iter() {186 handler(name);187 }188 }189}190191impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {192 fn unnamed_len(&self) -> usize {193 0194 }195196 fn unnamed_iter(197 &self,198 _ctx: Context,199 _tailstrict: bool,200 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,201 ) -> Result<()> {202 Ok(())203 }204205 fn named_iter(206 &self,207 ctx: Context,208 tailstrict: bool,209 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,210 ) -> Result<()> {211 for (name, value) in self.iter() {212 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;213 }214 Ok(())215 }216217 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {218 for (name, _) in self.iter() {219 handler(name);220 }221 }222}223224impl<A: ArgLike> ArgsLike for [A] {225 fn unnamed_len(&self) -> usize {226 self.len()227 }228229 fn unnamed_iter(230 &self,231 ctx: Context,232 tailstrict: bool,233 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,234 ) -> Result<()> {235 for (i, arg) in self.iter().enumerate() {236 handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;237 }238 Ok(())239 }240241 fn named_iter(242 &self,243 _ctx: Context,244 _tailstrict: bool,245 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,246 ) -> Result<()> {247 Ok(())248 }249250 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}251}252impl<A: ArgLike> ArgsLike for &[A] {253 fn unnamed_len(&self) -> usize {254 (*self).unnamed_len()255 }256257 fn unnamed_iter(258 &self,259 ctx: Context,260 tailstrict: bool,261 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,262 ) -> Result<()> {263 (*self).unnamed_iter(ctx, tailstrict, handler)264 }265266 fn named_iter(267 &self,268 ctx: Context,269 tailstrict: bool,270 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,271 ) -> Result<()> {272 (*self).named_iter(ctx, tailstrict, handler)273 }274275 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {276 (*self).named_names(handler)277 }278}279280281282283284285286287288pub fn parse_function_call(289 ctx: Context,290 body_ctx: Context,291 params: &ParamsDesc,292 args: &dyn ArgsLike,293 tailstrict: bool,294) -> Result<Context> {295 let mut passed_args = GcHashMap::with_capacity(params.len());296 if args.unnamed_len() > params.len() {297 throw!(TooManyArgsFunctionHas(params.len()))298 }299300 let mut filled_args = 0;301302 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {303 let name = params[id].0.clone();304 passed_args.insert(name, arg);305 filled_args += 1;306 Ok(())307 })?;308309 args.named_iter(ctx, tailstrict, &mut |name, value| {310 311 if !params.iter().any(|p| &p.0 == name) {312 throw!(UnknownFunctionParameter((name as &str).to_owned()));313 }314 if passed_args.insert(name.clone(), value).is_some() {315 throw!(BindingParameterASecondTime(name.clone()));316 }317 filled_args += 1;318 Ok(())319 })?;320321 if filled_args < params.len() {322 323 324 let future_context = Context::new_future();325 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);326327 for param in params.iter().filter(|p| p.1.is_some()) {328 if passed_args.contains_key(¶m.0.clone()) {329 continue;330 }331 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {332 future_context: future_context.clone(),333 name: param.0.clone(),334 value: param.1.clone().unwrap(),335 })));336337 defaults.insert(338 param.0.clone(),339 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {340 future_context: future_context.clone(),341 name: param.0.clone(),342 value: param.1.clone().unwrap(),343 }))),344 );345 filled_args += 1;346 }347348 349 if filled_args != params.len() {350 for param in params.iter().skip(args.unnamed_len()) {351 let mut found = false;352 args.named_names(&mut |name| {353 if name == ¶m.0 {354 found = true;355 }356 });357 if !found {358 throw!(FunctionParameterNotBoundInCall(param.0.clone()));359 }360 }361 unreachable!();362 }363364 Ok(body_ctx365 .extend(passed_args, None, None, None)366 .extend_bound(defaults)367 .into_future(future_context))368 } else {369 let body_ctx = body_ctx.extend(passed_args, None, None, None);370 Ok(body_ctx)371 }372}373374type BuiltinParamName = Cow<'static, str>;375376#[derive(Clone, Trace)]377pub struct BuiltinParam {378 pub name: BuiltinParamName,379 pub has_default: bool,380}381382383pub trait Builtin: Trace {384 fn name(&self) -> &str;385 fn params(&self) -> &[BuiltinParam];386 fn call(387 &self,388 context: Context,389 loc: Option<&ExprLocation>,390 args: &dyn ArgsLike,391 ) -> Result<Val>;392}393394pub trait StaticBuiltin: Builtin + Send + Sync395where396 Self: 'static,397{398 399 400}401402403404405406407408409pub fn parse_builtin_call(410 ctx: Context,411 params: &[BuiltinParam],412 args: &dyn ArgsLike,413 tailstrict: bool,414) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {415 let mut passed_args = GcHashMap::with_capacity(params.len());416 if args.unnamed_len() > params.len() {417 throw!(TooManyArgsFunctionHas(params.len()))418 }419420 let mut filled_args = 0;421422 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {423 let name = params[id].name.clone();424 passed_args.insert(name, arg);425 filled_args += 1;426 Ok(())427 })?;428429 args.named_iter(ctx, tailstrict, &mut |name, arg| {430 431 let p = params432 .iter()433 .find(|p| p.name == name as &str)434 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;435 if passed_args.insert(p.name.clone(), arg).is_some() {436 throw!(BindingParameterASecondTime(name.clone()));437 }438 filled_args += 1;439 Ok(())440 })?;441442 if filled_args < params.len() {443 for param in params.iter().filter(|p| p.has_default) {444 if passed_args.contains_key(¶m.name) {445 continue;446 }447 filled_args += 1;448 }449450 451 if filled_args != params.len() {452 for param in params.iter().skip(args.unnamed_len()) {453 let mut found = false;454 args.named_names(&mut |name| {455 if name as &str == ¶m.name as &str {456 found = true;457 }458 });459 if !found {460 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));461 }462 }463 unreachable!();464 }465 }466 Ok(passed_args)467}468469470471pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {472 let ctx = Context::new_future();473474 let mut bindings = GcHashMap::new();475476 #[derive(Trace)]477 struct DependsOnUnbound(IStr);478 impl LazyValValue for DependsOnUnbound {479 fn get(self: Box<Self>) -> Result<Val> {480 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())481 }482 }483484 for param in params.iter() {485 if let Some(v) = ¶m.1 {486 bindings.insert(487 param.0.clone(),488 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {489 future_context: ctx.clone(),490 name: param.0.clone(),491 value: v.clone(),492 }))),493 );494 } else {495 bindings.insert(496 param.0.clone(),497 LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),498 );499 }500 }501502 body_ctx.extend(bindings, None, None, None).into_future(ctx)503}