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;11use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};12use std::{borrow::Cow, collections::HashMap, convert::TryFrom};1314#[derive(Trace)]15struct EvaluateLazyVal {16 context: Context,17 expr: LocExpr,18}19impl LazyValValue for EvaluateLazyVal {20 fn get(self: Box<Self>) -> Result<Val> {21 evaluate(self.context, &self.expr)22 }23}2425pub trait ArgLike {26 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;27}28impl ArgLike for &LocExpr {29 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {30 Ok(if tailstrict {31 LazyVal::new_resolved(evaluate(ctx, self)?)32 } else {33 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {34 context: ctx,35 expr: (*self).clone(),36 })))37 })38 }39}40impl<T> ArgLike for T41where42 T: Typed + Clone,43 Val: TryFrom<T, Error = LocError>,44{45 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {46 let val: Val = Val::try_from(self.clone())?;47 Ok(LazyVal::new_resolved(val))48 }49}50pub enum TlaArg {51 String(IStr),52 Code(LocExpr),53 Val(Val),54}55impl ArgLike for TlaArg {56 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {57 match self {58 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),59 TlaArg::Code(code) => Ok(if tailstrict {60 LazyVal::new_resolved(evaluate(ctx, code)?)61 } else {62 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {63 context: ctx,64 expr: code.clone(),65 })))66 }),67 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),68 }69 }70}7172pub trait ArgsLike {73 fn unnamed_len(&self) -> usize;74 fn unnamed_iter(75 &self,76 ctx: Context,77 tailstrict: bool,78 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,79 ) -> Result<()>;80 fn named_iter(81 &self,82 ctx: Context,83 tailstrict: bool,84 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,85 ) -> Result<()>;86 fn named_names(&self, handler: &mut dyn FnMut(&IStr));87}8889impl ArgsLike for ArgsDesc {90 fn unnamed_len(&self) -> usize {91 self.unnamed.len()92 }9394 fn unnamed_iter(95 &self,96 ctx: Context,97 tailstrict: bool,98 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,99 ) -> Result<()> {100 for (id, arg) in self.unnamed.iter().enumerate() {101 handler(102 id,103 if tailstrict {104 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)105 } else {106 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {107 context: ctx.clone(),108 expr: arg.clone(),109 })))110 },111 )?;112 }113 Ok(())114 }115116 fn named_iter(117 &self,118 ctx: Context,119 tailstrict: bool,120 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,121 ) -> Result<()> {122 for (name, arg) in self.named.iter() {123 handler(124 name,125 if tailstrict {126 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)127 } else {128 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {129 context: ctx.clone(),130 expr: arg.clone(),131 })))132 },133 )?;134 }135 Ok(())136 }137138 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {139 for (name, _) in self.named.iter() {140 handler(name)141 }142 }143}144145impl<A: ArgLike> ArgsLike for [(IStr, A)] {146 fn unnamed_len(&self) -> usize {147 0148 }149150 fn unnamed_iter(151 &self,152 _ctx: Context,153 _tailstrict: bool,154 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,155 ) -> Result<()> {156 Ok(())157 }158159 fn named_iter(160 &self,161 ctx: Context,162 tailstrict: bool,163 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,164 ) -> Result<()> {165 for (name, val) in self.iter() {166 handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;167 }168 Ok(())169 }170171 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {172 for (name, _) in self.iter() {173 handler(name);174 }175 }176}177178impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {179 fn unnamed_len(&self) -> usize {180 0181 }182183 fn unnamed_iter(184 &self,185 _ctx: Context,186 _tailstrict: bool,187 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,188 ) -> Result<()> {189 Ok(())190 }191192 fn named_iter(193 &self,194 ctx: Context,195 tailstrict: bool,196 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,197 ) -> Result<()> {198 for (name, value) in self.iter() {199 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;200 }201 Ok(())202 }203204 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {205 for (name, _) in self.iter() {206 handler(name);207 }208 }209}210211impl<A: ArgLike> ArgsLike for [A] {212 fn unnamed_len(&self) -> usize {213 self.len()214 }215216 fn unnamed_iter(217 &self,218 ctx: Context,219 tailstrict: bool,220 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,221 ) -> Result<()> {222 for (i, arg) in self.iter().enumerate() {223 handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;224 }225 Ok(())226 }227228 fn named_iter(229 &self,230 _ctx: Context,231 _tailstrict: bool,232 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,233 ) -> Result<()> {234 Ok(())235 }236237 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}238}239impl<A: ArgLike> ArgsLike for &[A] {240 fn unnamed_len(&self) -> usize {241 (*self).unnamed_len()242 }243244 fn unnamed_iter(245 &self,246 ctx: Context,247 tailstrict: bool,248 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,249 ) -> Result<()> {250 (*self).unnamed_iter(ctx, tailstrict, handler)251 }252253 fn named_iter(254 &self,255 ctx: Context,256 tailstrict: bool,257 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,258 ) -> Result<()> {259 (*self).named_iter(ctx, tailstrict, handler)260 }261262 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {263 (*self).named_names(handler)264 }265}266267268269270271272273274275pub fn parse_function_call(276 ctx: Context,277 body_ctx: Context,278 params: &ParamsDesc,279 args: &dyn ArgsLike,280 tailstrict: bool,281) -> Result<Context> {282 let mut passed_args = GcHashMap::with_capacity(params.len());283 if args.unnamed_len() > params.len() {284 throw!(TooManyArgsFunctionHas(params.len()))285 }286287 let mut filled_args = 0;288289 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {290 let name = params[id].0.clone();291 passed_args.insert(name, arg);292 filled_args += 1;293 Ok(())294 })?;295296 args.named_iter(ctx, tailstrict, &mut |name, value| {297 298 if !params.iter().any(|p| &p.0 == name) {299 throw!(UnknownFunctionParameter((name as &str).to_owned()));300 }301 if passed_args.insert(name.clone(), value).is_some() {302 throw!(BindingParameterASecondTime(name.clone()));303 }304 filled_args += 1;305 Ok(())306 })?;307308 if filled_args < params.len() {309 310 311 let future_context = FutureWrapper::<Context>::new();312 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);313314 for param in params.iter().filter(|p| p.1.is_some()) {315 if passed_args.contains_key(¶m.0.clone()) {316 continue;317 }318 #[derive(Trace)]319 struct LazyNamedBinding {320 future_context: FutureWrapper<Context>,321 name: IStr,322 value: LocExpr,323 }324 impl LazyValValue for LazyNamedBinding {325 fn get(self: Box<Self>) -> Result<Val> {326 evaluate_named(self.future_context.unwrap(), &self.value, self.name)327 }328 }329 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {330 future_context: future_context.clone(),331 name: param.0.clone(),332 value: param.1.clone().unwrap(),333 })));334335 defaults.insert(336 param.0.clone(),337 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {338 future_context: future_context.clone(),339 name: param.0.clone(),340 value: param.1.clone().unwrap(),341 }))),342 );343 filled_args += 1;344 }345346 347 if filled_args != params.len() {348 for param in params.iter().skip(args.unnamed_len()) {349 let mut found = false;350 args.named_names(&mut |name| {351 if name == ¶m.0 {352 found = true;353 }354 });355 if !found {356 throw!(FunctionParameterNotBoundInCall(param.0.clone()));357 }358 }359 unreachable!();360 }361362 Ok(body_ctx363 .extend(passed_args, None, None, None)364 .extend_bound(defaults)365 .into_future(future_context))366 } else {367 let body_ctx = body_ctx.extend(passed_args, None, None, None);368 Ok(body_ctx)369 }370}371372type BuiltinParamName = Cow<'static, str>;373374#[derive(Clone, Trace)]375pub struct BuiltinParam {376 pub name: BuiltinParamName,377 pub has_default: bool,378}379380pub trait Builtin: Trace {381 fn name(&self) -> &str;382 fn params(&self) -> &[BuiltinParam];383 fn call(384 &self,385 context: Context,386 loc: Option<&ExprLocation>,387 args: &dyn ArgsLike,388 ) -> Result<Val>;389}390391pub trait StaticBuiltin: Builtin + Send + Sync392where393 Self: 'static,394{395 396 397}398399400401402403404405406pub fn parse_builtin_call(407 ctx: Context,408 params: &[BuiltinParam],409 args: &dyn ArgsLike,410 tailstrict: bool,411) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {412 let mut passed_args = GcHashMap::with_capacity(params.len());413 if args.unnamed_len() > params.len() {414 throw!(TooManyArgsFunctionHas(params.len()))415 }416417 let mut filled_args = 0;418419 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {420 let name = params[id].name.clone();421 passed_args.insert(name, arg);422 filled_args += 1;423 Ok(())424 })?;425426 args.named_iter(ctx, tailstrict, &mut |name, arg| {427 428 let p = params429 .iter()430 .find(|p| p.name == name as &str)431 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;432 if passed_args.insert(p.name.clone(), arg).is_some() {433 throw!(BindingParameterASecondTime(name.clone()));434 }435 filled_args += 1;436 Ok(())437 })?;438439 if filled_args < params.len() {440 for param in params.iter().filter(|p| p.has_default) {441 if passed_args.contains_key(¶m.name) {442 continue;443 }444 filled_args += 1;445 }446447 448 if filled_args != params.len() {449 for param in params.iter().skip(args.unnamed_len()) {450 let mut found = false;451 args.named_names(&mut |name| {452 if name as &str == ¶m.name as &str {453 found = true;454 }455 });456 if !found {457 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));458 }459 }460 unreachable!();461 }462 }463 Ok(passed_args)464}