difftreelog
refactor split Typed into FromUntyped and IntoUntyped
in: master
20 files changed
bindings/jsonnet/src/native.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/native.rs
+++ b/bindings/jsonnet/src/native.rs
@@ -6,7 +6,7 @@
use jrsonnet_evaluator::{
error::{Error, ErrorKind},
function::builtin::{NativeCallback, NativeCallbackHandler},
- typed::Typed,
+ typed::FromUntyped as _,
IStr, Val,
};
cmds/jrsonnet/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -11,10 +11,6 @@
workspace = true
[features]
-default = [
- "exp-regex",
-]
-
experimental = [
"exp-preserve-order",
"exp-destruct",
crates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/mod.rs
+++ b/crates/jrsonnet-evaluator/src/arr/mod.rs
@@ -5,11 +5,11 @@
rc::Rc,
};
-use jrsonnet_gcmodule::{cc_dyn, Cc, Trace};
+use jrsonnet_gcmodule::{cc_dyn, Cc};
use jrsonnet_interner::IBytes;
use jrsonnet_parser::{Expr, Spanned};
-use crate::{function::NativeFn, typed::Typed, Context, Result, Thunk, Val};
+use crate::{function::NativeFn, Context, Result, Thunk, Val};
mod spec;
pub use spec::{ArrayLike, *};
@@ -241,4 +241,3 @@
self.0.is_cheap()
}
}
-
crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth1use std::rc::Rc;2use std::{any::Any, cell::RefCell, fmt::Debug, mem::replace};34use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::{IBytes, IStr};6use jrsonnet_parser::{Expr, Spanned};78use super::ArrValue;9use crate::function::NativeFn;10use crate::{11 error::ErrorKind::InfiniteRecursionDetected, evaluate, typed::Typed, val::ThunkValue, Context,12 Error, ObjValue, Result, Thunk, Val,13};1415pub trait ArrayLike: Any + Trace + Debug {16 fn len(&self) -> usize;17 fn is_empty(&self) -> bool {18 self.len() == 019 }20 fn get(&self, index: usize) -> Result<Option<Val>>;21 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;22 fn get_cheap(&self, index: usize) -> Option<Val>;2324 fn is_cheap(&self) -> bool;25}2627#[derive(Debug, Trace)]28pub struct SliceArray {29 pub(crate) inner: ArrValue,30 pub(crate) from: u32,31 pub(crate) to: u32,32 pub(crate) step: u32,33}3435impl SliceArray {36 fn map_idx(&self, index: usize) -> usize {37 self.from as usize + self.step as usize * index38 }39}40impl ArrayLike for SliceArray {41 fn len(&self) -> usize {42 (self.to - self.from).div_ceil(self.step) as usize43 }4445 fn get(&self, index: usize) -> Result<Option<Val>> {46 self.inner.get(self.map_idx(index))47 }4849 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {50 self.inner.get_lazy(self.map_idx(index))51 }5253 fn get_cheap(&self, index: usize) -> Option<Val> {54 self.inner.get_cheap(self.map_idx(index))55 }56 fn is_cheap(&self) -> bool {57 self.inner.is_cheap()58 }59}6061#[derive(Trace, Debug)]62pub struct CharArray(pub Vec<char>);63impl ArrayLike for CharArray {64 fn len(&self) -> usize {65 self.0.len()66 }6768 fn get(&self, index: usize) -> Result<Option<Val>> {69 Ok(self.get_cheap(index))70 }7172 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {73 self.get_cheap(index).map(Thunk::evaluated)74 }7576 fn get_cheap(&self, index: usize) -> Option<Val> {77 self.0.get(index).map(|v| Val::string(*v))78 }79 fn is_cheap(&self) -> bool {80 true81 }82}8384#[derive(Trace, Debug)]85pub struct BytesArray(pub IBytes);86impl ArrayLike for BytesArray {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::Num((*v).into()))101 }102 fn is_cheap(&self) -> bool {103 true104 }105}106107#[derive(Debug, Trace, Clone)]108enum ArrayThunk {109 Computed(Val),110 Errored(Error),111 Waiting,112 Pending,113}114115#[derive(Debug, Trace, Clone)]116pub struct ExprArray {117 ctx: Context,118 src: Rc<Vec<Spanned<Expr>>>,119 cached: Cc<RefCell<Vec<ArrayThunk>>>,120}121impl ExprArray {122 pub fn new(ctx: Context, src: Rc<Vec<Spanned<Expr>>>) -> Self {123 Self {124 ctx,125 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; src.len()])),126 src,127 }128 }129}130impl ArrayLike for ExprArray {131 fn len(&self) -> usize {132 self.cached.borrow().len()133 }134 fn get(&self, index: usize) -> Result<Option<Val>> {135 if index >= self.len() {136 return Ok(None);137 }138 match &self.cached.borrow()[index] {139 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),140 ArrayThunk::Errored(e) => return Err(e.clone()),141 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),142 ArrayThunk::Waiting => {}143 }144145 let ArrayThunk::Waiting =146 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)147 else {148 unreachable!()149 };150151 let new_value = match evaluate(self.ctx.clone(), &self.src[index]) {152 Ok(v) => v,153 Err(e) => {154 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());155 return Err(e);156 }157 };158 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());159 Ok(Some(new_value))160 }161 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {162 #[derive(Trace)]163 struct ExprArrThunk {164 expr: ExprArray,165 index: usize,166 }167 impl ThunkValue for ExprArrThunk {168 type Output = Val;169170 fn get(&self) -> Result<Self::Output> {171 self.expr172 .get(self.index)173 .transpose()174 .expect("index checked")175 }176 }177178 if index >= self.len() {179 return None;180 }181 match &self.cached.borrow()[index] {182 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),183 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),184 ArrayThunk::Waiting | ArrayThunk::Pending => {}185 }186187 Some(Thunk::new(ExprArrThunk {188 expr: self.clone(),189 index,190 }))191 }192 fn get_cheap(&self, _index: usize) -> Option<Val> {193 None194 }195 fn is_cheap(&self) -> bool {196 false197 }198}199200#[derive(Trace, Debug)]201pub struct ExtendedArray {202 pub a: ArrValue,203 pub b: ArrValue,204 split: usize,205 len: usize,206}207impl ExtendedArray {208 pub fn new(a: ArrValue, b: ArrValue) -> Self {209 let a_len = a.len();210 let b_len = b.len();211 Self {212 a,213 b,214 split: a_len,215 len: a_len.checked_add(b_len).expect("too large array value"),216 }217 }218}219220struct WithExactSize<I>(I, usize);221impl<I, T> Iterator for WithExactSize<I>222where223 I: Iterator<Item = T>,224{225 type Item = T;226227 fn next(&mut self) -> Option<Self::Item> {228 self.0.next()229 }230 fn nth(&mut self, n: usize) -> Option<Self::Item> {231 self.0.nth(n)232 }233 fn size_hint(&self) -> (usize, Option<usize>) {234 (self.1, Some(self.1))235 }236}237impl<I> DoubleEndedIterator for WithExactSize<I>238where239 I: DoubleEndedIterator,240{241 fn next_back(&mut self) -> Option<Self::Item> {242 self.0.next_back()243 }244 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {245 self.0.nth_back(n)246 }247}248impl<I> ExactSizeIterator for WithExactSize<I>249where250 I: Iterator,251{252 fn len(&self) -> usize {253 self.1254 }255}256impl ArrayLike for ExtendedArray {257 fn get(&self, index: usize) -> Result<Option<Val>> {258 if self.split > index {259 self.a.get(index)260 } else {261 self.b.get(index - self.split)262 }263 }264 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {265 if self.split > index {266 self.a.get_lazy(index)267 } else {268 self.b.get_lazy(index - self.split)269 }270 }271272 fn len(&self) -> usize {273 self.len274 }275276 fn get_cheap(&self, index: usize) -> Option<Val> {277 if self.split > index {278 self.a.get_cheap(index)279 } else {280 self.b.get_cheap(index - self.split)281 }282 }283 fn is_cheap(&self) -> bool {284 self.a.is_cheap() && self.b.is_cheap()285 }286}287288#[derive(Trace, Debug)]289pub struct LazyArray(pub Vec<Thunk<Val>>);290impl ArrayLike for LazyArray {291 fn len(&self) -> usize {292 self.0.len()293 }294 fn get(&self, index: usize) -> Result<Option<Val>> {295 let Some(v) = self.0.get(index) else {296 return Ok(None);297 };298 v.evaluate().map(Some)299 }300 fn get_cheap(&self, _index: usize) -> Option<Val> {301 None302 }303 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {304 self.0.get(index).cloned()305 }306 fn is_cheap(&self) -> bool {307 false308 }309}310311#[derive(Trace, Debug)]312pub struct EagerArray(pub Vec<Val>);313impl ArrayLike for EagerArray {314 fn len(&self) -> usize {315 self.0.len()316 }317318 fn get(&self, index: usize) -> Result<Option<Val>> {319 Ok(self.0.get(index).cloned())320 }321322 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {323 self.0.get(index).cloned().map(Thunk::evaluated)324 }325326 fn get_cheap(&self, index: usize) -> Option<Val> {327 self.0.get(index).cloned()328 }329 fn is_cheap(&self) -> bool {330 true331 }332}333334/// Inclusive range type335#[derive(Debug, Trace, PartialEq, Eq)]336pub struct RangeArray {337 start: i32,338 end: i32,339}340impl RangeArray {341 pub fn empty() -> Self {342 Self::new_exclusive(0, 0)343 }344 pub fn new_exclusive(start: i32, end: i32) -> Self {345 end.checked_sub(1)346 .map_or_else(Self::empty, |end| Self { start, end })347 }348 pub fn new_inclusive(start: i32, end: i32) -> Self {349 Self { start, end }350 }351 fn range(&self) -> impl ExactSizeIterator<Item = i32> + DoubleEndedIterator {352 WithExactSize(353 self.start..=self.end,354 (self.end as usize)355 .wrapping_sub(self.start as usize)356 .wrapping_add(1),357 )358 }359}360361impl ArrayLike for RangeArray {362 fn len(&self) -> usize {363 self.range().len()364 }365 fn is_empty(&self) -> bool {366 self.range().len() == 0367 }368369 fn get(&self, index: usize) -> Result<Option<Val>> {370 Ok(self.get_cheap(index))371 }372373 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {374 self.get_cheap(index).map(Thunk::evaluated)375 }376377 fn get_cheap(&self, index: usize) -> Option<Val> {378 self.range().nth(index).map(|i| Val::Num(i.into()))379 }380 fn is_cheap(&self) -> bool {381 true382 }383}384385#[derive(Debug, Trace)]386pub struct ReverseArray(pub ArrValue);387impl ArrayLike for ReverseArray {388 fn len(&self) -> usize {389 self.0.len()390 }391392 fn get(&self, index: usize) -> Result<Option<Val>> {393 self.0.get(self.0.len() - index - 1)394 }395396 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {397 self.0.get_lazy(self.0.len() - index - 1)398 }399400 fn get_cheap(&self, index: usize) -> Option<Val> {401 self.0.get_cheap(self.0.len() - index - 1)402 }403 fn is_cheap(&self) -> bool {404 self.0.is_cheap()405 }406}407408#[derive(Trace, Clone, Debug)]409pub enum ArrayMapper {410 Plain(NativeFn!((Val) -> Val)),411 WithIndex(NativeFn!((u32, Val) -> Val)),412}413414#[derive(Trace, Debug, Clone)]415pub struct MappedArray {416 inner: ArrValue,417 cached: Cc<RefCell<Vec<ArrayThunk>>>,418 mapper: ArrayMapper,419}420impl MappedArray {421 pub fn new(inner: ArrValue, mapper: ArrayMapper) -> Self {422 let len = inner.len();423 Self {424 inner,425 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; len])),426 mapper,427 }428 }429 fn evaluate(&self, index: usize, value: Val) -> Result<Val> {430 match &self.mapper {431 ArrayMapper::Plain(f) => f.call(value),432 ArrayMapper::WithIndex(f) => f.call(index as u32, value),433 }434 }435}436impl ArrayLike for MappedArray {437 fn len(&self) -> usize {438 self.cached.borrow().len()439 }440441 fn get(&self, index: usize) -> Result<Option<Val>> {442 if index >= self.len() {443 return Ok(None);444 }445 match &self.cached.borrow()[index] {446 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),447 ArrayThunk::Errored(e) => return Err(e.clone()),448 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),449 ArrayThunk::Waiting => {}450 }451452 let ArrayThunk::Waiting =453 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)454 else {455 unreachable!()456 };457458 let val = self459 .inner460 .get(index)461 .transpose()462 .expect("index checked")463 .and_then(|r| self.evaluate(index, r));464465 let new_value = match val {466 Ok(v) => v,467 Err(e) => {468 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());469 return Err(e);470 }471 };472 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());473 Ok(Some(new_value))474 }475 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {476 #[derive(Trace)]477 struct MappedArrayThunk {478 arr: MappedArray,479 index: usize,480 }481 impl ThunkValue for MappedArrayThunk {482 type Output = Val;483484 fn get(&self) -> Result<Self::Output> {485 self.arr.get(self.index).transpose().expect("index checked")486 }487 }488489 if index >= self.len() {490 return None;491 }492 match &self.cached.borrow()[index] {493 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),494 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),495 ArrayThunk::Waiting | ArrayThunk::Pending => {}496 }497498 Some(Thunk::new(MappedArrayThunk {499 arr: self.clone(),500 index,501 }))502 }503504 fn get_cheap(&self, _index: usize) -> Option<Val> {505 None506 }507 fn is_cheap(&self) -> bool {508 false509 }510}511512#[derive(Trace, Debug)]513pub struct RepeatedArray {514 data: ArrValue,515 repeats: usize,516 total_len: usize,517}518impl RepeatedArray {519 pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {520 let total_len = data.len().checked_mul(repeats)?;521 Some(Self {522 data,523 repeats,524 total_len,525 })526 }527}528529impl ArrayLike for RepeatedArray {530 fn len(&self) -> usize {531 self.total_len532 }533534 fn get(&self, index: usize) -> Result<Option<Val>> {535 if index > self.total_len {536 return Ok(None);537 }538 self.data.get(index % self.data.len())539 }540541 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {542 if index > self.total_len {543 return None;544 }545 self.data.get_lazy(index % self.data.len())546 }547548 fn get_cheap(&self, index: usize) -> Option<Val> {549 if index > self.total_len {550 return None;551 }552 self.data.get_cheap(index % self.data.len())553 }554 fn is_cheap(&self) -> bool {555 self.data.is_cheap()556 }557}558559#[derive(Trace, Debug)]560pub struct PickObjectValues {561 obj: ObjValue,562 keys: Vec<IStr>,563}564565impl PickObjectValues {566 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {567 Self { obj, keys }568 }569}570571impl ArrayLike for PickObjectValues {572 fn len(&self) -> usize {573 self.keys.len()574 }575576 fn get(&self, index: usize) -> Result<Option<Val>> {577 let Some(key) = self.keys.get(index) else {578 return Ok(None);579 };580 Ok(Some(self.obj.get_or_bail(key.clone())?))581 }582583 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {584 let key = self.keys.get(index)?;585 Some(self.obj.get_lazy_or_bail(key.clone()))586 }587588 fn get_cheap(&self, _index: usize) -> Option<Val> {589 None590 }591592 fn is_cheap(&self) -> bool {593 false594 }595}596597#[derive(Trace, Debug)]598pub struct PickObjectKeyValues {599 obj: ObjValue,600 keys: Vec<IStr>,601}602603impl PickObjectKeyValues {604 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {605 Self { obj, keys }606 }607}608609#[derive(Typed)]610pub struct KeyValue {611 key: IStr,612 value: Thunk<Val>,613}614615impl ArrayLike for PickObjectKeyValues {616 fn len(&self) -> usize {617 self.keys.len()618 }619620 fn get(&self, index: usize) -> Result<Option<Val>> {621 let Some(key) = self.keys.get(index) else {622 return Ok(None);623 };624 Ok(Some(625 KeyValue::into_untyped(KeyValue {626 key: key.clone(),627 value: Thunk::evaluated(self.obj.get_or_bail(key.clone())?),628 })629 .expect("convertible"),630 ))631 }632633 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {634 let key = self.keys.get(index)?;635 // Nothing can fail in the key part, yet value is still636 // lazy-evaluated637 Some(Thunk::evaluated(638 KeyValue::into_untyped(KeyValue {639 key: key.clone(),640 value: self.obj.get_lazy_or_bail(key.clone()),641 })642 .expect("convertible"),643 ))644 }645646 fn get_cheap(&self, _index: usize) -> Option<Val> {647 None648 }649650 fn is_cheap(&self) -> bool {651 false652 }653}1use std::rc::Rc;2use std::{any::Any, cell::RefCell, fmt::Debug, mem::replace};34use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::{IBytes, IStr};6use jrsonnet_parser::{Expr, Spanned};78use super::ArrValue;9use crate::function::NativeFn;10use crate::{11 error::ErrorKind::InfiniteRecursionDetected,12 evaluate,13 typed::{IntoUntyped, Typed},14 val::ThunkValue,15 Context, Error, ObjValue, Result, Thunk, Val,16};1718pub trait ArrayLike: Any + Trace + Debug {19 fn len(&self) -> usize;20 fn is_empty(&self) -> bool {21 self.len() == 022 }23 fn get(&self, index: usize) -> Result<Option<Val>>;24 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;25 fn get_cheap(&self, index: usize) -> Option<Val>;2627 fn is_cheap(&self) -> bool;28}2930#[derive(Debug, Trace)]31pub struct SliceArray {32 pub(crate) inner: ArrValue,33 pub(crate) from: u32,34 pub(crate) to: u32,35 pub(crate) step: u32,36}3738impl SliceArray {39 fn map_idx(&self, index: usize) -> usize {40 self.from as usize + self.step as usize * index41 }42}43impl ArrayLike for SliceArray {44 fn len(&self) -> usize {45 (self.to - self.from).div_ceil(self.step) as usize46 }4748 fn get(&self, index: usize) -> Result<Option<Val>> {49 self.inner.get(self.map_idx(index))50 }5152 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {53 self.inner.get_lazy(self.map_idx(index))54 }5556 fn get_cheap(&self, index: usize) -> Option<Val> {57 self.inner.get_cheap(self.map_idx(index))58 }59 fn is_cheap(&self) -> bool {60 self.inner.is_cheap()61 }62}6364#[derive(Trace, Debug)]65pub struct CharArray(pub Vec<char>);66impl ArrayLike for CharArray {67 fn len(&self) -> usize {68 self.0.len()69 }7071 fn get(&self, index: usize) -> Result<Option<Val>> {72 Ok(self.get_cheap(index))73 }7475 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {76 self.get_cheap(index).map(Thunk::evaluated)77 }7879 fn get_cheap(&self, index: usize) -> Option<Val> {80 self.0.get(index).map(|v| Val::string(*v))81 }82 fn is_cheap(&self) -> bool {83 true84 }85}8687#[derive(Trace, Debug)]88pub struct BytesArray(pub IBytes);89impl ArrayLike for BytesArray {90 fn len(&self) -> usize {91 self.0.len()92 }9394 fn get(&self, index: usize) -> Result<Option<Val>> {95 Ok(self.get_cheap(index))96 }9798 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {99 self.get_cheap(index).map(Thunk::evaluated)100 }101102 fn get_cheap(&self, index: usize) -> Option<Val> {103 self.0.get(index).map(|v| Val::Num((*v).into()))104 }105 fn is_cheap(&self) -> bool {106 true107 }108}109110#[derive(Debug, Trace, Clone)]111enum ArrayThunk {112 Computed(Val),113 Errored(Error),114 Waiting,115 Pending,116}117118#[derive(Debug, Trace, Clone)]119pub struct ExprArray {120 ctx: Context,121 src: Rc<Vec<Spanned<Expr>>>,122 cached: Cc<RefCell<Vec<ArrayThunk>>>,123}124impl ExprArray {125 pub fn new(ctx: Context, src: Rc<Vec<Spanned<Expr>>>) -> Self {126 Self {127 ctx,128 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; src.len()])),129 src,130 }131 }132}133impl ArrayLike for ExprArray {134 fn len(&self) -> usize {135 self.cached.borrow().len()136 }137 fn get(&self, index: usize) -> Result<Option<Val>> {138 if index >= self.len() {139 return Ok(None);140 }141 match &self.cached.borrow()[index] {142 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),143 ArrayThunk::Errored(e) => return Err(e.clone()),144 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),145 ArrayThunk::Waiting => {}146 }147148 let ArrayThunk::Waiting =149 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)150 else {151 unreachable!()152 };153154 let new_value = match evaluate(self.ctx.clone(), &self.src[index]) {155 Ok(v) => v,156 Err(e) => {157 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());158 return Err(e);159 }160 };161 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());162 Ok(Some(new_value))163 }164 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {165 #[derive(Trace)]166 struct ExprArrThunk {167 expr: ExprArray,168 index: usize,169 }170 impl ThunkValue for ExprArrThunk {171 type Output = Val;172173 fn get(&self) -> Result<Self::Output> {174 self.expr175 .get(self.index)176 .transpose()177 .expect("index checked")178 }179 }180181 if index >= self.len() {182 return None;183 }184 match &self.cached.borrow()[index] {185 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),186 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),187 ArrayThunk::Waiting | ArrayThunk::Pending => {}188 }189190 Some(Thunk::new(ExprArrThunk {191 expr: self.clone(),192 index,193 }))194 }195 fn get_cheap(&self, _index: usize) -> Option<Val> {196 None197 }198 fn is_cheap(&self) -> bool {199 false200 }201}202203#[derive(Trace, Debug)]204pub struct ExtendedArray {205 pub a: ArrValue,206 pub b: ArrValue,207 split: usize,208 len: usize,209}210impl ExtendedArray {211 pub fn new(a: ArrValue, b: ArrValue) -> Self {212 let a_len = a.len();213 let b_len = b.len();214 Self {215 a,216 b,217 split: a_len,218 len: a_len.checked_add(b_len).expect("too large array value"),219 }220 }221}222223struct WithExactSize<I>(I, usize);224impl<I, T> Iterator for WithExactSize<I>225where226 I: Iterator<Item = T>,227{228 type Item = T;229230 fn next(&mut self) -> Option<Self::Item> {231 self.0.next()232 }233 fn nth(&mut self, n: usize) -> Option<Self::Item> {234 self.0.nth(n)235 }236 fn size_hint(&self) -> (usize, Option<usize>) {237 (self.1, Some(self.1))238 }239}240impl<I> DoubleEndedIterator for WithExactSize<I>241where242 I: DoubleEndedIterator,243{244 fn next_back(&mut self) -> Option<Self::Item> {245 self.0.next_back()246 }247 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {248 self.0.nth_back(n)249 }250}251impl<I> ExactSizeIterator for WithExactSize<I>252where253 I: Iterator,254{255 fn len(&self) -> usize {256 self.1257 }258}259impl ArrayLike for ExtendedArray {260 fn get(&self, index: usize) -> Result<Option<Val>> {261 if self.split > index {262 self.a.get(index)263 } else {264 self.b.get(index - self.split)265 }266 }267 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {268 if self.split > index {269 self.a.get_lazy(index)270 } else {271 self.b.get_lazy(index - self.split)272 }273 }274275 fn len(&self) -> usize {276 self.len277 }278279 fn get_cheap(&self, index: usize) -> Option<Val> {280 if self.split > index {281 self.a.get_cheap(index)282 } else {283 self.b.get_cheap(index - self.split)284 }285 }286 fn is_cheap(&self) -> bool {287 self.a.is_cheap() && self.b.is_cheap()288 }289}290291#[derive(Trace, Debug)]292pub struct LazyArray(pub Vec<Thunk<Val>>);293impl ArrayLike for LazyArray {294 fn len(&self) -> usize {295 self.0.len()296 }297 fn get(&self, index: usize) -> Result<Option<Val>> {298 let Some(v) = self.0.get(index) else {299 return Ok(None);300 };301 v.evaluate().map(Some)302 }303 fn get_cheap(&self, _index: usize) -> Option<Val> {304 None305 }306 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {307 self.0.get(index).cloned()308 }309 fn is_cheap(&self) -> bool {310 false311 }312}313314#[derive(Trace, Debug)]315pub struct EagerArray(pub Vec<Val>);316impl ArrayLike for EagerArray {317 fn len(&self) -> usize {318 self.0.len()319 }320321 fn get(&self, index: usize) -> Result<Option<Val>> {322 Ok(self.0.get(index).cloned())323 }324325 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {326 self.0.get(index).cloned().map(Thunk::evaluated)327 }328329 fn get_cheap(&self, index: usize) -> Option<Val> {330 self.0.get(index).cloned()331 }332 fn is_cheap(&self) -> bool {333 true334 }335}336337/// Inclusive range type338#[derive(Debug, Trace, PartialEq, Eq)]339pub struct RangeArray {340 start: i32,341 end: i32,342}343impl RangeArray {344 pub fn empty() -> Self {345 Self::new_exclusive(0, 0)346 }347 pub fn new_exclusive(start: i32, end: i32) -> Self {348 end.checked_sub(1)349 .map_or_else(Self::empty, |end| Self { start, end })350 }351 pub fn new_inclusive(start: i32, end: i32) -> Self {352 Self { start, end }353 }354 fn range(&self) -> impl ExactSizeIterator<Item = i32> + DoubleEndedIterator {355 WithExactSize(356 self.start..=self.end,357 (self.end as usize)358 .wrapping_sub(self.start as usize)359 .wrapping_add(1),360 )361 }362}363364impl ArrayLike for RangeArray {365 fn len(&self) -> usize {366 self.range().len()367 }368 fn is_empty(&self) -> bool {369 self.range().len() == 0370 }371372 fn get(&self, index: usize) -> Result<Option<Val>> {373 Ok(self.get_cheap(index))374 }375376 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {377 self.get_cheap(index).map(Thunk::evaluated)378 }379380 fn get_cheap(&self, index: usize) -> Option<Val> {381 self.range().nth(index).map(|i| Val::Num(i.into()))382 }383 fn is_cheap(&self) -> bool {384 true385 }386}387388#[derive(Debug, Trace)]389pub struct ReverseArray(pub ArrValue);390impl ArrayLike for ReverseArray {391 fn len(&self) -> usize {392 self.0.len()393 }394395 fn get(&self, index: usize) -> Result<Option<Val>> {396 self.0.get(self.0.len() - index - 1)397 }398399 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {400 self.0.get_lazy(self.0.len() - index - 1)401 }402403 fn get_cheap(&self, index: usize) -> Option<Val> {404 self.0.get_cheap(self.0.len() - index - 1)405 }406 fn is_cheap(&self) -> bool {407 self.0.is_cheap()408 }409}410411#[derive(Trace, Clone, Debug)]412pub enum ArrayMapper {413 Plain(NativeFn!((Val) -> Val)),414 WithIndex(NativeFn!((u32, Val) -> Val)),415}416417#[derive(Trace, Debug, Clone)]418pub struct MappedArray {419 inner: ArrValue,420 cached: Cc<RefCell<Vec<ArrayThunk>>>,421 mapper: ArrayMapper,422}423impl MappedArray {424 pub fn new(inner: ArrValue, mapper: ArrayMapper) -> Self {425 let len = inner.len();426 Self {427 inner,428 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; len])),429 mapper,430 }431 }432 fn evaluate(&self, index: usize, value: Val) -> Result<Val> {433 match &self.mapper {434 ArrayMapper::Plain(f) => f.call(value),435 ArrayMapper::WithIndex(f) => f.call(index as u32, value),436 }437 }438}439impl ArrayLike for MappedArray {440 fn len(&self) -> usize {441 self.cached.borrow().len()442 }443444 fn get(&self, index: usize) -> Result<Option<Val>> {445 if index >= self.len() {446 return Ok(None);447 }448 match &self.cached.borrow()[index] {449 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),450 ArrayThunk::Errored(e) => return Err(e.clone()),451 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),452 ArrayThunk::Waiting => {}453 }454455 let ArrayThunk::Waiting =456 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)457 else {458 unreachable!()459 };460461 let val = self462 .inner463 .get(index)464 .transpose()465 .expect("index checked")466 .and_then(|r| self.evaluate(index, r));467468 let new_value = match val {469 Ok(v) => v,470 Err(e) => {471 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());472 return Err(e);473 }474 };475 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());476 Ok(Some(new_value))477 }478 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {479 #[derive(Trace)]480 struct MappedArrayThunk {481 arr: MappedArray,482 index: usize,483 }484 impl ThunkValue for MappedArrayThunk {485 type Output = Val;486487 fn get(&self) -> Result<Self::Output> {488 self.arr.get(self.index).transpose().expect("index checked")489 }490 }491492 if index >= self.len() {493 return None;494 }495 match &self.cached.borrow()[index] {496 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),497 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),498 ArrayThunk::Waiting | ArrayThunk::Pending => {}499 }500501 Some(Thunk::new(MappedArrayThunk {502 arr: self.clone(),503 index,504 }))505 }506507 fn get_cheap(&self, _index: usize) -> Option<Val> {508 None509 }510 fn is_cheap(&self) -> bool {511 false512 }513}514515#[derive(Trace, Debug)]516pub struct RepeatedArray {517 data: ArrValue,518 repeats: usize,519 total_len: usize,520}521impl RepeatedArray {522 pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {523 let total_len = data.len().checked_mul(repeats)?;524 Some(Self {525 data,526 repeats,527 total_len,528 })529 }530}531532impl ArrayLike for RepeatedArray {533 fn len(&self) -> usize {534 self.total_len535 }536537 fn get(&self, index: usize) -> Result<Option<Val>> {538 if index > self.total_len {539 return Ok(None);540 }541 self.data.get(index % self.data.len())542 }543544 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {545 if index > self.total_len {546 return None;547 }548 self.data.get_lazy(index % self.data.len())549 }550551 fn get_cheap(&self, index: usize) -> Option<Val> {552 if index > self.total_len {553 return None;554 }555 self.data.get_cheap(index % self.data.len())556 }557 fn is_cheap(&self) -> bool {558 self.data.is_cheap()559 }560}561562#[derive(Trace, Debug)]563pub struct PickObjectValues {564 obj: ObjValue,565 keys: Vec<IStr>,566}567568impl PickObjectValues {569 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {570 Self { obj, keys }571 }572}573574impl ArrayLike for PickObjectValues {575 fn len(&self) -> usize {576 self.keys.len()577 }578579 fn get(&self, index: usize) -> Result<Option<Val>> {580 let Some(key) = self.keys.get(index) else {581 return Ok(None);582 };583 Ok(Some(self.obj.get_or_bail(key.clone())?))584 }585586 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {587 let key = self.keys.get(index)?;588 Some(self.obj.get_lazy_or_bail(key.clone()))589 }590591 fn get_cheap(&self, _index: usize) -> Option<Val> {592 None593 }594595 fn is_cheap(&self) -> bool {596 false597 }598}599600#[derive(Trace, Debug)]601pub struct PickObjectKeyValues {602 obj: ObjValue,603 keys: Vec<IStr>,604}605606impl PickObjectKeyValues {607 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {608 Self { obj, keys }609 }610}611612#[derive(Typed)]613pub struct KeyValue {614 key: IStr,615 value: Thunk<Val>,616}617618impl ArrayLike for PickObjectKeyValues {619 fn len(&self) -> usize {620 self.keys.len()621 }622623 fn get(&self, index: usize) -> Result<Option<Val>> {624 let Some(key) = self.keys.get(index) else {625 return Ok(None);626 };627 Ok(Some(628 KeyValue::into_untyped(KeyValue {629 key: key.clone(),630 value: Thunk::evaluated(self.obj.get_or_bail(key.clone())?),631 })632 .expect("convertible"),633 ))634 }635636 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {637 let key = self.keys.get(index)?;638 // Nothing can fail in the key part, yet value is still639 // lazy-evaluated640 Some(Thunk::evaluated(641 KeyValue::into_untyped(KeyValue {642 key: key.clone(),643 value: self.obj.get_lazy_or_bail(key.clone()),644 })645 .expect("convertible"),646 ))647 }648649 fn get_cheap(&self, _index: usize) -> Option<Val> {650 None651 }652653 fn is_cheap(&self) -> bool {654 false655 }656}crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -20,7 +20,7 @@
function::{CallLocation, FuncDesc, FuncVal},
gc::WithCapacityExt as _,
in_frame,
- typed::Typed,
+ typed::{FromUntyped, IntoUntyped as _, Typed},
val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk},
with_state, Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,
ResultExt, SupThis, Unbound, Val,
@@ -620,7 +620,7 @@
}
}
Slice(slice) => {
- fn parse_idx<T: Typed>(
+ fn parse_idx<T: Typed + FromUntyped>(
loc: CallLocation<'_>,
ctx: Context,
expr: Option<&Spanned<Expr>>,
crates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs
@@ -8,7 +8,7 @@
error::ErrorKind::*,
evaluate,
stdlib::std_format,
- typed::Typed,
+ typed::IntoUntyped as _,
val::{equals, StrValue},
Context, Result, Val,
};
crates/jrsonnet-evaluator/src/function/native.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/native.rs
+++ b/crates/jrsonnet-evaluator/src/function/native.rs
@@ -3,7 +3,11 @@
use jrsonnet_gcmodule::Trace;
use super::PreparedFuncVal;
-use crate::{bail, function::FuncVal, typed::Typed, CallLocation, Result, Val};
+use crate::{
+ function::FuncVal,
+ typed::{FromUntyped, IntoUntyped, Typed},
+ CallLocation, Result, Val,
+};
use jrsonnet_types::{ComplexValType, ValType};
#[derive(Debug, Trace, Clone)]
@@ -12,8 +16,8 @@
($i:expr; $($gen:ident)*) => {
impl<$($gen,)* O> NativeFn<($($gen,)* O,)>
where
- $($gen: Typed,)*
- O: Typed,
+ $($gen: Typed + IntoUntyped,)*
+ O: Typed + FromUntyped,
{
#[allow(non_snake_case, clippy::too_many_arguments)]
pub fn call(
@@ -22,7 +26,7 @@
) -> Result<O> {
let val = self.0.call(
CallLocation::native(),
- &[$(Typed::into_lazy_untyped($gen),)*],
+ &[$(IntoUntyped::into_lazy_untyped($gen),)*],
&[],
)?;
O::from_untyped(val)
@@ -30,11 +34,9 @@
}
impl<$($gen,)* O> Typed for NativeFn<($($gen,)* O,)> {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
-
- fn into_untyped(_typed: Self) -> Result<Val> {
- bail!("can only convert functions from jsonnet to native")
- }
+ }
+ impl<$($gen,)* O> FromUntyped for NativeFn<($($gen,)* O,)> {
fn from_untyped(untyped: Val) -> Result<Self> {
let func = FuncVal::from_untyped(untyped)?;
Ok(Self(
crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/stdlib/format.rs
+++ b/crates/jrsonnet-evaluator/src/stdlib/format.rs
@@ -9,7 +9,7 @@
use crate::{
bail,
error::{format_found, suggest_object_fields, ErrorKind::*},
- typed::Typed,
+ typed::FromUntyped,
Error, ObjValue, Result, Val,
};
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -1,6 +1,6 @@
use std::{collections::BTreeMap, marker::PhantomData, ops::Deref};
-use jrsonnet_gcmodule::{Cc, Trace};
+use jrsonnet_gcmodule::Trace;
use jrsonnet_interner::{IBytes, IStr};
pub use jrsonnet_macros::Typed;
use jrsonnet_types::{ComplexValType, ValType};
@@ -8,17 +8,17 @@
use crate::{
arr::{ArrValue, BytesArray},
bail,
- function::{FuncDesc, FuncVal},
+ function::FuncVal,
typed::CheckType,
val::{IndexableVal, NumValue, StrValue, ThunkMapper},
ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,
};
#[derive(Trace)]
-struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);
-impl<K> ThunkMapper<Val> for FromUntyped<K>
+struct ThunkFromUntyped<K: Trace>(PhantomData<fn() -> K>);
+impl<K> ThunkMapper<Val> for ThunkFromUntyped<K>
where
- K: Typed + Trace,
+ K: Typed + FromUntyped + Trace,
{
type Output = K;
@@ -26,12 +26,29 @@
K::from_untyped(from)
}
}
-impl<K: Trace> Default for FromUntyped<K> {
+impl<K: Trace> Default for ThunkFromUntyped<K> {
fn default() -> Self {
Self(PhantomData)
}
}
+#[derive(Trace)]
+struct ThunkIntoUntyped<K: Trace>(PhantomData<fn() -> K>);
+impl<K> ThunkMapper<K> for ThunkIntoUntyped<K>
+where
+ K: Typed + Trace + IntoUntyped,
+{
+ type Output = Val;
+ fn map(self, from: K) -> Result<Self::Output> {
+ K::into_untyped(from)
+ }
+}
+impl<K: Trace> Default for ThunkIntoUntyped<K> {
+ fn default() -> Self {
+ Self(PhantomData)
+ }
+}
+
pub trait TypedObj: Typed {
fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
fn parse(obj: &ObjValue) -> Result<Self>;
@@ -44,31 +61,41 @@
pub trait Typed: Sized {
const TYPE: &'static ComplexValType;
+}
+pub trait IntoUntyped: Typed {
+ // Whatever caller should use `into_lazy_untyped` instead of `into_untyped`
+ fn provides_lazy() -> bool {
+ false
+ }
fn into_untyped(typed: Self) -> Result<Val>;
fn into_lazy_untyped(typed: Self) -> Thunk<Val> {
Thunk::from(Self::into_untyped(typed))
}
+}
+pub trait IntoUntypedResult: Typed {
+ /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result
+ /// This method returns identity in impl Typed for Result, and should not be overriden
+ #[doc(hidden)]
+ fn into_untyped_result(typed: Self) -> Result<Val>;
+}
+impl<T> IntoUntypedResult for T
+where
+ T: IntoUntyped,
+{
+ fn into_untyped_result(typed: Self) -> Result<Val> {
+ T::into_untyped(typed)
+ }
+}
+
+pub trait FromUntyped: Typed {
fn from_untyped(untyped: Val) -> Result<Self>;
fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {
Self::from_untyped(lazy.evaluate()?)
}
- // Whatever caller should use `into_lazy_untyped` instead of `into_untyped`
- fn provides_lazy() -> bool {
- false
- }
-
// Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible
fn wants_lazy() -> bool {
false
- }
-
- /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result
- /// This method returns identity in impl Typed for Result, and should not be overriden
- #[doc(hidden)]
- fn into_result(typed: Self) -> Result<Val> {
- let value = Self::into_untyped(typed)?;
- Ok(value)
}
}
@@ -77,38 +104,30 @@
T: Typed + Trace + Clone,
{
const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);
+}
+impl<T> IntoUntyped for Thunk<T>
+where
+ T: Typed + IntoUntyped + Trace + Clone,
+{
fn into_untyped(typed: Self) -> Result<Val> {
T::into_untyped(typed.evaluate()?)
- }
-
- fn from_untyped(untyped: Val) -> Result<Self> {
- Self::from_lazy_untyped(Thunk::evaluated(untyped))
}
-
fn provides_lazy() -> bool {
true
}
fn into_lazy_untyped(inner: Self) -> Thunk<Val> {
- #[derive(Trace)]
- struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);
- impl<K> ThunkMapper<K> for IntoUntyped<K>
- where
- K: Typed + Trace,
- {
- type Output = Val;
+ inner.map(<ThunkIntoUntyped<T>>::default())
+ }
+}
- fn map(self, from: K) -> Result<Self::Output> {
- K::into_untyped(from)
- }
- }
- impl<K: Trace> Default for IntoUntyped<K> {
- fn default() -> Self {
- Self(PhantomData)
- }
- }
- inner.map(<IntoUntyped<T>>::default())
+impl<T> FromUntyped for Thunk<T>
+where
+ T: Typed + FromUntyped + Trace + Clone,
+{
+ fn from_untyped(untyped: Val) -> Result<Self> {
+ Self::from_lazy_untyped(Thunk::evaluated(untyped))
}
fn wants_lazy() -> bool {
@@ -116,7 +135,7 @@
}
fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {
- Ok(inner.map(<FromUntyped<T>>::default()))
+ Ok(inner.map(<ThunkFromUntyped<T>>::default()))
}
}
@@ -128,6 +147,8 @@
impl Typed for $ty {
const TYPE: &'static ComplexValType =
&ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));
+ }
+ impl FromUntyped for $ty {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -145,6 +166,8 @@
_ => unreachable!(),
}
}
+ }
+ impl IntoUntyped for $ty {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::Num(value.into()))
}
@@ -183,7 +206,9 @@
Some(MIN as f64),
Some(MAX as f64),
);
+ }
+ impl<const MIN: $ty, const MAX: $ty> FromUntyped for $name<MIN, MAX> {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -201,7 +226,9 @@
_ => unreachable!(),
}
}
+ }
+ impl<const MIN: $ty, const MAX: $ty> IntoUntyped for $name<MIN, MAX> {
#[allow(clippy::cast_lossless)]
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::try_num(value.0)?)
@@ -220,11 +247,13 @@
impl Typed for f64 {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);
-
+}
+impl IntoUntyped for f64 {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::try_num(value)?)
}
-
+}
+impl FromUntyped for f64 {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -237,11 +266,13 @@
pub struct PositiveF64(pub f64);
impl Typed for PositiveF64 {
const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);
-
+}
+impl IntoUntyped for PositiveF64 {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::try_num(value.0)?)
}
-
+}
+impl FromUntyped for PositiveF64 {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -253,11 +284,13 @@
impl Typed for usize {
const TYPE: &'static ComplexValType =
&ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));
-
+}
+impl IntoUntyped for usize {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::try_num(value)?)
}
-
+}
+impl FromUntyped for usize {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -276,11 +309,13 @@
impl Typed for IStr {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);
-
+}
+impl IntoUntyped for IStr {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::string(value))
}
-
+}
+impl FromUntyped for IStr {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -292,11 +327,13 @@
impl Typed for String {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);
-
+}
+impl IntoUntyped for String {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::string(value))
}
-
+}
+impl FromUntyped for String {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -308,11 +345,13 @@
impl Typed for StrValue {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);
-
+}
+impl IntoUntyped for StrValue {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::Str(value))
}
-
+}
+impl FromUntyped for StrValue {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -324,11 +363,13 @@
impl Typed for char {
const TYPE: &'static ComplexValType = &ComplexValType::Char;
-
+}
+impl IntoUntyped for char {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::string(value))
}
-
+}
+impl FromUntyped for char {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -338,12 +379,14 @@
}
}
+// TODO: View into vec using ArrayLike?
impl<T> Typed for Vec<T>
where
T: Typed,
{
const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);
-
+}
+impl<T: Typed + IntoUntyped> IntoUntyped for Vec<T> {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::Arr(
value
@@ -352,7 +395,8 @@
.collect::<Result<ArrValue>>()?,
))
}
-
+}
+impl<T: Typed + FromUntyped> FromUntyped for Vec<T> {
fn from_untyped(value: Val) -> Result<Self> {
let Val::Arr(a) = value else {
<Self as Typed>::TYPE.check(&value)?;
@@ -369,9 +413,19 @@
}
}
-impl<K: Typed + Ord, V: Typed> Typed for BTreeMap<K, V> {
+// TODO: View into BTreeMap using ObjectCore?
+impl<K, V> Typed for BTreeMap<K, V>
+where
+ K: Typed + Ord,
+ V: Typed,
+{
const TYPE: &'static ComplexValType = &ComplexValType::AttrsOf(V::TYPE);
-
+}
+impl<K, V> IntoUntyped for BTreeMap<K, V>
+where
+ K: Typed + Ord + IntoUntyped,
+ V: Typed + IntoUntyped,
+{
fn into_untyped(typed: Self) -> Result<Val> {
let mut out = ObjValueBuilder::with_capacity(typed.len());
for (k, v) in typed {
@@ -383,7 +437,12 @@
}
Ok(Val::Obj(out.build()))
}
-
+}
+impl<K, V> FromUntyped for BTreeMap<K, V>
+where
+ K: FromUntyped + Ord,
+ V: FromUntyped,
+{
fn from_untyped(value: Val) -> Result<Self> {
Self::TYPE.check(&value)?;
let obj = value.as_obj().expect("typecheck should fail");
@@ -416,32 +475,27 @@
impl Typed for Val {
const TYPE: &'static ComplexValType = &ComplexValType::Any;
-
+}
+impl IntoUntyped for Val {
fn into_untyped(typed: Self) -> Result<Val> {
Ok(typed)
}
+}
+impl FromUntyped for Val {
fn from_untyped(untyped: Val) -> Result<Self> {
Ok(untyped)
}
}
-// Hack
#[doc(hidden)]
impl<T> Typed for Result<T>
where
T: Typed,
{
const TYPE: &'static ComplexValType = &ComplexValType::Any;
-
- fn into_untyped(_typed: Self) -> Result<Val> {
- panic!("do not use this conversion")
- }
-
- fn from_untyped(_untyped: Val) -> Result<Self> {
- panic!("do not use this conversion")
- }
-
- fn into_result(typed: Self) -> Result<Val> {
+}
+impl<T: IntoUntyped> IntoUntypedResult for Result<T> {
+ fn into_untyped_result(typed: Self) -> Result<Val> {
typed.map(T::into_untyped)?
}
}
@@ -450,11 +504,13 @@
impl Typed for IBytes {
const TYPE: &'static ComplexValType =
&ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));
-
+}
+impl IntoUntyped for IBytes {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::Arr(ArrValue::bytes(value)))
}
-
+}
+impl FromUntyped for IBytes {
fn from_untyped(value: Val) -> Result<Self> {
let Val::Arr(a) = &value else {
<Self as Typed>::TYPE.check(&value)?;
@@ -477,11 +533,13 @@
pub struct M1;
impl Typed for M1 {
const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));
-
+}
+impl IntoUntyped for M1 {
fn into_untyped(_: Self) -> Result<Val> {
Ok(Val::Num(NumValue::new(-1.0).expect("finite")))
}
-
+}
+impl FromUntyped for M1 {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
Ok(Self)
@@ -499,13 +557,22 @@
$($id: Typed,)*
{
const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);
-
+ }
+ impl<$($id),*> IntoUntyped for $name<$($id),*>
+ where
+ $($id: Typed + IntoUntyped,)*
+ {
fn into_untyped(value: Self) -> Result<Val> {
match value {$(
$name::$id(v) => $id::into_untyped(v)
),*}
}
+ }
+ impl<$($id),*> FromUntyped for $name<$($id),*>
+ where
+ $($id: Typed + FromUntyped,)*
+ {
fn from_untyped(value: Val) -> Result<Self> {
$(
if $id::TYPE.check(&value).is_ok() {
@@ -544,11 +611,13 @@
impl Typed for ArrValue {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);
-
+}
+impl IntoUntyped for ArrValue {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::Arr(value))
}
-
+}
+impl FromUntyped for ArrValue {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -560,32 +629,17 @@
impl Typed for FuncVal {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
-
+}
+impl IntoUntyped for FuncVal {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::Func(value))
}
-
- fn from_untyped(value: Val) -> Result<Self> {
- <Self as Typed>::TYPE.check(&value)?;
- match value {
- Val::Func(a) => Ok(a),
- _ => unreachable!(),
- }
- }
}
-
-impl Typed for Cc<FuncDesc> {
- const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
-
- fn into_untyped(value: Self) -> Result<Val> {
- Ok(Val::Func(FuncVal::Normal(value)))
- }
-
+impl FromUntyped for FuncVal {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
- Val::Func(FuncVal::Normal(desc)) => Ok(desc),
- Val::Func(_) => bail!("expected normal function, not builtin"),
+ Val::Func(a) => Ok(a),
_ => unreachable!(),
}
}
@@ -593,11 +647,13 @@
impl Typed for ObjValue {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);
-
+}
+impl IntoUntyped for ObjValue {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::Obj(value))
}
-
+}
+impl FromUntyped for ObjValue {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -609,11 +665,13 @@
impl Typed for bool {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);
-
+}
+impl IntoUntyped for bool {
fn into_untyped(value: Self) -> Result<Val> {
Ok(Val::Bool(value))
}
-
+}
+impl FromUntyped for bool {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
match value {
@@ -622,19 +680,22 @@
}
}
}
+
impl Typed for IndexableVal {
const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[
&ComplexValType::Simple(ValType::Arr),
&ComplexValType::Simple(ValType::Str),
]);
-
+}
+impl IntoUntyped for IndexableVal {
fn into_untyped(value: Self) -> Result<Val> {
match value {
Self::Str(s) => Ok(Val::string(s)),
Self::Arr(a) => Ok(Val::Arr(a)),
}
}
-
+}
+impl FromUntyped for IndexableVal {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
value.into_indexable()
@@ -644,11 +705,13 @@
pub struct Null;
impl Typed for Null {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);
-
+}
+impl IntoUntyped for Null {
fn into_untyped(_: Self) -> Result<Val> {
Ok(Val::Null)
}
-
+}
+impl FromUntyped for Null {
fn from_untyped(value: Val) -> Result<Self> {
<Self as Typed>::TYPE.check(&value)?;
Ok(Self)
@@ -661,11 +724,19 @@
{
const TYPE: &'static ComplexValType =
&ComplexValType::UnionRef(&[&ComplexValType::Simple(ValType::Null), T::TYPE]);
-
+}
+impl<T> IntoUntyped for Option<T>
+where
+ T: Typed + IntoUntyped,
+{
fn into_untyped(typed: Self) -> Result<Val> {
typed.map_or_else(|| Ok(Val::Null), |v| T::into_untyped(v))
}
-
+}
+impl<T> FromUntyped for Option<T>
+where
+ T: Typed + FromUntyped,
+{
fn from_untyped(untyped: Val) -> Result<Self> {
if matches!(untyped, Val::Null) {
Ok(None)
@@ -677,11 +748,13 @@
impl Typed for NumValue {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);
-
+}
+impl IntoUntyped for NumValue {
fn into_untyped(typed: Self) -> Result<Val> {
Ok(Val::Num(typed))
}
-
+}
+impl FromUntyped for NumValue {
fn from_untyped(untyped: Val) -> Result<Self> {
Self::TYPE.check(&untyped)?;
match untyped {
crates/jrsonnet-interner/src/names.rsdiffbeforeafterboth--- a/crates/jrsonnet-interner/src/names.rs
+++ b/crates/jrsonnet-interner/src/names.rs
@@ -0,0 +1 @@
+
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -8,15 +8,15 @@
parse_macro_input,
punctuated::Punctuated,
spanned::Spanned,
- token::{self, Comma},
+ token::Comma,
Attribute, DeriveInput, Error, Expr, ExprClosure, FnArg, GenericArgument, Ident, ItemFn,
LitStr, Meta, Pat, Path, PathArguments, Result, ReturnType, Token, Type,
};
use self::typed::derive_typed_inner;
+mod names;
mod typed;
-mod names;
fn try_parse_attr_noargs<I>(attrs: &[Attribute], ident: I) -> Result<bool>
where
@@ -202,7 +202,7 @@
_ => {}
}
- let (optionality, ty) = if try_parse_attr_noargs(&mut arg.attrs, "default")? {
+ let (optionality, ty) = if try_parse_attr_noargs(&arg.attrs, "default")? {
remove_attr(&mut arg.attrs, "default");
(Optionality::TypeDefault, ty.clone())
} else if let Some(default) = parse_attr::<_, _>(&arg.attrs, "default")? {
@@ -322,7 +322,7 @@
let name = name.as_ref().map_or("<unnamed>", String::as_str);
let eval = quote! {jrsonnet_evaluator::in_description_frame(
|| format!("argument <{}> evaluation", #name),
- || <#ty>::from_untyped(value.evaluate()?),
+ || <#ty as FromUntyped>::from_untyped(value.evaluate()?),
)?};
let value = match optionality {
Optionality::Required => quote! {{
@@ -411,7 +411,7 @@
use ::jrsonnet_evaluator::{
State, Val,
function::{builtin::{Builtin, StaticBuiltin}, FunctionSignature, ParamParse, ParamName, ParamDefault, CallLocation},
- Result, Context, typed::Typed,
+ Result, Context, typed::{Typed, FromUntyped, IntoUntypedResult},
parser::Span, params, Thunk,
};
params!(
@@ -432,7 +432,7 @@
#[allow(unused_variables)]
fn call(&self, location: CallLocation<'_>, parsed: &[Option<Thunk<Val>>]) -> Result<Val> {
let result: #result = #name(#(#pass)*);
- <_ as Typed>::into_result(result)
+ <_ as IntoUntypedResult>::into_untyped_result(result)
}
fn as_any(&self) -> &dyn ::std::any::Any {
self
crates/jrsonnet-macros/src/names.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/names.rs
+++ b/crates/jrsonnet-macros/src/names.rs
@@ -1,6 +1,5 @@
use proc_macro2::TokenStream;
use quote::quote;
-use std::cell::RefCell;
#[derive(Default)]
pub struct Names {
crates/jrsonnet-macros/src/typed.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/typed.rs
+++ b/crates/jrsonnet-macros/src/typed.rs
@@ -178,7 +178,7 @@
None
};
- __value.map(<#ty as Typed>::from_untyped).transpose()?
+ __value.map(<#ty as FromUntyped>::from_untyped).transpose()?
},
}
}
@@ -219,7 +219,7 @@
return Err(ErrorKind::NoSuchField(__names[#error_text].clone(), vec![]).into());
};
- <#ty as Typed>::from_untyped(__value)?
+ <#ty as FromUntyped>::from_untyped(__value)?
},
}
}
@@ -258,14 +258,14 @@
out.field(__names[#name].clone())
#hide
#add
- .try_thunk(<#ty as Typed>::into_lazy_untyped(value))?;
+ .try_thunk(<#ty as IntoUntyped>::into_lazy_untyped(value))?;
}
} else {
quote! {
out.field(__names[#name].clone())
#hide
#add
- .try_value(<#ty as Typed>::into_untyped(value)?)?;
+ .try_value(<#ty as IntoUntyped>::into_untyped(value)?)?;
}
};
if self.is_option {
@@ -313,18 +313,21 @@
const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&[
#(#fields,)*
]);
+ }
+ impl #impl_generics FromUntyped for #ident #ty_generics #where_clause {
fn from_untyped(value: Val) -> JrResult<Self> {
let obj = value.as_obj().expect("shape is correct");
Self::parse(&obj)
}
+ }
+ impl #impl_generics IntoUntyped for #ident #ty_generics #where_clause {
fn into_untyped(value: Self) -> JrResult<Val> {
let mut out = ObjValueBuilder::with_capacity(#capacity);
value.serialize(&mut out)?;
Ok(Val::Obj(out.build()))
}
-
}
}
};
@@ -344,7 +347,7 @@
Ok(quote! {
const _: () = {
use ::jrsonnet_evaluator::{
- typed::{ComplexValType, Typed, TypedObj, CheckType},
+ typed::{ComplexValType, Typed, IntoUntyped, FromUntyped, TypedObj, CheckType},
Val, State,
error::{ErrorKind, Result as JrResult},
ObjValueBuilder, ObjValue, IStr,
crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/arrays.rs
+++ b/crates/jrsonnet-stdlib/src/arrays.rs
@@ -4,7 +4,7 @@
bail,
function::{builtin, FuncVal, NativeFn},
runtime_error,
- typed::{BoundedI32, BoundedUsize, Either2, Typed},
+ typed::{BoundedI32, BoundedUsize, Either2, FromUntyped},
val::{equals, ArrValue, IndexableVal},
Either, IStr, ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,
};
@@ -24,7 +24,7 @@
}
func.evaluate_trivial().map_or_else(
// TODO: Different mapped array impl avoiding allocating unnecessary vals
- || Ok(ArrValue::range_exclusive(0, *sz).map(Typed::from_untyped(Val::Func(func))?)),
+ || Ok(ArrValue::range_exclusive(0, *sz).map(FromUntyped::from_untyped(Val::Func(func))?)),
|trivial| {
let mut out = Vec::with_capacity(*sz as usize);
for _ in 0..*sz {
crates/jrsonnet-stdlib/src/keyf.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/keyf.rs
+++ b/crates/jrsonnet-stdlib/src/keyf.rs
@@ -1,5 +1,5 @@
use jrsonnet_evaluator::function::{CallLocation, FuncVal, PreparedFuncVal};
-use jrsonnet_evaluator::typed::{ComplexValType, Typed, ValType};
+use jrsonnet_evaluator::typed::{ComplexValType, FromUntyped, Typed, ValType};
use jrsonnet_evaluator::{Error, Result, Thunk, Val};
#[derive(Default, Clone)]
@@ -31,11 +31,9 @@
impl Typed for KeyF {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
+}
+impl FromUntyped for KeyF {
fn from_untyped(untyped: Val) -> Result<Self> {
FuncVal::from_untyped(untyped).map(Self::new)
- }
-
- fn into_untyped(_typed: Self) -> Result<Val> {
- unreachable!("unused, todo: port split of Typed trait from #193")
}
}
crates/jrsonnet-stdlib/src/manifest/ini.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/manifest/ini.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/ini.rs
@@ -2,7 +2,7 @@
use jrsonnet_evaluator::{
manifest::{ManifestFormat, ToStringFormat},
- typed::Typed,
+ typed::{FromUntyped, Typed},
ObjValue, Result, ResultExt, Val,
};
use jrsonnet_parser::IStr;
crates/jrsonnet-stdlib/src/manifest/xml.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/manifest/xml.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/xml.rs
@@ -1,7 +1,7 @@
use jrsonnet_evaluator::{
bail, in_description_frame,
manifest::{ManifestFormat, ToStringFormat},
- typed::{ComplexValType, Either2, Typed, ValType},
+ typed::{ComplexValType, Either2, FromUntyped, Typed, ValType},
val::ArrValue,
Either, ObjValue, Result, ResultExt, Val,
};
@@ -32,11 +32,8 @@
}
impl Typed for JSONMLValue {
const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);
-
- fn into_untyped(_typed: Self) -> Result<Val> {
- unreachable!("not used, reserved for parseXML?")
- }
-
+}
+impl FromUntyped for JSONMLValue {
fn from_untyped(untyped: Val) -> Result<Self> {
let val = <Either![ArrValue, String]>::from_untyped(untyped)
.description("parsing JSONML value (an array or string)")?;
@@ -73,7 +70,7 @@
children: in_description_frame(
|| "parsing children".to_owned(),
|| {
- Typed::from_untyped(Val::Arr(arr.slice(
+ FromUntyped::from_untyped(Val::Arr(arr.slice(
Some(if has_attrs { 2 } else { 1 }),
None,
None,
crates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -4,7 +4,7 @@
bail,
error::{ErrorKind::*, Result},
function::builtin,
- typed::{Either2, Typed, M1},
+ typed::{Either2, FromUntyped, M1},
val::{ArrValue, IndexableVal},
Either, IStr, Val,
};
tests/tests/builtin.rsdiffbeforeafterboth--- a/tests/tests/builtin.rs
+++ b/tests/tests/builtin.rs
@@ -5,7 +5,7 @@
function::{CallLocation, FuncVal, builtin, builtin::Builtin},
parser::Source,
trace::PathResolver,
- typed::Typed,
+ typed::FromUntyped,
};
use jrsonnet_gcmodule::Trace;
use jrsonnet_stdlib::ContextInitializer as StdContextInitializer;
tests/tests/typed_obj.rsdiffbeforeafterboth--- a/tests/tests/typed_obj.rs
+++ b/tests/tests/typed_obj.rs
@@ -2,7 +2,11 @@
use std::fmt::Debug;
-use jrsonnet_evaluator::{Result, State, trace::PathResolver, typed::Typed};
+use jrsonnet_evaluator::{
+ Result, State,
+ trace::PathResolver,
+ typed::{FromUntyped, IntoUntyped, Typed},
+};
use jrsonnet_stdlib::ContextInitializer;
#[derive(Clone, Typed, PartialEq, Debug)]
@@ -11,7 +15,9 @@
b: u16,
}
-fn test_roundtrip<T: Typed + PartialEq + Debug + Clone>(value: T) -> Result<()> {
+fn test_roundtrip<T: Typed + PartialEq + Debug + Clone + FromUntyped + IntoUntyped>(
+ value: T,
+) -> Result<()> {
let untyped = T::into_untyped(value.clone())?;
let value2 = T::from_untyped(untyped.clone())?;
ensure_eq!(value, value2);