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}331332333#[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 629 630 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}