1use std::rc::Rc;23use jrsonnet_gcmodule::{Acyclic, Trace};4use jrsonnet_ir::{IStr, function::FunctionSignature};5use rustc_hash::FxHashSet;67use super::{CallLocation, FuncVal};8use crate::{Result, Thunk, Val, bail, error::ErrorKind::*};910#[derive(Debug, Trace, Clone)]11pub struct PreparedFuncVal {12 fun: FuncVal,13 prepared: Rc<PreparedCall>,14}1516impl PreparedFuncVal {17 pub fn new(fun: FuncVal, unnamed: usize, named: &[IStr]) -> Result<Self> {18 let prepared = prepare_call(fun.params(), unnamed, named)?;19 Ok(Self {20 fun,21 prepared: Rc::new(prepared),22 })23 }24 pub fn call(25 &self,26 loc: CallLocation<'_>,27 unnamed: &[Thunk<Val>],28 named: &[Thunk<Val>],29 ) -> Result<Val> {30 self.fun31 .evaluate_prepared(&self.prepared, loc, unnamed, named, false)32 }33}3435#[derive(Acyclic, Debug)]36pub struct PreparedCall {37 38 named: Vec<(usize, usize)>,39 defaults: Vec<usize>,40}4142impl PreparedCall {43 pub fn named(&self) -> &[(usize, usize)] {44 &self.named45 }46 pub fn defaults(&self) -> &[usize] {47 &self.defaults48 }49 pub const fn empty() -> Self {50 Self {51 named: Vec::new(),52 defaults: Vec::new(),53 }54 }55}5657pub fn prepare_call(58 params: FunctionSignature,59 unnamed: usize,60 named: &[IStr],61) -> Result<PreparedCall> {62 if unnamed > params.len() {63 bail!(TooManyArgsFunctionHas(params.len(), params))64 }6566 67 if named.is_empty() {68 let mut defaults = Vec::new();69 for (param_id, param) in params.iter().enumerate().skip(unnamed) {70 if param.has_default() {71 defaults.push(param_id);72 } else {73 bail!(FunctionParameterNotBoundInCall(74 param.name().clone(),75 params.clone(),76 ))77 }78 }79 return Ok(PreparedCall {80 named: Vec::new(),81 defaults,82 });83 }8485 let expected_defaults = (params.len() - unnamed).saturating_sub(named.len());86 let mut ops = PreparedCall {87 named: Vec::with_capacity(named.len()),88 defaults: Vec::with_capacity(expected_defaults),89 };9091 92 let mut passed: FxHashSet<usize> = (0..unnamed).collect();9394 for (input_id, name) in named.iter().enumerate() {95 96 let Some(param_idx) = params.iter().position(|p| p.name() == name) else {97 bail!(UnknownFunctionParameter(name.clone()));98 };99 if !passed.insert(param_idx) {100 bail!(BindingParameterASecondTime(name.clone()));101 }102 ops.named.push((param_idx, input_id));103 }104105 if named.len() + unnamed < params.len() {106 let mut defaults = 0;107108 for (param_id, param) in params109 .iter()110 .enumerate()111 .skip(unnamed)112 .filter(|p| p.1.has_default())113 {114 115 if !param.name().is_anonymous() && passed.contains(¶m_id) {116 continue;117 }118 defaults += 1;119120 ops.defaults.push(param_id);121 }122123 124 if defaults != expected_defaults {125 for param in params.iter().skip(unnamed) {126 let mut found = false;127 for name in named {128 if param.name() == name {129 found = true;130 }131 }132 if !found {133 bail!(FunctionParameterNotBoundInCall(134 param.name().clone(),135 params136 ));137 }138 }139 unreachable!();140 }141 }142143 Ok(ops)144}145pub fn parse_prepared_builtin_call(146 prepared: &PreparedCall,147 params: FunctionSignature,148 unnamed: &[Thunk<Val>],149 named: &[Thunk<Val>],150) -> Vec<Option<Thunk<Val>>> {151 let mut passed_args = vec![None; params.len()];152153 for (param_idx, unnamed) in unnamed.iter().enumerate() {154 passed_args[param_idx] = Some(unnamed.clone());155 }156157 for (param_idx, arg_idx) in prepared.named.iter().copied() {158 passed_args[param_idx] = Some(named[arg_idx].clone());159 }160161 passed_args162}