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

difftreelog

source

crates/jrsonnet-evaluator/src/function/mod.rs5.2 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::{ExprLocation, LocExpr, ParamsDesc};89use self::{10	builtin::{Builtin, StaticBuiltin},11	native::NativeDesc,12	parse::{parse_default_function_call, parse_function_call},13};14use crate::{evaluate, gc::TraceBox, typed::Any, Context, Result, State, Val};1516pub mod arglike;17pub mod builtin;18pub mod native;19pub mod parse;2021/// Function callsite location.22/// Either from other jsonnet code, specified by expression location, or from native (without location).23#[derive(Clone, Copy)]24pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);25impl<'l> CallLocation<'l> {26	/// Construct new location for calls coming from specified jsonnet expression location.27	pub const fn new(loc: &'l ExprLocation) -> Self {28		Self(Some(loc))29	}30}31impl CallLocation<'static> {32	/// Construct new location for calls coming from native code.33	pub const fn native() -> Self {34		Self(None)35	}36}3738/// Represents Jsonnet function defined in code.39#[derive(Debug, PartialEq, Trace)]40pub struct FuncDesc {41	/// # Example42	///43	/// In expressions like this, deducted to `a`, unspecified otherwise.44	/// ```jsonnet45	/// local a = function() ...46	/// local a() ...47	/// { a: function() ... }48	/// { a() = ... }49	/// ```50	pub name: IStr,51	/// Context, in which this function was evaluated.52	///53	/// # Example54	/// In55	/// ```jsonnet56	/// local a = 2;57	/// function() ...58	/// ```59	/// context will contain `a`.60	pub ctx: Context,6162	/// Function parameter definition63	pub params: ParamsDesc,64	/// Function body65	pub body: LocExpr,66}67impl FuncDesc {68	/// Create body context, but fill arguments without defaults with lazy error69	pub fn default_body_context(&self) -> Result<Context> {70		parse_default_function_call(self.ctx.clone(), &self.params)71	}7273	/// Create context, with which body code will run74	pub fn call_body_context(75		&self,76		s: State,77		call_ctx: Context,78		args: &dyn ArgsLike,79		tailstrict: bool,80	) -> Result<Context> {81		parse_function_call(82			s,83			call_ctx,84			self.ctx.clone(),85			&self.params,86			args,87			tailstrict,88		)89	}90}9192/// Represents a Jsonnet function value, including plain functions and user-provided builtins.93#[allow(clippy::module_name_repetitions)]94#[derive(Trace, Clone)]95pub enum FuncVal {96	/// Identity function, kept this way for comparsions.97	Id,98	/// Plain function implemented in jsonnet.99	Normal(Cc<FuncDesc>),100	/// Standard library function.101	StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),102	/// User-provided function.103	Builtin(Cc<TraceBox<dyn Builtin>>),104}105106impl Debug for FuncVal {107	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {108		match self {109			Self::Id => f.debug_tuple("Id").finish(),110			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),111			Self::StaticBuiltin(arg0) => {112				f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()113			}114			Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),115		}116	}117}118119impl FuncVal {120	/// Amount of non-default required arguments121	pub fn params_len(&self) -> usize {122		match self {123			Self::Id => 1,124			Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),125			Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),126			Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),127		}128	}129	/// Function name, as defined in code.130	pub fn name(&self) -> IStr {131		match self {132			Self::Id => "id".into(),133			Self::Normal(normal) => normal.name.clone(),134			Self::StaticBuiltin(builtin) => builtin.name().into(),135			Self::Builtin(builtin) => builtin.name().into(),136		}137	}138	/// Call function using arguments evaluated in specified `call_ctx` [`Context`].139	///140	/// If `tailstrict` is specified - then arguments will be evaluated before being passed to function body.141	pub fn evaluate(142		&self,143		s: State,144		call_ctx: Context,145		loc: CallLocation<'_>,146		args: &dyn ArgsLike,147		tailstrict: bool,148	) -> Result<Val> {149		match self {150			Self::Id => {151				#[allow(clippy::unnecessary_wraps)]152				#[builtin]153				const fn builtin_id(v: Any) -> Result<Any> {154					Ok(v)155				}156				static ID: &builtin_id = &builtin_id {};157158				ID.call(s, call_ctx, loc, args)159			}160			Self::Normal(func) => {161				let body_ctx = func.call_body_context(s.clone(), call_ctx, args, tailstrict)?;162				evaluate(s, body_ctx, &func.body)163			}164			Self::StaticBuiltin(b) => b.call(s, call_ctx, loc, args),165			Self::Builtin(b) => b.call(s, call_ctx, loc, args),166		}167	}168	/// Helper method, which calls [`Self::evaluate`] with sensible defaults for native code.169	pub fn evaluate_simple(&self, s: State, args: &dyn ArgsLike) -> Result<Val> {170		self.evaluate(s, Context::default(), CallLocation::native(), args, true)171	}172	/// Convert jsonnet function to plain `Fn` value.173	pub fn into_native<D: NativeDesc>(self) -> D::Value {174		D::into_native(self)175	}176177	/// Is this function an indentity function.178	///179	/// Currently only works for builtin `std.id`, aka `Self::Id` value, `function(x) x` defined by jsonnet will not count as identity.180	pub const fn is_identity(&self) -> bool {181		matches!(self, Self::Id)182	}183	/// Identity function value.184	pub const fn identity() -> Self {185		Self::Id186	}187}