difftreelog
feat make std.map accept strings
in: master
5 files changed
crates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth--- 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<Item = char>) -> 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(),
crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth--- 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<Vec<char>>);
+#[cfg(feature = "nightly")]
+type CharArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;
+#[cfg(feature = "nightly")]
+type CharArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;
+#[cfg(feature = "nightly")]
+type CharArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + 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<Option<Val>> {
+ Ok(self.get_cheap(index))
+ }
+
+ fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
+ self.get_cheap(index).map(Thunk::evaluated)
+ }
+
+ fn get_cheap(&self, index: usize) -> Option<Val> {
+ 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<CharArrayCheapIter<'_>> {
+ Some(
+ self.0
+ .iter()
+ .map(|v| Val::Str(StrValue::Flat(IStr::from(*v)))),
+ )
+ }
+}
+impl From<CharArray> 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<Item = Result<Val>> + 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)*),
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- 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
crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-interner/src/lib.rs
+++ b/crates/jrsonnet-interner/src/lib.rs
@@ -205,6 +205,12 @@
s.as_str().into()
}
}
+impl From<char> 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)
crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth1use jrsonnet_evaluator::{2 error::{ErrorKind::RuntimeError, Result},3 function::{builtin, FuncVal},4 throw,5 typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},6 val::{equals, ArrValue, IndexableVal, StrValue},7 Either, IStr, Val,8};910#[builtin]11pub fn builtin_make_array(sz: BoundedI32<0, { i32::MAX }>, func: FuncVal) -> Result<ArrValue> {12 if *sz == 0 {13 return Ok(ArrValue::empty());14 }15 if let Some(trivial) = func.evaluate_trivial() {16 let mut out = Vec::with_capacity(*sz as usize);17 for _ in 0..*sz {18 out.push(trivial.clone())19 }20 Ok(ArrValue::eager(out))21 } else {22 Ok(ArrValue::range_exclusive(0, *sz).map(func))23 }24}2526#[builtin]27pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Val> {28 Ok(match what {29 Either2::A(s) => Val::Str(StrValue::Flat(s.repeat(count).into())),30 Either2::B(arr) => Val::Arr(31 ArrValue::repeated(arr, count)32 .ok_or_else(|| RuntimeError("repeated length overflow".into()))?,33 ),34 })35}3637#[builtin]38pub fn builtin_slice(39 indexable: IndexableVal,40 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,41 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,42 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,43) -> Result<Val> {44 indexable.slice(index, end, step).map(Val::from)45}4647#[builtin]48pub fn builtin_map(func: FuncVal, arr: IndexableVal) -> ArrValue {49 let arr = arr.to_array();50 arr.map(func)51}5253#[builtin]54pub fn builtin_flatmap(55 func: NativeFn<((Either![String, Val],), Val)>,56 arr: IndexableVal,57) -> Result<IndexableVal> {58 use std::fmt::Write;59 match arr {60 IndexableVal::Str(str) => {61 let mut out = String::new();62 for c in str.chars() {63 match func(Either2::A(c.to_string()))? {64 Val::Str(o) => write!(out, "{o}").unwrap(),65 Val::Null => continue,66 _ => throw!("in std.join all items should be strings"),67 };68 }69 Ok(IndexableVal::Str(out.into()))70 }71 IndexableVal::Arr(a) => {72 let mut out = Vec::new();73 for el in a.iter() {74 let el = el?;75 match func(Either2::B(el))? {76 Val::Arr(o) => {77 for oe in o.iter() {78 out.push(oe?);79 }80 }81 Val::Null => continue,82 _ => throw!("in std.join all items should be arrays"),83 };84 }85 Ok(IndexableVal::Arr(out.into()))86 }87 }88}8990#[builtin]91pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {92 arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),), false)?))93}9495#[builtin]96pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {97 let mut acc = init;98 for i in arr.iter() {99 acc = func.evaluate_simple(&(acc, i?), false)?;100 }101 Ok(acc)102}103104#[builtin]105pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {106 let mut acc = init;107 for i in arr.iter().rev() {108 acc = func.evaluate_simple(&(i?, acc), false)?;109 }110 Ok(acc)111}112113#[builtin]114pub fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {115 if to < from {116 return Ok(ArrValue::empty());117 }118 Ok(ArrValue::range_inclusive(from, to))119}120121#[builtin]122pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {123 use std::fmt::Write;124 Ok(match sep {125 IndexableVal::Arr(joiner_items) => {126 let mut out = Vec::new();127128 let mut first = true;129 for item in arr.iter() {130 let item = item?.clone();131 if let Val::Arr(items) = item {132 if !first {133 out.reserve(joiner_items.len());134 // TODO: extend135 for item in joiner_items.iter() {136 out.push(item?);137 }138 }139 first = false;140 out.reserve(items.len());141 for item in items.iter() {142 out.push(item?);143 }144 } else if matches!(item, Val::Null) {145 continue;146 } else {147 throw!("in std.join all items should be arrays");148 }149 }150151 IndexableVal::Arr(out.into())152 }153 IndexableVal::Str(sep) => {154 let mut out = String::new();155156 let mut first = true;157 for item in arr.iter() {158 let item = item?.clone();159 if let Val::Str(item) = item {160 if !first {161 out += &sep;162 }163 first = false;164 write!(out, "{item}").unwrap()165 } else if matches!(item, Val::Null) {166 continue;167 } else {168 throw!("in std.join all items should be strings");169 }170 }171172 IndexableVal::Str(out.into())173 }174 })175}176177#[builtin]178pub fn builtin_reverse(arr: ArrValue) -> ArrValue {179 arr.reversed()180}181182#[builtin]183pub fn builtin_any(arr: ArrValue) -> Result<bool> {184 for v in arr.iter() {185 let v = bool::from_untyped(v?)?;186 if v {187 return Ok(true);188 }189 }190 Ok(false)191}192193#[builtin]194pub fn builtin_all(arr: ArrValue) -> Result<bool> {195 for v in arr.iter() {196 let v = bool::from_untyped(v?)?;197 if !v {198 return Ok(false);199 }200 }201 Ok(true)202}203204#[builtin]205pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {206 match arr {207 IndexableVal::Str(str) => {208 let x: IStr = IStr::from_untyped(x)?;209 Ok(!x.is_empty() && str.contains(&*x))210 }211 IndexableVal::Arr(a) => {212 for item in a.iter() {213 let item = item?;214 if equals(&item, &x)? {215 return Ok(true);216 }217 }218 Ok(false)219 }220 }221}222223#[builtin]224pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {225 let mut count = 0;226 for item in arr.iter() {227 if equals(&item?, &x)? {228 count += 1;229 }230 }231 Ok(count)232}