1use jrsonnet_parser::function::FunctionSignature;2use jrsonnet_parser::{ExprParams, IStr};3use rustc_hash::{FxHashMap, FxHashSet};45use crate::destructure::destruct;6use crate::gc::WithCapacityExt;7use crate::val::ThunkValue as _;8use crate::{bail, error::ErrorKind::*, Result};9use crate::{evaluate_named, evaluate_named_param, Context, ContextBuilder, Pending, Thunk, Val};1011pub struct PreparedCall {12 13 named: Vec<(usize, usize)>,14 defaults: Vec<usize>,15}1617pub fn prepare_call(18 params: FunctionSignature,19 unnamed: usize,20 named: &[IStr],21) -> Result<PreparedCall> {22 if unnamed > params.len() {23 bail!(TooManyArgsFunctionHas(params.len(), params))24 }2526 let expected_defaults = params.len() - unnamed - named.len();27 let mut ops = PreparedCall {28 named: Vec::with_capacity(named.len()),29 defaults: Vec::with_capacity(expected_defaults),30 };3132 33 let mut passed: FxHashSet<usize> = (0..unnamed).collect();3435 for (input_id, name) in named.iter().enumerate() {36 37 let Some(param_idx) = params.iter().position(|p| p.name() == name) else {38 bail!(UnknownFunctionParameter(name.clone()));39 };40 if !passed.insert(param_idx) {41 bail!(BindingParameterASecondTime(name.clone()));42 }43 ops.named.push((param_idx, input_id));44 }4546 if named.len() + unnamed < params.len() {47 let mut defaults = 0;4849 for (param_id, param) in params50 .iter()51 .enumerate()52 .skip(unnamed)53 .filter(|p| p.1.has_default())54 {55 56 if !param.name().is_anonymous() && passed.contains(¶m_id) {57 continue;58 }59 defaults += 1;6061 ops.defaults.push(param_id);62 }6364 65 if defaults != expected_defaults {66 for param in params.iter().skip(unnamed) {67 let mut found = false;68 for name in named {69 if param.name() == name {70 found = true;71 }72 }73 if !found {74 bail!(FunctionParameterNotBoundInCall(75 param.name().clone(),76 params77 ));78 }79 }80 unreachable!();81 }82 }8384 Ok(ops)85}86pub fn parse_prepared_function_call(87 body_ctx: Context,88 prepared: &PreparedCall,89 params: &ExprParams,90 unnamed: &[Thunk<Val>],91 named: &[Thunk<Val>],92) -> Result<Context> {93 let mut passed_args = FxHashMap::with_capacity(params.binds_len());9495 let destruct_ctx = Pending::new();9697 for (param_idx, unnamed) in unnamed.iter().enumerate() {98 destruct(99 ¶ms.exprs[param_idx].destruct,100 unnamed.clone(),101 destruct_ctx.clone(),102 &mut passed_args,103 )?;104 }105106 for (param_idx, arg_idx) in prepared.named.iter().copied() {107 destruct(108 ¶ms.exprs[param_idx].destruct,109 named[arg_idx].clone(),110 destruct_ctx.clone(),111 &mut passed_args,112 )?;113 }114115 if prepared.defaults.is_empty() {116 let body_ctx = body_ctx117 .extend_bindings(passed_args)118 .into_future(destruct_ctx);119 Ok(body_ctx)120 } else {121 let fctx = Context::new_future();122 let mut defaults = FxHashMap::with_capacity(params.binds_len() - passed_args.len());123 for param_idx in prepared.defaults.iter().copied() {124 125 destruct(126 ¶ms.exprs[param_idx].destruct,127 {128 let ctx = fctx.clone();129 let params = params.clone();130 Thunk!(move || {131 let param = ¶ms.exprs[param_idx];132 let name = param.destruct.name();133 let value = param.default.as_ref().expect("default exists");134 evaluate_named_param(ctx.unwrap(), value, name)135 })136 },137 fctx.clone(),138 &mut defaults,139 )?;140 }141142 let mut ctx = ContextBuilder::extend(body_ctx);143 ctx.binds(passed_args);144 ctx.binds(defaults);145 Ok(ctx.build().into_future(fctx).into_future(destruct_ctx))146 }147}148pub fn parse_prepared_builtin_call(149 prepared: &PreparedCall,150 params: FunctionSignature,151 unnamed: &[Thunk<Val>],152 named: &[Thunk<Val>],153) -> Result<Vec<Option<Thunk<Val>>>> {154 let mut passed_args = vec![None; params.len()];155156 for (param_idx, unnamed) in unnamed.iter().enumerate() {157 passed_args[param_idx] = Some(unnamed.clone());158 }159160 for (param_idx, arg_idx) in prepared.named.iter().copied() {161 passed_args[param_idx] = Some(named[arg_idx].clone());162 }163164 Ok(passed_args)165}