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

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/destructure.rs6.9 KiBsourcehistory
1use jrsonnet_gcmodule::Trace;2use jrsonnet_interner::IStr;3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};45use crate::{6	bail,7	error::{ErrorKind::*, Result},8	evaluate, evaluate_method, evaluate_named,9	gc::GcHashMap,10	val::ThunkValue,11	Context, Pending, 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				bail!(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::arr::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>) -> Result<Self::Output> {47					let v = self.parent.evaluate()?;48					let Val::Arr(arr) = v else {49						bail!("expected array");50					};51					if !self.has_rest {52						if arr.len() != self.min_len {53							bail!("expected {} elements, got {}", self.min_len, arr.len())54						}55					} else if arr.len() < self.min_len {56						bail!(57							"expected at least {} elements, but array was only {}",58							self.min_len,59							arr.len()60						)61					}62					Ok(arr)63				}64			}6566			let full = Thunk::new(DataThunk {67				min_len: start.len() + end.len(),68				has_rest: rest.is_some(),69				parent,70			});7172			{73				#[derive(Trace)]74				struct BaseThunk {75					full: Thunk<ArrValue>,76					index: usize,77				}78				impl ThunkValue for BaseThunk {79					type Output = Val;8081					fn get(self: Box<Self>) -> Result<Self::Output> {82						let full = self.full.evaluate()?;83						Ok(full.get(self.index)?.expect("length is checked"))84					}85				}86				for (i, d) in start.iter().enumerate() {87					destruct(88						d,89						Thunk::new(BaseThunk {90							full: full.clone(),91							index: i,92						}),93						fctx.clone(),94						new_bindings,95					)?;96				}97			}9899			match rest {100				Some(DestructRest::Keep(v)) => {101					#[derive(Trace)]102					struct RestThunk {103						full: Thunk<ArrValue>,104						start: usize,105						end: usize,106					}107					impl ThunkValue for RestThunk {108						type Output = Val;109110						fn get(self: Box<Self>) -> Result<Self::Output> {111							let full = self.full.evaluate()?;112							let to = full.len() - self.end;113							Ok(Val::Arr(full.slice(114								Some(self.start as i32),115								Some(to as i32),116								None,117							)))118						}119					}120121					destruct(122						&Destruct::Full(v.clone()),123						Thunk::new(RestThunk {124							full: full.clone(),125							start: start.len(),126							end: end.len(),127						}),128						fctx.clone(),129						new_bindings,130					)?;131				}132				Some(DestructRest::Drop) | None => {}133			}134135			{136				#[derive(Trace)]137				struct EndThunk {138					full: Thunk<ArrValue>,139					index: usize,140					end: usize,141				}142				impl ThunkValue for EndThunk {143					type Output = Val;144145					fn get(self: Box<Self>) -> Result<Self::Output> {146						let full = self.full.evaluate()?;147						Ok(full148							.get(full.len() - self.end + self.index)?149							.expect("length is checked"))150					}151				}152				for (i, d) in end.iter().enumerate() {153					destruct(154						d,155						Thunk::new(EndThunk {156							full: full.clone(),157							index: i,158							end: end.len(),159						}),160						fctx.clone(),161						new_bindings,162					)?;163				}164			}165		}166		#[cfg(feature = "exp-destruct")]167		Destruct::Object { fields, rest } => {168			use crate::obj::ObjValue;169170			#[derive(Trace)]171			struct DataThunk {172				parent: Thunk<Val>,173				field_names: Vec<IStr>,174				has_rest: bool,175			}176			impl ThunkValue for DataThunk {177				type Output = ObjValue;178179				fn get(self: Box<Self>) -> Result<Self::Output> {180					let v = self.parent.evaluate()?;181					let Val::Obj(obj) = v else {182						bail!("expected object");183					};184					for field in &self.field_names {185						if !obj.has_field_ex(field.clone(), true) {186							bail!("missing field: {field}");187						}188					}189					if !self.has_rest {190						let len = obj.len();191						if len != self.field_names.len() {192							bail!("too many fields, and rest not found");193						}194					}195					Ok(obj)196				}197			}198			let field_names: Vec<_> = fields199				.iter()200				.filter(|f| f.2.is_none())201				.map(|f| f.0.clone())202				.collect();203			let full = Thunk::new(DataThunk {204				parent,205				field_names,206				has_rest: rest.is_some(),207			});208209			for (field, d, default) in fields {210				#[derive(Trace)]211				struct FieldThunk {212					full: Thunk<ObjValue>,213					field: IStr,214					default: Option<(Pending<Context>, LocExpr)>,215				}216				impl ThunkValue for FieldThunk {217					type Output = Val;218219					fn get(self: Box<Self>) -> Result<Self::Output> {220						let full = self.full.evaluate()?;221						if let Some(field) = full.get(self.field)? {222							Ok(field)223						} else {224							let (fctx, expr) = self.default.as_ref().expect("shape is checked");225							Ok(evaluate(fctx.clone().unwrap(), expr)?)226						}227					}228				}229				let value = Thunk::new(FieldThunk {230					full: full.clone(),231					field: field.clone(),232					default: default.clone().map(|e| (fctx.clone(), e)),233				});234				if let Some(d) = d {235					destruct(d, value, fctx.clone(), new_bindings)?;236				} else {237					destruct(238						&Destruct::Full(field.clone()),239						value,240						fctx.clone(),241						new_bindings,242					)?;243				}244			}245		}246	}247	Ok(())248}249250pub fn evaluate_dest(251	d: &BindSpec,252	fctx: Pending<Context>,253	new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,254) -> Result<()> {255	match d {256		BindSpec::Field { into, value } => {257			#[derive(Trace)]258			struct EvaluateThunkValue {259				name: Option<IStr>,260				fctx: Pending<Context>,261				expr: LocExpr,262			}263			impl ThunkValue for EvaluateThunkValue {264				type Output = Val;265				fn get(self: Box<Self>) -> Result<Self::Output> {266					self.name.map_or_else(267						|| evaluate(self.fctx.unwrap(), &self.expr),268						|name| evaluate_named(self.fctx.unwrap(), &self.expr, name),269					)270				}271			}272			let data = Thunk::new(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>) -> 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(MethodThunk {307					fctx,308					name: name.clone(),309					params: params.clone(),310					value: value.clone(),311				}),312			);313			if old.is_some() {314				bail!(DuplicateLocalVar(name.clone()))315			}316		}317	}318	Ok(())319}