git.delta.rocks / jrsonnet / refs/commits / 112adb2810f2

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::{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	// Param, named input.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	// Fast path: positional-only (no named args). Avoids HashMap entirely.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	// FIXME: bitmask89	let mut passed: FxHashSet<usize> = (0..unnamed).collect();9091	for (input_id, name) in named.iter().enumerate() {92		// FIXME: O(n) for arg existence check93		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			// Skip already passed parameters112			if !param.name().is_anonymous() && passed.contains(&param_id) {113				continue;114			}115			defaults += 1;116117			ops.defaults.push(param_id);118		}119120		// Some args still weren't filled121		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}