git.delta.rocks / jrsonnet / refs/commits / 15dca76ce252

difftreelog

source

crates/jrsonnet-evaluator/src/arr/spec.rs13.7 KiBsourcehistory
1use std::{any::Any, cell::RefCell, fmt::Debug, iter, mem::replace};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5use jrsonnet_parser::LocExpr;67use super::ArrValue;8use crate::{9	error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, typed::Typed,10	val::ThunkValue, Context, Error, ObjValue, Result, Thunk, Val,11};1213pub trait ArrayLike: Any + Trace + Debug {14	fn len(&self) -> usize;15	fn is_empty(&self) -> bool {16		self.len() == 017	}18	fn get(&self, index: usize) -> Result<Option<Val>>;19	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;20	fn get_cheap(&self, index: usize) -> Option<Val>;2122	fn is_cheap(&self) -> bool;23}2425#[derive(Debug, Trace)]26pub struct SliceArray {27	pub(crate) inner: ArrValue,28	pub(crate) from: u32,29	pub(crate) to: u32,30	pub(crate) step: u32,31}3233impl SliceArray {34	fn map_idx(&self, index: usize) -> usize {35		self.from as usize + self.step as usize * index36	}37}38impl ArrayLike for SliceArray {39	fn len(&self) -> usize {40		((self.to - self.from + self.step - 1) / self.step) as usize41	}4243	fn get(&self, index: usize) -> Result<Option<Val>> {44		self.inner.get(self.map_idx(index))45	}4647	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {48		self.inner.get_lazy(self.map_idx(index))49	}5051	fn get_cheap(&self, index: usize) -> Option<Val> {52		self.inner.get_cheap(self.map_idx(index))53	}54	fn is_cheap(&self) -> bool {55		self.inner.is_cheap()56	}57}5859#[derive(Trace, Debug)]60pub struct CharArray(pub Vec<char>);61impl ArrayLike for CharArray {62	fn len(&self) -> usize {63		self.0.len()64	}6566	fn get(&self, index: usize) -> Result<Option<Val>> {67		Ok(self.get_cheap(index))68	}6970	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {71		self.get_cheap(index).map(Thunk::evaluated)72	}7374	fn get_cheap(&self, index: usize) -> Option<Val> {75		self.0.get(index).map(|v| Val::string(*v))76	}77	fn is_cheap(&self) -> bool {78		true79	}80}8182#[derive(Trace, Debug)]83pub struct BytesArray(pub IBytes);84impl ArrayLike for BytesArray {85	fn len(&self) -> usize {86		self.0.len()87	}8889	fn get(&self, index: usize) -> Result<Option<Val>> {90		Ok(self.get_cheap(index))91	}9293	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {94		self.get_cheap(index).map(Thunk::evaluated)95	}9697	fn get_cheap(&self, index: usize) -> Option<Val> {98		self.0.get(index).map(|v| Val::Num((*v).into()))99	}100	fn is_cheap(&self) -> bool {101		true102	}103}104105#[derive(Debug, Trace, Clone)]106enum ArrayThunk<T: 'static + Trace> {107	Computed(Val),108	Errored(Error),109	Waiting(T),110	Pending,111}112113#[derive(Debug, Trace, Clone)]114pub struct ExprArray {115	ctx: Context,116	cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,117}118impl ExprArray {119	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {120		Self {121			ctx,122			cached: Cc::new(RefCell::new(123				items.into_iter().map(ArrayThunk::Waiting).collect(),124			)),125		}126	}127}128impl ArrayLike for ExprArray {129	fn len(&self) -> usize {130		self.cached.borrow().len()131	}132	fn get(&self, index: usize) -> Result<Option<Val>> {133		if index >= self.len() {134			return Ok(None);135		}136		match &self.cached.borrow()[index] {137			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),138			ArrayThunk::Errored(e) => return Err(e.clone()),139			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),140			ArrayThunk::Waiting(..) => {}141		};142143		let ArrayThunk::Waiting(expr) =144			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)145		else {146			unreachable!()147		};148149		let new_value = match evaluate(self.ctx.clone(), &expr) {150			Ok(v) => v,151			Err(e) => {152				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());153				return Err(e);154			}155		};156		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());157		Ok(Some(new_value))158	}159	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {160		if index >= self.len() {161			return None;162		}163		match &self.cached.borrow()[index] {164			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),165			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),166			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}167		};168169		#[derive(Trace)]170		struct ExprArrThunk {171			expr: ExprArray,172			index: usize,173		}174		impl ThunkValue for ExprArrThunk {175			type Output = Val;176177			fn get(&self) -> Result<Self::Output> {178				self.expr179					.get(self.index)180					.transpose()181					.expect("index checked")182			}183		}184185		Some(Thunk::new(ExprArrThunk {186			expr: self.clone(),187			index,188		}))189	}190	fn get_cheap(&self, _index: usize) -> Option<Val> {191		None192	}193	fn is_cheap(&self) -> bool {194		false195	}196}197198#[derive(Trace, Debug)]199pub struct ExtendedArray {200	pub a: ArrValue,201	pub b: ArrValue,202	split: usize,203	len: usize,204}205impl ExtendedArray {206	pub fn new(a: ArrValue, b: ArrValue) -> Self {207		let a_len = a.len();208		let b_len = b.len();209		Self {210			a,211			b,212			split: a_len,213			len: a_len.checked_add(b_len).expect("too large array value"),214		}215	}216}217218struct WithExactSize<I>(I, usize);219impl<I, T> Iterator for WithExactSize<I>220where221	I: Iterator<Item = T>,222{223	type Item = T;224225	fn next(&mut self) -> Option<Self::Item> {226		self.0.next()227	}228	fn nth(&mut self, n: usize) -> Option<Self::Item> {229		self.0.nth(n)230	}231	fn size_hint(&self) -> (usize, Option<usize>) {232		(self.1, Some(self.1))233	}234}235impl<I> DoubleEndedIterator for WithExactSize<I>236where237	I: DoubleEndedIterator,238{239	fn next_back(&mut self) -> Option<Self::Item> {240		self.0.next_back()241	}242	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {243		self.0.nth_back(n)244	}245}246impl<I> ExactSizeIterator for WithExactSize<I>247where248	I: Iterator,249{250	fn len(&self) -> usize {251		self.1252	}253}254impl ArrayLike for ExtendedArray {255	fn get(&self, index: usize) -> Result<Option<Val>> {256		if self.split > index {257			self.a.get(index)258		} else {259			self.b.get(index - self.split)260		}261	}262	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {263		if self.split > index {264			self.a.get_lazy(index)265		} else {266			self.b.get_lazy(index - self.split)267		}268	}269270	fn len(&self) -> usize {271		self.len272	}273274	fn get_cheap(&self, index: usize) -> Option<Val> {275		if self.split > index {276			self.a.get_cheap(index)277		} else {278			self.b.get_cheap(index - self.split)279		}280	}281	fn is_cheap(&self) -> bool {282		self.a.is_cheap() && self.b.is_cheap()283	}284}285286#[derive(Trace, Debug)]287pub struct LazyArray(pub Vec<Thunk<Val>>);288impl ArrayLike for LazyArray {289	fn len(&self) -> usize {290		self.0.len()291	}292	fn get(&self, index: usize) -> Result<Option<Val>> {293		let Some(v) = self.0.get(index) else {294			return Ok(None);295		};296		v.evaluate().map(Some)297	}298	fn get_cheap(&self, _index: usize) -> Option<Val> {299		None300	}301	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {302		self.0.get(index).cloned()303	}304	fn is_cheap(&self) -> bool {305		false306	}307}308309#[derive(Trace, Debug)]310pub struct EagerArray(pub Vec<Val>);311impl ArrayLike for EagerArray {312	fn len(&self) -> usize {313		self.0.len()314	}315316	fn get(&self, index: usize) -> Result<Option<Val>> {317		Ok(self.0.get(index).cloned())318	}319320	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {321		self.0.get(index).cloned().map(Thunk::evaluated)322	}323324	fn get_cheap(&self, index: usize) -> Option<Val> {325		self.0.get(index).cloned()326	}327	fn is_cheap(&self) -> bool {328		true329	}330}331332/// Inclusive range type333#[derive(Debug, Trace, PartialEq, Eq)]334pub struct RangeArray {335	start: i32,336	end: i32,337}338impl RangeArray {339	pub fn empty() -> Self {340		Self::new_exclusive(0, 0)341	}342	pub fn new_exclusive(start: i32, end: i32) -> Self {343		end.checked_sub(1)344			.map_or_else(Self::empty, |end| Self { start, end })345	}346	pub fn new_inclusive(start: i32, end: i32) -> Self {347		Self { start, end }348	}349	fn range(&self) -> impl ExactSizeIterator<Item = i32> + DoubleEndedIterator {350		WithExactSize(351			self.start..=self.end,352			(self.end as usize)353				.wrapping_sub(self.start as usize)354				.wrapping_add(1),355		)356	}357}358359impl ArrayLike for RangeArray {360	fn len(&self) -> usize {361		self.range().len()362	}363	fn is_empty(&self) -> bool {364		self.range().len() == 0365	}366367	fn get(&self, index: usize) -> Result<Option<Val>> {368		Ok(self.get_cheap(index))369	}370371	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {372		self.get_cheap(index).map(Thunk::evaluated)373	}374375	fn get_cheap(&self, index: usize) -> Option<Val> {376		self.range().nth(index).map(|i| Val::Num(i.into()))377	}378	fn is_cheap(&self) -> bool {379		true380	}381}382383#[derive(Debug, Trace)]384pub struct ReverseArray(pub ArrValue);385impl ArrayLike for ReverseArray {386	fn len(&self) -> usize {387		self.0.len()388	}389390	fn get(&self, index: usize) -> Result<Option<Val>> {391		self.0.get(self.0.len() - index - 1)392	}393394	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {395		self.0.get_lazy(self.0.len() - index - 1)396	}397398	fn get_cheap(&self, index: usize) -> Option<Val> {399		self.0.get_cheap(self.0.len() - index - 1)400	}401	fn is_cheap(&self) -> bool {402		self.0.is_cheap()403	}404}405406#[derive(Trace, Debug, Clone)]407pub struct MappedArray<const WITH_INDEX: bool> {408	inner: ArrValue,409	cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,410	mapper: FuncVal,411}412impl<const WITH_INDEX: bool> MappedArray<WITH_INDEX> {413	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {414		let len = inner.len();415		Self {416			inner,417			cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),418			mapper,419		}420	}421	fn evaluate(&self, index: usize, value: Val) -> Result<Val> {422		if WITH_INDEX {423			self.mapper.evaluate_simple(&(index, value), false)424		} else {425			self.mapper.evaluate_simple(&(value,), false)426		}427	}428}429impl<const WITH_INDEX: bool> ArrayLike for MappedArray<WITH_INDEX> {430	fn len(&self) -> usize {431		self.cached.borrow().len()432	}433434	fn get(&self, index: usize) -> Result<Option<Val>> {435		if index >= self.len() {436			return Ok(None);437		}438		match &self.cached.borrow()[index] {439			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),440			ArrayThunk::Errored(e) => return Err(e.clone()),441			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),442			ArrayThunk::Waiting(..) => {}443		};444445		let ArrayThunk::Waiting(()) =446			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)447		else {448			unreachable!()449		};450451		let val = self452			.inner453			.get(index)454			.transpose()455			.expect("index checked")456			.and_then(|r| self.evaluate(index, r));457458		let new_value = match val {459			Ok(v) => v,460			Err(e) => {461				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());462				return Err(e);463			}464		};465		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());466		Ok(Some(new_value))467	}468	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {469		if index >= self.len() {470			return None;471		}472		match &self.cached.borrow()[index] {473			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),474			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),475			ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}476		};477478		#[derive(Trace)]479		struct MappedArrayThunk<const WITH_INDEX: bool> {480			arr: MappedArray<WITH_INDEX>,481			index: usize,482		}483		impl<const WITH_INDEX: bool> ThunkValue for MappedArrayThunk<WITH_INDEX> {484			type Output = Val;485486			fn get(&self) -> Result<Self::Output> {487				self.arr.get(self.index).transpose().expect("index checked")488			}489		}490491		Some(Thunk::new(MappedArrayThunk {492			arr: self.clone(),493			index,494		}))495	}496497	fn get_cheap(&self, _index: usize) -> Option<Val> {498		None499	}500	fn is_cheap(&self) -> bool {501		false502	}503}504505#[derive(Trace, Debug)]506pub struct RepeatedArray {507	data: ArrValue,508	repeats: usize,509	total_len: usize,510}511impl RepeatedArray {512	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {513		let total_len = data.len().checked_mul(repeats)?;514		Some(Self {515			data,516			repeats,517			total_len,518		})519	}520}521522impl ArrayLike for RepeatedArray {523	fn len(&self) -> usize {524		self.total_len525	}526527	fn get(&self, index: usize) -> Result<Option<Val>> {528		if index > self.total_len {529			return Ok(None);530		}531		self.data.get(index % self.data.len())532	}533534	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {535		if index > self.total_len {536			return None;537		}538		self.data.get_lazy(index % self.data.len())539	}540541	fn get_cheap(&self, index: usize) -> Option<Val> {542		if index > self.total_len {543			return None;544		}545		self.data.get_cheap(index % self.data.len())546	}547	fn is_cheap(&self) -> bool {548		self.data.is_cheap()549	}550}551552#[derive(Trace, Debug)]553pub struct PickObjectValues {554	obj: ObjValue,555	keys: Vec<IStr>,556}557558impl PickObjectValues {559	pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {560		Self { obj, keys }561	}562}563564impl ArrayLike for PickObjectValues {565	fn len(&self) -> usize {566		self.keys.len()567	}568569	fn get(&self, index: usize) -> Result<Option<Val>> {570		let Some(key) = self.keys.get(index) else {571			return Ok(None);572		};573		Ok(Some(self.obj.get_or_bail(key.clone())?))574	}575576	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {577		let key = self.keys.get(index)?;578		Some(self.obj.get_lazy_or_bail(key.clone()))579	}580581	fn get_cheap(&self, _index: usize) -> Option<Val> {582		None583	}584585	fn is_cheap(&self) -> bool {586		false587	}588}589590#[derive(Trace, Debug)]591pub struct PickObjectKeyValues {592	obj: ObjValue,593	keys: Vec<IStr>,594}595596impl PickObjectKeyValues {597	pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {598		Self { obj, keys }599	}600}601602#[derive(Typed)]603pub struct KeyValue {604	key: IStr,605	value: Thunk<Val>,606}607608impl ArrayLike for PickObjectKeyValues {609	fn len(&self) -> usize {610		self.keys.len()611	}612613	fn get(&self, index: usize) -> Result<Option<Val>> {614		let Some(key) = self.keys.get(index) else {615			return Ok(None);616		};617		Ok(Some(618			KeyValue::into_untyped(KeyValue {619				key: key.clone(),620				value: Thunk::evaluated(self.obj.get_or_bail(key.clone())?),621			})622			.expect("convertible"),623		))624	}625626	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {627		let key = self.keys.get(index)?;628		// Nothing can fail in the key part, yet value is still629		// lazy-evaluated630		Some(Thunk::evaluated(631			KeyValue::into_untyped(KeyValue {632				key: key.clone(),633				value: self.obj.get_lazy_or_bail(key.clone()),634			})635			.expect("convertible"),636		))637	}638639	fn get_cheap(&self, _index: usize) -> Option<Val> {640		None641	}642643	fn is_cheap(&self) -> bool {644		false645	}646}