git.delta.rocks / jrsonnet / refs/commits / 704dc29e95f2

difftreelog

refactor drop default args parser

uslxmzwlYaroslav Bolyukin2026-05-05parent: #5858c93.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
after · crates/jrsonnet-evaluator/src/function/mod.rs
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, ContextBuilder, Result, Thunk, Val,15	analyze::{LDestruct, LExpr, LFunction},16	evaluate::{destructure::destruct, ensure_sufficient_stack, evaluate, evaluate_trivial},17	function::builtin::BuiltinFunc,18};1920pub mod builtin;21mod native;22pub(crate) mod prepared;2324pub use jrsonnet_ir::function::*;25pub use native::NativeFn;26pub(crate) use prepared::PreparedFuncVal;2728/// Function callsite location.29/// Either from other jsonnet code, specified by expression location, or from native (without location).30#[derive(Clone, Copy)]31pub struct CallLocation<'l>(pub Option<&'l Span>);32impl<'l> CallLocation<'l> {33	/// Construct new location for calls coming from specified jsonnet expression location.34	pub const fn new(loc: &'l Span) -> Self {35		Self(Some(loc))36	}37}38impl CallLocation<'static> {39	/// Construct new location for calls coming from native code.40	pub const fn native() -> Self {41		Self(None)42	}43}4445/// Represents Jsonnet function defined in code.46#[derive(Trace, Educe)]47#[educe(Debug, PartialEq)]48pub struct FuncDesc {49	/// # Example50	///51	/// In expressions like this, deducted to `a`, unspecified otherwise.52	/// ```jsonnet53	/// local a = function() ...54	/// local a() ...55	/// { a: function() ... }56	/// { a() = ... }57	/// ```58	pub name: IStr,59	/// Context, in which this function was evaluated.60	///61	/// # Example62	/// In63	/// ```jsonnet64	/// local a = 2;65	/// function() ...66	/// ```67	/// context will contain `a`.68	pub ctx: Context,6970	#[educe(PartialEq(method = Rc::ptr_eq))]71	pub func: Rc<LFunction>,72}7374impl FuncDesc {75	pub fn signature(&self) -> FunctionSignature {76		self.func.signature.clone()77	}7879	pub fn call(80		&self,81		unnamed: &[Thunk<Val>],82		named: &[Thunk<Val>],83		prepared: &PreparedCall,84	) -> Result<Val> {85		let has_defaults = !prepared.defaults().is_empty();86		let mut builder = ContextBuilder::extend(self.ctx.clone(), self.func.params.len());8788		let fctx = Context::new_future();89		for (param_idx, thunk) in unnamed.iter().enumerate() {90			destruct(91				&self.func.params[param_idx].destruct,92				thunk.clone(),93				fctx.clone(),94				&mut builder,95			);96		}9798		for &(param_idx, arg_idx) in prepared.named() {99			destruct(100				&self.func.params[param_idx].destruct,101				named[arg_idx].clone(),102				fctx.clone(),103				&mut builder,104			);105		}106107		if has_defaults {108			for &param_idx in prepared.defaults() {109				let param = &self.func.params[param_idx];110				if let Some(default_expr) = &param.default {111					let default_expr = default_expr.clone();112					let fctxc = fctx.clone();113					let thunk = Thunk!(move || {114						let ctx = fctxc.unwrap();115						evaluate(ctx, &default_expr)116					});117					destruct(&param.destruct, thunk, fctx.clone(), &mut builder);118				}119			}120		};121		let ctx = builder.build().into_future(fctx);122		ensure_sufficient_stack(|| evaluate(ctx, &self.func.body))123	}124125	pub fn evaluate_trivial(&self) -> Option<Val> {126		evaluate_trivial(&self.func.body)127	}128}129130/// Represents a Jsonnet function value, including plain functions and user-provided builtins.131#[allow(clippy::module_name_repetitions)]132#[derive(Trace, Clone)]133pub enum FuncVal {134	/// Plain function implemented in jsonnet.135	Normal(Cc<FuncDesc>),136	/// User-provided function.137	Builtin(BuiltinFunc),138}139140impl Debug for FuncVal {141	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {142		match self {143			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),144			Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),145		}146	}147}148149#[allow(clippy::unnecessary_wraps)]150#[builtin]151pub const fn builtin_id(x: Thunk<Val>) -> Thunk<Val> {152	x153}154155impl FuncVal {156	pub fn builtin(builtin: impl Builtin) -> Self {157		Self::Builtin(BuiltinFunc::new(builtin))158	}159160	pub fn params(&self) -> FunctionSignature {161		match self {162			Self::Builtin(i) => i.params(),163			Self::Normal(p) => p.signature(),164		}165	}166	/// Amount of non-default required arguments167	pub fn params_len(&self) -> u32 {168		self.params().iter().filter(|p| !p.has_default()).count() as u32169	}170	/// Function name, as defined in code.171	pub fn name(&self) -> IStr {172		match self {173			Self::Normal(normal) => normal.name.clone(),174			Self::Builtin(builtin) => builtin.name().into(),175		}176	}177178	pub(crate) fn evaluate_prepared(179		&self,180		prepared: &PreparedCall,181		loc: CallLocation<'_>,182		unnamed: &[Thunk<Val>],183		named: &[Thunk<Val>],184		_tailstrict: bool,185	) -> Result<Val> {186		match self {187			FuncVal::Normal(func) => func.call(unnamed, named, prepared),188			FuncVal::Builtin(b) => {189				let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named);190				b.call(loc, &args)191			}192		}193	}194195	/// Is this function an identity function.196	///197	/// Currently only works for builtin `std.id`, aka `Self::Id` value, and `function(x) x`.198	///199	/// This function should only be used for optimization, not for the conditional logic, i.e code should work with syntetic identity function too200	pub fn is_identity(&self) -> bool {201		match self {202			Self::Builtin(b) => b.as_any().downcast_ref::<builtin_id>().is_some(),203			Self::Normal(desc) => {204				if desc.func.params.len() != 1 {205					return false;206				}207				let param = &desc.func.params[0];208				if param.default.is_some() {209					return false;210				}211				#[allow(irrefutable_let_patterns, reason = "refutable with exp-destruct")]212				let LDestruct::Full(id) = &param.destruct else {213					return false;214				};215				matches!(&*desc.func.body, LExpr::Local(v) if v == id)216			}217		}218	}219220	pub fn evaluate_trivial(&self) -> Option<Val> {221		match self {222			Self::Normal(n) => n.evaluate_trivial(),223			Self::Builtin(_) => None,224		}225	}226}227228impl<T> From<T> for FuncVal229where230	T: Builtin,231{232	fn from(value: T) -> Self {233		Self::builtin(value)234	}235}
deletedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use std::rc::Rc;
-
-use crate::{
-	Context, ContextBuilder, Result, Thunk,
-	analyze::LFunction,
-	evaluate::{destructure::destruct, evaluate},
-};
-
-/// Creates Context with all argument default values applied
-/// and with unbound values causing error to be returned.
-pub fn parse_default_function_call(body_ctx: Context, func: &Rc<LFunction>) -> Result<Context> {
-	let fctx = Context::new_future();
-	let mut builder = ContextBuilder::extend(body_ctx, func.params.len());
-
-	for param in &func.params {
-		if let Some(default_expr) = &param.default {
-			let default_expr = default_expr.clone();
-			let fctxc = fctx.clone();
-			let thunk = Thunk!(move || {
-				let ctx = fctxc.unwrap();
-				evaluate(ctx, &default_expr)
-			});
-			destruct(&param.destruct, thunk, fctx.clone(), &mut builder);
-		} else {
-			let name = param.name.clone().unwrap_or_else(|| "<param>".into());
-			let thunk = Thunk::errored(
-				crate::error::ErrorKind::FunctionParameterNotBoundInCall(
-					jrsonnet_ir::function::ParamName::Named(name),
-					jrsonnet_ir::function::FunctionSignature::empty(),
-				)
-				.into(),
-			);
-			destruct(&param.destruct, thunk, fctx.clone(), &mut builder);
-		}
-	}
-
-	let ctx = builder.build().into_future(fctx);
-	Ok(ctx)
-}