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

difftreelog

feat object destructuring defaults

Yaroslav Bolyukin2022-06-03parent: #3961a53.patch.diff
in: master

5 files changed

modifiedcrates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/dynamic.rs
1use std::cell::RefCell;23use gcmodule::{Cc, Trace};45#[derive(Clone, Trace)]6pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);7impl<T: Trace + 'static> Pending<T> {8	pub fn new() -> Self {9		Self(Cc::new(RefCell::new(None)))10	}11	/// # Panics12	/// If wrapper is filled already13	pub fn fill(self, value: T) {14		assert!(self.0.borrow().is_none(), "wrapper is filled already");15		self.0.borrow_mut().replace(value);16	}17}18impl<T: Clone + Trace + 'static> Pending<T> {19	/// # Panics20	/// If wrapper is not yet filled21	pub fn unwrap(&self) -> T {22		self.0.borrow().as_ref().cloned().unwrap()23	}24}2526impl<T: Trace + 'static> Default for Pending<T> {27	fn default() -> Self {28		Self::new()29	}30}
after · crates/jrsonnet-evaluator/src/dynamic.rs
1use std::cell::RefCell;23use gcmodule::{Cc, Trace};45#[derive(Clone, Trace)]6pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);7impl<T: Trace + 'static> Pending<T> {8	pub fn new() -> Self {9		Self(Cc::new(RefCell::new(None)))10	}11	pub fn new_filled(v: T) -> Self {12		Self(Cc::new(RefCell::new(Some(v))))13	}14	/// # Panics15	/// If wrapper is filled already16	pub fn fill(self, value: T) {17		assert!(self.0.borrow().is_none(), "wrapper is filled already");18		self.0.borrow_mut().replace(value);19	}20}21impl<T: Clone + Trace + 'static> Pending<T> {22	/// # Panics23	/// If wrapper is not yet filled24	pub fn unwrap(&self) -> T {25		self.0.borrow().as_ref().cloned().unwrap()26	}27}2829impl<T: Trace + 'static> Default for Pending<T> {30	fn default() -> Self {31		Self::new()32	}33}
modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -12,9 +12,11 @@
 };
 
 #[allow(clippy::too_many_lines)]
+#[allow(unused_variables)]
 pub fn destruct(
 	d: &Destruct,
 	parent: Thunk<Val>,
+	fctx: Pending<Context>,
 	new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,
 ) -> Result<()> {
 	match d {
@@ -89,6 +91,7 @@
 							full: full.clone(),
 							index: i,
 						})),
+						fctx.clone(),
 						new_bindings,
 					)?;
 				}
@@ -119,6 +122,7 @@
 							start: start.len(),
 							end: end.len(),
 						})),
+						fctx.clone(),
 						new_bindings,
 					)?;
 				}
@@ -151,6 +155,7 @@
 							index: i,
 							end: end.len(),
 						})),
+						fctx.clone(),
 						new_bindings,
 					)?;
 				}
@@ -189,36 +194,51 @@
 					Ok(obj)
 				}
 			}
-			let field_names: Vec<_> = fields.iter().map(|f| f.0.clone()).collect();
+			let field_names: Vec<_> = fields
+				.iter()
+				.filter(|f| f.2.is_none())
+				.map(|f| f.0.clone())
+				.collect();
 			let full = Thunk::new(tb!(DataThunk {
 				parent,
 				field_names: field_names.clone(),
 				has_rest: rest.is_some()
 			}));
 
-			for (field, d) in fields {
+			for (field, d, default) in fields {
 				#[derive(Trace)]
 				struct FieldThunk {
 					full: Thunk<ObjValue>,
 					field: IStr,
+					default: Option<(Pending<Context>, LocExpr)>,
 				}
 				impl ThunkValue for FieldThunk {
 					type Output = Val;
 
 					fn get(self: Box<Self>, s: State) -> Result<Self::Output> {
 						let full = self.full.evaluate(s.clone())?;
-						let field = full.get(s, self.field)?.expect("shape is checked");
-						Ok(field)
+						if let Some(field) = full.get(s.clone(), self.field)? {
+							Ok(field)
+						} else {
+							let (fctx, expr) = self.default.as_ref().expect("shape is checked");
+							Ok(evaluate(s, fctx.clone().unwrap(), &expr)?)
+						}
 					}
 				}
 				let value = Thunk::new(tb!(FieldThunk {
 					full: full.clone(),
-					field: field.clone()
+					field: field.clone(),
+					default: default.clone().map(|e| (fctx.clone(), e)),
 				}));
 				if let Some(d) = d {
-					destruct(d, value, new_bindings)?;
+					destruct(d, value, fctx.clone(), new_bindings)?;
 				} else {
-					destruct(&Destruct::Full(field.clone()), value, new_bindings)?;
+					destruct(
+						&Destruct::Full(field.clone()),
+						value,
+						fctx.clone(),
+						new_bindings,
+					)?;
 				}
 			}
 		}
@@ -251,10 +271,10 @@
 			}
 			let data = Thunk::new(tb!(EvaluateThunkValue {
 				name: into.name(),
-				fctx,
+				fctx: fctx.clone(),
 				expr: value.clone(),
 			}));
-			destruct(into, data, new_bindings)?;
+			destruct(into, data, fctx, new_bindings)?;
 		}
 		BindSpec::Function {
 			name,
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -56,7 +56,12 @@
 
 	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {
 		let name = params[id].0.clone();
-		destruct(&name, arg, &mut passed_args)?;
+		destruct(
+			&name,
+			arg,
+			Pending::new_filled(ctx.clone()),
+			&mut passed_args,
+		)?;
 		filled_positionals += 1;
 		Ok(())
 	})?;
@@ -96,6 +101,7 @@
 					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
 					value: param.1.clone().expect("default exists"),
 				})),
+				fctx.clone(),
 				&mut defaults,
 			)?;
 			if param.0.name().is_some() {
@@ -230,6 +236,7 @@
 					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
 					value: v.clone(),
 				})),
+				fctx.clone(),
 				&mut bindings,
 			)?;
 		} else {
@@ -238,6 +245,7 @@
 				Thunk::new(tb!(DependsOnUnbound(
 					param.0.name().unwrap_or_else(|| "<destruct>".into())
 				))),
+				fctx.clone(),
 				&mut bindings,
 			)?;
 		}
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -188,7 +188,7 @@
 }
 
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq, Trace)]
+#[derive(Debug, Clone, PartialEq, Trace)]
 pub enum Destruct {
 	Full(IStr),
 	#[cfg(feature = "exp-destruct")]
@@ -201,7 +201,7 @@
 	},
 	#[cfg(feature = "exp-destruct")]
 	Object {
-		fields: Vec<(IStr, Option<Destruct>)>,
+		fields: Vec<(IStr, Option<Destruct>, Option<LocExpr>)>,
 		rest: Option<DestructRest>,
 	},
 }
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]
 
 use std::rc::Rc;
 
@@ -109,7 +109,7 @@
 			}
 		pub rule destruct_object(s: &ParserSettings) -> expr::Destruct
 			= "{" _
-				fields:(name:id() _ into:(":" _ into:destruct(s) {into})? {(name, into)})**comma()
+				fields:(name:id() into:(_ ":" _ into:destruct(s) {into})? default:(_ "=" _ v:expr(s) {v})? {(name, into, default)})**comma()
 				rest:(
 					comma() rest:destruct_rest()? {rest}
 					/ comma()? {None}