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}5051pub fn prepare_call(52 params: FunctionSignature,53 unnamed: usize,54 named: &[IStr],55) -> Result<PreparedCall> {56 if unnamed > params.len() {57 bail!(TooManyArgsFunctionHas(params.len(), params))58 }5960 61 if named.is_empty() {62 let mut defaults = Vec::new();63 for (param_id, param) in params.iter().enumerate().skip(unnamed) {64 if param.has_default() {65 defaults.push(param_id);66 } else {67 bail!(FunctionParameterNotBoundInCall(68 param.name().clone(),69 params.clone(),70 ))71 }72 }73 return Ok(PreparedCall {74 named: Vec::new(),75 defaults,76 });77 }7879 let expected_defaults = (params.len() - unnamed).saturating_sub(named.len());80 let mut ops = PreparedCall {81 named: Vec::with_capacity(named.len()),82 defaults: Vec::with_capacity(expected_defaults),83 };8485 86 let mut passed: FxHashSet<usize> = (0..unnamed).collect();8788 for (input_id, name) in named.iter().enumerate() {89 90 let Some(param_idx) = params.iter().position(|p| p.name() == name) else {91 bail!(UnknownFunctionParameter(name.clone()));92 };93 if !passed.insert(param_idx) {94 bail!(BindingParameterASecondTime(name.clone()));95 }96 ops.named.push((param_idx, input_id));97 }9899 if named.len() + unnamed < params.len() {100 let mut defaults = 0;101102 for (param_id, param) in params103 .iter()104 .enumerate()105 .skip(unnamed)106 .filter(|p| p.1.has_default())107 {108 109 if !param.name().is_anonymous() && passed.contains(¶m_id) {110 continue;111 }112 defaults += 1;113114 ops.defaults.push(param_id);115 }116117 118 if defaults != expected_defaults {119 for param in params.iter().skip(unnamed) {120 let mut found = false;121 for name in named {122 if param.name() == name {123 found = true;124 }125 }126 if !found {127 bail!(FunctionParameterNotBoundInCall(128 param.name().clone(),129 params130 ));131 }132 }133 unreachable!();134 }135 }136137 Ok(ops)138}139pub fn parse_prepared_builtin_call(140 prepared: &PreparedCall,141 params: FunctionSignature,142 unnamed: &[Thunk<Val>],143 named: &[Thunk<Val>],144) -> Vec<Option<Thunk<Val>>> {145 let mut passed_args = vec![None; params.len()];146147 for (param_idx, unnamed) in unnamed.iter().enumerate() {148 passed_args[param_idx] = Some(unnamed.clone());149 }150151 for (param_idx, arg_idx) in prepared.named.iter().copied() {152 passed_args[param_idx] = Some(named[arg_idx].clone());153 }154155 passed_args156}