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: ArrValue) -> Result<ArrValue> {49 Ok(arr.map(func))50}5152#[builtin]53pub fn builtin_flatmap(54 func: NativeFn<((Either![String, Val],), Val)>,55 arr: IndexableVal,56) -> Result<IndexableVal> {57 use std::fmt::Write;58 match arr {59 IndexableVal::Str(str) => {60 let mut out = String::new();61 for c in str.chars() {62 match func(Either2::A(c.to_string()))? {63 Val::Str(o) => write!(out, "{o}").unwrap(),64 Val::Null => continue,65 _ => throw!("in std.join all items should be strings"),66 };67 }68 Ok(IndexableVal::Str(out.into()))69 }70 IndexableVal::Arr(a) => {71 let mut out = Vec::new();72 for el in a.iter() {73 let el = el?;74 match func(Either2::B(el))? {75 Val::Arr(o) => {76 for oe in o.iter() {77 out.push(oe?);78 }79 }80 Val::Null => continue,81 _ => throw!("in std.join all items should be arrays"),82 };83 }84 Ok(IndexableVal::Arr(out.into()))85 }86 }87}8889#[builtin]90pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {91 arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),), false)?))92}9394#[builtin]95pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {96 let mut acc = init;97 for i in arr.iter() {98 acc = func.evaluate_simple(&(acc, i?), false)?;99 }100 Ok(acc)101}102103#[builtin]104pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {105 let mut acc = init;106 for i in arr.iter().rev() {107 acc = func.evaluate_simple(&(i?, acc), false)?;108 }109 Ok(acc)110}111112#[builtin]113pub fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {114 if to < from {115 return Ok(ArrValue::empty());116 }117 Ok(ArrValue::range_inclusive(from, to))118}119120#[builtin]121pub fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {122 use std::fmt::Write;123 Ok(match sep {124 IndexableVal::Arr(joiner_items) => {125 let mut out = Vec::new();126127 let mut first = true;128 for item in arr.iter() {129 let item = item?.clone();130 if let Val::Arr(items) = item {131 if !first {132 out.reserve(joiner_items.len());133 // TODO: extend134 for item in joiner_items.iter() {135 out.push(item?);136 }137 }138 first = false;139 out.reserve(items.len());140 for item in items.iter() {141 out.push(item?);142 }143 } else if matches!(item, Val::Null) {144 continue;145 } else {146 throw!("in std.join all items should be arrays");147 }148 }149150 IndexableVal::Arr(out.into())151 }152 IndexableVal::Str(sep) => {153 let mut out = String::new();154155 let mut first = true;156 for item in arr.iter() {157 let item = item?.clone();158 if let Val::Str(item) = item {159 if !first {160 out += &sep;161 }162 first = false;163 write!(out, "{item}").unwrap()164 } else if matches!(item, Val::Null) {165 continue;166 } else {167 throw!("in std.join all items should be strings");168 }169 }170171 IndexableVal::Str(out.into())172 }173 })174}175176#[builtin]177pub fn builtin_reverse(arr: ArrValue) -> ArrValue {178 arr.reversed()179}180181#[builtin]182pub fn builtin_any(arr: ArrValue) -> Result<bool> {183 for v in arr.iter() {184 let v = bool::from_untyped(v?)?;185 if v {186 return Ok(true);187 }188 }189 Ok(false)190}191192#[builtin]193pub fn builtin_all(arr: ArrValue) -> Result<bool> {194 for v in arr.iter() {195 let v = bool::from_untyped(v?)?;196 if !v {197 return Ok(false);198 }199 }200 Ok(true)201}202203#[builtin]204pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {205 match arr {206 IndexableVal::Str(str) => {207 let x: IStr = IStr::from_untyped(x)?;208 Ok(!x.is_empty() && str.contains(&*x))209 }210 IndexableVal::Arr(a) => {211 for item in a.iter() {212 let item = item?;213 if equals(&item, &x)? {214 return Ok(true);215 }216 }217 Ok(false)218 }219 }220}221222#[builtin]223pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {224 let mut count = 0;225 for item in arr.iter() {226 if equals(&item?, &x)? {227 count += 1;228 }229 }230 Ok(count)231}1use 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}