1use std::{borrow::Cow, collections::HashMap};23use gcmodule::Trace;4use jrsonnet_interner::IStr;5pub use jrsonnet_macros::builtin;6use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};78use crate::{9 error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw, typed::Typed,10 val::LazyValValue, Context, FutureWrapper, GcHashMap, LazyVal, Result, State, Val,11};1213#[derive(Clone, Copy)]14pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);15impl<'l> CallLocation<'l> {16 pub const fn new(loc: &'l ExprLocation) -> Self {17 Self(Some(loc))18 }19}20impl CallLocation<'static> {21 pub const fn native() -> Self {22 Self(None)23 }24}2526#[derive(Trace)]27struct EvaluateLazyVal {28 ctx: Context,29 expr: LocExpr,30}31impl LazyValValue for EvaluateLazyVal {32 fn get(self: Box<Self>, s: State) -> Result<Val> {33 evaluate(s, self.ctx, &self.expr)34 }35}3637#[derive(Trace)]38struct EvaluateNamedLazyVal {39 ctx: FutureWrapper<Context>,40 name: IStr,41 value: LocExpr,42}43impl LazyValValue for EvaluateNamedLazyVal {44 fn get(self: Box<Self>, s: State) -> Result<Val> {45 evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)46 }47}48pub trait ArgLike {49 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal>;50}51impl ArgLike for &LocExpr {52 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {53 Ok(if tailstrict {54 LazyVal::new_resolved(evaluate(s, ctx, self)?)55 } else {56 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {57 ctx,58 expr: (*self).clone(),59 })))60 })61 }62}63impl<T> ArgLike for T64where65 T: Typed + Clone,66{67 fn evaluate_arg(&self, s: State, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {68 let val = T::into_untyped(self.clone(), s)?;69 Ok(LazyVal::new_resolved(val))70 }71}72pub enum TlaArg {73 String(IStr),74 Code(LocExpr),75 Val(Val),76}77impl ArgLike for TlaArg {78 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {79 match self {80 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),81 TlaArg::Code(code) => Ok(if tailstrict {82 LazyVal::new_resolved(evaluate(s, ctx, code)?)83 } else {84 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {85 ctx,86 expr: code.clone(),87 })))88 }),89 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),90 }91 }92}9394pub trait ArgsLike {95 fn unnamed_len(&self) -> usize;96 fn unnamed_iter(97 &self,98 s: State,99 ctx: Context,100 tailstrict: bool,101 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,102 ) -> Result<()>;103 fn named_iter(104 &self,105 s: State,106 ctx: Context,107 tailstrict: bool,108 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,109 ) -> Result<()>;110 fn named_names(&self, handler: &mut dyn FnMut(&IStr));111}112113impl ArgsLike for ArgsDesc {114 fn unnamed_len(&self) -> usize {115 self.unnamed.len()116 }117118 fn unnamed_iter(119 &self,120 s: State,121 ctx: Context,122 tailstrict: bool,123 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,124 ) -> Result<()> {125 for (id, arg) in self.unnamed.iter().enumerate() {126 handler(127 id,128 if tailstrict {129 LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)130 } else {131 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {132 ctx: ctx.clone(),133 expr: arg.clone(),134 })))135 },136 )?;137 }138 Ok(())139 }140141 fn named_iter(142 &self,143 s: State,144 ctx: Context,145 tailstrict: bool,146 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,147 ) -> Result<()> {148 for (name, arg) in &self.named {149 handler(150 name,151 if tailstrict {152 LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)153 } else {154 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {155 ctx: ctx.clone(),156 expr: arg.clone(),157 })))158 },159 )?;160 }161 Ok(())162 }163164 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {165 for (name, _) in &self.named {166 handler(name);167 }168 }169}170171impl ArgsLike for [(); 0] {172 fn unnamed_len(&self) -> usize {173 0174 }175176 fn unnamed_iter(177 &self,178 _s: State,179 _ctx: Context,180 _tailstrict: bool,181 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,182 ) -> Result<()> {183 Ok(())184 }185186 fn named_iter(187 &self,188 _s: State,189 _ctx: Context,190 _tailstrict: bool,191 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,192 ) -> Result<()> {193 Ok(())194 }195196 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}197}198199impl<A: ArgLike> ArgsLike for [(IStr, A)] {200 fn unnamed_len(&self) -> usize {201 0202 }203204 fn unnamed_iter(205 &self,206 _s: State,207 _ctx: Context,208 _tailstrict: bool,209 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,210 ) -> Result<()> {211 Ok(())212 }213214 fn named_iter(215 &self,216 s: State,217 ctx: Context,218 tailstrict: bool,219 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,220 ) -> Result<()> {221 for (name, val) in self.iter() {222 handler(name, val.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;223 }224 Ok(())225 }226227 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {228 for (name, _) in self.iter() {229 handler(name);230 }231 }232}233234impl<A: ArgLike, S> ArgsLike for HashMap<IStr, A, S> {235 fn unnamed_len(&self) -> usize {236 0237 }238239 fn unnamed_iter(240 &self,241 _s: State,242 _ctx: Context,243 _tailstrict: bool,244 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,245 ) -> Result<()> {246 Ok(())247 }248249 fn named_iter(250 &self,251 s: State,252 ctx: Context,253 tailstrict: bool,254 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,255 ) -> Result<()> {256 for (name, value) in self.iter() {257 handler(258 name,259 value.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?,260 )?;261 }262 Ok(())263 }264265 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {266 for (name, _) in self.iter() {267 handler(name);268 }269 }270}271272impl<A: ArgLike> ArgsLike for [A] {273 fn unnamed_len(&self) -> usize {274 self.len()275 }276277 fn unnamed_iter(278 &self,279 s: State,280 ctx: Context,281 tailstrict: bool,282 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,283 ) -> Result<()> {284 for (i, arg) in self.iter().enumerate() {285 handler(i, arg.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;286 }287 Ok(())288 }289290 fn named_iter(291 &self,292 _s: State,293 _ctx: Context,294 _tailstrict: bool,295 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,296 ) -> Result<()> {297 Ok(())298 }299300 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}301}302impl<A: ArgLike> ArgsLike for &[A] {303 fn unnamed_len(&self) -> usize {304 (*self).unnamed_len()305 }306307 fn unnamed_iter(308 &self,309 s: State,310 ctx: Context,311 tailstrict: bool,312 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,313 ) -> Result<()> {314 (*self).unnamed_iter(s, ctx, tailstrict, handler)315 }316317 fn named_iter(318 &self,319 s: State,320 ctx: Context,321 tailstrict: bool,322 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,323 ) -> Result<()> {324 (*self).named_iter(s, ctx, tailstrict, handler)325 }326327 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {328 (*self).named_names(handler);329 }330}331332333334335336337338339340pub fn parse_function_call(341 s: State,342 ctx: Context,343 body_ctx: Context,344 params: &ParamsDesc,345 args: &dyn ArgsLike,346 tailstrict: bool,347) -> Result<Context> {348 let mut passed_args = GcHashMap::with_capacity(params.len());349 if args.unnamed_len() > params.len() {350 throw!(TooManyArgsFunctionHas(params.len()))351 }352353 let mut filled_args = 0;354355 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {356 let name = params[id].0.clone();357 passed_args.insert(name, arg);358 filled_args += 1;359 Ok(())360 })?;361362 args.named_iter(s, ctx, tailstrict, &mut |name, value| {363 364 if !params.iter().any(|p| &p.0 == name) {365 throw!(UnknownFunctionParameter((name as &str).to_owned()));366 }367 if passed_args.insert(name.clone(), value).is_some() {368 throw!(BindingParameterASecondTime(name.clone()));369 }370 filled_args += 1;371 Ok(())372 })?;373374 if filled_args < params.len() {375 376 377 let fctx = Context::new_future();378 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);379380 for param in params.iter().filter(|p| p.1.is_some()) {381 if passed_args.contains_key(¶m.0.clone()) {382 continue;383 }384385 defaults.insert(386 param.0.clone(),387 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {388 ctx: fctx.clone(),389 name: param.0.clone(),390 value: param.1.clone().expect("default exists"),391 }))),392 );393 filled_args += 1;394 }395396 397 if filled_args != params.len() {398 for param in params.iter().skip(args.unnamed_len()) {399 let mut found = false;400 args.named_names(&mut |name| {401 if name == ¶m.0 {402 found = true;403 }404 });405 if !found {406 throw!(FunctionParameterNotBoundInCall(param.0.clone()));407 }408 }409 unreachable!();410 }411412 Ok(body_ctx413 .extend(passed_args, None, None, None)414 .extend_bound(defaults)415 .into_future(fctx))416 } else {417 let body_ctx = body_ctx.extend(passed_args, None, None, None);418 Ok(body_ctx)419 }420}421422type BuiltinParamName = Cow<'static, str>;423424#[derive(Clone, Trace)]425pub struct BuiltinParam {426 pub name: BuiltinParamName,427 pub has_default: bool,428}429430431pub trait Builtin: Trace {432 fn name(&self) -> &str;433 fn params(&self) -> &[BuiltinParam];434 fn call(&self, s: State, ctx: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;435}436437pub trait StaticBuiltin: Builtin + Send + Sync438where439 Self: 'static,440{441 442 443}444445446447448449450451452pub fn parse_builtin_call(453 s: State,454 ctx: Context,455 params: &[BuiltinParam],456 args: &dyn ArgsLike,457 tailstrict: bool,458) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {459 let mut passed_args = GcHashMap::with_capacity(params.len());460 if args.unnamed_len() > params.len() {461 throw!(TooManyArgsFunctionHas(params.len()))462 }463464 let mut filled_args = 0;465466 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {467 let name = params[id].name.clone();468 passed_args.insert(name, arg);469 filled_args += 1;470 Ok(())471 })?;472473 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {474 475 let p = params476 .iter()477 .find(|p| p.name == name as &str)478 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;479 if passed_args.insert(p.name.clone(), arg).is_some() {480 throw!(BindingParameterASecondTime(name.clone()));481 }482 filled_args += 1;483 Ok(())484 })?;485486 if filled_args < params.len() {487 for param in params.iter().filter(|p| p.has_default) {488 if passed_args.contains_key(¶m.name) {489 continue;490 }491 filled_args += 1;492 }493494 495 if filled_args != params.len() {496 for param in params.iter().skip(args.unnamed_len()) {497 let mut found = false;498 args.named_names(&mut |name| {499 if name as &str == ¶m.name as &str {500 found = true;501 }502 });503 if !found {504 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));505 }506 }507 unreachable!();508 }509 }510 Ok(passed_args)511}512513514515pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {516 #[derive(Trace)]517 struct DependsOnUnbound(IStr);518 impl LazyValValue for DependsOnUnbound {519 fn get(self: Box<Self>, _: State) -> Result<Val> {520 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())521 }522 }523524 let fctx = Context::new_future();525526 let mut bindings = GcHashMap::new();527528 for param in params.iter() {529 if let Some(v) = ¶m.1 {530 bindings.insert(531 param.0.clone(),532 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {533 ctx: fctx.clone(),534 name: param.0.clone(),535 value: v.clone(),536 }))),537 );538 } else {539 bindings.insert(540 param.0.clone(),541 LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),542 );543 }544 }545546 body_ctx547 .extend(bindings, None, None, None)548 .into_future(fctx)549}