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 iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {35 self.inner36 .iter()37 .skip(self.from as usize)38 .take((self.to - self.from) as usize)39 .step_by(self.step as usize)40 }4142 fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {43 self.inner44 .iter_lazy()45 .skip(self.from as usize)46 .take((self.to - self.from) as usize)47 .step_by(self.step as usize)48 }4950 fn iter_cheap(&self) -> Option<impl crate::arr::ArrayLikeIter<Val> + '_> {51 Some(52 self.inner53 .iter_cheap()?54 .skip(self.from as usize)55 .take((self.to - self.from) as usize)56 .step_by(self.step as usize),57 )58 }59}60impl ArrayLike for SliceArray {61 fn len(&self) -> usize {62 iter::repeat(())63 .take((self.to - self.from) as usize)64 .step_by(self.step as usize)65 .count()66 }6768 fn get(&self, index: usize) -> Result<Option<Val>> {69 self.iter().nth(index).transpose()70 }7172 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {73 self.iter_lazy().nth(index)74 }7576 fn get_cheap(&self, index: usize) -> Option<Val> {77 self.iter_cheap()?.nth(index)78 }79 fn is_cheap(&self) -> bool {80 self.inner.is_cheap()81 }82}8384#[derive(Trace, Debug)]85pub struct CharArray(pub Vec<char>);86impl ArrayLike for CharArray {87 fn len(&self) -> usize {88 self.0.len()89 }9091 fn get(&self, index: usize) -> Result<Option<Val>> {92 Ok(self.get_cheap(index))93 }9495 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {96 self.get_cheap(index).map(Thunk::evaluated)97 }9899 fn get_cheap(&self, index: usize) -> Option<Val> {100 self.0.get(index).map(|v| Val::string(*v))101 }102 fn is_cheap(&self) -> bool {103 true104 }105}106107#[derive(Trace, Debug)]108pub struct BytesArray(pub IBytes);109impl ArrayLike for BytesArray {110 fn len(&self) -> usize {111 self.0.len()112 }113114 fn get(&self, index: usize) -> Result<Option<Val>> {115 Ok(self.get_cheap(index))116 }117118 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {119 self.get_cheap(index).map(Thunk::evaluated)120 }121122 fn get_cheap(&self, index: usize) -> Option<Val> {123 self.0.get(index).map(|v| Val::Num((*v).into()))124 }125 fn is_cheap(&self) -> bool {126 true127 }128}129130#[derive(Debug, Trace, Clone)]131enum ArrayThunk<T: 'static + Trace> {132 Computed(Val),133 Errored(Error),134 Waiting(T),135 Pending,136}137138#[derive(Debug, Trace, Clone)]139pub struct ExprArray {140 ctx: Context,141 cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,142}143impl ExprArray {144 pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {145 Self {146 ctx,147 cached: Cc::new(RefCell::new(148 items.into_iter().map(ArrayThunk::Waiting).collect(),149 )),150 }151 }152}153impl ArrayLike for ExprArray {154 fn len(&self) -> usize {155 self.cached.borrow().len()156 }157 fn get(&self, index: usize) -> Result<Option<Val>> {158 if index >= self.len() {159 return Ok(None);160 }161 match &self.cached.borrow()[index] {162 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),163 ArrayThunk::Errored(e) => return Err(e.clone()),164 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),165 ArrayThunk::Waiting(..) => {}166 };167168 let ArrayThunk::Waiting(expr) =169 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)170 else {171 unreachable!()172 };173174 let new_value = match evaluate(self.ctx.clone(), &expr) {175 Ok(v) => v,176 Err(e) => {177 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());178 return Err(e);179 }180 };181 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());182 Ok(Some(new_value))183 }184 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {185 if index >= self.len() {186 return None;187 }188 match &self.cached.borrow()[index] {189 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),190 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),191 ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}192 };193194 #[derive(Trace)]195 struct ExprArrThunk {196 expr: ExprArray,197 index: usize,198 }199 impl ThunkValue for ExprArrThunk {200 type Output = Val;201202 fn get(&self) -> Result<Self::Output> {203 self.expr204 .get(self.index)205 .transpose()206 .expect("index checked")207 }208 }209210 Some(Thunk::new(ExprArrThunk {211 expr: self.clone(),212 index,213 }))214 }215 fn get_cheap(&self, _index: usize) -> Option<Val> {216 None217 }218 fn is_cheap(&self) -> bool {219 false220 }221}222223#[derive(Trace, Debug)]224pub struct ExtendedArray {225 pub a: ArrValue,226 pub b: ArrValue,227 split: usize,228 len: usize,229}230impl ExtendedArray {231 pub fn new(a: ArrValue, b: ArrValue) -> Self {232 let a_len = a.len();233 let b_len = b.len();234 Self {235 a,236 b,237 split: a_len,238 len: a_len.checked_add(b_len).expect("too large array value"),239 }240 }241}242243struct WithExactSize<I>(I, usize);244impl<I, T> Iterator for WithExactSize<I>245where246 I: Iterator<Item = T>,247{248 type Item = T;249250 fn next(&mut self) -> Option<Self::Item> {251 self.0.next()252 }253 fn nth(&mut self, n: usize) -> Option<Self::Item> {254 self.0.nth(n)255 }256 fn size_hint(&self) -> (usize, Option<usize>) {257 (self.1, Some(self.1))258 }259}260impl<I> DoubleEndedIterator for WithExactSize<I>261where262 I: DoubleEndedIterator,263{264 fn next_back(&mut self) -> Option<Self::Item> {265 self.0.next_back()266 }267 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {268 self.0.nth_back(n)269 }270}271impl<I> ExactSizeIterator for WithExactSize<I>272where273 I: Iterator,274{275 fn len(&self) -> usize {276 self.1277 }278}279impl ArrayLike for ExtendedArray {280 fn get(&self, index: usize) -> Result<Option<Val>> {281 if self.split > index {282 self.a.get(index)283 } else {284 self.b.get(index - self.split)285 }286 }287 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {288 if self.split > index {289 self.a.get_lazy(index)290 } else {291 self.b.get_lazy(index - self.split)292 }293 }294295 fn len(&self) -> usize {296 self.len297 }298299 fn get_cheap(&self, index: usize) -> Option<Val> {300 if self.split > index {301 self.a.get_cheap(index)302 } else {303 self.b.get_cheap(index - self.split)304 }305 }306 fn is_cheap(&self) -> bool {307 self.a.is_cheap() && self.b.is_cheap()308 }309}310311#[derive(Trace, Debug)]312pub struct LazyArray(pub Vec<Thunk<Val>>);313impl ArrayLike for LazyArray {314 fn len(&self) -> usize {315 self.0.len()316 }317 fn get(&self, index: usize) -> Result<Option<Val>> {318 let Some(v) = self.0.get(index) else {319 return Ok(None);320 };321 v.evaluate().map(Some)322 }323 fn get_cheap(&self, _index: usize) -> Option<Val> {324 None325 }326 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {327 self.0.get(index).cloned()328 }329 fn is_cheap(&self) -> bool {330 false331 }332}333334#[derive(Trace, Debug)]335pub struct EagerArray(pub Vec<Val>);336impl ArrayLike for EagerArray {337 fn len(&self) -> usize {338 self.0.len()339 }340341 fn get(&self, index: usize) -> Result<Option<Val>> {342 Ok(self.0.get(index).cloned())343 }344345 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {346 self.0.get(index).cloned().map(Thunk::evaluated)347 }348349 fn get_cheap(&self, index: usize) -> Option<Val> {350 self.0.get(index).cloned()351 }352 fn is_cheap(&self) -> bool {353 true354 }355}356357358#[derive(Debug, Trace, PartialEq, Eq)]359pub struct RangeArray {360 start: i32,361 end: i32,362}363impl RangeArray {364 pub fn empty() -> Self {365 Self::new_exclusive(0, 0)366 }367 pub fn new_exclusive(start: i32, end: i32) -> Self {368 end.checked_sub(1)369 .map_or_else(Self::empty, |end| Self { start, end })370 }371 pub fn new_inclusive(start: i32, end: i32) -> Self {372 Self { start, end }373 }374 fn range(&self) -> impl ExactSizeIterator<Item = i32> + DoubleEndedIterator {375 WithExactSize(376 self.start..=self.end,377 (self.end as usize)378 .wrapping_sub(self.start as usize)379 .wrapping_add(1),380 )381 }382}383384impl ArrayLike for RangeArray {385 fn len(&self) -> usize {386 self.range().len()387 }388 fn is_empty(&self) -> bool {389 self.range().len() == 0390 }391392 fn get(&self, index: usize) -> Result<Option<Val>> {393 Ok(self.get_cheap(index))394 }395396 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {397 self.get_cheap(index).map(Thunk::evaluated)398 }399400 fn get_cheap(&self, index: usize) -> Option<Val> {401 self.range().nth(index).map(|i| Val::Num(i.into()))402 }403 fn is_cheap(&self) -> bool {404 true405 }406}407408#[derive(Debug, Trace)]409pub struct ReverseArray(pub ArrValue);410impl ArrayLike for ReverseArray {411 fn len(&self) -> usize {412 self.0.len()413 }414415 fn get(&self, index: usize) -> Result<Option<Val>> {416 self.0.get(self.0.len() - index - 1)417 }418419 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {420 self.0.get_lazy(self.0.len() - index - 1)421 }422423 fn get_cheap(&self, index: usize) -> Option<Val> {424 self.0.get_cheap(self.0.len() - index - 1)425 }426 fn is_cheap(&self) -> bool {427 self.0.is_cheap()428 }429}430431#[derive(Trace, Debug, Clone)]432pub struct MappedArray<const WITH_INDEX: bool> {433 inner: ArrValue,434 cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,435 mapper: FuncVal,436}437impl<const WITH_INDEX: bool> MappedArray<WITH_INDEX> {438 pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {439 let len = inner.len();440 Self {441 inner,442 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),443 mapper,444 }445 }446 fn evaluate(&self, index: usize, value: Val) -> Result<Val> {447 if WITH_INDEX {448 self.mapper.evaluate_simple(&(index, value), false)449 } else {450 self.mapper.evaluate_simple(&(value,), false)451 }452 }453}454impl<const WITH_INDEX: bool> ArrayLike for MappedArray<WITH_INDEX> {455 fn len(&self) -> usize {456 self.cached.borrow().len()457 }458459 fn get(&self, index: usize) -> Result<Option<Val>> {460 if index >= self.len() {461 return Ok(None);462 }463 match &self.cached.borrow()[index] {464 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),465 ArrayThunk::Errored(e) => return Err(e.clone()),466 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),467 ArrayThunk::Waiting(..) => {}468 };469470 let ArrayThunk::Waiting(()) =471 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)472 else {473 unreachable!()474 };475476 let val = self477 .inner478 .get(index)479 .transpose()480 .expect("index checked")481 .and_then(|r| self.evaluate(index, r));482483 let new_value = match val {484 Ok(v) => v,485 Err(e) => {486 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());487 return Err(e);488 }489 };490 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());491 Ok(Some(new_value))492 }493 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {494 if index >= self.len() {495 return None;496 }497 match &self.cached.borrow()[index] {498 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),499 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),500 ArrayThunk::Waiting(()) | ArrayThunk::Pending => {}501 };502503 #[derive(Trace)]504 struct MappedArrayThunk<const WITH_INDEX: bool> {505 arr: MappedArray<WITH_INDEX>,506 index: usize,507 }508 impl<const WITH_INDEX: bool> ThunkValue for MappedArrayThunk<WITH_INDEX> {509 type Output = Val;510511 fn get(&self) -> Result<Self::Output> {512 self.arr.get(self.index).transpose().expect("index checked")513 }514 }515516 Some(Thunk::new(MappedArrayThunk {517 arr: self.clone(),518 index,519 }))520 }521522 fn get_cheap(&self, _index: usize) -> Option<Val> {523 None524 }525 fn is_cheap(&self) -> bool {526 false527 }528}529530#[derive(Trace, Debug)]531pub struct RepeatedArray {532 data: ArrValue,533 repeats: usize,534 total_len: usize,535}536impl RepeatedArray {537 pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {538 let total_len = data.len().checked_mul(repeats)?;539 Some(Self {540 data,541 repeats,542 total_len,543 })544 }545}546547impl ArrayLike for RepeatedArray {548 fn len(&self) -> usize {549 self.total_len550 }551552 fn get(&self, index: usize) -> Result<Option<Val>> {553 if index > self.total_len {554 return Ok(None);555 }556 self.data.get(index % self.data.len())557 }558559 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {560 if index > self.total_len {561 return None;562 }563 self.data.get_lazy(index % self.data.len())564 }565566 fn get_cheap(&self, index: usize) -> Option<Val> {567 if index > self.total_len {568 return None;569 }570 self.data.get_cheap(index % self.data.len())571 }572 fn is_cheap(&self) -> bool {573 self.data.is_cheap()574 }575}576577#[derive(Trace, Debug)]578pub struct PickObjectValues {579 obj: ObjValue,580 keys: Vec<IStr>,581}582583impl PickObjectValues {584 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {585 Self { obj, keys }586 }587}588589impl ArrayLike for PickObjectValues {590 fn len(&self) -> usize {591 self.keys.len()592 }593594 fn get(&self, index: usize) -> Result<Option<Val>> {595 let Some(key) = self.keys.get(index) else {596 return Ok(None);597 };598 Ok(Some(self.obj.get_or_bail(key.clone())?))599 }600601 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {602 let key = self.keys.get(index)?;603 Some(self.obj.get_lazy_or_bail(key.clone()))604 }605606 fn get_cheap(&self, _index: usize) -> Option<Val> {607 None608 }609610 fn is_cheap(&self) -> bool {611 false612 }613}614615#[derive(Trace, Debug)]616pub struct PickObjectKeyValues {617 obj: ObjValue,618 keys: Vec<IStr>,619}620621impl PickObjectKeyValues {622 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {623 Self { obj, keys }624 }625}626627#[derive(Typed)]628pub struct KeyValue {629 key: IStr,630 value: Thunk<Val>,631}632633impl ArrayLike for PickObjectKeyValues {634 fn len(&self) -> usize {635 self.keys.len()636 }637638 fn get(&self, index: usize) -> Result<Option<Val>> {639 let Some(key) = self.keys.get(index) else {640 return Ok(None);641 };642 Ok(Some(643 KeyValue::into_untyped(KeyValue {644 key: key.clone(),645 value: Thunk::evaluated(self.obj.get_or_bail(key.clone())?),646 })647 .expect("convertible"),648 ))649 }650651 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {652 let key = self.keys.get(index)?;653 654 655 Some(Thunk::evaluated(656 KeyValue::into_untyped(KeyValue {657 key: key.clone(),658 value: self.obj.get_lazy_or_bail(key.clone()),659 })660 .expect("convertible"),661 ))662 }663664 fn get_cheap(&self, _index: usize) -> Option<Val> {665 None666 }667668 fn is_cheap(&self) -> bool {669 false670 }671}