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
--- a/crates/jrsonnet-evaluator/src/dynamic.rs
+++ b/crates/jrsonnet-evaluator/src/dynamic.rs
@@ -8,6 +8,9 @@
 	pub fn new() -> Self {
 		Self(Cc::new(RefCell::new(None)))
 	}
+	pub fn new_filled(v: T) -> Self {
+		Self(Cc::new(RefCell::new(Some(v))))
+	}
 	/// # Panics
 	/// If wrapper is filled already
 	pub fn fill(self, value: T) {
modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
after · crates/jrsonnet-evaluator/src/evaluate/destructure.rs
1use gcmodule::Trace;2use jrsonnet_interner::IStr;3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};45use crate::{6	error::{Error::*, Result},7	evaluate, evaluate_method, evaluate_named,8	gc::GcHashMap,9	tb, throw,10	val::ThunkValue,11	Context, Pending, State, Thunk, Val,12};1314#[allow(clippy::too_many_lines)]15#[allow(unused_variables)]16pub fn destruct(17	d: &Destruct,18	parent: Thunk<Val>,19	fctx: Pending<Context>,20	new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,21) -> Result<()> {22	match d {23		Destruct::Full(v) => {24			let old = new_bindings.insert(v.clone(), parent);25			if old.is_some() {26				throw!(DuplicateLocalVar(v.clone()))27			}28		}29		#[cfg(feature = "exp-destruct")]30		Destruct::Skip => {}31		#[cfg(feature = "exp-destruct")]32		Destruct::Array { start, rest, end } => {33			use jrsonnet_parser::DestructRest;3435			use crate::{throw_runtime, val::ArrValue};3637			#[derive(Trace)]38			struct DataThunk {39				parent: Thunk<Val>,40				min_len: usize,41				has_rest: bool,42			}43			impl ThunkValue for DataThunk {44				type Output = ArrValue;4546				fn get(self: Box<Self>, s: State) -> Result<Self::Output> {47					let v = self.parent.evaluate(s)?;48					let arr = match v {49						Val::Arr(a) => a,50						_ => throw_runtime!("expected array"),51					};52					if !self.has_rest {53						if arr.len() != self.min_len {54							throw_runtime!("expected {} elements, got {}", self.min_len, arr.len())55						}56					} else if arr.len() < self.min_len {57						throw_runtime!(58							"expected at least {} elements, but array was only {}",59							self.min_len,60							arr.len()61						)62					}63					Ok(arr)64				}65			}6667			let full = Thunk::new(tb!(DataThunk {68				min_len: start.len() + end.len(),69				has_rest: rest.is_some(),70				parent,71			}));7273			{74				#[derive(Trace)]75				struct BaseThunk {76					full: Thunk<ArrValue>,77					index: usize,78				}79				impl ThunkValue for BaseThunk {80					type Output = Val;8182					fn get(self: Box<Self>, s: State) -> Result<Self::Output> {83						let full = self.full.evaluate(s.clone())?;84						Ok(full.get(s, self.index)?.expect("length is checked"))85					}86				}87				for (i, d) in start.iter().enumerate() {88					destruct(89						d,90						Thunk::new(tb!(BaseThunk {91							full: full.clone(),92							index: i,93						})),94						fctx.clone(),95						new_bindings,96					)?;97				}98			}99100			match rest {101				Some(DestructRest::Keep(v)) => {102					#[derive(Trace)]103					struct RestThunk {104						full: Thunk<ArrValue>,105						start: usize,106						end: usize,107					}108					impl ThunkValue for RestThunk {109						type Output = Val;110111						fn get(self: Box<Self>, s: State) -> Result<Self::Output> {112							let full = self.full.evaluate(s)?;113							let to = full.len() - self.end;114							Ok(Val::Arr(full.slice(Some(self.start), Some(to), None)))115						}116					}117118					destruct(119						&Destruct::Full(v.clone()),120						Thunk::new(tb!(RestThunk {121							full: full.clone(),122							start: start.len(),123							end: end.len(),124						})),125						fctx.clone(),126						new_bindings,127					)?;128				}129				Some(DestructRest::Drop) => {}130				None => {}131			}132133			{134				#[derive(Trace)]135				struct EndThunk {136					full: Thunk<ArrValue>,137					index: usize,138					end: usize,139				}140				impl ThunkValue for EndThunk {141					type Output = Val;142143					fn get(self: Box<Self>, s: State) -> Result<Self::Output> {144						let full = self.full.evaluate(s.clone())?;145						Ok(full146							.get(s, full.len() - self.end + self.index)?147							.expect("length is checked"))148					}149				}150				for (i, d) in end.iter().enumerate() {151					destruct(152						d,153						Thunk::new(tb!(EndThunk {154							full: full.clone(),155							index: i,156							end: end.len(),157						})),158						fctx.clone(),159						new_bindings,160					)?;161				}162			}163		}164		#[cfg(feature = "exp-destruct")]165		Destruct::Object { fields, rest } => {166			use crate::{obj::ObjValue, throw_runtime};167168			#[derive(Trace)]169			struct DataThunk {170				parent: Thunk<Val>,171				field_names: Vec<IStr>,172				has_rest: bool,173			}174			impl ThunkValue for DataThunk {175				type Output = ObjValue;176177				fn get(self: Box<Self>, s: State) -> Result<Self::Output> {178					let v = self.parent.evaluate(s)?;179					let obj = match v {180						Val::Obj(o) => o,181						_ => throw_runtime!("expected object"),182					};183					for field in &self.field_names {184						if !obj.has_field_ex(field.clone(), true) {185							throw_runtime!("missing field: {}", field);186						}187					}188					if !self.has_rest {189						let len = obj.len();190						if len != self.field_names.len() {191							throw_runtime!("too many fields, and rest not found");192						}193					}194					Ok(obj)195				}196			}197			let field_names: Vec<_> = fields198				.iter()199				.filter(|f| f.2.is_none())200				.map(|f| f.0.clone())201				.collect();202			let full = Thunk::new(tb!(DataThunk {203				parent,204				field_names: field_names.clone(),205				has_rest: rest.is_some()206			}));207208			for (field, d, default) in fields {209				#[derive(Trace)]210				struct FieldThunk {211					full: Thunk<ObjValue>,212					field: IStr,213					default: Option<(Pending<Context>, LocExpr)>,214				}215				impl ThunkValue for FieldThunk {216					type Output = Val;217218					fn get(self: Box<Self>, s: State) -> Result<Self::Output> {219						let full = self.full.evaluate(s.clone())?;220						if let Some(field) = full.get(s.clone(), self.field)? {221							Ok(field)222						} else {223							let (fctx, expr) = self.default.as_ref().expect("shape is checked");224							Ok(evaluate(s, fctx.clone().unwrap(), &expr)?)225						}226					}227				}228				let value = Thunk::new(tb!(FieldThunk {229					full: full.clone(),230					field: field.clone(),231					default: default.clone().map(|e| (fctx.clone(), e)),232				}));233				if let Some(d) = d {234					destruct(d, value, fctx.clone(), new_bindings)?;235				} else {236					destruct(237						&Destruct::Full(field.clone()),238						value,239						fctx.clone(),240						new_bindings,241					)?;242				}243			}244		}245	}246	Ok(())247}248249pub fn evaluate_dest(250	d: &BindSpec,251	fctx: Pending<Context>,252	new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,253) -> Result<()> {254	match d {255		BindSpec::Field { into, value } => {256			#[derive(Trace)]257			struct EvaluateThunkValue {258				name: Option<IStr>,259				fctx: Pending<Context>,260				expr: LocExpr,261			}262			impl ThunkValue for EvaluateThunkValue {263				type Output = Val;264				fn get(self: Box<Self>, s: State) -> Result<Self::Output> {265					if let Some(name) = self.name {266						evaluate_named(s, self.fctx.unwrap(), &self.expr, name)267					} else {268						evaluate(s, self.fctx.unwrap(), &self.expr)269					}270				}271			}272			let data = Thunk::new(tb!(EvaluateThunkValue {273				name: into.name(),274				fctx: fctx.clone(),275				expr: value.clone(),276			}));277			destruct(into, data, fctx, new_bindings)?;278		}279		BindSpec::Function {280			name,281			params,282			value,283		} => {284			#[derive(Trace)]285			struct MethodThunk {286				fctx: Pending<Context>,287				name: IStr,288				params: ParamsDesc,289				value: LocExpr,290			}291			impl ThunkValue for MethodThunk {292				type Output = Val;293294				fn get(self: Box<Self>, _s: State) -> Result<Self::Output> {295					Ok(evaluate_method(296						self.fctx.unwrap(),297						self.name,298						self.params,299						self.value,300					))301				}302			}303304			let old = new_bindings.insert(305				name.clone(),306				Thunk::new(tb!(MethodThunk {307					fctx,308					name: name.clone(),309					params: params.clone(),310					value: value.clone()311				})),312			);313			if old.is_some() {314				throw!(DuplicateLocalVar(name.clone()))315			}316		}317	}318	Ok(())319}
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}