1use std::{borrow::Cow, collections::HashMap, convert::TryFrom};23use gcmodule::Trace;4use jrsonnet_interner::IStr;5pub use jrsonnet_macros::builtin;6use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};78use crate::{9 error::{Error::*, LocError},10 evaluate, evaluate_named,11 gc::TraceBox,12 throw,13 typed::Typed,14 val::LazyValValue,15 Context, FutureWrapper, GcHashMap, LazyVal, Result, Val,16};1718#[derive(Clone, Copy)]19pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);20impl<'l> CallLocation<'l> {21 pub const fn new(loc: &'l ExprLocation) -> Self {22 Self(Some(loc))23 }24}25impl CallLocation<'static> {26 pub const fn native() -> Self {27 Self(None)28 }29}3031#[derive(Trace)]32struct EvaluateLazyVal {33 context: Context,34 expr: LocExpr,35}36impl LazyValValue for EvaluateLazyVal {37 fn get(self: Box<Self>) -> Result<Val> {38 evaluate(self.context, &self.expr)39 }40}4142#[derive(Trace)]43struct EvaluateNamedLazyVal {44 future_context: FutureWrapper<Context>,45 name: IStr,46 value: LocExpr,47}48impl LazyValValue for EvaluateNamedLazyVal {49 fn get(self: Box<Self>) -> Result<Val> {50 evaluate_named(self.future_context.unwrap(), &self.value, self.name)51 }52}5354pub trait ArgLike {55 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;56}57impl ArgLike for &LocExpr {58 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {59 Ok(if tailstrict {60 LazyVal::new_resolved(evaluate(ctx, self)?)61 } else {62 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {63 context: ctx,64 expr: (*self).clone(),65 })))66 })67 }68}69impl<T> ArgLike for T70where71 T: Typed + Clone,72 Val: TryFrom<T, Error = LocError>,73{74 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {75 let val: Val = Val::try_from(self.clone())?;76 Ok(LazyVal::new_resolved(val))77 }78}79pub enum TlaArg {80 String(IStr),81 Code(LocExpr),82 Val(Val),83}84impl ArgLike for TlaArg {85 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {86 match self {87 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),88 TlaArg::Code(code) => Ok(if tailstrict {89 LazyVal::new_resolved(evaluate(ctx, code)?)90 } else {91 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {92 context: ctx,93 expr: code.clone(),94 })))95 }),96 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),97 }98 }99}100101pub trait ArgsLike {102 fn unnamed_len(&self) -> usize;103 fn unnamed_iter(104 &self,105 ctx: Context,106 tailstrict: bool,107 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,108 ) -> Result<()>;109 fn named_iter(110 &self,111 ctx: Context,112 tailstrict: bool,113 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,114 ) -> Result<()>;115 fn named_names(&self, handler: &mut dyn FnMut(&IStr));116}117118impl ArgsLike for ArgsDesc {119 fn unnamed_len(&self) -> usize {120 self.unnamed.len()121 }122123 fn unnamed_iter(124 &self,125 ctx: Context,126 tailstrict: bool,127 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,128 ) -> Result<()> {129 for (id, arg) in self.unnamed.iter().enumerate() {130 handler(131 id,132 if tailstrict {133 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)134 } else {135 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {136 context: ctx.clone(),137 expr: arg.clone(),138 })))139 },140 )?;141 }142 Ok(())143 }144145 fn named_iter(146 &self,147 ctx: Context,148 tailstrict: bool,149 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,150 ) -> Result<()> {151 for (name, arg) in self.named.iter() {152 handler(153 name,154 if tailstrict {155 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)156 } else {157 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {158 context: ctx.clone(),159 expr: arg.clone(),160 })))161 },162 )?;163 }164 Ok(())165 }166167 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {168 for (name, _) in self.named.iter() {169 handler(name)170 }171 }172}173174impl<A: ArgLike> ArgsLike for [(IStr, A)] {175 fn unnamed_len(&self) -> usize {176 0177 }178179 fn unnamed_iter(180 &self,181 _ctx: Context,182 _tailstrict: bool,183 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,184 ) -> Result<()> {185 Ok(())186 }187188 fn named_iter(189 &self,190 ctx: Context,191 tailstrict: bool,192 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,193 ) -> Result<()> {194 for (name, val) in self.iter() {195 handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;196 }197 Ok(())198 }199200 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {201 for (name, _) in self.iter() {202 handler(name);203 }204 }205}206207impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {208 fn unnamed_len(&self) -> usize {209 0210 }211212 fn unnamed_iter(213 &self,214 _ctx: Context,215 _tailstrict: bool,216 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,217 ) -> Result<()> {218 Ok(())219 }220221 fn named_iter(222 &self,223 ctx: Context,224 tailstrict: bool,225 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,226 ) -> Result<()> {227 for (name, value) in self.iter() {228 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;229 }230 Ok(())231 }232233 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {234 for (name, _) in self.iter() {235 handler(name);236 }237 }238}239240impl<A: ArgLike> ArgsLike for [A] {241 fn unnamed_len(&self) -> usize {242 self.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 for (i, arg) in self.iter().enumerate() {252 handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;253 }254 Ok(())255 }256257 fn named_iter(258 &self,259 _ctx: Context,260 _tailstrict: bool,261 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,262 ) -> Result<()> {263 Ok(())264 }265266 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}267}268impl<A: ArgLike> ArgsLike for &[A] {269 fn unnamed_len(&self) -> usize {270 (*self).unnamed_len()271 }272273 fn unnamed_iter(274 &self,275 ctx: Context,276 tailstrict: bool,277 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,278 ) -> Result<()> {279 (*self).unnamed_iter(ctx, tailstrict, handler)280 }281282 fn named_iter(283 &self,284 ctx: Context,285 tailstrict: bool,286 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,287 ) -> Result<()> {288 (*self).named_iter(ctx, tailstrict, handler)289 }290291 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {292 (*self).named_names(handler)293 }294}295296297298299300301302303304pub fn parse_function_call(305 ctx: Context,306 body_ctx: Context,307 params: &ParamsDesc,308 args: &dyn ArgsLike,309 tailstrict: bool,310) -> Result<Context> {311 let mut passed_args = GcHashMap::with_capacity(params.len());312 if args.unnamed_len() > params.len() {313 throw!(TooManyArgsFunctionHas(params.len()))314 }315316 let mut filled_args = 0;317318 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {319 let name = params[id].0.clone();320 passed_args.insert(name, arg);321 filled_args += 1;322 Ok(())323 })?;324325 args.named_iter(ctx, tailstrict, &mut |name, value| {326 327 if !params.iter().any(|p| &p.0 == name) {328 throw!(UnknownFunctionParameter((name as &str).to_owned()));329 }330 if passed_args.insert(name.clone(), value).is_some() {331 throw!(BindingParameterASecondTime(name.clone()));332 }333 filled_args += 1;334 Ok(())335 })?;336337 if filled_args < params.len() {338 339 340 let future_context = Context::new_future();341 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);342343 for param in params.iter().filter(|p| p.1.is_some()) {344 if passed_args.contains_key(¶m.0.clone()) {345 continue;346 }347 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {348 future_context: future_context.clone(),349 name: param.0.clone(),350 value: param.1.clone().unwrap(),351 })));352353 defaults.insert(354 param.0.clone(),355 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {356 future_context: future_context.clone(),357 name: param.0.clone(),358 value: param.1.clone().unwrap(),359 }))),360 );361 filled_args += 1;362 }363364 365 if filled_args != params.len() {366 for param in params.iter().skip(args.unnamed_len()) {367 let mut found = false;368 args.named_names(&mut |name| {369 if name == ¶m.0 {370 found = true;371 }372 });373 if !found {374 throw!(FunctionParameterNotBoundInCall(param.0.clone()));375 }376 }377 unreachable!();378 }379380 Ok(body_ctx381 .extend(passed_args, None, None, None)382 .extend_bound(defaults)383 .into_future(future_context))384 } else {385 let body_ctx = body_ctx.extend(passed_args, None, None, None);386 Ok(body_ctx)387 }388}389390type BuiltinParamName = Cow<'static, str>;391392#[derive(Clone, Trace)]393pub struct BuiltinParam {394 pub name: BuiltinParamName,395 pub has_default: bool,396}397398399pub trait Builtin: Trace {400 fn name(&self) -> &str;401 fn params(&self) -> &[BuiltinParam];402 fn call(&self, context: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;403}404405pub trait StaticBuiltin: Builtin + Send + Sync406where407 Self: 'static,408{409 410 411}412413414415416417418419420pub fn parse_builtin_call(421 ctx: Context,422 params: &[BuiltinParam],423 args: &dyn ArgsLike,424 tailstrict: bool,425) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {426 let mut passed_args = GcHashMap::with_capacity(params.len());427 if args.unnamed_len() > params.len() {428 throw!(TooManyArgsFunctionHas(params.len()))429 }430431 let mut filled_args = 0;432433 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {434 let name = params[id].name.clone();435 passed_args.insert(name, arg);436 filled_args += 1;437 Ok(())438 })?;439440 args.named_iter(ctx, tailstrict, &mut |name, arg| {441 442 let p = params443 .iter()444 .find(|p| p.name == name as &str)445 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;446 if passed_args.insert(p.name.clone(), arg).is_some() {447 throw!(BindingParameterASecondTime(name.clone()));448 }449 filled_args += 1;450 Ok(())451 })?;452453 if filled_args < params.len() {454 for param in params.iter().filter(|p| p.has_default) {455 if passed_args.contains_key(¶m.name) {456 continue;457 }458 filled_args += 1;459 }460461 462 if filled_args != params.len() {463 for param in params.iter().skip(args.unnamed_len()) {464 let mut found = false;465 args.named_names(&mut |name| {466 if name as &str == ¶m.name as &str {467 found = true;468 }469 });470 if !found {471 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));472 }473 }474 unreachable!();475 }476 }477 Ok(passed_args)478}479480481482pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {483 let ctx = Context::new_future();484485 let mut bindings = GcHashMap::new();486487 #[derive(Trace)]488 struct DependsOnUnbound(IStr);489 impl LazyValValue for DependsOnUnbound {490 fn get(self: Box<Self>) -> Result<Val> {491 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())492 }493 }494495 for param in params.iter() {496 if let Some(v) = ¶m.1 {497 bindings.insert(498 param.0.clone(),499 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {500 future_context: ctx.clone(),501 name: param.0.clone(),502 value: v.clone(),503 }))),504 );505 } else {506 bindings.insert(507 param.0.clone(),508 LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),509 );510 }511 }512513 body_ctx.extend(bindings, None, None, None).into_future(ctx)514}