git.delta.rocks / jrsonnet / refs/commits / 88a0ba11fe45

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate/destructure.rs6.6 KiBsourcehistory
1use gcmodule::Trace;2use jrsonnet_interner::IStr;3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};45use crate::{6	error::{Error::*, Result},7	evaluate, evaluate_method,8	gc::GcHashMap,9	tb, throw,10	val::ThunkValue,11	Context, Pending, State, Thunk, Val,12};1314fn destruct(15	d: &Destruct,16	parent: Thunk<Val>,17	new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,18) -> Result<()> {19	Ok(match d {20		Destruct::Full(v) => {21			let old = new_bindings.insert(v.clone(), parent);22			if old.is_some() {23				throw!(DuplicateLocalVar(v.clone()))24			}25		}26		#[cfg(feature = "exp-destruct")]27		Destruct::Skip => {}28		#[cfg(feature = "exp-destruct")]29		Destruct::Array { start, rest, end } => {30			use jrsonnet_parser::DestructRest;3132			use crate::{throw_runtime, val::ArrValue};3334			#[derive(Trace)]35			struct DataThunk {36				parent: Thunk<Val>,37				min_len: usize,38				has_rest: bool,39			}40			impl ThunkValue for DataThunk {41				type Output = ArrValue;4243				fn get(self: Box<Self>, s: State) -> Result<Self::Output> {44					let v = self.parent.evaluate(s)?;45					let arr = match v {46						Val::Arr(a) => a,47						_ => throw_runtime!("expected array"),48					};49					if !self.has_rest {50						if arr.len() != self.min_len {51							throw_runtime!("expected {} elements, got {}", self.min_len, arr.len())52						}53					} else if arr.len() < self.min_len {54						throw_runtime!(55							"expected at least {} elements, but array was only {}",56							self.min_len,57							arr.len()58						)59					}60					Ok(arr)61				}62			}6364			let full = Thunk::new(tb!(DataThunk {65				min_len: start.len() + end.len(),66				has_rest: rest.is_some(),67				parent,68			}));6970			{71				#[derive(Trace)]72				struct BaseThunk {73					full: Thunk<ArrValue>,74					index: usize,75				}76				impl ThunkValue for BaseThunk {77					type Output = Val;7879					fn get(self: Box<Self>, s: State) -> Result<Self::Output> {80						let full = self.full.evaluate(s.clone())?;81						Ok(full.get(s, self.index)?.expect("length is checked"))82					}83				}84				for (i, d) in start.iter().enumerate() {85					destruct(86						d,87						Thunk::new(tb!(BaseThunk {88							full: full.clone(),89							index: i,90						})),91						new_bindings,92					)?;93				}94			}9596			match rest {97				Some(DestructRest::Keep(v)) => {98					#[derive(Trace)]99					struct RestThunk {100						full: Thunk<ArrValue>,101						start: usize,102						end: usize,103					}104					impl ThunkValue for RestThunk {105						type Output = Val;106107						fn get(self: Box<Self>, s: State) -> Result<Self::Output> {108							let full = self.full.evaluate(s)?;109							let to = full.len() - self.end;110							Ok(Val::Arr(full.slice(Some(self.start), Some(to), None)))111						}112					}113114					destruct(115						&Destruct::Full(v.clone()),116						Thunk::new(tb!(RestThunk {117							full: full.clone(),118							start: start.len(),119							end: end.len(),120						})),121						new_bindings,122					)?;123				}124				Some(DestructRest::Drop) => {}125				None => {}126			}127128			{129				#[derive(Trace)]130				struct EndThunk {131					full: Thunk<ArrValue>,132					index: usize,133					end: usize,134				}135				impl ThunkValue for EndThunk {136					type Output = Val;137138					fn get(self: Box<Self>, s: State) -> Result<Self::Output> {139						let full = self.full.evaluate(s.clone())?;140						Ok(full141							.get(s, full.len() - self.end + self.index)?142							.expect("length is checked"))143					}144				}145				for (i, d) in end.iter().enumerate() {146					destruct(147						d,148						Thunk::new(tb!(EndThunk {149							full: full.clone(),150							index: i,151							end: end.len(),152						})),153						new_bindings,154					)?;155				}156			}157		}158		#[cfg(feature = "exp-destruct")]159		Destruct::Object { fields, rest } => {160			use jrsonnet_parser::DestructRest;161162			use crate::{obj::ObjValue, throw_runtime};163164			#[derive(Trace)]165			struct DataThunk {166				parent: Thunk<Val>,167				field_names: Vec<IStr>,168				has_rest: bool,169			}170			impl ThunkValue for DataThunk {171				type Output = ObjValue;172173				fn get(self: Box<Self>, s: State) -> Result<Self::Output> {174					let v = self.parent.evaluate(s)?;175					let obj = match v {176						Val::Obj(o) => o,177						_ => throw_runtime!("expected object"),178					};179					for field in &self.field_names {180						if !obj.has_field_ex(field.clone(), true) {181							throw_runtime!("missing field: {}", field);182						}183					}184					if !self.has_rest {185						let len = obj.len();186						if len != self.field_names.len() {187							throw_runtime!("too many fields, and rest not found");188						}189					}190					Ok(obj)191				}192			}193			let field_names: Vec<_> = fields.iter().map(|f| f.0.clone()).collect();194			let full = Thunk::new(tb!(DataThunk {195				parent,196				field_names: field_names.clone(),197				has_rest: rest.is_some()198			}));199200			for (field, d) in fields {201				#[derive(Trace)]202				struct FieldThunk {203					full: Thunk<ObjValue>,204					field: IStr,205				}206				impl ThunkValue for FieldThunk {207					type Output = Val;208209					fn get(self: Box<Self>, s: State) -> Result<Self::Output> {210						let full = self.full.evaluate(s.clone())?;211						let field = full.get(s, self.field)?.expect("shape is checked");212						Ok(field)213					}214				}215				let value = Thunk::new(tb!(FieldThunk {216					full: full.clone(),217					field: field.clone()218				}));219				if let Some(d) = d {220					destruct(d, value, new_bindings)?;221				} else {222					destruct(&Destruct::Full(field.clone()), value, new_bindings)?;223				}224			}225		}226	})227}228229pub fn evaluate_dest(230	d: &BindSpec,231	fctx: Pending<Context>,232	new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,233) -> Result<()> {234	match d {235		BindSpec::Field { into, value } => {236			#[derive(Trace)]237			struct EvaluateThunkValue {238				fctx: Pending<Context>,239				expr: LocExpr,240			}241			impl ThunkValue for EvaluateThunkValue {242				type Output = Val;243				fn get(self: Box<Self>, s: State) -> Result<Self::Output> {244					evaluate(s, self.fctx.unwrap(), &self.expr)245				}246			}247			// TODO: Generate some name, as destructure spec may be used with plain functions248			let data = Thunk::new(tb!(EvaluateThunkValue {249				fctx,250				expr: value.clone(),251			}));252			destruct(into, data, new_bindings)?;253		}254		BindSpec::Function {255			name,256			params,257			value,258		} => {259			#[derive(Trace)]260			struct MethodThunk {261				fctx: Pending<Context>,262				name: IStr,263				params: ParamsDesc,264				value: LocExpr,265			}266			impl ThunkValue for MethodThunk {267				type Output = Val;268269				fn get(self: Box<Self>, _s: State) -> Result<Self::Output> {270					Ok(evaluate_method(271						self.fctx.unwrap(),272						self.name,273						self.params,274						self.value,275					))276				}277			}278279			let old = new_bindings.insert(280				name.clone(),281				Thunk::new(tb!(MethodThunk {282					fctx,283					name: name.clone(),284					params: params.clone(),285					value: value.clone()286				})),287			);288			if old.is_some() {289				throw!(DuplicateLocalVar(name.clone()))290			}291		}292	}293	Ok(())294}