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::{9 Result, Thunk, Val, bail,10 error::ErrorKind::*,11};1213#[derive(Debug, Trace, Clone)]14pub struct PreparedFuncVal {15 fun: FuncVal,16 prepared: Rc<PreparedCall>,17}1819impl PreparedFuncVal {20 pub fn new(fun: FuncVal, unnamed: usize, named: &[IStr]) -> Result<Self> {21 let prepared = prepare_call(fun.params(), unnamed, named)?;22 Ok(Self {23 fun,24 prepared: Rc::new(prepared),25 })26 }27 pub fn call(28 &self,29 loc: CallLocation<'_>,30 unnamed: &[Thunk<Val>],31 named: &[Thunk<Val>],32 ) -> Result<Val> {33 self.fun34 .evaluate_prepared(&self.prepared, loc, unnamed, named, false)35 }36}3738#[derive(Acyclic, Debug)]39pub struct PreparedCall {40 41 named: Vec<(usize, usize)>,42 defaults: Vec<usize>,43}4445impl PreparedCall {46 pub fn named(&self) -> &[(usize, usize)] {47 &self.named48 }49 pub fn defaults(&self) -> &[usize] {50 &self.defaults51 }52}5354pub fn prepare_call(55 params: FunctionSignature,56 unnamed: usize,57 named: &[IStr],58) -> Result<PreparedCall> {59 if unnamed > params.len() {60 bail!(TooManyArgsFunctionHas(params.len(), params))61 }6263 64 if named.is_empty() {65 let mut defaults = Vec::new();66 for (param_id, param) in params.iter().enumerate().skip(unnamed) {67 if param.has_default() {68 defaults.push(param_id);69 } else {70 bail!(FunctionParameterNotBoundInCall(71 param.name().clone(),72 params.clone(),73 ))74 }75 }76 return Ok(PreparedCall {77 named: Vec::new(),78 defaults,79 });80 }8182 let expected_defaults = (params.len() - unnamed).saturating_sub(named.len());83 let mut ops = PreparedCall {84 named: Vec::with_capacity(named.len()),85 defaults: Vec::with_capacity(expected_defaults),86 };8788 89 let mut passed: FxHashSet<usize> = (0..unnamed).collect();9091 for (input_id, name) in named.iter().enumerate() {92 93 let Some(param_idx) = params.iter().position(|p| p.name() == name) else {94 bail!(UnknownFunctionParameter(name.clone()));95 };96 if !passed.insert(param_idx) {97 bail!(BindingParameterASecondTime(name.clone()));98 }99 ops.named.push((param_idx, input_id));100 }101102 if named.len() + unnamed < params.len() {103 let mut defaults = 0;104105 for (param_id, param) in params106 .iter()107 .enumerate()108 .skip(unnamed)109 .filter(|p| p.1.has_default())110 {111 112 if !param.name().is_anonymous() && passed.contains(¶m_id) {113 continue;114 }115 defaults += 1;116117 ops.defaults.push(param_id);118 }119120 121 if defaults != expected_defaults {122 for param in params.iter().skip(unnamed) {123 let mut found = false;124 for name in named {125 if param.name() == name {126 found = true;127 }128 }129 if !found {130 bail!(FunctionParameterNotBoundInCall(131 param.name().clone(),132 params133 ));134 }135 }136 unreachable!();137 }138 }139140 Ok(ops)141}142pub fn parse_prepared_builtin_call(143 prepared: &PreparedCall,144 params: FunctionSignature,145 unnamed: &[Thunk<Val>],146 named: &[Thunk<Val>],147) -> Vec<Option<Thunk<Val>>> {148 let mut passed_args = vec![None; params.len()];149150 for (param_idx, unnamed) in unnamed.iter().enumerate() {151 passed_args[param_idx] = Some(unnamed.clone());152 }153154 for (param_idx, arg_idx) in prepared.named.iter().copied() {155 passed_args[param_idx] = Some(named[arg_idx].clone());156 }157158 passed_args159}