git.delta.rocks / jrsonnet / refs/commits / 3c251da01a10

difftreelog

feat minimal tco

yzrzokypYaroslav Bolyukin2026-05-07parent: #e468723.patch.diff
in: master

3 files changed

modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
5use crate::{5use crate::{
6 Context, LocalsFrame, PackedContext, Result, SupThis, Thunk, Unbound, Val,6 Context, LocalsFrame, PackedContext, Result, SupThis, Thunk, Unbound, Val,
7 analyze::{7 analyze::{ClosureShape, LBind, LDestruct, LDestructField, LDestructRest, LocalSlot},
8 ClosureShape, LBind, LDestruct, LDestructField, LDestructRest, LLocalExpr, LocalSlot,
9 },
10 bail,8 bail,
11 evaluate::evaluate,9 evaluate::evaluate,
189 }187 }
190}188}
191
192pub fn evaluate_local_expr(parent: Context, l: &LLocalExpr) -> Result<Val> {
193 let ctx = parent
194 .pack_captures_sup_this(&l.frame_shape)
195 .enter(|fill, ctx| {
196 fill_letrec_binds(fill, ctx, &l.binds);
197 });
198 evaluate(ctx, &l.body)
199}
200189
201pub trait CloneableUnbound<T>: Unbound<Bound = T> + Clone {}190pub trait CloneableUnbound<T>: Unbound<Bound = T> + Clone {}
202impl<V, T> CloneableUnbound<T> for V where V: Unbound<Bound = T> + Clone {}191impl<V, T> CloneableUnbound<T> for V where V: Unbound<Bound = T> + Clone {}
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -7,7 +7,7 @@
 
 use self::{
 	compspec::{evaluate_arr_comp, evaluate_obj_comp},
-	destructure::{evaluate_local_expr, evaluate_locals_unbound},
+	destructure::evaluate_locals_unbound,
 	operator::evaluate_binary_op_special,
 };
 use crate::{
@@ -116,129 +116,143 @@
 }
 
 #[allow(clippy::too_many_lines)]
-pub fn evaluate(ctx: Context, expr: &LExpr) -> Result<Val> {
-	Ok(match expr {
-		LExpr::Null => Val::Null,
-		LExpr::Bool(b) => Val::Bool(*b),
-		LExpr::Str(s) => Val::string(s.clone()),
-		LExpr::Num(n) => Val::Num(*n),
-		LExpr::Slot(slot) => ctx.slot(*slot).evaluate()?,
-		LExpr::BadLocal(name) => panic!("unresolvable reference: {name}"),
-		LExpr::Arr { shape, items } => Val::Arr(ArrValue::expr(ctx, shape, items.clone())),
-		LExpr::UnaryOp(op, value) => {
-			let value = evaluate(ctx, value)?;
-			evaluate_unary_op(*op, &value)?
-		}
-		LExpr::BinaryOp { lhs, op, rhs } => evaluate_binary_op_special(ctx, lhs, *op, rhs)?,
-		LExpr::LocalExpr(local_expr) => evaluate_local_expr(ctx, local_expr)?,
-		LExpr::IfElse {
-			cond,
-			cond_then,
-			cond_else,
-		} => {
-			let cond_val = evaluate(ctx.clone(), cond)?;
-			let Val::Bool(b) = cond_val else {
-				bail!(TypeMismatch(
-					"if condition",
-					vec![ValType::Bool],
-					cond_val.value_type()
-				))
-			};
-			if b {
-				evaluate(ctx, cond_then)?
-			} else if let Some(e) = cond_else {
-				evaluate(ctx, e)?
-			} else {
+pub fn evaluate(mut ctx: Context, mut expr: &LExpr) -> Result<Val> {
+	loop {
+		return Ok(match expr {
+			LExpr::Null => Val::Null,
+			LExpr::Bool(b) => Val::Bool(*b),
+			LExpr::Str(s) => Val::string(s.clone()),
+			LExpr::Num(n) => Val::Num(*n),
+			LExpr::Slot(slot) => ctx.slot(*slot).evaluate()?,
+			LExpr::BadLocal(name) => panic!("unresolvable reference: {name}"),
+			LExpr::Arr { shape, items } => Val::Arr(ArrValue::expr(ctx, shape, items.clone())),
+			LExpr::UnaryOp(op, value) => {
+				let value = evaluate(ctx, value)?;
+				evaluate_unary_op(*op, &value)?
+			}
+			LExpr::BinaryOp { lhs, op, rhs } => evaluate_binary_op_special(ctx, lhs, *op, rhs)?,
+			LExpr::LocalExpr(l) => {
+				ctx = ctx
+					.pack_captures_sup_this(&l.frame_shape)
+					.enter(|fill, ctx| {
+						fill_letrec_binds(fill, ctx, &l.binds);
+					});
+				expr = &l.body;
+				continue;
+			}
+			LExpr::IfElse {
+				cond,
+				cond_then,
+				cond_else,
+			} => {
+				let cond_val = evaluate(ctx.clone(), cond)?;
+				let Val::Bool(b) = cond_val else {
+					bail!(TypeMismatch(
+						"if condition",
+						vec![ValType::Bool],
+						cond_val.value_type()
+					))
+				};
+				if b {
+					expr = cond_then;
+					continue;
+				} else if let Some(e) = cond_else {
+					expr = e;
+					continue;
+				}
 				Val::Null
 			}
-		}
-		LExpr::Error(s, e) => in_frame(
-			CallLocation::new(s),
-			|| "error statement".to_owned(),
-			|| bail!(RuntimeError(evaluate(ctx, e)?.to_string()?,)),
-		)?,
-		LExpr::AssertExpr { assert, rest } => {
-			evaluate_assert(ctx.clone(), assert)?;
-			evaluate(ctx, rest)?
-		}
+			LExpr::Error(s, e) => in_frame(
+				CallLocation::new(s),
+				|| "error statement".to_owned(),
+				|| bail!(RuntimeError(evaluate(ctx, e)?.to_string()?,)),
+			)?,
+			LExpr::AssertExpr { assert, rest } => {
+				evaluate_assert(ctx.clone(), assert)?;
+				expr = rest;
+				continue;
+			}
 
-		LExpr::Function(func) => evaluate_method(
-			ctx,
-			func.name.clone().unwrap_or_else(names::anonymous),
-			func,
-		),
-		LExpr::IdentityFunction => Val::Func(FuncVal::identity()),
-		LExpr::Apply {
-			applicable,
-			args,
-			tailstrict,
-		} => evaluate_apply(
-			ctx,
-			applicable,
-			args,
-			CallLocation::new(&args.span),
-			*tailstrict,
-		)?,
-		LExpr::Index { indexable, parts } => evaluate_index(ctx, indexable, parts)?,
-		LExpr::Obj(body) => evaluate_obj_body(None, ctx, body)?,
-		LExpr::ObjExtend(lhs, body) => {
-			let lhs_val = evaluate(ctx.clone(), lhs)?;
-			let Val::Obj(lhs_obj) = lhs_val else {
-				bail!(TypeMismatch(
-					"object extend lhs",
-					vec![ValType::Obj],
-					lhs_val.value_type(),
-				))
-			};
-			evaluate_obj_body(Some(lhs_obj), ctx, body)?
-		}
-		LExpr::ArrComp(comp) => evaluate_arr_comp(ctx, comp)?,
-		LExpr::Slice(slice) => {
-			let val = evaluate(ctx.clone(), &slice.value)?;
-			let indexable = val.into_indexable()?;
-			let start = slice
-				.start
-				.as_ref()
-				.map(|e| evaluate(ctx.clone(), e))
-				.transpose()?
-				.map(|v| -> Result<i32> { i32::from_untyped(v).description("slice start value") })
-				.transpose()?;
-			let end = slice
-				.end
-				.as_ref()
-				.map(|e| evaluate(ctx.clone(), e))
-				.transpose()?
-				.map(|v| -> Result<i32> { i32::from_untyped(v).description("slice end value") })
-				.transpose()?;
-			let step = slice
-				.step
-				.as_ref()
-				.map(|e| evaluate(ctx, e))
-				.transpose()?
-				.map(|v| -> Result<BoundedUsize<1, { i32::MAX as usize }>> {
-					BoundedUsize::from_untyped(v).description("slice step value")
+			LExpr::Function(func) => evaluate_method(
+				ctx,
+				func.name.clone().unwrap_or_else(names::anonymous),
+				func,
+			),
+			LExpr::IdentityFunction => Val::Func(FuncVal::identity()),
+			LExpr::Apply {
+				applicable,
+				args,
+				tailstrict,
+			} => evaluate_apply(
+				ctx,
+				applicable,
+				args,
+				CallLocation::new(&args.span),
+				*tailstrict,
+			)?,
+			LExpr::Index { indexable, parts } => evaluate_index(ctx, indexable, parts)?,
+			LExpr::Obj(body) => evaluate_obj_body(None, ctx, body)?,
+			LExpr::ObjExtend(lhs, body) => {
+				let lhs_val = evaluate(ctx.clone(), lhs)?;
+				let Val::Obj(lhs_obj) = lhs_val else {
+					bail!(TypeMismatch(
+						"object extend lhs",
+						vec![ValType::Obj],
+						lhs_val.value_type(),
+					))
+				};
+				evaluate_obj_body(Some(lhs_obj), ctx, body)?
+			}
+			LExpr::ArrComp(comp) => evaluate_arr_comp(ctx, comp)?,
+			LExpr::Slice(slice) => {
+				let val = evaluate(ctx.clone(), &slice.value)?;
+				let indexable = val.into_indexable()?;
+				let start = slice
+					.start
+					.as_ref()
+					.map(|e| evaluate(ctx.clone(), e))
+					.transpose()?
+					.map(|v| -> Result<i32> {
+						i32::from_untyped(v).description("slice start value")
+					})
+					.transpose()?;
+				let end = slice
+					.end
+					.as_ref()
+					.map(|e| evaluate(ctx.clone(), e))
+					.transpose()?
+					.map(|v| -> Result<i32> { i32::from_untyped(v).description("slice end value") })
+					.transpose()?;
+				let step = slice
+					.step
+					.as_ref()
+					.map(|e| evaluate(ctx, e))
+					.transpose()?
+					.map(|v| -> Result<BoundedUsize<1, { i32::MAX as usize }>> {
+						BoundedUsize::from_untyped(v).description("slice step value")
+					})
+					.transpose()?;
+				Val::from(indexable.slice32(start, end, step)?)
+			}
+			LExpr::Super => Val::Obj(ctx.try_sup_this()?.standalone_super().ok_or(NoSuperFound)?),
+			LExpr::Import {
+				kind,
+				kind_span,
+				path,
+			} => with_state(|state| {
+				let resolved = state.resolve_from(kind_span.0.source_path(), &path.clone())?;
+				Ok::<_, Error>(match kind.value {
+					ImportKind::Normal => in_frame(
+						CallLocation::new(&kind.span),
+						|| "import".to_string(),
+						|| state.import_resolved(resolved),
+					)?,
+					ImportKind::Str => Val::string(state.import_resolved_str(resolved)?),
+					ImportKind::Bin => Val::arr(state.import_resolved_bin(resolved)?),
 				})
-				.transpose()?;
-			Val::from(indexable.slice32(start, end, step)?)
-		}
-		LExpr::Super => Val::Obj(ctx.try_sup_this()?.standalone_super().ok_or(NoSuperFound)?),
-		LExpr::Import {
-			kind,
-			kind_span,
-			path,
-		} => with_state(|state| {
-			let resolved = state.resolve_from(kind_span.0.source_path(), &path.clone())?;
-			Ok::<_, Error>(match kind.value {
-				ImportKind::Normal => in_frame(
-					CallLocation::new(&kind.span),
-					|| "import".to_string(),
-					|| state.import_resolved(resolved),
-				)?,
-				ImportKind::Str => Val::string(state.import_resolved_str(resolved)?),
-				ImportKind::Bin => Val::arr(state.import_resolved_bin(resolved)?),
-			})
-		})?,
-	})
+			})?,
+		});
+	}
 }
 
 fn evaluate_apply(
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -14,7 +14,8 @@
 	Context, PackedContextSupThis, Result, Thunk, Val,
 	analyze::LFunction,
 	arr::arridx,
-	evaluate::{destructure::destruct, ensure_sufficient_stack, evaluate, evaluate_trivial},
+	ensure_sufficient_stack,
+	evaluate::{destructure::destruct, evaluate, evaluate_trivial},
 	function::builtin::BuiltinFunc,
 };
 
@@ -68,7 +69,7 @@
 		self.func.signature.clone()
 	}
 
-	pub fn call(
+	fn call(
 		&self,
 		unnamed: &[Thunk<Val>],
 		named: &[Thunk<Val>],