git.delta.rocks / jrsonnet / refs/commits / 1e2e6e2fa219

difftreelog

source

crates/jrsonnet-evaluator/src/arr/spec.rs28.2 KiBsourcehistory
1//! Those implementations are a bit sketchy, as this is mostly performance experiments2//! of not yet finished nightly rust features34use std::{cell::RefCell, iter, mem::replace, rc::Rc};56use jrsonnet_gcmodule::{Cc, Trace};7use jrsonnet_interner::{IBytes, IStr};8use jrsonnet_parser::LocExpr;910use super::ArrValue;11use crate::{12	error::ErrorKind::InfiniteRecursionDetected,13	evaluate,14	function::FuncVal,15	val::{StrValue, ThunkValue},16	Context, Error, Result, Thunk, Val,17};1819pub trait ArrayLike: Sized + Into<ArrValue> {20	#[cfg(feature = "nightly")]21	type Iter<'t>22	where23		Self: 't;24	#[cfg(feature = "nightly")]25	type IterLazy<'t>26	where27		Self: 't;28	#[cfg(feature = "nightly")]29	type IterCheap<'t>30	where31		Self: 't;3233	fn len(&self) -> usize;34	fn is_empty(&self) -> bool {35		self.len() == 036	}37	fn get(&self, index: usize) -> Result<Option<Val>>;38	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;39	fn get_cheap(&self, index: usize) -> Option<Val>;40	#[cfg(feature = "nightly")]41	#[allow(clippy::iter_not_returning_iterator)]42	fn iter(&self) -> Self::Iter<'_>;43	#[cfg(feature = "nightly")]44	fn iter_lazy(&self) -> Self::IterLazy<'_>;45	#[cfg(feature = "nightly")]46	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>>;4748	fn reverse(self) -> ArrValue {49		ArrValue::Reverse(Cc::new(ReverseArray(self.into())))50	}51}5253#[derive(Debug, Clone, Trace)]54pub struct SliceArray {55	pub(crate) inner: ArrValue,56	pub(crate) from: u32,57	pub(crate) to: u32,58	pub(crate) step: u32,59}6061impl SliceArray {62	#[cfg(not(feature = "nightly"))]63	fn iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {64		self.inner65			.iter()66			.skip(self.from as usize)67			.take((self.to - self.from) as usize)68			.step_by(self.step as usize)69	}7071	#[cfg(not(feature = "nightly"))]72	fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {73		self.inner74			.iter_lazy()75			.skip(self.from as usize)76			.take((self.to - self.from) as usize)77			.step_by(self.step as usize)78	}7980	#[cfg(not(feature = "nightly"))]81	fn iter_cheap(&self) -> Option<impl crate::arr::ArrayLikeIter<Val> + '_> {82		Some(83			self.inner84				.iter_cheap()?85				.skip(self.from as usize)86				.take((self.to - self.from) as usize)87				.step_by(self.step as usize),88		)89	}90}91#[cfg(feature = "nightly")]92type SliceArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;93#[cfg(feature = "nightly")]94type SliceArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;95#[cfg(feature = "nightly")]96type SliceArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;97impl ArrayLike for SliceArray {98	#[cfg(feature = "nightly")]99	type Iter<'t> = SliceArrayIter<'t>;100	#[cfg(feature = "nightly")]101	type IterLazy<'t> = SliceArrayLazyIter<'t>;102	#[cfg(feature = "nightly")]103	type IterCheap<'t> = SliceArrayCheapIter<'t>;104105	fn len(&self) -> usize {106		iter::repeat(())107			.take((self.to - self.from) as usize)108			.step_by(self.step as usize)109			.count()110	}111112	fn get(&self, index: usize) -> Result<Option<Val>> {113		self.iter().nth(index).transpose()114	}115116	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {117		self.iter_lazy().nth(index)118	}119120	fn get_cheap(&self, index: usize) -> Option<Val> {121		self.iter_cheap()?.nth(index)122	}123124	#[cfg(feature = "nightly")]125	fn iter(&self) -> SliceArrayIter<'_> {126		self.inner127			.iter()128			.skip(self.from as usize)129			.take((self.to - self.from) as usize)130			.step_by(self.step as usize)131	}132133	#[cfg(feature = "nightly")]134	fn iter_lazy(&self) -> SliceArrayLazyIter<'_> {135		self.inner136			.iter_lazy()137			.skip(self.from as usize)138			.take((self.to - self.from) as usize)139			.step_by(self.step as usize)140	}141142	#[cfg(feature = "nightly")]143	fn iter_cheap(&self) -> Option<SliceArrayCheapIter<'_>> {144		Some(145			self.inner146				.iter_cheap()?147				.skip(self.from as usize)148				.take((self.to - self.from) as usize)149				.step_by(self.step as usize),150		)151	}152}153impl From<SliceArray> for ArrValue {154	fn from(value: SliceArray) -> Self {155		Self::Slice(Cc::new(value))156	}157}158159#[derive(Trace, Debug, Clone)]160pub struct CharArray(pub Rc<Vec<char>>);161#[cfg(feature = "nightly")]162type CharArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;163#[cfg(feature = "nightly")]164type CharArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;165#[cfg(feature = "nightly")]166type CharArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;167impl ArrayLike for CharArray {168	#[cfg(feature = "nightly")]169	type Iter<'t> = CharArrayIter<'t>;170	#[cfg(feature = "nightly")]171	type IterLazy<'t> = CharArrayLazyIter<'t>;172	#[cfg(feature = "nightly")]173	type IterCheap<'t> = CharArrayCheapIter<'t>;174175	fn len(&self) -> usize {176		self.0.len()177	}178179	fn get(&self, index: usize) -> Result<Option<Val>> {180		Ok(self.get_cheap(index))181	}182183	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {184		self.get_cheap(index).map(Thunk::evaluated)185	}186187	fn get_cheap(&self, index: usize) -> Option<Val> {188		self.0189			.get(index)190			.map(|v| Val::Str(StrValue::Flat(IStr::from(*v))))191	}192193	#[cfg(feature = "nightly")]194	fn iter(&self) -> CharArrayIter<'_> {195		self.0196			.iter()197			.map(|v| Ok(Val::Str(StrValue::Flat(IStr::from(*v)))))198	}199200	#[cfg(feature = "nightly")]201	fn iter_lazy(&self) -> CharArrayLazyIter<'_> {202		self.0203			.iter()204			.map(|v| Thunk::evaluated(Val::Str(StrValue::Flat(IStr::from(*v)))))205	}206207	#[cfg(feature = "nightly")]208	fn iter_cheap(&self) -> Option<CharArrayCheapIter<'_>> {209		Some(210			self.0211				.iter()212				.map(|v| Val::Str(StrValue::Flat(IStr::from(*v)))),213		)214	}215}216impl From<CharArray> for ArrValue {217	fn from(value: CharArray) -> Self {218		ArrValue::Chars(value)219	}220}221222#[derive(Trace, Debug, Clone)]223pub struct BytesArray(pub IBytes);224#[cfg(feature = "nightly")]225type BytesArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;226#[cfg(feature = "nightly")]227type BytesArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;228#[cfg(feature = "nightly")]229type BytesArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;230impl ArrayLike for BytesArray {231	#[cfg(feature = "nightly")]232	type Iter<'t> = BytesArrayIter<'t>;233	#[cfg(feature = "nightly")]234	type IterLazy<'t> = BytesArrayLazyIter<'t>;235	#[cfg(feature = "nightly")]236	type IterCheap<'t> = BytesArrayCheapIter<'t>;237238	fn len(&self) -> usize {239		self.0.len()240	}241242	fn get(&self, index: usize) -> Result<Option<Val>> {243		Ok(self.get_cheap(index))244	}245246	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {247		self.get_cheap(index).map(Thunk::evaluated)248	}249250	fn get_cheap(&self, index: usize) -> Option<Val> {251		self.0.get(index).map(|v| Val::Num(f64::from(*v)))252	}253254	#[cfg(feature = "nightly")]255	fn iter(&self) -> BytesArrayIter<'_> {256		self.0.iter().map(|v| Ok(Val::Num(f64::from(*v))))257	}258259	#[cfg(feature = "nightly")]260	fn iter_lazy(&self) -> BytesArrayLazyIter<'_> {261		self.0262			.iter()263			.map(|v| Thunk::evaluated(Val::Num(f64::from(*v))))264	}265266	#[cfg(feature = "nightly")]267	fn iter_cheap(&self) -> Option<BytesArrayCheapIter<'_>> {268		Some(self.0.iter().map(|v| Val::Num(f64::from(*v))))269	}270}271impl From<BytesArray> for ArrValue {272	fn from(value: BytesArray) -> Self {273		ArrValue::Bytes(value)274	}275}276277#[derive(Debug, Trace, Clone)]278enum ArrayThunk<T: 'static + Trace> {279	Computed(Val),280	Errored(Error),281	Waiting(T),282	Pending,283}284285#[derive(Debug, Trace)]286pub struct ExprArrayInner {287	ctx: Context,288	cached: RefCell<Vec<ArrayThunk<LocExpr>>>,289}290#[derive(Debug, Trace, Clone)]291pub struct ExprArray(pub Cc<ExprArrayInner>);292impl ExprArray {293	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {294		Self(Cc::new(ExprArrayInner {295			ctx,296			cached: RefCell::new(items.into_iter().map(ArrayThunk::Waiting).collect()),297		}))298	}299}300#[cfg(feature = "nightly")]301type ExprArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;302#[cfg(feature = "nightly")]303type ExprArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;304#[cfg(feature = "nightly")]305type ExprArrayCheapIter<'t> = iter::Empty<Val>;306impl ArrayLike for ExprArray {307	#[cfg(feature = "nightly")]308	type Iter<'t> = ExprArrayIter<'t>;309	#[cfg(feature = "nightly")]310	type IterLazy<'t> = ExprArrayLazyIter<'t>;311	#[cfg(feature = "nightly")]312	type IterCheap<'t> = ExprArrayCheapIter<'t>;313314	fn len(&self) -> usize {315		self.0.cached.borrow().len()316	}317	fn get(&self, index: usize) -> Result<Option<Val>> {318		if index >= self.len() {319			return Ok(None);320		}321		match &self.0.cached.borrow()[index] {322			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),323			ArrayThunk::Errored(e) => return Err(e.clone()),324			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),325			ArrayThunk::Waiting(..) => {}326		};327328		let ArrayThunk::Waiting(expr) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {329			unreachable!()330		};331332		let new_value = match evaluate(self.0.ctx.clone(), &expr) {333			Ok(v) => v,334			Err(e) => {335				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());336				return Err(e);337			}338		};339		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());340		Ok(Some(new_value))341	}342	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {343		#[derive(Trace)]344		struct ArrayElement {345			arr_thunk: ExprArray,346			index: usize,347		}348349		impl ThunkValue for ArrayElement {350			type Output = Val;351352			fn get(self: Box<Self>) -> Result<Self::Output> {353				self.arr_thunk354					.get(self.index)355					.transpose()356					.expect("index checked")357			}358		}359360		if index >= self.len() {361			return None;362		}363		match &self.0.cached.borrow()[index] {364			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),365			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),366			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}367		};368369		Some(Thunk::new(ArrayElement {370			arr_thunk: self.clone(),371			index,372		}))373	}374	fn get_cheap(&self, _index: usize) -> Option<Val> {375		None376	}377378	#[cfg(feature = "nightly")]379	fn iter(&self) -> ExprArrayIter<'_> {380		(0..self.len()).map(|i| self.get(i).transpose().expect("index checked"))381	}382	#[cfg(feature = "nightly")]383	fn iter_lazy(&self) -> ExprArrayLazyIter<'_> {384		(0..self.len()).map(|i| self.get_lazy(i).expect("index checked"))385	}386	#[cfg(feature = "nightly")]387	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>> {388		None389	}390}391impl From<ExprArray> for ArrValue {392	fn from(value: ExprArray) -> Self {393		Self::Expr(value)394	}395}396397#[derive(Trace, Debug, Clone)]398pub struct ExtendedArray {399	pub a: ArrValue,400	pub b: ArrValue,401	split: usize,402	len: usize,403}404#[cfg(feature = "nightly")]405406type ExtendedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;407#[cfg(feature = "nightly")]408type ExtendedArrayLazyIter<'t> =409	impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;410#[cfg(feature = "nightly")]411type ExtendedArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;412impl ExtendedArray {413	pub fn new(a: ArrValue, b: ArrValue) -> Self {414		let a_len = a.len();415		let b_len = b.len();416		Self {417			a,418			b,419			split: a_len,420			len: a_len.checked_add(b_len).expect("too large array value"),421		}422	}423}424425struct WithExactSize<I>(I, usize);426impl<I, T> Iterator for WithExactSize<I>427where428	I: Iterator<Item = T>,429{430	type Item = T;431432	fn next(&mut self) -> Option<Self::Item> {433		self.0.next()434	}435	fn nth(&mut self, n: usize) -> Option<Self::Item> {436		self.0.nth(n)437	}438	fn size_hint(&self) -> (usize, Option<usize>) {439		(self.1, Some(self.1))440	}441}442impl<I> DoubleEndedIterator for WithExactSize<I>443where444	I: DoubleEndedIterator,445{446	fn next_back(&mut self) -> Option<Self::Item> {447		self.0.next_back()448	}449	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {450		self.0.nth_back(n)451	}452}453impl<I> ExactSizeIterator for WithExactSize<I>454where455	I: Iterator,456{457	fn len(&self) -> usize {458		self.1459	}460}461impl ArrayLike for ExtendedArray {462	#[cfg(feature = "nightly")]463	type Iter<'t> = ExtendedArrayIter<'t>;464	#[cfg(feature = "nightly")]465	type IterLazy<'t> = ExtendedArrayLazyIter<'t>;466	#[cfg(feature = "nightly")]467	type IterCheap<'t> = ExtendedArrayCheapIter<'t>;468469	fn get(&self, index: usize) -> Result<Option<Val>> {470		if self.split > index {471			self.a.get(index)472		} else {473			self.b.get(index - self.split)474		}475	}476	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {477		if self.split > index {478			self.a.get_lazy(index)479		} else {480			self.b.get_lazy(index - self.split)481		}482	}483484	fn len(&self) -> usize {485		self.len486	}487488	fn get_cheap(&self, index: usize) -> Option<Val> {489		if self.split > index {490			self.a.get_cheap(index)491		} else {492			self.b.get_cheap(index - self.split)493		}494	}495496	#[cfg(feature = "nightly")]497	fn iter(&self) -> ExtendedArrayIter<'_> {498		WithExactSize(self.a.iter().chain(self.b.iter()), self.len)499	}500	#[cfg(feature = "nightly")]501	fn iter_lazy(&self) -> ExtendedArrayLazyIter<'_> {502		WithExactSize(self.a.iter_lazy().chain(self.b.iter_lazy()), self.len)503	}504	#[cfg(feature = "nightly")]505	fn iter_cheap(&self) -> Option<ExtendedArrayCheapIter<'_>> {506		let a = self.a.iter_cheap()?;507		let b = self.b.iter_cheap()?;508		Some(WithExactSize(a.chain(b), self.len))509	}510}511impl From<ExtendedArray> for ArrValue {512	fn from(value: ExtendedArray) -> Self {513		Self::Extended(Cc::new(value))514	}515}516517#[derive(Trace, Debug, Clone)]518pub struct LazyArray(pub Cc<Vec<Thunk<Val>>>);519#[cfg(feature = "nightly")]520type LazyArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;521#[cfg(feature = "nightly")]522type LazyArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;523#[cfg(feature = "nightly")]524type LazyArrayCheapIter<'t> = iter::Empty<Val>;525impl ArrayLike for LazyArray {526	#[cfg(feature = "nightly")]527	type Iter<'t> = LazyArrayIter<'t>;528529	#[cfg(feature = "nightly")]530	type IterLazy<'t> = LazyArrayLazyIter<'t>;531532	#[cfg(feature = "nightly")]533	type IterCheap<'t> = LazyArrayCheapIter<'t>;534535	fn len(&self) -> usize {536		self.0.len()537	}538	fn get(&self, index: usize) -> Result<Option<Val>> {539		let Some(v) = self.0.get(index) else {540			return Ok(None);541		};542		v.evaluate().map(Some)543	}544	fn get_cheap(&self, _index: usize) -> Option<Val> {545		None546	}547	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {548		self.0.get(index).cloned()549	}550	#[cfg(feature = "nightly")]551	fn iter(&self) -> LazyArrayIter<'_> {552		self.0.iter().map(Thunk::evaluate)553	}554	#[cfg(feature = "nightly")]555	fn iter_lazy(&self) -> LazyArrayLazyIter<'_> {556		self.0.iter().cloned()557	}558	#[cfg(feature = "nightly")]559	fn iter_cheap(&self) -> Option<LazyArrayCheapIter<'_>> {560		None561	}562}563impl From<LazyArray> for ArrValue {564	fn from(value: LazyArray) -> Self {565		Self::Lazy(value)566	}567}568569#[derive(Trace, Debug, Clone)]570pub struct EagerArray(pub Cc<Vec<Val>>);571#[cfg(feature = "nightly")]572type EagerArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;573#[cfg(feature = "nightly")]574type EagerArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;575#[cfg(feature = "nightly")]576type EagerArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;577impl ArrayLike for EagerArray {578	#[cfg(feature = "nightly")]579	type Iter<'t> = EagerArrayIter<'t>;580581	#[cfg(feature = "nightly")]582	type IterLazy<'t> = EagerArrayLazyIter<'t>;583584	#[cfg(feature = "nightly")]585	type IterCheap<'t> = EagerArrayCheapIter<'t>;586587	fn len(&self) -> usize {588		self.0.len()589	}590591	fn get(&self, index: usize) -> Result<Option<Val>> {592		Ok(self.0.get(index).cloned())593	}594595	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {596		self.0.get(index).cloned().map(Thunk::evaluated)597	}598599	fn get_cheap(&self, index: usize) -> Option<Val> {600		self.0.get(index).cloned()601	}602603	#[cfg(feature = "nightly")]604	fn iter(&self) -> EagerArrayIter<'_> {605		self.0.iter().cloned().map(Ok)606	}607608	#[cfg(feature = "nightly")]609	fn iter_lazy(&self) -> EagerArrayLazyIter<'_> {610		self.0.iter().cloned().map(Thunk::evaluated)611	}612613	#[cfg(feature = "nightly")]614	fn iter_cheap(&self) -> Option<EagerArrayCheapIter<'_>> {615		Some(self.0.iter().cloned())616	}617}618impl From<EagerArray> for ArrValue {619	fn from(value: EagerArray) -> Self {620		Self::Eager(value)621	}622}623624/// Inclusive range type625#[derive(Debug, Trace, Clone, PartialEq, Eq)]626pub struct RangeArray {627	start: i32,628	end: i32,629}630impl RangeArray {631	pub fn empty() -> Self {632		Self::new_exclusive(0, 0)633	}634	pub fn new_exclusive(start: i32, end: i32) -> Self {635		end.checked_sub(1)636			.map_or_else(Self::empty, |end| Self { start, end })637	}638	pub fn new_inclusive(start: i32, end: i32) -> Self {639		Self { start, end }640	}641	fn range(&self) -> impl Iterator<Item = i32> + ExactSizeIterator + DoubleEndedIterator {642		WithExactSize(643			self.start..=self.end,644			(self.end as usize)645				.wrapping_sub(self.start as usize)646				.wrapping_add(1),647		)648	}649}650651#[cfg(feature = "nightly")]652type RangeArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;653#[cfg(feature = "nightly")]654type RangeArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;655#[cfg(feature = "nightly")]656type RangeArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;657impl ArrayLike for RangeArray {658	#[cfg(feature = "nightly")]659	type Iter<'t> = RangeArrayIter<'t>;660661	#[cfg(feature = "nightly")]662	type IterLazy<'t> = RangeArrayLazyIter<'t>;663664	#[cfg(feature = "nightly")]665	type IterCheap<'t> = RangeArrayCheapIter<'t>;666667	fn len(&self) -> usize {668		self.range().len()669	}670	fn is_empty(&self) -> bool {671		self.range().len() == 0672	}673674	fn get(&self, index: usize) -> Result<Option<Val>> {675		Ok(self.get_cheap(index))676	}677678	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {679		self.get_cheap(index).map(Thunk::evaluated)680	}681682	fn get_cheap(&self, index: usize) -> Option<Val> {683		self.range().nth(index).map(|i| Val::Num(f64::from(i)))684	}685686	#[cfg(feature = "nightly")]687	fn iter(&self) -> RangeArrayIter<'_> {688		self.range().map(|i| Ok(Val::Num(f64::from(i))))689	}690691	#[cfg(feature = "nightly")]692	fn iter_lazy(&self) -> RangeArrayLazyIter<'_> {693		self.range()694			.map(|i| Thunk::evaluated(Val::Num(f64::from(i))))695	}696697	#[cfg(feature = "nightly")]698	fn iter_cheap(&self) -> Option<RangeArrayCheapIter<'_>> {699		Some(self.range().map(|i| Val::Num(f64::from(i))))700	}701}702impl From<RangeArray> for ArrValue {703	fn from(value: RangeArray) -> Self {704		Self::Range(value)705	}706}707708#[derive(Debug, Trace, Clone)]709pub struct ReverseArray(pub ArrValue);710impl ArrayLike for ReverseArray {711	#[cfg(feature = "nightly")]712	type Iter<'t> = iter::Rev<UnknownArrayIter<'t>>;713714	#[cfg(feature = "nightly")]715	type IterLazy<'t> = iter::Rev<UnknownArrayIterLazy<'t>>;716717	#[cfg(feature = "nightly")]718	type IterCheap<'t> = iter::Rev<UnknownArrayIterCheap<'t>>;719720	fn len(&self) -> usize {721		self.0.len()722	}723724	fn get(&self, index: usize) -> Result<Option<Val>> {725		self.0.get(self.0.len() - index - 1)726	}727728	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {729		self.0.get_lazy(self.0.len() - index - 1)730	}731732	fn get_cheap(&self, index: usize) -> Option<Val> {733		self.0.get_cheap(self.0.len() - index - 1)734	}735736	#[cfg(feature = "nightly")]737	fn iter(&self) -> iter::Rev<UnknownArrayIter<'_>> {738		self.0.iter().rev()739	}740741	#[cfg(feature = "nightly")]742	fn iter_lazy(&self) -> iter::Rev<UnknownArrayIterLazy<'_>> {743		self.0.iter_lazy().rev()744	}745746	#[cfg(feature = "nightly")]747	fn iter_cheap(&self) -> Option<iter::Rev<UnknownArrayIterCheap<'_>>> {748		Some(self.0.iter_cheap()?.rev())749	}750	fn reverse(self) -> ArrValue {751		self.0752	}753}754impl From<ReverseArray> for ArrValue {755	fn from(value: ReverseArray) -> Self {756		Self::Reverse(Cc::new(value))757	}758}759760#[derive(Trace, Debug)]761pub struct MappedArrayInner {762	inner: ArrValue,763	cached: RefCell<Vec<ArrayThunk<()>>>,764	mapper: FuncVal,765}766#[derive(Trace, Debug, Clone)]767pub struct MappedArray(Cc<MappedArrayInner>);768impl MappedArray {769	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {770		let len = inner.len();771		Self(Cc::new(MappedArrayInner {772			inner,773			cached: RefCell::new(vec![ArrayThunk::Waiting(()); len]),774			mapper,775		}))776	}777}778#[cfg(feature = "nightly")]779type MappedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;780#[cfg(feature = "nightly")]781type MappedArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;782#[cfg(feature = "nightly")]783type MappedArrayCheapIter<'t> = iter::Empty<Val>;784impl ArrayLike for MappedArray {785	#[cfg(feature = "nightly")]786	type Iter<'t> = MappedArrayIter<'t>;787	#[cfg(feature = "nightly")]788	type IterLazy<'t> = MappedArrayLazyIter<'t>;789	#[cfg(feature = "nightly")]790	type IterCheap<'t> = MappedArrayCheapIter<'t>;791792	fn len(&self) -> usize {793		self.0.cached.borrow().len()794	}795796	fn get(&self, index: usize) -> Result<Option<Val>> {797		if index >= self.len() {798			return Ok(None);799		}800		match &self.0.cached.borrow()[index] {801			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),802			ArrayThunk::Errored(e) => return Err(e.clone()),803			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),804			ArrayThunk::Waiting(..) => {}805		};806807		let ArrayThunk::Waiting(_) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {808			unreachable!()809		};810811		let val = self812			.0813			.inner814			.get(index)815			.transpose()816			.expect("index checked")817			.and_then(|r| self.0.mapper.evaluate_simple(&(r,), false));818819		let new_value = match val {820			Ok(v) => v,821			Err(e) => {822				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());823				return Err(e);824			}825		};826		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());827		Ok(Some(new_value))828	}829	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {830		#[derive(Trace)]831		struct ArrayElement {832			arr_thunk: MappedArray,833			index: usize,834		}835836		impl ThunkValue for ArrayElement {837			type Output = Val;838839			fn get(self: Box<Self>) -> Result<Self::Output> {840				self.arr_thunk841					.get(self.index)842					.transpose()843					.expect("index checked")844			}845		}846847		if index >= self.len() {848			return None;849		}850		match &self.0.cached.borrow()[index] {851			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),852			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),853			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}854		};855856		Some(Thunk::new(ArrayElement {857			arr_thunk: self.clone(),858			index,859		}))860	}861862	fn get_cheap(&self, _index: usize) -> Option<Val> {863		None864	}865866	#[cfg(feature = "nightly")]867	fn iter(&self) -> MappedArrayIter<'_> {868		(0..self.len()).map(|i| self.get(i).transpose().expect("length checked"))869	}870871	#[cfg(feature = "nightly")]872	fn iter_lazy(&self) -> MappedArrayLazyIter<'_> {873		(0..self.len()).map(|i| self.get_lazy(i).expect("length checked"))874	}875876	#[cfg(feature = "nightly")]877	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>> {878		None879	}880}881impl From<MappedArray> for ArrValue {882	fn from(value: MappedArray) -> Self {883		Self::Mapped(value)884	}885}886887#[derive(Trace, Debug)]888pub struct RepeatedArrayInner {889	data: ArrValue,890	repeats: usize,891	total_len: usize,892}893#[derive(Trace, Debug, Clone)]894pub struct RepeatedArray(Cc<RepeatedArrayInner>);895impl RepeatedArray {896	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {897		let total_len = data.len().checked_mul(repeats)?;898		Some(Self(Cc::new(RepeatedArrayInner {899			data,900			repeats,901			total_len,902		})))903	}904	pub fn is_cheap(&self) -> bool {905		self.0.data.is_cheap()906	}907}908909#[cfg(feature = "nightly")]910type RepeatedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;911#[cfg(feature = "nightly")]912type RepeatedArrayLazyIter<'t> =913	impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;914#[cfg(feature = "nightly")]915type RepeatedArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;916impl ArrayLike for RepeatedArray {917	#[cfg(feature = "nightly")]918	type Iter<'t> = RepeatedArrayIter<'t>;919	#[cfg(feature = "nightly")]920	type IterLazy<'t> = RepeatedArrayLazyIter<'t>;921	#[cfg(feature = "nightly")]922	type IterCheap<'t> = RepeatedArrayCheapIter<'t>;923924	fn len(&self) -> usize {925		self.0.total_len926	}927928	fn get(&self, index: usize) -> Result<Option<Val>> {929		if index > self.0.total_len {930			return Ok(None);931		}932		self.0.data.get(index % self.0.data.len())933	}934935	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {936		if index > self.0.total_len {937			return None;938		}939		self.0.data.get_lazy(index % self.0.data.len())940	}941942	fn get_cheap(&self, index: usize) -> Option<Val> {943		if index > self.0.total_len {944			return None;945		}946		self.0.data.get_cheap(index % self.0.data.len())947	}948949	#[cfg(feature = "nightly")]950	fn iter(&self) -> RepeatedArrayIter<'_> {951		(0..self.0.total_len)952			.map(|i| self.get(i))953			.map(Result::transpose)954			.map(Option::unwrap)955	}956957	#[cfg(feature = "nightly")]958	fn iter_lazy(&self) -> RepeatedArrayLazyIter<'_> {959		(0..self.0.total_len)960			.map(|i| self.get_lazy(i))961			.map(Option::unwrap)962	}963964	#[cfg(feature = "nightly")]965	fn iter_cheap(&self) -> Option<RepeatedArrayCheapIter<'_>> {966		if !self.0.data.is_cheap() {967			return None;968		}969		Some(970			(0..self.0.total_len)971				.map(|i| self.get_cheap(i))972				.map(Option::unwrap),973		)974	}975}976impl From<RepeatedArray> for ArrValue {977	fn from(value: RepeatedArray) -> Self {978		Self::Repeated(value)979	}980}981982#[cfg(feature = "nightly")]983macro_rules! impl_iter_enum {984	($n:ident => $v:ident) => {985		pub enum $n<'t> {986			Bytes(<BytesArray as ArrayLike>::$v<'t>),987			Expr(<ExprArray as ArrayLike>::$v<'t>),988			Lazy(<LazyArray as ArrayLike>::$v<'t>),989			Eager(<EagerArray as ArrayLike>::$v<'t>),990			Range(<RangeArray as ArrayLike>::$v<'t>),991			Slice(Box<<SliceArray as ArrayLike>::$v<'t>>),992			Extended(Box<<ExtendedArray as ArrayLike>::$v<'t>>),993			Reverse(Box<<ReverseArray as ArrayLike>::$v<'t>>),994			Mapped(Box<<MappedArray as ArrayLike>::$v<'t>>),995			Repeated(Box<<RepeatedArray as ArrayLike>::$v<'t>>),996		}997	};998}9991000macro_rules! pass {1001	($t:ident.$m:ident($($ident:ident),*)) => {1002		match $t {1003			Self::Bytes(e) => e.$m($($ident)*),1004			Self::Chars(e) => e.$m($($ident)*),1005			Self::Expr(e) => e.$m($($ident)*),1006			Self::Lazy(e) => e.$m($($ident)*),1007			Self::Eager(e) => e.$m($($ident)*),1008			Self::Range(e) => e.$m($($ident)*),1009			Self::Slice(e) => e.$m($($ident)*),1010			Self::Extended(e) => e.$m($($ident)*),1011			Self::Reverse(e) => e.$m($($ident)*),1012			Self::Mapped(e) => e.$m($($ident)*),1013			Self::Repeated(e) => e.$m($($ident)*),1014		}1015	};1016}1017pub(super) use pass;10181019#[cfg(feature = "nightly")]1020macro_rules! pass_iter_call {1021	($t:ident.$c:ident $(in $wrap:ident)? => $e:ident) => {1022		match $t {1023			ArrValue::Bytes(e) => $e::Bytes($($wrap!)?(e.$c())),1024			ArrValue::Lazy(e) => $e::Lazy($($wrap!)?(e.$c())),1025			ArrValue::Expr(e) => $e::Expr($($wrap!)?(e.$c())),1026			ArrValue::Eager(e) => $e::Eager($($wrap!)?(e.$c())),1027			ArrValue::Range(e) => $e::Range($($wrap!)?(e.$c())),1028			ArrValue::Slice(e) => $e::Slice(Box::new($($wrap!)?(e.$c()))),1029			ArrValue::Extended(e) => $e::Extended(Box::new($($wrap!)?(e.$c()))),1030			ArrValue::Reverse(e) => $e::Reverse(Box::new($($wrap!)?(e.$c()))),1031			ArrValue::Mapped(e) => $e::Mapped(Box::new($($wrap!)?(e.$c()))),1032			ArrValue::Repeated(e) => $e::Repeated(Box::new($($wrap!)?(e.$c()))),1033		}1034	};1035}1036#[cfg(feature = "nightly")]1037pub(super) use pass_iter_call;10381039#[cfg(feature = "nightly")]1040macro_rules! impl_iter {1041	($t:ident => $out:ty) => {1042		impl Iterator for $t<'_> {1043			type Item = $out;10441045			fn next(&mut self) -> Option<Self::Item> {1046				pass!(self.next())1047			}1048			fn nth(&mut self, count: usize) -> Option<Self::Item> {1049				pass!(self.nth(count))1050			}1051			fn size_hint(&self) -> (usize, Option<usize>) {1052				pass!(self.size_hint())1053			}1054		}1055		impl DoubleEndedIterator for $t<'_> {1056			fn next_back(&mut self) -> Option<Self::Item> {1057				pass!(self.next_back())1058			}1059			fn nth_back(&mut self, count: usize) -> Option<Self::Item> {1060				pass!(self.nth_back(count))1061			}1062		}1063		impl ExactSizeIterator for $t<'_> {1064			fn len(&self) -> usize {1065				match self {1066					Self::Bytes(e) => e.len(),1067					Self::Expr(e) => e.len(),1068					Self::Lazy(e) => e.len(),1069					Self::Eager(e) => e.len(),1070					Self::Range(e) => e.len(),1071					Self::Slice(e) => e.len(),1072					Self::Extended(e) => {1073						e.size_hint().1.expect("overflow is checked in constructor")1074					}1075					Self::Reverse(e) => e.len(),1076					Self::Mapped(e) => e.len(),1077					Self::Repeated(e) => e.len(),1078				}1079			}1080		}1081	};1082}10831084#[cfg(feature = "nightly")]1085impl_iter_enum!(UnknownArrayIter => Iter);1086#[cfg(feature = "nightly")]1087impl_iter!(UnknownArrayIter => Result<Val>);1088#[cfg(feature = "nightly")]1089impl_iter_enum!(UnknownArrayIterLazy => IterLazy);1090#[cfg(feature = "nightly")]1091impl_iter!(UnknownArrayIterLazy => Thunk<Val>);1092#[cfg(feature = "nightly")]1093impl_iter_enum!(UnknownArrayIterCheap => IterCheap);1094#[cfg(feature = "nightly")]1095impl_iter!(UnknownArrayIterCheap => Val);