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

difftreelog

source

crates/jrsonnet-evaluator/src/function/mod.rs5.0 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	PackedContextSupThis, Result, Thunk, Val,15	analyze::LFunction,16	evaluate::{17		destructure::{build_b_thunk, destruct},18		ensure_sufficient_stack, evaluate, evaluate_trivial,19	},20	function::builtin::BuiltinFunc,21};2223pub mod builtin;24mod native;25pub(crate) mod prepared;2627pub use jrsonnet_ir::function::*;28pub use native::NativeFn;29pub(crate) use prepared::PreparedFuncVal;3031/// Function callsite location.32/// Either from other jsonnet code, specified by expression location, or from native (without location).33#[derive(Clone, Copy)]34pub struct CallLocation<'l>(pub Option<&'l Span>);35impl<'l> CallLocation<'l> {36	/// Construct new location for calls coming from specified jsonnet expression location.37	pub const fn new(loc: &'l Span) -> Self {38		Self(Some(loc))39	}40}41impl CallLocation<'static> {42	/// Construct new location for calls coming from native code.43	pub const fn native() -> Self {44		Self(None)45	}46}4748/// Represents Jsonnet function defined in code.49#[derive(Trace, Educe)]50#[educe(Debug, PartialEq)]51pub struct FuncDesc {52	/// # Example53	///54	/// In expressions like this, deducted to `a`, unspecified otherwise.55	/// ```jsonnet56	/// local a = function() ...57	/// local a() ...58	/// { a: function() ... }59	/// { a() = ... }60	/// ```61	pub name: IStr,62	pub(crate) body_captures: PackedContextSupThis,6364	#[educe(PartialEq(method = Rc::ptr_eq))]65	pub func: Rc<LFunction>,66}6768impl FuncDesc {69	pub fn signature(&self) -> FunctionSignature {70		self.func.signature.clone()71	}7273	pub fn call(74		&self,75		unnamed: &[Thunk<Val>],76		named: &[Thunk<Val>],77		prepared: &PreparedCall,78	) -> Result<Val> {79		let body_ctx = self.body_captures.clone().enter(|fill, ctx| {80			// Place each provided arg-thunk into its destructured slots.81			for (param_idx, thunk) in unnamed.iter().enumerate() {82				destruct(83					&self.func.params[param_idx].destruct,84					fill,85					thunk.clone(),86					&ctx,87				);88			}89			for &(param_idx, arg_idx) in prepared.named() {90				destruct(91					&self.func.params[param_idx].destruct,92					fill,93					named[arg_idx].clone(),94					&ctx,95				);96			}9798			for &param_idx in prepared.defaults() {99				let param = &self.func.params[param_idx];100				let (shape, expr) = param.default.as_ref().expect("default exists");101				let thunk = build_b_thunk(&ctx, shape, expr.clone());102				destruct(&param.destruct, fill, thunk, &ctx);103			}104		});105106		ensure_sufficient_stack(|| evaluate(body_ctx, &self.func.body))107	}108109	pub fn evaluate_trivial(&self) -> Option<Val> {110		evaluate_trivial(&self.func.body)111	}112}113114/// Represents a Jsonnet function value, including plain functions and user-provided builtins.115#[allow(clippy::module_name_repetitions)]116#[derive(Trace, Clone)]117pub enum FuncVal {118	/// Plain function implemented in jsonnet.119	Normal(Cc<FuncDesc>),120	/// User-provided function.121	Builtin(BuiltinFunc),122}123124impl Debug for FuncVal {125	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {126		match self {127			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),128			Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),129		}130	}131}132133#[allow(clippy::unnecessary_wraps)]134#[builtin]135pub const fn builtin_id(x: Thunk<Val>) -> Thunk<Val> {136	x137}138139impl FuncVal {140	pub fn builtin(builtin: impl Builtin) -> Self {141		Self::Builtin(BuiltinFunc::new(builtin))142	}143144	pub fn identity() -> Self {145		Self::builtin(builtin_id {})146	}147148	pub fn params(&self) -> FunctionSignature {149		match self {150			Self::Builtin(i) => i.params(),151			Self::Normal(p) => p.signature(),152		}153	}154	/// Amount of non-default required arguments155	pub fn params_len(&self) -> u32 {156		self.params().iter().filter(|p| !p.has_default()).count() as u32157	}158	/// Function name, as defined in code.159	pub fn name(&self) -> IStr {160		match self {161			Self::Normal(normal) => normal.name.clone(),162			Self::Builtin(builtin) => builtin.name().into(),163		}164	}165166	pub(crate) fn evaluate_prepared(167		&self,168		prepared: &PreparedCall,169		loc: CallLocation<'_>,170		unnamed: &[Thunk<Val>],171		named: &[Thunk<Val>],172		_tailstrict: bool,173	) -> Result<Val> {174		match self {175			FuncVal::Normal(func) => func.call(unnamed, named, prepared),176			FuncVal::Builtin(b) => {177				let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named);178				b.call(loc, &args)179			}180		}181	}182183	/// Is this function an identity function.184	///185	/// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too186	pub fn is_identity(&self) -> bool {187		match self {188			Self::Builtin(b) => b.as_any().downcast_ref::<builtin_id>().is_some(),189			Self::Normal(_) => false,190		}191	}192193	pub fn evaluate_trivial(&self) -> Option<Val> {194		match self {195			Self::Normal(n) => n.evaluate_trivial(),196			Self::Builtin(_) => None,197		}198	}199}200201impl<T> From<T> for FuncVal202where203	T: Builtin,204{205	fn from(value: T) -> Self {206		Self::builtin(value)207	}208}