git.delta.rocks / jrsonnet / refs/commits / c0a2148b4ae2

difftreelog

refactor use PreparedFunction for NativeFn

kutrystyYaroslav Bolyukin2026-03-21parent: #0111266.patch.diff
in: master

9 files changed

modifiedcrates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth
8use jrsonnet_interner::IBytes;9use jrsonnet_interner::IBytes;
9use jrsonnet_parser::{Expr, Spanned};10use jrsonnet_parser::{Expr, Spanned};
1011
11use crate::{function::FuncVal, Context, Result, Thunk, Val};12use crate::{typed::NativeFn, Context, Result, Thunk, Val};
1213
13mod spec;14mod spec;
14pub use spec::{ArrayLike, *};15pub use spec::{ArrayLike, *};
61 }62 }
6263
63 #[must_use]64 #[must_use]
64 pub fn map(self, mapper: FuncVal) -> Self {65 pub fn map(self, mapper: NativeFn!((Val) -> Val)) -> Self {
65 Self::new(<MappedArray<false>>::new(self, mapper))66 Self::new(<MappedArray>::new(self, ArrayMapper::Plain(mapper)))
66 }67 }
6768
68 #[must_use]69 #[must_use]
69 pub fn map_with_index(self, mapper: FuncVal) -> Self {70 pub fn map_with_index(self, mapper: NativeFn!((u32, Val) -> Val)) -> Self {
70 Self::new(<MappedArray<true>>::new(self, mapper))71 Self::new(<MappedArray>::new(self, ArrayMapper::WithIndex(mapper)))
71 }72 }
7273
73 pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {74 pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {
modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
6use jrsonnet_parser::{Expr, Spanned};6use jrsonnet_parser::{Expr, Spanned};
77
8use super::ArrValue;8use super::ArrValue;
9use crate::typed::NativeFn;
10use crate::val::NumValue;
9use crate::{11use crate::{
10 error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, typed::Typed,12 error::ErrorKind::InfiniteRecursionDetected, evaluate, typed::Typed, val::ThunkValue, Context,
11 val::ThunkValue, Context, Error, ObjValue, Result, Thunk, Val,13 Error, ObjValue, Result, Thunk, Val,
12};14};
1315
404 }406 }
405}407}
408
409#[derive(Trace, Clone, Debug)]
410pub enum ArrayMapper {
411 Plain(NativeFn!((Val) -> Val)),
412 WithIndex(NativeFn!((u32, Val) -> Val)),
413}
406414
407#[derive(Trace, Debug, Clone)]415#[derive(Trace, Debug, Clone)]
408pub struct MappedArray<const WITH_INDEX: bool> {416pub struct MappedArray {
409 inner: ArrValue,417 inner: ArrValue,
410 cached: Cc<RefCell<Vec<ArrayThunk>>>,418 cached: Cc<RefCell<Vec<ArrayThunk>>>,
411 mapper: FuncVal,419 mapper: ArrayMapper,
412}420}
413impl<const WITH_INDEX: bool> MappedArray<WITH_INDEX> {421impl MappedArray {
414 pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {422 pub fn new(inner: ArrValue, mapper: ArrayMapper) -> Self {
415 let len = inner.len();423 let len = inner.len();
416 Self {424 Self {
417 inner,425 inner,
420 }428 }
421 }429 }
422 fn evaluate(&self, index: usize, value: Val) -> Result<Val> {430 fn evaluate(&self, index: usize, value: Val) -> Result<Val> {
423 if WITH_INDEX {
424 self.mapper.evaluate_simple(&(index, value), false)431 match &self.mapper {
425 } else {432 ArrayMapper::Plain(f) => f.call(value),
426 self.mapper.evaluate_simple(&(value,), false)433 ArrayMapper::WithIndex(f) => f.call(index as u32, value),
427 }434 }
428 }435 }
429}436}
430impl<const WITH_INDEX: bool> ArrayLike for MappedArray<WITH_INDEX> {437impl ArrayLike for MappedArray {
431 fn len(&self) -> usize {438 fn len(&self) -> usize {
432 self.cached.borrow().len()439 self.cached.borrow().len()
433 }440 }
468 }475 }
469 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {476 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
470 #[derive(Trace)]477 #[derive(Trace)]
471 struct MappedArrayThunk<const WITH_INDEX: bool> {478 struct MappedArrayThunk {
472 arr: MappedArray<WITH_INDEX>,479 arr: MappedArray,
473 index: usize,480 index: usize,
474 }481 }
475 impl<const WITH_INDEX: bool> ThunkValue for MappedArrayThunk<WITH_INDEX> {482 impl ThunkValue for MappedArrayThunk {
476 type Output = Val;483 type Output = Val;
477484
478 fn get(&self) -> Result<Self::Output> {485 fn get(&self) -> Result<Self::Output> {
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
8use jrsonnet_parser::{Destruct, Expr, ExprParams, Span, Spanned};8use jrsonnet_parser::{Destruct, Expr, ExprParams, Span, Spanned};
99
10use self::{10use self::{
11 arglike::OptionalContext,
12 builtin::{Builtin, StaticBuiltin},11 builtin::{Builtin, StaticBuiltin},
13 native::NativeDesc,
14 parse::{parse_builtin_call, parse_default_function_call, parse_function_call},12 parse::{parse_builtin_call, parse_default_function_call, parse_function_call},
15 prepared::{parse_prepared_builtin_call, parse_prepared_function_call, PreparedCall},13 prepared::{parse_prepared_builtin_call, parse_prepared_function_call, PreparedCall},
16};14};
17use crate::{15use crate::{
18 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,16 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,
19 ContextBuilder, Result, Thunk, Val,17 Result, Thunk, Val,
20};18};
2119
22pub mod arglike;20pub mod arglike;
200 }198 }
201 }199 }
202 }200 }
203 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(
204 &self,
205 args: &A,
206 tailstrict: bool,
207 ) -> Result<Val> {
208 self.evaluate(
209 ContextBuilder::new().build(),
210 CallLocation::native(),
211 args,
212 tailstrict,
213 )
214 }
215201
216 pub(crate) fn evaluate_prepared(202 pub(crate) fn evaluate_prepared(
217 &self,203 &self,
247 }233 }
248 }234 }
249 }235 }
250 /// Convert jsonnet function to plain `Fn` value.
251 pub fn into_native<D: NativeDesc>(self) -> D::Value {
252 D::into_native(self)
253 }
254236
255 /// Is this function an indentity function.237 /// Is this function an indentity function.
256 ///238 ///
modifiedcrates/jrsonnet-evaluator/src/function/native.rsdiffbeforeafterboth
1use super::{1use super::PreparedFuncVal;
2 arglike::{ArgLike, OptionalContext},
3 FuncVal,
4};
5use crate::{typed::Typed, Result};2use crate::{typed::Typed, CallLocation, Result, Thunk};
6
7pub trait NativeDesc {
8 type Value;
9 fn into_native(val: FuncVal) -> Self::Value;
10}
11macro_rules! impl_native_desc {
12 ($($gen:ident)*) => {
13 impl<$($gen,)* O> NativeDesc for (($($gen,)*), O)
14 where
15 $($gen: ArgLike + OptionalContext,)*
16 O: Typed,
17 {
18 type Value = Box<dyn Fn($($gen,)*) -> Result<O>>;
19
20 #[allow(non_snake_case)]
21 fn into_native(val: FuncVal) -> Self::Value {
22 Box::new(move |$($gen),*| {
23 let val = val.evaluate_simple(
24 &($($gen,)*),
25 false,
26 )?;
27 O::from_untyped(val)
28 })
29 }
30 }
31 };
32 ($($cur:ident)* @ $c:ident $($rest:ident)*) => {
33 impl_native_desc!($($cur)*);
34 impl_native_desc!($($cur)* $c @ $($rest)*);
35 };
36 ($($cur:ident)* @) => {
37 impl_native_desc!($($cur)*);
38 }
39}
40
41impl_native_desc! {
42 @ A B C D E F G H I J K L
43}
443
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
8use crate::{8use crate::{
9 arr::{ArrValue, BytesArray},9 arr::{ArrValue, BytesArray},
10 bail,10 bail,
11 function::{native::NativeDesc, FuncDesc, FuncVal},11 function::{CallLocation, FuncDesc, FuncVal, PreparedFuncVal},
12 typed::CheckType,12 typed::CheckType,
13 val::{IndexableVal, NumValue, StrValue, ThunkMapper},13 val::{IndexableVal, NumValue, StrValue, ThunkMapper},
14 ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,14 ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,
675 }675 }
676}676}
677677
678#[derive(Debug, Trace, Clone)]
678pub struct NativeFn<D: NativeDesc>(D::Value);679pub struct NativeFn<D: 'static>(pub(crate) PreparedFuncVal, PhantomData<D>);
679impl<D: NativeDesc> Deref for NativeFn<D> {680macro_rules! impl_native_desc {
680 type Target = D::Value;681 ($i:expr; $($gen:ident)*) => {
681
682 fn deref(&self) -> &Self::Target {
683 &self.0
684 }
685}
686impl<D: NativeDesc> Typed for NativeFn<D> {682 impl<$($gen,)* O> NativeFn<($($gen,)* O,)>
683 where
684 $($gen: Typed,)*
685 O: Typed,
686 {
687 pub fn call(
688 &self,
689 $($gen: $gen,)*
690 ) -> Result<O> {
691 let val = self.0.call(
692 CallLocation::native(),
693 &[$(Typed::into_lazy_untyped($gen),)*],
694 &[],
695 )?;
696 O::from_untyped(val)
697 }
698 }
699 impl<$($gen,)* O> Typed for NativeFn<($($gen,)* O,)> {
687 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);700 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
688701
689 fn into_untyped(_typed: Self) -> Result<Val> {702 fn into_untyped(_typed: Self) -> Result<Val> {
690 bail!("can only convert functions from jsonnet to native")703 bail!("can only convert functions from jsonnet to native")
691 }704 }
692705
693 fn from_untyped(untyped: Val) -> Result<Self> {706 fn from_untyped(untyped: Val) -> Result<Self> {
707 let func = FuncVal::from_untyped(untyped)?;
694 Ok(Self(708 Ok(Self(
695 untyped
696 .as_func()
697 .expect("shape is checked")
698 .into_native::<D>(),709 PreparedFuncVal::new(func, $i, &[])?,
710 PhantomData,
699 ))711 ))
700 }712 }
701}713 }
714 };
715 ($i:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {
716 impl_native_desc!($i; $($cur)*);
717 impl_native_desc!($i + 1; $($cur)* $c @ $($rest)*);
718 };
719 ($i:expr; $($cur:ident)* @) => {
720 impl_native_desc!($i; $($cur)*);
721 }
722}
723
724impl_native_desc! {
725 0; @ A B C D E F G H I J K L
726}
727
728mod native_macro {
729 #[macro_export]
730 macro_rules! NativeFn {
731 (($($t:ty),* $(,)?) -> $res:ty) => {
732 NativeFn<($($t,)* $res)>
733 }
734 }
735}
736pub use crate::NativeFn;
702737
703impl Typed for NumValue {738impl Typed for NumValue {
704 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);739 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
3use proc_macro2::TokenStream;3use proc_macro2::TokenStream;
4use quote::{quote, quote_spanned};4use quote::{quote, quote_spanned};
5use syn::{5use syn::{
6 parenthesized,
7 parse::{Parse, ParseStream},
8 parse_macro_input,
9 punctuated::Punctuated,
10 spanned::Spanned,
11 token::{self, Comma},
6 Attribute, DeriveInput, Error, Expr, ExprClosure, FnArg, GenericArgument, Ident, ItemFn, LitStr, Meta, Pat, Path, PathArguments, Result, ReturnType, Token, Type, parenthesized, parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::{self, Comma}12 Attribute, DeriveInput, Error, Expr, ExprClosure, FnArg, GenericArgument, Ident, ItemFn,
13 LitStr, Meta, Pat, Path, PathArguments, Result, ReturnType, Token, Type,
7};14};
815
modifiedcrates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth
23 return Ok(ArrValue::empty());23 return Ok(ArrValue::empty());
24 }24 }
25 func.evaluate_trivial().map_or_else(25 func.evaluate_trivial().map_or_else(
26 // TODO: Different mapped array impl avoiding allocating unnecessary vals
26 || Ok(ArrValue::range_exclusive(0, *sz).map(func)),27 || Ok(ArrValue::range_exclusive(0, *sz).map(Typed::from_untyped(Val::Func(func))?)),
27 |trivial| {28 |trivial| {
28 let mut out = Vec::with_capacity(*sz as usize);29 let mut out = Vec::with_capacity(*sz as usize);
29 for _ in 0..*sz {30 for _ in 0..*sz {
58}59}
5960
60#[builtin]61#[builtin]
61pub fn builtin_map(func: FuncVal, arr: IndexableVal) -> ArrValue {62pub fn builtin_map(func: NativeFn!((Val) -> Val), arr: IndexableVal) -> ArrValue {
62 let arr = arr.to_array();63 let arr = arr.to_array();
63 arr.map(func)64 arr.map(func)
64}65}
6566
66#[builtin]67#[builtin]
67pub fn builtin_map_with_index(func: FuncVal, arr: IndexableVal) -> ArrValue {68pub fn builtin_map_with_index(func: NativeFn!((u32, Val) -> Val), arr: IndexableVal) -> ArrValue {
68 let arr = arr.to_array();69 let arr = arr.to_array();
69 arr.map_with_index(func)70 arr.map_with_index(func)
70}71}
7172
72#[builtin]73#[builtin]
73pub fn builtin_map_with_key(func: FuncVal, obj: ObjValue) -> Result<ObjValue> {74pub fn builtin_map_with_key(
75 func: NativeFn!((IStr, Val) -> Val),
76 obj: ObjValue,
77) -> Result<ObjValue> {
74 let mut out = ObjValueBuilder::new();78 let mut out = ObjValueBuilder::new();
80 true,84 true,
81 ) {85 ) {
82 let v = v?;86 let v = v?;
83 out.field(k.clone())87 out.field(k.clone()).value(func.call(k, v)?);
84 .value(func.evaluate_simple(&(k, v), false)?);
85 }88 }
86 Ok(out.build())89 Ok(out.build())
87}90}
8891
89#[builtin]92#[builtin]
90pub fn builtin_flatmap(93pub fn builtin_flatmap(
91 func: NativeFn<((Either![String, Val],), Val)>,94 func: NativeFn!((Either![String, Val]) -> Val),
92 arr: IndexableVal,95 arr: IndexableVal,
93) -> Result<IndexableVal> {96) -> Result<IndexableVal> {
94 use std::fmt::Write;97 use std::fmt::Write;
95 match arr {98 match arr {
96 IndexableVal::Str(str) => {99 IndexableVal::Str(str) => {
97 let mut out = String::new();100 let mut out = String::new();
98 for c in str.chars() {101 for c in str.chars() {
99 match func(Either2::A(c.to_string()))? {102 match func.call(Either2::A(c.to_string()))? {
100 Val::Str(o) => write!(out, "{o}").unwrap(),103 Val::Str(o) => write!(out, "{o}").unwrap(),
101 Val::Null => {},104 Val::Null => {}
102 _ => bail!("in std.join all items should be strings"),105 _ => bail!("in std.join all items should be strings"),
103 }106 }
104 }107 }
108 let mut out = Vec::new();111 let mut out = Vec::new();
109 for el in a.iter() {112 for el in a.iter() {
110 let el = el?;113 let el = el?;
111 match func(Either2::B(el))? {114 match func.call(Either2::B(el))? {
112 Val::Arr(o) => {115 Val::Arr(o) => {
113 for oe in o.iter() {116 for oe in o.iter() {
114 out.push(oe?);117 out.push(oe?);
115 }118 }
116 }119 }
117 Val::Null => {},120 Val::Null => {}
118 _ => bail!("in std.join all items should be arrays"),121 _ => bail!("in std.join all items should be arrays"),
119 }122 }
120 }123 }
123 }126 }
124}127}
128
129type FilterFunc = NativeFn!((Val) -> bool);
125130
126#[builtin]131#[builtin]
127pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {132pub fn builtin_filter(func: FilterFunc, arr: ArrValue) -> Result<ArrValue> {
128 arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),), false)?))133 arr.filter(|val| func.call(val.clone()))
129}134}
130135
131#[builtin]136#[builtin]
132pub fn builtin_filter_map(137pub fn builtin_filter_map(
133 filter_func: FuncVal,138 filter_func: FilterFunc,
134 map_func: FuncVal,139 map_func: NativeFn!((Val) -> Val),
135 arr: ArrValue,140 arr: ArrValue,
136) -> Result<ArrValue> {141) -> Result<ArrValue> {
137 Ok(builtin_filter(filter_func, arr)?.map(map_func))142 Ok(builtin_filter(filter_func, arr)?.map(map_func))
138}143}
139144
140#[builtin]145#[builtin]
141pub fn builtin_foldl(func: FuncVal, arr: Either![ArrValue, IStr], init: Val) -> Result<Val> {146pub fn builtin_foldl(
147 func: NativeFn!((Val, Either![Val, char]) -> Val),
148 arr: Either![ArrValue, IStr],
149 init: Val,
150) -> Result<Val> {
142 let mut acc = init;151 let mut acc = init;
143 match arr {152 match arr {
144 Either2::A(arr) => {153 Either2::A(arr) => {
145 for i in arr.iter() {154 for i in arr.iter() {
146 acc = func.evaluate_simple(&(acc, i?), false)?;155 acc = func.call(acc, Either2::A(i?))?;
147 }156 }
148 }157 }
149 Either2::B(arr) => {158 Either2::B(arr) => {
150 for i in arr.chars() {159 for c in arr.chars() {
151 acc = func.evaluate_simple(&(acc, Val::string(i)), false)?;160 acc = func.call(acc, Either2::B(c))?;
152 }161 }
153 }162 }
154 }163 }
155 Ok(acc)164 Ok(acc)
156}165}
157166
158#[builtin]167#[builtin]
159pub fn builtin_foldr(func: FuncVal, arr: Either![ArrValue, IStr], init: Val) -> Result<Val> {168pub fn builtin_foldr(
169 func: NativeFn!((Either![Val, char], Val) -> Val),
170 arr: Either![ArrValue, IStr],
171 init: Val,
172) -> Result<Val> {
160 let mut acc = init;173 let mut acc = init;
161 match arr {174 match arr {
162 Either2::A(arr) => {175 Either2::A(arr) => {
163 for i in arr.iter().rev() {176 for i in arr.iter().rev() {
164 acc = func.evaluate_simple(&(i?, acc), false)?;177 acc = func.call(Either2::A(i?), acc)?;
165 }178 }
166 }179 }
167 Either2::B(arr) => {180 Either2::B(arr) => {
168 for i in arr.chars().rev() {181 for c in arr.chars().rev() {
169 acc = func.evaluate_simple(&(Val::string(i), acc), false)?;182 acc = func.call(Either2::B(c), acc)?;
170 }183 }
171 }184 }
172 }185 }
modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
38mod compat;38mod compat;
39mod encoding;39mod encoding;
40mod hash;40mod hash;
41mod keyf;
41mod manifest;42mod manifest;
42mod math;43mod math;
43mod misc;44mod misc;
50mod sort;51mod sort;
51mod strings;52mod strings;
52mod types;53mod types;
53mod keyf;
5454
55#[allow(clippy::too_many_lines)]55#[allow(clippy::too_many_lines)]
56pub fn stdlib_uncached(settings: Cc<RefCell<Settings>>) -> ObjValue {56pub fn stdlib_uncached(settings: Cc<RefCell<Settings>>) -> ObjValue {
deletedtests/tests/as_native.rsdiffbeforeafterboth

no changes