git.delta.rocks / jrsonnet / refs/commits / 54c4db58ad3d

difftreelog

source

crates/jrsonnet-evaluator/src/function/mod.rs5.9 KiBsourcehistory
1use std::fmt::Debug;23pub use arglike::{ArgLike, ArgsLike, TlaArg};4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6pub use jrsonnet_macros::builtin;7use jrsonnet_parser::{Destruct, Expr, ExprLocation, LocExpr, ParamsDesc};89use self::{10	arglike::OptionalContext,11	builtin::{Builtin, StaticBuiltin},12	native::NativeDesc,13	parse::{parse_default_function_call, parse_function_call},14};15use crate::{16	evaluate, evaluate_trivial, gc::TraceBox, typed::Any, Context, ContextBuilder, Result, Val,17};1819pub mod arglike;20pub mod builtin;21pub mod native;22pub mod parse;2324/// Function callsite location.25/// Either from other jsonnet code, specified by expression location, or from native (without location).26#[derive(Clone, Copy)]27pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);28impl<'l> CallLocation<'l> {29	/// Construct new location for calls coming from specified jsonnet expression location.30	pub const fn new(loc: &'l ExprLocation) -> Self {31		Self(Some(loc))32	}33}34impl CallLocation<'static> {35	/// Construct new location for calls coming from native code.36	pub const fn native() -> Self {37		Self(None)38	}39}4041/// Represents Jsonnet function defined in code.42#[derive(Debug, PartialEq, Trace)]43pub struct FuncDesc {44	/// # Example45	///46	/// In expressions like this, deducted to `a`, unspecified otherwise.47	/// ```jsonnet48	/// local a = function() ...49	/// local a() ...50	/// { a: function() ... }51	/// { a() = ... }52	/// ```53	pub name: IStr,54	/// Context, in which this function was evaluated.55	///56	/// # Example57	/// In58	/// ```jsonnet59	/// local a = 2;60	/// function() ...61	/// ```62	/// context will contain `a`.63	pub ctx: Context,6465	/// Function parameter definition66	pub params: ParamsDesc,67	/// Function body68	pub body: LocExpr,69}70impl FuncDesc {71	/// Create body context, but fill arguments without defaults with lazy error72	pub fn default_body_context(&self) -> Result<Context> {73		parse_default_function_call(self.ctx.clone(), &self.params)74	}7576	/// Create context, with which body code will run77	pub fn call_body_context(78		&self,79		call_ctx: Context,80		args: &dyn ArgsLike,81		tailstrict: bool,82	) -> Result<Context> {83		parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)84	}8586	pub fn evaluate_trivial(&self) -> Option<Val> {87		evaluate_trivial(&self.body)88	}89}9091/// Represents a Jsonnet function value, including plain functions and user-provided builtins.92#[allow(clippy::module_name_repetitions)]93#[derive(Trace, Clone)]94pub enum FuncVal {95	/// Identity function, kept this way for comparsions.96	Id,97	/// Plain function implemented in jsonnet.98	Normal(Cc<FuncDesc>),99	/// Standard library function.100	StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),101	/// User-provided function.102	Builtin(Cc<TraceBox<dyn Builtin>>),103}104105impl Debug for FuncVal {106	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {107		match self {108			Self::Id => f.debug_tuple("Id").finish(),109			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),110			Self::StaticBuiltin(arg0) => {111				f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()112			}113			Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),114		}115	}116}117118impl FuncVal {119	/// Amount of non-default required arguments120	pub fn params_len(&self) -> usize {121		match self {122			Self::Id => 1,123			Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),124			Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),125			Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),126		}127	}128	/// Function name, as defined in code.129	pub fn name(&self) -> IStr {130		match self {131			Self::Id => "id".into(),132			Self::Normal(normal) => normal.name.clone(),133			Self::StaticBuiltin(builtin) => builtin.name().into(),134			Self::Builtin(builtin) => builtin.name().into(),135		}136	}137	/// Call function using arguments evaluated in specified `call_ctx` [`Context`].138	///139	/// If `tailstrict` is specified - then arguments will be evaluated before being passed to function body.140	pub fn evaluate(141		&self,142		call_ctx: Context,143		loc: CallLocation<'_>,144		args: &dyn ArgsLike,145		tailstrict: bool,146	) -> Result<Val> {147		match self {148			Self::Id => {149				#[allow(clippy::unnecessary_wraps)]150				#[builtin]151				const fn builtin_id(v: Any) -> Result<Any> {152					Ok(v)153				}154				static ID: &builtin_id = &builtin_id {};155156				ID.call(call_ctx, loc, args)157			}158			Self::Normal(func) => {159				let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;160				evaluate(body_ctx, &func.body)161			}162			Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),163			Self::Builtin(b) => b.call(call_ctx, loc, args),164		}165	}166	pub fn evaluate_simple<A: ArgsLike + OptionalContext>(&self, args: &A) -> Result<Val> {167		self.evaluate(168			ContextBuilder::dangerous_empty_state().build(),169			CallLocation::native(),170			args,171			true,172		)173	}174	/// Convert jsonnet function to plain `Fn` value.175	pub fn into_native<D: NativeDesc>(self) -> D::Value {176		D::into_native(self)177	}178179	/// Is this function an indentity function.180	///181	/// Currently only works for builtin `std.id`, aka `Self::Id` value, and `function(x) x`.182	///183	/// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too184	pub fn is_identity(&self) -> bool {185		match self {186			Self::Id => true,187			Self::Normal(desc) => {188				if desc.params.len() != 1 {189					return false;190				}191				let param = &desc.params[0];192				if param.1.is_some() {193					return false;194				}195				#[allow(clippy::infallible_destructuring_match)]196				let id = match &param.0 {197					Destruct::Full(id) => id,198					#[cfg(feature = "exp-destruct")]199					_ => return false,200				};201				&desc.body.0 as &Expr == &Expr::Var(id.clone())202			}203			_ => false,204		}205	}206	/// Identity function value.207	pub const fn identity() -> Self {208		Self::Id209	}210211	pub fn evaluate_trivial(&self) -> Option<Val> {212		match self {213			FuncVal::Normal(n) => n.evaluate_trivial(),214			_ => None,215		}216	}217}