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

difftreelog

source

crates/jrsonnet-evaluator/src/function.rs12.7 KiBsourcehistory
1use std::{borrow::Cow, collections::HashMap};23use gcmodule::Trace;4use jrsonnet_interner::IStr;5pub use jrsonnet_macros::builtin;6use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};78use crate::{9	error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw, typed::Typed,10	val::LazyValValue, Context, FutureWrapper, GcHashMap, LazyVal, Result, State, Val,11};1213#[derive(Clone, Copy)]14pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);15impl<'l> CallLocation<'l> {16	pub const fn new(loc: &'l ExprLocation) -> Self {17		Self(Some(loc))18	}19}20impl CallLocation<'static> {21	pub const fn native() -> Self {22		Self(None)23	}24}2526#[derive(Trace)]27struct EvaluateLazyVal {28	ctx: Context,29	expr: LocExpr,30}31impl LazyValValue for EvaluateLazyVal {32	fn get(self: Box<Self>, s: State) -> Result<Val> {33		evaluate(s, self.ctx, &self.expr)34	}35}3637#[derive(Trace)]38struct EvaluateNamedLazyVal {39	ctx: FutureWrapper<Context>,40	name: IStr,41	value: LocExpr,42}43impl LazyValValue for EvaluateNamedLazyVal {44	fn get(self: Box<Self>, s: State) -> Result<Val> {45		evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)46	}47}48pub trait ArgLike {49	fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal>;50}51impl ArgLike for &LocExpr {52	fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {53		Ok(if tailstrict {54			LazyVal::new_resolved(evaluate(s, ctx, self)?)55		} else {56			LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {57				ctx,58				expr: (*self).clone(),59			})))60		})61	}62}63impl<T> ArgLike for T64where65	T: Typed + Clone,66{67	fn evaluate_arg(&self, s: State, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {68		let val = T::into_untyped(self.clone(), s)?;69		Ok(LazyVal::new_resolved(val))70	}71}72pub enum TlaArg {73	String(IStr),74	Code(LocExpr),75	Val(Val),76}77impl ArgLike for TlaArg {78	fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {79		match self {80			TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),81			TlaArg::Code(code) => Ok(if tailstrict {82				LazyVal::new_resolved(evaluate(s, ctx, code)?)83			} else {84				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {85					ctx,86					expr: code.clone(),87				})))88			}),89			TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),90		}91	}92}9394pub trait ArgsLike {95	fn unnamed_len(&self) -> usize;96	fn unnamed_iter(97		&self,98		s: State,99		ctx: Context,100		tailstrict: bool,101		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,102	) -> Result<()>;103	fn named_iter(104		&self,105		s: State,106		ctx: Context,107		tailstrict: bool,108		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,109	) -> Result<()>;110	fn named_names(&self, handler: &mut dyn FnMut(&IStr));111}112113impl ArgsLike for ArgsDesc {114	fn unnamed_len(&self) -> usize {115		self.unnamed.len()116	}117118	fn unnamed_iter(119		&self,120		s: State,121		ctx: Context,122		tailstrict: bool,123		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,124	) -> Result<()> {125		for (id, arg) in self.unnamed.iter().enumerate() {126			handler(127				id,128				if tailstrict {129					LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)130				} else {131					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {132						ctx: ctx.clone(),133						expr: arg.clone(),134					})))135				},136			)?;137		}138		Ok(())139	}140141	fn named_iter(142		&self,143		s: State,144		ctx: Context,145		tailstrict: bool,146		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,147	) -> Result<()> {148		for (name, arg) in self.named.iter() {149			handler(150				name,151				if tailstrict {152					LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)153				} else {154					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {155						ctx: ctx.clone(),156						expr: arg.clone(),157					})))158				},159			)?;160		}161		Ok(())162	}163164	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {165		for (name, _) in self.named.iter() {166			handler(name)167		}168	}169}170171impl ArgsLike for [(); 0] {172	fn unnamed_len(&self) -> usize {173		0174	}175176	fn unnamed_iter(177		&self,178		_s: State,179		_ctx: Context,180		_tailstrict: bool,181		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,182	) -> Result<()> {183		Ok(())184	}185186	fn named_iter(187		&self,188		_s: State,189		_ctx: Context,190		_tailstrict: bool,191		_handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,192	) -> Result<()> {193		Ok(())194	}195196	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}197}198199impl<A: ArgLike> ArgsLike for [(IStr, A)] {200	fn unnamed_len(&self) -> usize {201		0202	}203204	fn unnamed_iter(205		&self,206		_s: State,207		_ctx: Context,208		_tailstrict: bool,209		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,210	) -> Result<()> {211		Ok(())212	}213214	fn named_iter(215		&self,216		s: State,217		ctx: Context,218		tailstrict: bool,219		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,220	) -> Result<()> {221		for (name, val) in self.iter() {222			handler(name, val.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;223		}224		Ok(())225	}226227	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {228		for (name, _) in self.iter() {229			handler(name);230		}231	}232}233234impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {235	fn unnamed_len(&self) -> usize {236		0237	}238239	fn unnamed_iter(240		&self,241		_s: State,242		_ctx: Context,243		_tailstrict: bool,244		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,245	) -> Result<()> {246		Ok(())247	}248249	fn named_iter(250		&self,251		s: State,252		ctx: Context,253		tailstrict: bool,254		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,255	) -> Result<()> {256		for (name, value) in self.iter() {257			handler(258				name,259				value.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?,260			)?;261		}262		Ok(())263	}264265	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {266		for (name, _) in self.iter() {267			handler(name);268		}269	}270}271272impl<A: ArgLike> ArgsLike for [A] {273	fn unnamed_len(&self) -> usize {274		self.len()275	}276277	fn unnamed_iter(278		&self,279		s: State,280		ctx: Context,281		tailstrict: bool,282		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,283	) -> Result<()> {284		for (i, arg) in self.iter().enumerate() {285			handler(i, arg.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;286		}287		Ok(())288	}289290	fn named_iter(291		&self,292		_s: State,293		_ctx: Context,294		_tailstrict: bool,295		_handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,296	) -> Result<()> {297		Ok(())298	}299300	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}301}302impl<A: ArgLike> ArgsLike for &[A] {303	fn unnamed_len(&self) -> usize {304		(*self).unnamed_len()305	}306307	fn unnamed_iter(308		&self,309		s: State,310		ctx: Context,311		tailstrict: bool,312		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,313	) -> Result<()> {314		(*self).unnamed_iter(s, ctx, tailstrict, handler)315	}316317	fn named_iter(318		&self,319		s: State,320		ctx: Context,321		tailstrict: bool,322		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,323	) -> Result<()> {324		(*self).named_iter(s, ctx, tailstrict, handler)325	}326327	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {328		(*self).named_names(handler)329	}330}331332/// Creates correct [context](Context) for function body evaluation returning error on invalid call.333///334/// ## Parameters335/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)336/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)337/// * `params`: function parameters' definition338/// * `args`: passed function arguments339/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily340pub fn parse_function_call(341	s: State,342	ctx: Context,343	body_ctx: Context,344	params: &ParamsDesc,345	args: &dyn ArgsLike,346	tailstrict: bool,347) -> Result<Context> {348	let mut passed_args = GcHashMap::with_capacity(params.len());349	if args.unnamed_len() > params.len() {350		throw!(TooManyArgsFunctionHas(params.len()))351	}352353	let mut filled_args = 0;354355	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {356		let name = params[id].0.clone();357		passed_args.insert(name, arg);358		filled_args += 1;359		Ok(())360	})?;361362	args.named_iter(s, ctx, tailstrict, &mut |name, value| {363		// FIXME: O(n) for arg existence check364		if !params.iter().any(|p| &p.0 == name) {365			throw!(UnknownFunctionParameter((name as &str).to_owned()));366		}367		if passed_args.insert(name.clone(), value).is_some() {368			throw!(BindingParameterASecondTime(name.clone()));369		}370		filled_args += 1;371		Ok(())372	})?;373374	if filled_args < params.len() {375		// Some args are unset, but maybe we have defaults for them376		// Default values should be created in newly created context377		let fctx = Context::new_future();378		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);379380		for param in params.iter().filter(|p| p.1.is_some()) {381			if passed_args.contains_key(&param.0.clone()) {382				continue;383			}384			LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {385				ctx: fctx.clone(),386				name: param.0.clone(),387				value: param.1.clone().unwrap(),388			})));389390			defaults.insert(391				param.0.clone(),392				LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {393					ctx: fctx.clone(),394					name: param.0.clone(),395					value: param.1.clone().unwrap(),396				}))),397			);398			filled_args += 1;399		}400401		// Some args still wasn't filled402		if filled_args != params.len() {403			for param in params.iter().skip(args.unnamed_len()) {404				let mut found = false;405				args.named_names(&mut |name| {406					if name == &param.0 {407						found = true;408					}409				});410				if !found {411					throw!(FunctionParameterNotBoundInCall(param.0.clone()));412				}413			}414			unreachable!();415		}416417		Ok(body_ctx418			.extend(passed_args, None, None, None)419			.extend_bound(defaults)420			.into_future(fctx))421	} else {422		let body_ctx = body_ctx.extend(passed_args, None, None, None);423		Ok(body_ctx)424	}425}426427type BuiltinParamName = Cow<'static, str>;428429#[derive(Clone, Trace)]430pub struct BuiltinParam {431	pub name: BuiltinParamName,432	pub has_default: bool,433}434435/// Do not implement it directly, instead use #[builtin] macro436pub trait Builtin: Trace {437	fn name(&self) -> &str;438	fn params(&self) -> &[BuiltinParam];439	fn call(&self, s: State, ctx: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;440}441442pub trait StaticBuiltin: Builtin + Send + Sync443where444	Self: 'static,445{446	// In impl, to make it object safe:447	// const INST: &'static Self;448}449450/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead451///452/// ## Parameters453/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)454/// * `params`: function parameters' definition455/// * `args`: passed function arguments456/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily457pub fn parse_builtin_call(458	s: State,459	ctx: Context,460	params: &[BuiltinParam],461	args: &dyn ArgsLike,462	tailstrict: bool,463) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {464	let mut passed_args = GcHashMap::with_capacity(params.len());465	if args.unnamed_len() > params.len() {466		throw!(TooManyArgsFunctionHas(params.len()))467	}468469	let mut filled_args = 0;470471	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {472		let name = params[id].name.clone();473		passed_args.insert(name, arg);474		filled_args += 1;475		Ok(())476	})?;477478	args.named_iter(s, ctx, tailstrict, &mut |name, arg| {479		// FIXME: O(n) for arg existence check480		let p = params481			.iter()482			.find(|p| p.name == name as &str)483			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;484		if passed_args.insert(p.name.clone(), arg).is_some() {485			throw!(BindingParameterASecondTime(name.clone()));486		}487		filled_args += 1;488		Ok(())489	})?;490491	if filled_args < params.len() {492		for param in params.iter().filter(|p| p.has_default) {493			if passed_args.contains_key(&param.name) {494				continue;495			}496			filled_args += 1;497		}498499		// Some args still wasn't filled500		if filled_args != params.len() {501			for param in params.iter().skip(args.unnamed_len()) {502				let mut found = false;503				args.named_names(&mut |name| {504					if name as &str == &param.name as &str {505						found = true;506					}507				});508				if !found {509					throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));510				}511			}512			unreachable!();513		}514	}515	Ok(passed_args)516}517518/// Creates Context, which has all argument default values applied519/// and with unbound values causing error to be returned520pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {521	let fctx = Context::new_future();522523	let mut bindings = GcHashMap::new();524525	#[derive(Trace)]526	struct DependsOnUnbound(IStr);527	impl LazyValValue for DependsOnUnbound {528		fn get(self: Box<Self>, _: State) -> Result<Val> {529			Err(FunctionParameterNotBoundInCall(self.0.clone()).into())530		}531	}532533	for param in params.iter() {534		if let Some(v) = &param.1 {535			bindings.insert(536				param.0.clone(),537				LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {538					ctx: fctx.clone(),539					name: param.0.clone(),540					value: v.clone(),541				}))),542			);543		} else {544			bindings.insert(545				param.0.clone(),546				LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),547			);548		}549	}550551	body_ctx552		.extend(bindings, None, None, None)553		.into_future(fctx)554}