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

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	evaluate::{destructure::destruct, ensure_sufficient_stack, evaluate, evaluate_trivial},18	function::builtin::BuiltinFunc,19};2021pub mod builtin;22mod native;23pub(crate) mod prepared;2425pub use jrsonnet_ir::function::*;26pub use native::NativeFn;27pub(crate) use prepared::PreparedFuncVal;2829/// Function callsite location.30/// Either from other jsonnet code, specified by expression location, or from native (without location).31#[derive(Clone, Copy)]32pub struct CallLocation<'l>(pub Option<&'l Span>);33impl<'l> CallLocation<'l> {34	/// Construct new location for calls coming from specified jsonnet expression location.35	pub const fn new(loc: &'l Span) -> Self {36		Self(Some(loc))37	}38}39impl CallLocation<'static> {40	/// Construct new location for calls coming from native code.41	pub const fn native() -> Self {42		Self(None)43	}44}4546/// Represents Jsonnet function defined in code.47#[derive(Trace, Educe)]48#[educe(Debug, PartialEq)]49pub struct FuncDesc {50	/// # Example51	///52	/// In expressions like this, deducted to `a`, unspecified otherwise.53	/// ```jsonnet54	/// local a = function() ...55	/// local a() ...56	/// { a: function() ... }57	/// { a() = ... }58	/// ```59	pub name: IStr,60	pub(crate) body_captures: PackedContextSupThis,6162	#[educe(PartialEq(method = Rc::ptr_eq))]63	pub func: Rc<LFunction>,64}6566impl FuncDesc {67	pub fn signature(&self) -> FunctionSignature {68		self.func.signature.clone()69	}7071	pub fn call(72		&self,73		unnamed: &[Thunk<Val>],74		named: &[Thunk<Val>],75		prepared: &PreparedCall,76	) -> Result<Val> {77		let body_ctx = self.body_captures.clone().enter(|fill, ctx| {78			// Place each provided arg-thunk into its destructured slots.79			for (param_idx, thunk) in unnamed.iter().enumerate() {80				destruct(81					&self.func.params[param_idx].destruct,82					fill,83					thunk.clone(),84					ctx,85				);86			}87			for &(param_idx, arg_idx) in prepared.named() {88				destruct(89					&self.func.params[param_idx].destruct,90					fill,91					named[arg_idx].clone(),92					ctx,93				);94			}9596			for &param_idx in prepared.defaults() {97				let param = &self.func.params[param_idx];98				let (shape, expr) = param.default.as_ref().expect("default exists");99				let expr = expr.clone();100				let env = Context::enter_using(ctx, shape);101102				destruct(103					&param.destruct,104					fill,105					Thunk!(move || evaluate(env, &expr)),106					ctx,107				);108			}109		});110111		ensure_sufficient_stack(|| evaluate(body_ctx, &self.func.body))112	}113114	pub fn evaluate_trivial(&self) -> Option<Val> {115		evaluate_trivial(&self.func.body)116	}117}118119/// Represents a Jsonnet function value, including plain functions and user-provided builtins.120#[allow(clippy::module_name_repetitions)]121#[derive(Trace, Clone)]122pub enum FuncVal {123	/// Plain function implemented in jsonnet.124	Normal(Cc<FuncDesc>),125	/// User-provided function.126	Builtin(BuiltinFunc),127}128129impl Debug for FuncVal {130	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {131		match self {132			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),133			Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),134		}135	}136}137138#[allow(clippy::unnecessary_wraps)]139#[builtin]140pub const fn builtin_id(x: Thunk<Val>) -> Thunk<Val> {141	x142}143144impl FuncVal {145	pub fn builtin(builtin: impl Builtin) -> Self {146		Self::Builtin(BuiltinFunc::new(builtin))147	}148149	pub fn identity() -> Self {150		Self::builtin(builtin_id {})151	}152153	pub fn params(&self) -> FunctionSignature {154		match self {155			Self::Builtin(i) => i.params(),156			Self::Normal(p) => p.signature(),157		}158	}159	/// Amount of non-default required arguments160	pub fn params_len32(&self) -> u32 {161		arridx(self.params().iter().filter(|p| !p.has_default()).count())162	}163	/// Function name, as defined in code.164	pub fn name(&self) -> IStr {165		match self {166			Self::Normal(normal) => normal.name.clone(),167			Self::Builtin(builtin) => builtin.name().into(),168		}169	}170171	pub(crate) fn evaluate_prepared(172		&self,173		prepared: &PreparedCall,174		loc: CallLocation<'_>,175		unnamed: &[Thunk<Val>],176		named: &[Thunk<Val>],177		_tailstrict: bool,178	) -> Result<Val> {179		match self {180			FuncVal::Normal(func) => func.call(unnamed, named, prepared),181			FuncVal::Builtin(b) => {182				let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named);183				b.call(loc, &args)184			}185		}186	}187188	/// Is this function an identity function.189	///190	/// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too191	pub fn is_identity(&self) -> bool {192		match self {193			Self::Builtin(b) => b.as_any().downcast_ref::<builtin_id>().is_some(),194			Self::Normal(_) => false,195		}196	}197198	pub fn evaluate_trivial(&self) -> Option<Val> {199		match self {200			Self::Normal(n) => n.evaluate_trivial(),201			Self::Builtin(_) => None,202		}203	}204}205206impl<T> From<T> for FuncVal207where208	T: Builtin,209{210	fn from(value: T) -> Self {211		Self::builtin(value)212	}213}