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

difftreelog

source

crates/jrsonnet-evaluator/src/function/mod.rs5.1 KiBsourcehistory
1use std::{fmt::Debug, rc::Rc};23use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6use jrsonnet_ir::Span;7pub use jrsonnet_macros::builtin;89use self::{10	builtin::Builtin,11	prepared::{PreparedCall, parse_prepared_builtin_call},12};13use crate::{14	Context, PackedContextSupThis, Result, Thunk, Val,15	analyze::LFunction,16	arr::arridx,17	ensure_sufficient_stack,18	evaluate::{destructure::destruct, evaluate, evaluate_trivial},19	function::builtin::BuiltinFunc,20};2122pub mod builtin;23mod native;24pub(crate) mod prepared;2526pub use jrsonnet_ir::function::*;27pub use native::NativeFn;28pub(crate) use prepared::PreparedFuncVal;2930/// Function callsite location.31/// Either from other jsonnet code, specified by expression location, or from native (without location).32#[derive(Clone, Copy)]33pub struct CallLocation<'l>(pub Option<&'l Span>);34impl<'l> CallLocation<'l> {35	/// Construct new location for calls coming from specified jsonnet expression location.36	pub const fn new(loc: &'l Span) -> Self {37		Self(Some(loc))38	}39}40impl CallLocation<'static> {41	/// Construct new location for calls coming from native code.42	pub const fn native() -> Self {43		Self(None)44	}45}4647/// Represents Jsonnet function defined in code.48#[derive(Trace, Educe)]49#[educe(Debug, PartialEq)]50pub struct FuncDesc {51	/// # Example52	///53	/// In expressions like this, deducted to `a`, unspecified otherwise.54	/// ```jsonnet55	/// local a = function() ...56	/// local a() ...57	/// { a: function() ... }58	/// { a() = ... }59	/// ```60	pub name: IStr,61	pub(crate) body_captures: PackedContextSupThis,6263	#[educe(PartialEq(method = Rc::ptr_eq))]64	pub func: Rc<LFunction>,65}6667impl FuncDesc {68	pub fn signature(&self) -> FunctionSignature {69		self.func.signature.clone()70	}7172	fn call(73		&self,74		unnamed: &[Thunk<Val>],75		named: &[Thunk<Val>],76		prepared: &PreparedCall,77	) -> Result<Val> {78		let body_ctx = self.body_captures.clone().enter(|fill, ctx| {79			// Place each provided arg-thunk into its destructured slots.80			for (param_idx, thunk) in unnamed.iter().enumerate() {81				destruct(82					&self.func.params[param_idx].destruct,83					fill,84					thunk.clone(),85					ctx,86				);87			}88			for &(param_idx, arg_idx) in prepared.named() {89				destruct(90					&self.func.params[param_idx].destruct,91					fill,92					named[arg_idx].clone(),93					ctx,94				);95			}9697			for &param_idx in prepared.defaults() {98				let param = &self.func.params[param_idx];99				let (shape, expr) = param.default.as_ref().expect("default exists");100				let expr = expr.clone();101				let env = Context::enter_using(ctx, shape);102103				destruct(104					&param.destruct,105					fill,106					Thunk!(move || evaluate(env, &expr)),107					ctx,108				);109			}110		});111112		ensure_sufficient_stack(|| evaluate(body_ctx, &self.func.body))113	}114115	pub fn evaluate_trivial(&self) -> Option<Val> {116		evaluate_trivial(&self.func.body)117	}118}119120/// Represents a Jsonnet function value, including plain functions and user-provided builtins.121#[allow(clippy::module_name_repetitions)]122#[derive(Trace, Clone)]123pub enum FuncVal {124	/// Plain function implemented in jsonnet.125	Normal(Cc<FuncDesc>),126	/// User-provided function.127	Builtin(BuiltinFunc),128}129130impl Debug for FuncVal {131	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {132		match self {133			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),134			Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),135		}136	}137}138139#[allow(clippy::unnecessary_wraps)]140#[builtin]141pub const fn builtin_id(x: Thunk<Val>) -> Thunk<Val> {142	x143}144145impl FuncVal {146	pub fn builtin(builtin: impl Builtin) -> Self {147		Self::Builtin(BuiltinFunc::new(builtin))148	}149150	pub fn identity() -> Self {151		Self::builtin(builtin_id {})152	}153154	pub fn params(&self) -> FunctionSignature {155		match self {156			Self::Builtin(i) => i.params(),157			Self::Normal(p) => p.signature(),158		}159	}160	/// Amount of non-default required arguments161	pub fn params_len32(&self) -> u32 {162		arridx(self.params().iter().filter(|p| !p.has_default()).count())163	}164	/// Function name, as defined in code.165	pub fn name(&self) -> IStr {166		match self {167			Self::Normal(normal) => normal.name.clone(),168			Self::Builtin(builtin) => builtin.name().into(),169		}170	}171172	pub(crate) fn evaluate_prepared(173		&self,174		prepared: &PreparedCall,175		loc: CallLocation<'_>,176		unnamed: &[Thunk<Val>],177		named: &[Thunk<Val>],178		_tailstrict: bool,179	) -> Result<Val> {180		match self {181			FuncVal::Normal(func) => func.call(unnamed, named, prepared),182			FuncVal::Builtin(b) => {183				let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named);184				b.call(loc, &args)185			}186		}187	}188189	/// Is this function an identity function.190	///191	/// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too192	pub fn is_identity(&self) -> bool {193		match self {194			Self::Builtin(b) => b.as_any().downcast_ref::<builtin_id>().is_some(),195			Self::Normal(_) => false,196		}197	}198199	pub fn evaluate_trivial(&self) -> Option<Val> {200		match self {201			Self::Normal(n) => n.evaluate_trivial(),202			Self::Builtin(_) => None,203		}204	}205}206207impl<T> From<T> for FuncVal208where209	T: Builtin,210{211	fn from(value: T) -> Self {212		Self::builtin(value)213	}214}