git.delta.rocks / jrsonnet / refs/commits / 78d82dfdf2a1

difftreelog

source

crates/jrsonnet-evaluator/src/function.rs10.9 KiBsourcehistory
1use crate::{2	error::{Error::*, LocError},3	evaluate, evaluate_named,4	gc::TraceBox,5	throw,6	typed::Typed,7	Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val,8};9use gcmodule::Trace;10use jrsonnet_interner::IStr;11pub use jrsonnet_macros::builtin;12use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};13use std::{borrow::Cow, collections::HashMap, convert::TryFrom};1415#[derive(Trace)]16struct EvaluateLazyVal {17	context: Context,18	expr: LocExpr,19}20impl LazyValValue for EvaluateLazyVal {21	fn get(self: Box<Self>) -> Result<Val> {22		evaluate(self.context, &self.expr)23	}24}2526pub trait ArgLike {27	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;28}29impl ArgLike for &LocExpr {30	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {31		Ok(if tailstrict {32			LazyVal::new_resolved(evaluate(ctx, self)?)33		} else {34			LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {35				context: ctx,36				expr: (*self).clone(),37			})))38		})39	}40}41impl<T> ArgLike for T42where43	T: Typed + Clone,44	Val: TryFrom<T, Error = LocError>,45{46	fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {47		let val: Val = Val::try_from(self.clone())?;48		Ok(LazyVal::new_resolved(val))49	}50}51pub enum TlaArg {52	String(IStr),53	Code(LocExpr),54	Val(Val),55}56impl ArgLike for TlaArg {57	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {58		match self {59			TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),60			TlaArg::Code(code) => Ok(if tailstrict {61				LazyVal::new_resolved(evaluate(ctx, code)?)62			} else {63				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {64					context: ctx,65					expr: code.clone(),66				})))67			}),68			TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),69		}70	}71}7273pub trait ArgsLike {74	fn unnamed_len(&self) -> usize;75	fn unnamed_iter(76		&self,77		ctx: Context,78		tailstrict: bool,79		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,80	) -> Result<()>;81	fn named_iter(82		&self,83		ctx: Context,84		tailstrict: bool,85		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,86	) -> Result<()>;87	fn named_names(&self, handler: &mut dyn FnMut(&IStr));88}8990impl ArgsLike for ArgsDesc {91	fn unnamed_len(&self) -> usize {92		self.unnamed.len()93	}9495	fn unnamed_iter(96		&self,97		ctx: Context,98		tailstrict: bool,99		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,100	) -> Result<()> {101		for (id, arg) in self.unnamed.iter().enumerate() {102			handler(103				id,104				if tailstrict {105					LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)106				} else {107					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {108						context: ctx.clone(),109						expr: arg.clone(),110					})))111				},112			)?;113		}114		Ok(())115	}116117	fn named_iter(118		&self,119		ctx: Context,120		tailstrict: bool,121		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,122	) -> Result<()> {123		for (name, arg) in self.named.iter() {124			handler(125				name,126				if tailstrict {127					LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)128				} else {129					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {130						context: ctx.clone(),131						expr: arg.clone(),132					})))133				},134			)?;135		}136		Ok(())137	}138139	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {140		for (name, _) in self.named.iter() {141			handler(name)142		}143	}144}145146impl<A: ArgLike> ArgsLike for [(IStr, A)] {147	fn unnamed_len(&self) -> usize {148		0149	}150151	fn unnamed_iter(152		&self,153		_ctx: Context,154		_tailstrict: bool,155		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,156	) -> Result<()> {157		Ok(())158	}159160	fn named_iter(161		&self,162		ctx: Context,163		tailstrict: bool,164		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,165	) -> Result<()> {166		for (name, val) in self.iter() {167			handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;168		}169		Ok(())170	}171172	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {173		for (name, _) in self.iter() {174			handler(name);175		}176	}177}178179impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {180	fn unnamed_len(&self) -> usize {181		0182	}183184	fn unnamed_iter(185		&self,186		_ctx: Context,187		_tailstrict: bool,188		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,189	) -> Result<()> {190		Ok(())191	}192193	fn named_iter(194		&self,195		ctx: Context,196		tailstrict: bool,197		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,198	) -> Result<()> {199		for (name, value) in self.iter() {200			handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;201		}202		Ok(())203	}204205	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {206		for (name, _) in self.iter() {207			handler(name);208		}209	}210}211212impl<A: ArgLike> ArgsLike for [A] {213	fn unnamed_len(&self) -> usize {214		self.len()215	}216217	fn unnamed_iter(218		&self,219		ctx: Context,220		tailstrict: bool,221		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,222	) -> Result<()> {223		for (i, arg) in self.iter().enumerate() {224			handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;225		}226		Ok(())227	}228229	fn named_iter(230		&self,231		_ctx: Context,232		_tailstrict: bool,233		_handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,234	) -> Result<()> {235		Ok(())236	}237238	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}239}240impl<A: ArgLike> ArgsLike for &[A] {241	fn unnamed_len(&self) -> usize {242		(*self).unnamed_len()243	}244245	fn unnamed_iter(246		&self,247		ctx: Context,248		tailstrict: bool,249		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,250	) -> Result<()> {251		(*self).unnamed_iter(ctx, tailstrict, handler)252	}253254	fn named_iter(255		&self,256		ctx: Context,257		tailstrict: bool,258		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,259	) -> Result<()> {260		(*self).named_iter(ctx, tailstrict, handler)261	}262263	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {264		(*self).named_names(handler)265	}266}267268/// Creates correct [context](Context) for function body evaluation returning error on invalid call.269///270/// ## Parameters271/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)272/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)273/// * `params`: function parameters' definition274/// * `args`: passed function arguments275/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily276pub fn parse_function_call(277	ctx: Context,278	body_ctx: Context,279	params: &ParamsDesc,280	args: &dyn ArgsLike,281	tailstrict: bool,282) -> Result<Context> {283	let mut passed_args = GcHashMap::with_capacity(params.len());284	if args.unnamed_len() > params.len() {285		throw!(TooManyArgsFunctionHas(params.len()))286	}287288	let mut filled_args = 0;289290	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {291		let name = params[id].0.clone();292		passed_args.insert(name, arg);293		filled_args += 1;294		Ok(())295	})?;296297	args.named_iter(ctx, tailstrict, &mut |name, value| {298		// FIXME: O(n) for arg existence check299		if !params.iter().any(|p| &p.0 == name) {300			throw!(UnknownFunctionParameter((name as &str).to_owned()));301		}302		if passed_args.insert(name.clone(), value).is_some() {303			throw!(BindingParameterASecondTime(name.clone()));304		}305		filled_args += 1;306		Ok(())307	})?;308309	if filled_args < params.len() {310		// Some args are unset, but maybe we have defaults for them311		// Default values should be created in newly created context312		let future_context = FutureWrapper::<Context>::new();313		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);314315		for param in params.iter().filter(|p| p.1.is_some()) {316			if passed_args.contains_key(&param.0.clone()) {317				continue;318			}319			#[derive(Trace)]320			struct LazyNamedBinding {321				future_context: FutureWrapper<Context>,322				name: IStr,323				value: LocExpr,324			}325			impl LazyValValue for LazyNamedBinding {326				fn get(self: Box<Self>) -> Result<Val> {327					evaluate_named(self.future_context.unwrap(), &self.value, self.name)328				}329			}330			LazyVal::new(TraceBox(Box::new(LazyNamedBinding {331				future_context: future_context.clone(),332				name: param.0.clone(),333				value: param.1.clone().unwrap(),334			})));335336			defaults.insert(337				param.0.clone(),338				LazyVal::new(TraceBox(Box::new(LazyNamedBinding {339					future_context: future_context.clone(),340					name: param.0.clone(),341					value: param.1.clone().unwrap(),342				}))),343			);344			filled_args += 1;345		}346347		// Some args still wasn't filled348		if filled_args != params.len() {349			for param in params.iter().skip(args.unnamed_len()) {350				let mut found = false;351				args.named_names(&mut |name| {352					if name == &param.0 {353						found = true;354					}355				});356				if !found {357					throw!(FunctionParameterNotBoundInCall(param.0.clone()));358				}359			}360			unreachable!();361		}362363		Ok(body_ctx364			.extend(passed_args, None, None, None)365			.extend_bound(defaults)366			.into_future(future_context))367	} else {368		let body_ctx = body_ctx.extend(passed_args, None, None, None);369		Ok(body_ctx)370	}371}372373type BuiltinParamName = Cow<'static, str>;374375#[derive(Clone, Trace)]376pub struct BuiltinParam {377	pub name: BuiltinParamName,378	pub has_default: bool,379}380381/// Do not implement it directly, instead use #[builtin] macro382pub trait Builtin: Trace {383	fn name(&self) -> &str;384	fn params(&self) -> &[BuiltinParam];385	fn call(386		&self,387		context: Context,388		loc: Option<&ExprLocation>,389		args: &dyn ArgsLike,390	) -> Result<Val>;391}392393pub trait StaticBuiltin: Builtin + Send + Sync394where395	Self: 'static,396{397	// In impl, to make it object safe:398	// const INST: &'static Self;399}400401/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead402///403/// ## Parameters404/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)405/// * `params`: function parameters' definition406/// * `args`: passed function arguments407/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily408pub fn parse_builtin_call(409	ctx: Context,410	params: &[BuiltinParam],411	args: &dyn ArgsLike,412	tailstrict: bool,413) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {414	let mut passed_args = GcHashMap::with_capacity(params.len());415	if args.unnamed_len() > params.len() {416		throw!(TooManyArgsFunctionHas(params.len()))417	}418419	let mut filled_args = 0;420421	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {422		let name = params[id].name.clone();423		passed_args.insert(name, arg);424		filled_args += 1;425		Ok(())426	})?;427428	args.named_iter(ctx, tailstrict, &mut |name, arg| {429		// FIXME: O(n) for arg existence check430		let p = params431			.iter()432			.find(|p| p.name == name as &str)433			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;434		if passed_args.insert(p.name.clone(), arg).is_some() {435			throw!(BindingParameterASecondTime(name.clone()));436		}437		filled_args += 1;438		Ok(())439	})?;440441	if filled_args < params.len() {442		for param in params.iter().filter(|p| p.has_default) {443			if passed_args.contains_key(&param.name) {444				continue;445			}446			filled_args += 1;447		}448449		// Some args still wasn't filled450		if filled_args != params.len() {451			for param in params.iter().skip(args.unnamed_len()) {452				let mut found = false;453				args.named_names(&mut |name| {454					if name as &str == &param.name as &str {455						found = true;456					}457				});458				if !found {459					throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));460				}461			}462			unreachable!();463		}464	}465	Ok(passed_args)466}