From 068a27ea4069ca1cbcdf11af21e08064943703a4 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Wed, 03 May 2023 17:59:53 +0000 Subject: [PATCH] feat: make std.map accept strings --- --- a/crates/jrsonnet-evaluator/src/arr/mod.rs +++ b/crates/jrsonnet-evaluator/src/arr/mod.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use jrsonnet_gcmodule::{Cc, Trace}; use jrsonnet_interner::IBytes; use jrsonnet_parser::LocExpr; @@ -14,6 +16,8 @@ pub enum ArrValue { /// Layout optimized byte array. Bytes(BytesArray), + /// Layout optimized char array. + Chars(CharArray), /// Every element is lazy evaluated. Lazy(LazyArray), /// Every element is defined somewhere in source code @@ -66,6 +70,9 @@ pub fn bytes(bytes: IBytes) -> Self { Self::Bytes(BytesArray(bytes)) } + pub fn chars(chars: impl Iterator) -> Self { + Self::Chars(CharArray(Rc::new(chars.collect()))) + } #[must_use] pub fn map(self, mapper: FuncVal) -> Self { @@ -237,7 +244,9 @@ /// Is this vec supports `.get_cheap()?` pub fn is_cheap(&self) -> bool { match self { - ArrValue::Eager(_) | ArrValue::Range(..) | ArrValue::Bytes(_) => true, + ArrValue::Eager(_) | ArrValue::Range(..) | ArrValue::Bytes(_) | ArrValue::Chars(_) => { + true + } ArrValue::Extended(v) => v.a.is_cheap() && v.b.is_cheap(), ArrValue::Slice(r) => r.inner.is_cheap(), ArrValue::Reverse(i) => i.0.is_cheap(), --- a/crates/jrsonnet-evaluator/src/arr/spec.rs +++ b/crates/jrsonnet-evaluator/src/arr/spec.rs @@ -1,15 +1,18 @@ //! Those implementations are a bit sketchy, as this is mostly performance experiments //! of not yet finished nightly rust features -use std::{cell::RefCell, iter, mem::replace}; +use std::{cell::RefCell, iter, mem::replace, rc::Rc}; use jrsonnet_gcmodule::{Cc, Trace}; -use jrsonnet_interner::IBytes; +use jrsonnet_interner::{IBytes, IStr}; use jrsonnet_parser::LocExpr; use super::ArrValue; use crate::{ - error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, val::ThunkValue, + error::ErrorKind::InfiniteRecursionDetected, + evaluate, + function::FuncVal, + val::{StrValue, ThunkValue}, Context, Error, Result, Thunk, Val, }; @@ -154,6 +157,69 @@ } #[derive(Trace, Debug, Clone)] +pub struct CharArray(pub Rc>); +#[cfg(feature = "nightly")] +type CharArrayIter<'t> = impl DoubleEndedIterator> + ExactSizeIterator + 't; +#[cfg(feature = "nightly")] +type CharArrayLazyIter<'t> = impl DoubleEndedIterator> + ExactSizeIterator + 't; +#[cfg(feature = "nightly")] +type CharArrayCheapIter<'t> = impl DoubleEndedIterator + ExactSizeIterator + 't; +impl ArrayLike for CharArray { + #[cfg(feature = "nightly")] + type Iter<'t> = CharArrayIter<'t>; + #[cfg(feature = "nightly")] + type IterLazy<'t> = CharArrayLazyIter<'t>; + #[cfg(feature = "nightly")] + type IterCheap<'t> = CharArrayCheapIter<'t>; + + fn len(&self) -> usize { + self.0.len() + } + + fn get(&self, index: usize) -> Result> { + Ok(self.get_cheap(index)) + } + + fn get_lazy(&self, index: usize) -> Option> { + self.get_cheap(index).map(Thunk::evaluated) + } + + fn get_cheap(&self, index: usize) -> Option { + self.0 + .get(index) + .map(|v| Val::Str(StrValue::Flat(IStr::from(*v)))) + } + + #[cfg(feature = "nightly")] + fn iter(&self) -> CharArrayIter<'_> { + self.0 + .iter() + .map(|v| Ok(Val::Str(StrValue::Flat(IStr::from(*v))))) + } + + #[cfg(feature = "nightly")] + fn iter_lazy(&self) -> CharArrayLazyIter<'_> { + self.0 + .iter() + .map(|v| Thunk::evaluated(Val::Str(StrValue::Flat(IStr::from(*v))))) + } + + #[cfg(feature = "nightly")] + fn iter_cheap(&self) -> Option> { + Some( + self.0 + .iter() + .map(|v| Val::Str(StrValue::Flat(IStr::from(*v)))), + ) + } +} +impl From for ArrValue { + fn from(value: CharArray) -> Self { + ArrValue::Chars(value) + } +} + +#[derive(Trace, Debug, Clone)] pub struct BytesArray(pub IBytes); #[cfg(feature = "nightly")] type BytesArrayIter<'t> = impl DoubleEndedIterator> + ExactSizeIterator + 't; @@ -935,6 +1001,7 @@ ($t:ident.$m:ident($($ident:ident),*)) => { match $t { Self::Bytes(e) => e.$m($($ident)*), + Self::Chars(e) => e.$m($($ident)*), Self::Expr(e) => e.$m($($ident)*), Self::Lazy(e) => e.$m($($ident)*), Self::Eager(e) => e.$m($($ident)*), --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -149,6 +149,12 @@ Arr(ArrValue), } impl IndexableVal { + pub fn to_array(self) -> ArrValue { + match self { + IndexableVal::Str(s) => ArrValue::chars(s.chars()), + IndexableVal::Arr(arr) => arr, + } + } /// Slice the value. /// /// # Implementation --- a/crates/jrsonnet-interner/src/lib.rs +++ b/crates/jrsonnet-interner/src/lib.rs @@ -205,6 +205,12 @@ s.as_str().into() } } +impl From for IStr { + fn from(value: char) -> Self { + let mut buf = [0; 5]; + Self::from(&*value.encode_utf8(&mut buf)) + } +} impl From<&[u8]> for IBytes { fn from(v: &[u8]) -> Self { intern_bytes(v) --- a/crates/jrsonnet-stdlib/src/arrays.rs +++ b/crates/jrsonnet-stdlib/src/arrays.rs @@ -45,8 +45,9 @@ } #[builtin] -pub fn builtin_map(func: FuncVal, arr: ArrValue) -> Result { - Ok(arr.map(func)) +pub fn builtin_map(func: FuncVal, arr: IndexableVal) -> ArrValue { + let arr = arr.to_array(); + arr.map(func) } #[builtin] -- gitstuff