git.delta.rocks / jrsonnet / refs/commits / 0111266c91b4

difftreelog

perf use prepared call for KeyF

szllnstvYaroslav Bolyukin2026-03-21parent: #5df60b8.patch.diff
in: master

9 files changed

modifiedcrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth
3use jrsonnet_gcmodule::{cc_dyn, Trace, TraceBox};3use jrsonnet_gcmodule::{cc_dyn, Trace, TraceBox};
4use jrsonnet_parser::function::{FunctionSignature, ParamDefault, ParamName, ParamParse};4use jrsonnet_parser::function::{FunctionSignature, ParamDefault, ParamName, ParamParse};
55
6use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};6use super::CallLocation;
7use crate::{Context, Result, Val};7use crate::{Result, Thunk, Val};
88
9#[macro_export]9#[macro_export]
10macro_rules! params {10macro_rules! params {
34 self.0.params()34 self.0.params()
35 }35 }
3636
37 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {37 fn call(&self, loc: CallLocation<'_>, args: &[Option<Thunk<Val>>]) -> Result<Val> {
38 self.0.call(ctx, loc, args)38 self.0.call(loc, args)
39 }39 }
4040
41 fn as_any(&self) -> &dyn Any {41 fn as_any(&self) -> &dyn Any {
52 /// Parameter names for named calls52 /// Parameter names for named calls
53 fn params(&self) -> FunctionSignature;53 fn params(&self) -> FunctionSignature;
54 /// Call the builtin54 /// Call the builtin
55 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;55 fn call(&self, loc: CallLocation<'_>, args: &[Option<Thunk<Val>>]) -> Result<Val>;
5656
57 fn as_any(&self) -> &dyn Any;57 fn as_any(&self) -> &dyn Any;
58}58}
96 self.params.clone()96 self.params.clone()
97 }97 }
9898
99 fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {99 fn call(&self, _loc: CallLocation<'_>, args: &[Option<Thunk<Val>>]) -> Result<Val> {
100 let args = parse_builtin_call(ctx, self.params.clone(), args, true)?;
101 let args = args100 let args = args
102 .into_iter()101 .into_iter()
103 .map(|a| a.expect("legacy natives have no default params"))102 .map(|a| a.as_ref().expect("legacy natives have no default params"))
104 .map(|a| a.evaluate())103 .map(|a| a.evaluate())
105 .collect::<Result<Vec<Val>>>()?;104 .collect::<Result<Vec<Val>>>()?;
106 self.handler.call(&args)105 self.handler.call(&args)
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
11 arglike::OptionalContext,11 arglike::OptionalContext,
12 builtin::{Builtin, StaticBuiltin},12 builtin::{Builtin, StaticBuiltin},
13 native::NativeDesc,13 native::NativeDesc,
14 parse::{parse_default_function_call, parse_function_call},14 parse::{parse_builtin_call, parse_default_function_call, parse_function_call},
15 prepared::{parse_prepared_builtin_call, parse_prepared_function_call, PreparedCall},
15};16};
16use crate::{17use crate::{
17 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,18 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,
22pub mod builtin;23pub mod builtin;
23pub mod native;24pub mod native;
24pub mod parse;25pub mod parse;
25pub mod prepared;26mod prepared;
27
28pub use prepared::PreparedFuncVal;
2629
27pub use jrsonnet_parser::function::*;30pub use jrsonnet_parser::function::*;
2831
173 tailstrict: bool,176 tailstrict: bool,
174 ) -> Result<Val> {177 ) -> Result<Val> {
175 match self {178 match self {
176 Self::Id => ID.call(call_ctx, loc, args),
177 Self::Normal(func) => {179 Self::Normal(func) => {
178 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;180 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;
179 evaluate(body_ctx, &func.body)181 evaluate(body_ctx, &func.body)
184 }186 }
185 thunk.evaluate()187 thunk.evaluate()
186 }188 }
189 Self::Id => {
190 let args = parse_builtin_call(call_ctx, ID.params(), args, tailstrict)?;
191 ID.call(loc, &args)
192 }
187 Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),193 Self::StaticBuiltin(b) => {
194 let args = parse_builtin_call(call_ctx, b.params(), args, tailstrict)?;
195 b.call(loc, &args)
196 }
188 Self::Builtin(b) => b.call(call_ctx, loc, args),197 Self::Builtin(b) => {
198 let args = parse_builtin_call(call_ctx, b.params(), args, tailstrict)?;
199 b.call(loc, &args)
200 }
189 }201 }
190 }202 }
191 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(203 pub fn evaluate_simple<A: ArgsLike + OptionalContext>(
201 )213 )
202 }214 }
215
216 pub(crate) fn evaluate_prepared(
217 &self,
218 prepared: &PreparedCall,
219 loc: CallLocation<'_>,
220 unnamed: &[Thunk<Val>],
221 named: &[Thunk<Val>],
222 _tailstrict: bool,
223 ) -> Result<Val> {
224 match self {
225 FuncVal::Id => {
226 let args = parse_prepared_builtin_call(prepared, ID.params(), unnamed, named)?;
227 ID.call(loc, &args)
228 }
229 FuncVal::Normal(func) => {
230 let body_ctx = parse_prepared_function_call(
231 func.ctx.clone(),
232 prepared,
233 &func.params,
234 unnamed,
235 named,
236 )?;
237 evaluate(body_ctx, &func.body)
238 }
239 FuncVal::Thunk(t) => t.evaluate(),
240 FuncVal::StaticBuiltin(b) => {
241 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named)?;
242 b.call(loc, &args)
243 }
244 FuncVal::Builtin(b) => {
245 let args = parse_prepared_builtin_call(prepared, b.params(), unnamed, named)?;
246 b.call(loc, &args)
247 }
248 }
249 }
203 /// Convert jsonnet function to plain `Fn` value.250 /// Convert jsonnet function to plain `Fn` value.
204 pub fn into_native<D: NativeDesc>(self) -> D::Value {251 pub fn into_native<D: NativeDesc>(self) -> D::Value {
205 D::into_native(self)252 D::into_native(self)
modifiedcrates/jrsonnet-evaluator/src/function/prepared.rsdiffbeforeafterboth
1use std::rc::Rc;
2
3use jrsonnet_gcmodule::{Acyclic, Trace};
1use jrsonnet_parser::function::FunctionSignature;4use jrsonnet_parser::function::FunctionSignature;
2use jrsonnet_parser::{ExprParams, IStr};5use jrsonnet_parser::{ExprParams, IStr};
3use rustc_hash::{FxHashMap, FxHashSet};6use rustc_hash::{FxHashMap, FxHashSet};
7use crate::{bail, error::ErrorKind::*, Result};10use crate::{bail, error::ErrorKind::*, Result};
8use crate::{evaluate_named_param, Context, ContextBuilder, Pending, Thunk, Val};11use crate::{evaluate_named_param, Context, ContextBuilder, Pending, Thunk, Val};
912
13use super::{CallLocation, FuncVal};
14
15#[derive(Debug, Trace, Clone)]
16pub struct PreparedFuncVal {
17 fun: FuncVal,
18 prepared: Rc<PreparedCall>,
19}
20
21impl PreparedFuncVal {
22 pub fn new(fun: FuncVal, unnamed: usize, named: &[IStr]) -> Result<Self> {
23 let prepared = prepare_call(fun.params(), unnamed, named)?;
24 Ok(Self {
25 fun,
26 prepared: Rc::new(prepared),
27 })
28 }
29 pub fn call(
30 &self,
31 loc: CallLocation<'_>,
32 unnamed: &[Thunk<Val>],
33 named: &[Thunk<Val>],
34 ) -> Result<Val> {
35 self.fun
36 .evaluate_prepared(&self.prepared, loc, unnamed, named, false)
37 }
38}
39
40#[derive(Acyclic, Debug)]
10pub struct PreparedCall {41pub struct PreparedCall {
11 // Param, named input.42 // Param, named input.
12 named: Vec<(usize, usize)>,43 named: Vec<(usize, usize)>,
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},
12 Attribute, DeriveInput, Error, Expr, ExprClosure, FnArg, GenericArgument, Ident, ItemFn,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}
13 LitStr, Pat, Path, PathArguments, Result, ReturnType, Token, Type,
14};7};
158
9fn try_parse_attr_noargs<I>(attrs: &[Attribute], ident: I) -> Result<bool>
10where
11 Ident: PartialEq<I>,
12{
13 let attrs = attrs
14 .iter()
15 .filter(|a| a.path().is_ident(&ident))
16 .collect::<Vec<_>>();
17 if attrs.len() > 1 {
18 return Err(Error::new(
19 attrs[1].span(),
20 "this attribute may be specified only once",
21 ));
22 } else if attrs.is_empty() {
23 return Ok(false);
24 }
25 let attr = attrs[0];
26
27 match attr.meta {
28 Meta::Path(_) => Ok(true),
29 _ => Ok(false),
30 }
31}
16fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>32fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>
17where33where
18 Ident: PartialEq<I>,34 Ident: PartialEq<I>,
125 Required,141 Required,
126 Optional,142 Optional,
127 Default(Expr),143 Default(Expr),
144 TypeDefault,
128}145}
129146
130#[allow(clippy::large_enum_variant, reason = "this macro is not that hot for it to matter")]147#[allow(
170 _ => {}190 _ => {}
171 }191 }
172192
173 let (optionality, ty) = if let Some(default) = parse_attr::<_, _>(&arg.attrs, "default")? {193 let (optionality, ty) = if try_parse_attr_noargs(&mut arg.attrs, "default")? {
194 remove_attr(&mut arg.attrs, "default");
195 (Optionality::TypeDefault, ty.clone())
196 } else if let Some(default) = parse_attr::<_, _>(&arg.attrs, "default")? {
174 remove_attr(&mut arg.attrs, "default");197 remove_attr(&mut arg.attrs, "default");
175 (Optionality::Default(default), ty.clone())198 (Optionality::Default(default), ty.clone())
176 } else if let Some(ty) = extract_type_from_option(ty)? {199 } else if let Some(ty) = extract_type_from_option(ty)? {
245 .map_or_else(|| quote! {unnamed}, |n| quote! {named(#n)});268 .map_or_else(|| quote! {unnamed}, |n| quote! {named(#n)});
246 let default = match optionality {269 let default = match optionality {
247 Optionality::Required => quote!(ParamDefault::None),270 Optionality::Required => quote!(ParamDefault::None),
248 Optionality::Optional => quote!(ParamDefault::Exists),271 Optionality::Optional | Optionality::TypeDefault => quote!(ParamDefault::Exists),
249 Optionality::Default(e) => quote!(ParamDefault::Literal(stringify!(#e))),272 Optionality::Default(e) => quote!(ParamDefault::Literal(stringify!(#e))),
250 };273 };
251 Some(quote! {274 Some(quote! {
305 let v: #ty = #expr;328 let v: #ty = #expr;
306 v329 v
307 },},330 },},
331 Optionality::TypeDefault => quote! {if let Some(value) = &parsed[#id] {
332 #eval
333 } else {
334 let v: #ty = Default::default();
335 v
336 },},
308 };337 };
309 quote! {338 quote! {
310 #(#cfg_attrs)*339 #(#cfg_attrs)*
371 State, Val,400 State, Val,
372 function::{builtin::{Builtin, StaticBuiltin}, FunctionSignature, ParamParse, ParamName, ParamDefault, CallLocation, ArgsLike, parse::parse_builtin_call},401 function::{builtin::{Builtin, StaticBuiltin}, FunctionSignature, ParamParse, ParamName, ParamDefault, CallLocation, ArgsLike, parse::parse_builtin_call},
373 Result, Context, typed::Typed,402 Result, Context, typed::Typed,
374 parser::Span, params,403 parser::Span, params, Thunk,
375 };404 };
376 params!(405 params!(
377 #(#params_desc)*406 #(#params_desc)*
389 PARAMS.with(|p| p.clone())418 PARAMS.with(|p| p.clone())
390 }419 }
391 #[allow(unused_variables)]420 #[allow(unused_variables)]
392 fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {421 fn call(&self, location: CallLocation<'_>, parsed: &[Option<Thunk<Val>>]) -> Result<Val> {
393 let parsed = parse_builtin_call(ctx.clone(), self.params(), args, false)?;
394
395 let result: #result = #name(#(#pass)*);422 let result: #result = #name(#(#pass)*);
396 <_ as Typed>::into_result(result)423 <_ as Typed>::into_result(result)
addedcrates/jrsonnet-stdlib/src/keyf.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
50mod sort;50mod sort;
51mod strings;51mod strings;
52mod types;52mod types;
53mod keyf;
5354
54#[allow(clippy::too_many_lines)]55#[allow(clippy::too_many_lines)]
55pub fn stdlib_uncached(settings: Cc<RefCell<Settings>>) -> ObjValue {56pub fn stdlib_uncached(settings: Cc<RefCell<Settings>>) -> ObjValue {
modifiedcrates/jrsonnet-stdlib/src/sets.rsdiffbeforeafterboth
1use std::cmp::Ordering;1use std::cmp::Ordering;
22
3use jrsonnet_evaluator::{3use jrsonnet_evaluator::{
4 function::{builtin, FuncVal},4 function::builtin, operator::evaluate_compare_op, val::ArrValue, Result, Thunk, Val,
5 operator::evaluate_compare_op,
6 val::ArrValue,
7 Result, Thunk, Val,
8};5};
9use jrsonnet_parser::BinaryOpType;6use jrsonnet_parser::BinaryOpType;
7
8use crate::keyf::KeyF;
109
11#[builtin]10#[builtin]
12#[allow(non_snake_case)]11#[allow(non_snake_case)]
13pub fn builtin_set_member(x: Thunk<Val>, arr: ArrValue, keyF: Option<FuncVal>) -> Result<bool> {12pub fn builtin_set_member(x: Thunk<Val>, arr: ArrValue, #[default] keyF: KeyF) -> Result<bool> {
14 let mut low = 0;13 let mut low = 0;
15 let mut high = arr.len();14 let mut high = arr.len();
16
17 let keyF = keyF
18 .unwrap_or(FuncVal::Id)
19 .into_native::<((Thunk<Val>,), Val)>();
2015
21 let x = keyF(x)?;16 let x = keyF.eval(x)?;
2217
23 while low < high {18 while low < high {
24 let middle = usize::midpoint(high, low);19 let middle = usize::midpoint(high, low);
25 let comp = keyF(arr.get_lazy(middle).expect("in bounds"))?;20 let comp = keyF.eval(arr.get_lazy(middle).expect("in bounds"))?;
26 match evaluate_compare_op(&comp, &x, BinaryOpType::Lt)? {21 match evaluate_compare_op(&comp, &x, BinaryOpType::Lt)? {
27 Ordering::Less => low = middle + 1,22 Ordering::Less => low = middle + 1,
28 Ordering::Equal => return Ok(true),23 Ordering::Equal => return Ok(true),
3429
35#[builtin]30#[builtin]
36#[allow(non_snake_case, clippy::redundant_closure)]31#[allow(non_snake_case, clippy::redundant_closure)]
37pub fn builtin_set_inter(a: ArrValue, b: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {32pub fn builtin_set_inter(a: ArrValue, b: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {
38 let mut a = a.iter_lazy();33 let mut a = a.iter_lazy();
39 let mut b = b.iter_lazy();34 let mut b = b.iter_lazy();
4035
41 let keyF = keyF
42 .unwrap_or(FuncVal::identity())
43 .into_native::<((Thunk<Val>,), Val)>();
44 let keyF = |v| keyF(v);36 let keyF = |v| keyF.eval(v);
4537
46 let mut av = a.next();38 let mut av = a.next();
47 let mut bv = b.next();39 let mut bv = b.next();
7365
74#[builtin]66#[builtin]
75#[allow(non_snake_case, clippy::redundant_closure)]67#[allow(non_snake_case, clippy::redundant_closure)]
76pub fn builtin_set_diff(a: ArrValue, b: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {68pub fn builtin_set_diff(a: ArrValue, b: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {
77 let mut a = a.iter_lazy();69 let mut a = a.iter_lazy();
78 let mut b = b.iter_lazy();70 let mut b = b.iter_lazy();
7971
80 let keyF = keyF
81 .unwrap_or(FuncVal::identity())
82 .into_native::<((Thunk<Val>,), Val)>();
83 let keyF = |v| keyF(v);72 let keyF = |v| keyF.eval(v);
8473
85 let mut av = a.next();74 let mut av = a.next();
86 let mut bv = b.next();75 let mut bv = b.next();
119108
120#[builtin]109#[builtin]
121#[allow(non_snake_case, clippy::redundant_closure)]110#[allow(non_snake_case, clippy::redundant_closure)]
122pub fn builtin_set_union(a: ArrValue, b: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {111pub fn builtin_set_union(a: ArrValue, b: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {
123 let mut a = a.iter_lazy();112 let mut a = a.iter_lazy();
124 let mut b = b.iter_lazy();113 let mut b = b.iter_lazy();
125114
126 let keyF = keyF
127 .unwrap_or(FuncVal::identity())
128 .into_native::<((Thunk<Val>,), Val)>();
129 let keyF = |v| keyF(v);115 let keyF = |v| keyF.eval(v);
130116
131 let mut av = a.next();117 let mut av = a.next();
132 let mut bv = b.next();118 let mut bv = b.next();
modifiedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth
44
5use jrsonnet_evaluator::{5use jrsonnet_evaluator::{
6 bail,6 bail,
7 function::{builtin, FuncVal},7 function::builtin,
8 operator::evaluate_compare_op,8 operator::evaluate_compare_op,
9 val::{equals, ArrValue},9 val::{equals, ArrValue},
10 Result, Thunk, Val,10 Result, Thunk, Val,
11};11};
12use jrsonnet_parser::BinaryOpType;12use jrsonnet_parser::BinaryOpType;
1313
14use crate::eval_on_empty;14use crate::{eval_on_empty, keyf::KeyF};
1515
16#[derive(Copy, Clone)]16#[derive(Copy, Clone)]
17enum SortKeyType {17enum SortKeyType {
70 Ok(values)70 Ok(values)
71}71}
7272
73fn sort_keyf(values: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {73fn sort_keyf(values: ArrValue, keyf: KeyF) -> Result<Vec<Thunk<Val>>> {
74 // Slow path, user provided key getter74 // Slow path, user provided key getter
75 let mut vk = Vec::with_capacity(values.len());75 let mut vk = Vec::with_capacity(values.len());
76 for value in values.iter_lazy() {76 for value in values.iter_lazy() {
77 vk.push((77 vk.push((value.clone(), keyf.eval(value)?));
78 value.clone(),
79 keyf.evaluate_simple(&(value.clone(),), false)?,
80 ));
81 }78 }
82 let sort_type = get_sort_type(&vk, |v| &v.1)?;79 let sort_type = get_sort_type(&vk, |v| &v.1)?;
112}109}
113110
114/// * `key_getter` - None, if identity sort required111/// * `key_getter` - None, if identity sort required
115pub fn sort(values: ArrValue, key_getter: FuncVal) -> Result<ArrValue> {112pub fn sort(values: ArrValue, key_getter: KeyF) -> Result<ArrValue> {
116 if values.len() <= 1 {113 if values.len() <= 1 {
117 return Ok(values);114 return Ok(values);
118 }115 }
126}123}
127124
128#[builtin]125#[builtin]
129pub fn builtin_sort(126pub fn builtin_sort(arr: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {
130 arr: ArrValue,
131
132 #[default(FuncVal::identity())] keyF: FuncVal,
133) -> Result<ArrValue> {
134 super::sort::sort(arr, keyF)127 super::sort::sort(arr, keyF)
135}128}
147 Ok(out)140 Ok(out)
148}141}
149142
150fn uniq_keyf(arr: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {143fn uniq_keyf(arr: ArrValue, keyf: KeyF) -> Result<Vec<Thunk<Val>>> {
151 let mut out = Vec::new();144 let mut out = Vec::new();
152 let last_value = arr.get_lazy(0).unwrap();145 let last_value = arr.get_lazy(0).unwrap();
153 let mut last_key = keyf.evaluate_simple(&(last_value.clone(),), false)?;146 let mut last_key = keyf.eval(last_value.clone())?;
154 out.push(last_value);147 out.push(last_value);
155148
156 for next in arr.iter_lazy().skip(1) {149 for next in arr.iter_lazy().skip(1) {
157 let next_key = keyf.evaluate_simple(&(next.clone(),), false)?;150 let next_key = keyf.eval(next.clone())?;
158 if !equals(&last_key, &next_key)? {151 if !equals(&last_key, &next_key)? {
159 out.push(next.clone());152 out.push(next.clone());
160 }153 }
165158
166#[builtin]159#[builtin]
167#[allow(non_snake_case)]160#[allow(non_snake_case)]
168pub fn builtin_uniq(161pub fn builtin_uniq(arr: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {
169 arr: ArrValue,
170
171 #[default(FuncVal::identity())] keyF: FuncVal,
172) -> Result<ArrValue> {
173 if arr.len() <= 1 {162 if arr.len() <= 1 {
174 return Ok(arr);163 return Ok(arr);
184173
185#[builtin]174#[builtin]
186#[allow(non_snake_case)]175#[allow(non_snake_case)]
187pub fn builtin_set(176pub fn builtin_set(arr: ArrValue, #[default] keyF: KeyF) -> Result<ArrValue> {
188 arr: ArrValue,
189
190 #[default(FuncVal::identity())] keyF: FuncVal,
191) -> Result<ArrValue> {
192 if arr.len() <= 1 {177 if arr.len() <= 1 {
193 return Ok(arr);178 return Ok(arr);
204 }189 }
205}190}
206
207fn eval_keyf(val: Val, key_f: Option<&FuncVal>) -> Result<Val> {
208 if let Some(key_f) = key_f {
209 key_f.evaluate_simple(&(val,), false)
210 } else {
211 Ok(val)
212 }
213}
214191
215fn array_top1(arr: ArrValue, key_f: Option<&FuncVal>, ordering: Ordering) -> Result<Val> {192fn array_top1(arr: ArrValue, keyf: KeyF, ordering: Ordering) -> Result<Val> {
216 let mut iter = arr.iter();193 let mut iter = arr.iter();
217 let mut min = iter.next().expect("not empty")?;194 let mut min = iter.next().expect("not empty")?;
218 let mut min_key = eval_keyf(min.clone(), key_f)?;195 let mut min_key = keyf.eval(Thunk::evaluated(min.clone()))?;
219 for item in iter {196 for item in iter {
220 let cur = item?;197 let cur = item?;
221 let cur_key = eval_keyf(cur.clone(), key_f)?;198 let cur_key = keyf.eval(Thunk::evaluated(cur.clone()))?;
222 if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {199 if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {
223 min = cur;200 min = cur;
224 min_key = cur_key;201 min_key = cur_key;
230#[builtin]207#[builtin]
231pub fn builtin_min_array(208pub fn builtin_min_array(
232 arr: ArrValue,209 arr: ArrValue,
233 keyF: Option<FuncVal>,210 #[default] keyF: KeyF,
234 onEmpty: Option<Thunk<Val>>,211 onEmpty: Option<Thunk<Val>>,
235) -> Result<Val> {212) -> Result<Val> {
236 if arr.is_empty() {213 if arr.is_empty() {
237 return eval_on_empty(onEmpty);214 return eval_on_empty(onEmpty);
238 }215 }
239 array_top1(arr, keyF.as_ref(), Ordering::Less)216 array_top1(arr, keyF, Ordering::Less)
240}217}
241#[builtin]218#[builtin]
242pub fn builtin_max_array(219pub fn builtin_max_array(
243 arr: ArrValue,220 arr: ArrValue,
244 keyF: Option<FuncVal>,221 #[default] keyF: KeyF,
245 onEmpty: Option<Thunk<Val>>,222 onEmpty: Option<Thunk<Val>>,
246) -> Result<Val> {223) -> Result<Val> {
247 if arr.is_empty() {224 if arr.is_empty() {
248 return eval_on_empty(onEmpty);225 return eval_on_empty(onEmpty);
249 }226 }
250 array_top1(arr, keyF.as_ref(), Ordering::Greater)227 array_top1(arr, keyF, Ordering::Greater)
251}228}
252229
modifiedtests/tests/builtin.rsdiffbeforeafterboth
18#[test]18#[test]
19fn basic_function() -> Result<()> {19fn basic_function() -> Result<()> {
20 let a: a = a {};20 let a: a = a {};
21 let v =21 let v = u32::from_untyped(a.call(CallLocation::native(), &[])?)?;
22 u32::from_untyped(a.call(ContextBuilder::new().build(), CallLocation::native(), &())?)?;
2322
24 ensure_eq!(v, 1);23 ensure_eq!(v, 1);
25 Ok(())24 Ok(())