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}4849pub trait ArgLike {50 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal>;51}52impl ArgLike for &LocExpr {53 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {54 Ok(if tailstrict {55 LazyVal::new_resolved(evaluate(s, ctx, self)?)56 } else {57 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {58 ctx,59 expr: (*self).clone(),60 })))61 })62 }63}64impl<T> ArgLike for T65where66 T: Typed + Clone,67{68 fn evaluate_arg(&self, s: State, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {69 let val = T::into_untyped(self.clone(), s)?;70 Ok(LazyVal::new_resolved(val))71 }72}73pub enum TlaArg {74 String(IStr),75 Code(LocExpr),76 Val(Val),77}78impl ArgLike for TlaArg {79 fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {80 match self {81 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),82 TlaArg::Code(code) => Ok(if tailstrict {83 LazyVal::new_resolved(evaluate(s, ctx, code)?)84 } else {85 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {86 ctx,87 expr: code.clone(),88 })))89 }),90 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),91 }92 }93}9495pub trait ArgsLike {96 fn unnamed_len(&self) -> usize;97 fn unnamed_iter(98 &self,99 s: State,100 ctx: Context,101 tailstrict: bool,102 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,103 ) -> Result<()>;104 fn named_iter(105 &self,106 s: State,107 ctx: Context,108 tailstrict: bool,109 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,110 ) -> Result<()>;111 fn named_names(&self, handler: &mut dyn FnMut(&IStr));112}113114impl ArgsLike for ArgsDesc {115 fn unnamed_len(&self) -> usize {116 self.unnamed.len()117 }118119 fn unnamed_iter(120 &self,121 s: State,122 ctx: Context,123 tailstrict: bool,124 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,125 ) -> Result<()> {126 for (id, arg) in self.unnamed.iter().enumerate() {127 handler(128 id,129 if tailstrict {130 LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)131 } else {132 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {133 ctx: ctx.clone(),134 expr: arg.clone(),135 })))136 },137 )?;138 }139 Ok(())140 }141142 fn named_iter(143 &self,144 s: State,145 ctx: Context,146 tailstrict: bool,147 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,148 ) -> Result<()> {149 for (name, arg) in self.named.iter() {150 handler(151 name,152 if tailstrict {153 LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)154 } else {155 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {156 ctx: ctx.clone(),157 expr: arg.clone(),158 })))159 },160 )?;161 }162 Ok(())163 }164165 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {166 for (name, _) in self.named.iter() {167 handler(name)168 }169 }170}171172impl<A: ArgLike> ArgsLike for [(IStr, A)] {173 fn unnamed_len(&self) -> usize {174 0175 }176177 fn unnamed_iter(178 &self,179 _s: State,180 _ctx: Context,181 _tailstrict: bool,182 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,183 ) -> Result<()> {184 Ok(())185 }186187 fn named_iter(188 &self,189 s: State,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(s.clone(), 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 _s: State,215 _ctx: Context,216 _tailstrict: bool,217 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,218 ) -> Result<()> {219 Ok(())220 }221222 fn named_iter(223 &self,224 s: State,225 ctx: Context,226 tailstrict: bool,227 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,228 ) -> Result<()> {229 for (name, value) in self.iter() {230 handler(231 name,232 value.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?,233 )?;234 }235 Ok(())236 }237238 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {239 for (name, _) in self.iter() {240 handler(name);241 }242 }243}244245impl<A: ArgLike> ArgsLike for [A] {246 fn unnamed_len(&self) -> usize {247 self.len()248 }249250 fn unnamed_iter(251 &self,252 s: State,253 ctx: Context,254 tailstrict: bool,255 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,256 ) -> Result<()> {257 for (i, arg) in self.iter().enumerate() {258 handler(i, arg.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;259 }260 Ok(())261 }262263 fn named_iter(264 &self,265 _s: State,266 _ctx: Context,267 _tailstrict: bool,268 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,269 ) -> Result<()> {270 Ok(())271 }272273 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}274}275impl<A: ArgLike> ArgsLike for &[A] {276 fn unnamed_len(&self) -> usize {277 (*self).unnamed_len()278 }279280 fn unnamed_iter(281 &self,282 s: State,283 ctx: Context,284 tailstrict: bool,285 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,286 ) -> Result<()> {287 (*self).unnamed_iter(s, ctx, tailstrict, handler)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 (*self).named_iter(s, ctx, tailstrict, handler)298 }299300 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {301 (*self).named_names(handler)302 }303}304305306307308309310311312313pub fn parse_function_call(314 s: State,315 ctx: Context,316 body_ctx: Context,317 params: &ParamsDesc,318 args: &dyn ArgsLike,319 tailstrict: bool,320) -> Result<Context> {321 let mut passed_args = GcHashMap::with_capacity(params.len());322 if args.unnamed_len() > params.len() {323 throw!(TooManyArgsFunctionHas(params.len()))324 }325326 let mut filled_args = 0;327328 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {329 let name = params[id].0.clone();330 passed_args.insert(name, arg);331 filled_args += 1;332 Ok(())333 })?;334335 args.named_iter(s, ctx, tailstrict, &mut |name, value| {336 337 if !params.iter().any(|p| &p.0 == name) {338 throw!(UnknownFunctionParameter((name as &str).to_owned()));339 }340 if passed_args.insert(name.clone(), value).is_some() {341 throw!(BindingParameterASecondTime(name.clone()));342 }343 filled_args += 1;344 Ok(())345 })?;346347 if filled_args < params.len() {348 349 350 let fctx = Context::new_future();351 let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);352353 for param in params.iter().filter(|p| p.1.is_some()) {354 if passed_args.contains_key(¶m.0.clone()) {355 continue;356 }357 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {358 ctx: fctx.clone(),359 name: param.0.clone(),360 value: param.1.clone().unwrap(),361 })));362363 defaults.insert(364 param.0.clone(),365 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {366 ctx: fctx.clone(),367 name: param.0.clone(),368 value: param.1.clone().unwrap(),369 }))),370 );371 filled_args += 1;372 }373374 375 if filled_args != params.len() {376 for param in params.iter().skip(args.unnamed_len()) {377 let mut found = false;378 args.named_names(&mut |name| {379 if name == ¶m.0 {380 found = true;381 }382 });383 if !found {384 throw!(FunctionParameterNotBoundInCall(param.0.clone()));385 }386 }387 unreachable!();388 }389390 Ok(body_ctx391 .extend(passed_args, None, None, None)392 .extend_bound(defaults)393 .into_future(fctx))394 } else {395 let body_ctx = body_ctx.extend(passed_args, None, None, None);396 Ok(body_ctx)397 }398}399400type BuiltinParamName = Cow<'static, str>;401402#[derive(Clone, Trace)]403pub struct BuiltinParam {404 pub name: BuiltinParamName,405 pub has_default: bool,406}407408409pub trait Builtin: Trace {410 fn name(&self) -> &str;411 fn params(&self) -> &[BuiltinParam];412 fn call(&self, s: State, ctx: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;413}414415pub trait StaticBuiltin: Builtin + Send + Sync416where417 Self: 'static,418{419 420 421}422423424425426427428429430pub fn parse_builtin_call(431 s: State,432 ctx: Context,433 params: &[BuiltinParam],434 args: &dyn ArgsLike,435 tailstrict: bool,436) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {437 let mut passed_args = GcHashMap::with_capacity(params.len());438 if args.unnamed_len() > params.len() {439 throw!(TooManyArgsFunctionHas(params.len()))440 }441442 let mut filled_args = 0;443444 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {445 let name = params[id].name.clone();446 passed_args.insert(name, arg);447 filled_args += 1;448 Ok(())449 })?;450451 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {452 453 let p = params454 .iter()455 .find(|p| p.name == name as &str)456 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;457 if passed_args.insert(p.name.clone(), arg).is_some() {458 throw!(BindingParameterASecondTime(name.clone()));459 }460 filled_args += 1;461 Ok(())462 })?;463464 if filled_args < params.len() {465 for param in params.iter().filter(|p| p.has_default) {466 if passed_args.contains_key(¶m.name) {467 continue;468 }469 filled_args += 1;470 }471472 473 if filled_args != params.len() {474 for param in params.iter().skip(args.unnamed_len()) {475 let mut found = false;476 args.named_names(&mut |name| {477 if name as &str == ¶m.name as &str {478 found = true;479 }480 });481 if !found {482 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));483 }484 }485 unreachable!();486 }487 }488 Ok(passed_args)489}490491492493pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {494 let fctx = Context::new_future();495496 let mut bindings = GcHashMap::new();497498 #[derive(Trace)]499 struct DependsOnUnbound(IStr);500 impl LazyValValue for DependsOnUnbound {501 fn get(self: Box<Self>, _: State) -> Result<Val> {502 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())503 }504 }505506 for param in params.iter() {507 if let Some(v) = ¶m.1 {508 bindings.insert(509 param.0.clone(),510 LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {511 ctx: fctx.clone(),512 name: param.0.clone(),513 value: v.clone(),514 }))),515 );516 } else {517 bindings.insert(518 param.0.clone(),519 LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),520 );521 }522 }523524 body_ctx525 .extend(bindings, None, None, None)526 .into_future(fctx)527}