git.delta.rocks / jrsonnet / refs/commits / cf33b6edf3e1

difftreelog

source

crates/jrsonnet-evaluator/src/function/prepared.rs3.5 KiBsourcehistory
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	// Param, named input.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	// Fast path: positional-only (no named args). Avoids HashMap entirely.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	// FIXME: bitmask86	let mut passed: FxHashSet<usize> = (0..unnamed).collect();8788	for (input_id, name) in named.iter().enumerate() {89		// FIXME: O(n) for arg existence check90		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			// Skip already passed parameters109			if !param.name().is_anonymous() && passed.contains(&param_id) {110				continue;111			}112			defaults += 1;113114			ops.defaults.push(param_id);115		}116117		// Some args still weren't filled118		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}