git.delta.rocks / jrsonnet / refs/heads / master

difftreelog

source

crates/jrsonnet-evaluator/src/function/prepared.rs3.6 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	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	// Fast path: positional-only (no named args). Avoids HashMap entirely.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	// FIXME: bitmask92	let mut passed: FxHashSet<usize> = (0..unnamed).collect();9394	for (input_id, name) in named.iter().enumerate() {95		// FIXME: O(n) for arg existence check96		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			// Skip already passed parameters115			if !param.name().is_anonymous() && passed.contains(&param_id) {116				continue;117			}118			defaults += 1;119120			ops.defaults.push(param_id);121		}122123		// Some args still weren't filled124		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}