git.delta.rocks / jrsonnet / refs/commits / 5e8cbcdbc860

difftreelog

feat function folder abstraction

vwxqrkzyYaroslav Bolyukin2026-05-12parent: #db1fd8f.patch.diff
in: master

15 files changed

modifiedcrates/jrsonnet-evaluator/src/analyze.rsdiffbeforeafterboth
2075 pub errored: bool,2075 pub errored: bool,
2076}2076}
2077
2078/// Only useful for slot reference analysis
2079pub trait LVisitor: Sized {
2080 fn visit_lexpr(&mut self, e: &LExpr) {
2081 visit_lexpr(self, e);
2082 }
2083 fn visit_lslot(&mut self, _slot: LSlot) {}
2084 fn visit_closure(&mut self, shape: &ClosureShape) {
2085 for &s in &*shape.captures {
2086 self.visit_lslot(s);
2087 }
2088 }
2089}
2090
2091pub fn visit_lexpr<V: LVisitor>(v: &mut V, e: &LExpr) {
2092 match e {
2093 LExpr::Slot(s) => v.visit_lslot(*s),
2094 LExpr::Trivial(_)
2095 | LExpr::ArrConst(_)
2096 | LExpr::BadLocal(_)
2097 | LExpr::Import { .. }
2098 | LExpr::IdentityFunction
2099 | LExpr::Super => {}
2100 LExpr::Arr { shape, items: _ } => v.visit_closure(shape),
2101 LExpr::ArrComp(c) => {
2102 v.visit_closure(&c.value_shape);
2103 for s in &c.compspecs {
2104 match s {
2105 // Outer-scope references reach closure over frame_shape
2106 LCompSpec::If(_e) => {}
2107 LCompSpec::For { frame_shape, .. } => v.visit_closure(frame_shape),
2108 #[cfg(feature = "exp-object-iteration")]
2109 LCompSpec::ForObj { frame_shape, .. } => v.visit_closure(frame_shape),
2110 }
2111 }
2112 }
2113 LExpr::Obj(b) => visit_l_obj_body(v, b),
2114 LExpr::ObjExtend(base, b) => {
2115 v.visit_lexpr(base);
2116 visit_l_obj_body(v, b);
2117 }
2118 LExpr::UnaryOp(_op, e) => v.visit_lexpr(e),
2119 LExpr::BinaryOp { lhs, rhs, .. } => {
2120 v.visit_lexpr(lhs);
2121 v.visit_lexpr(rhs);
2122 }
2123 LExpr::AssertExpr { assert, rest } => {
2124 v.visit_lexpr(&assert.cond.value);
2125 if let Some(m) = &assert.message {
2126 v.visit_lexpr(m);
2127 }
2128 v.visit_lexpr(rest);
2129 }
2130 LExpr::Error(_, e) => v.visit_lexpr(e),
2131 LExpr::LocalExpr(l) => v.visit_closure(&l.frame_shape),
2132 LExpr::Apply {
2133 applicable, args, ..
2134 } => {
2135 v.visit_lexpr(applicable);
2136 for u in &args.value.unnamed {
2137 v.visit_lexpr(u);
2138 }
2139 for n in &args.value.values {
2140 v.visit_lexpr(n);
2141 }
2142 }
2143 LExpr::Index { indexable, parts } => {
2144 v.visit_lexpr(indexable);
2145 for p in parts {
2146 v.visit_lexpr(&p.value);
2147 }
2148 }
2149 LExpr::Function(f) => v.visit_closure(&f.body_shape),
2150 LExpr::IfElse {
2151 cond,
2152 cond_then,
2153 cond_else,
2154 } => {
2155 v.visit_lexpr(cond);
2156 v.visit_lexpr(cond_then);
2157 if let Some(e) = cond_else {
2158 v.visit_lexpr(e);
2159 }
2160 }
2161 LExpr::Slice(s) => {
2162 v.visit_lexpr(&s.value);
2163 if let Some(e) = &s.start {
2164 v.visit_lexpr(e);
2165 }
2166 if let Some(e) = &s.end {
2167 v.visit_lexpr(e);
2168 }
2169 if let Some(e) = &s.step {
2170 v.visit_lexpr(e);
2171 }
2172 }
2173 }
2174}
2175
2176fn visit_l_obj_body<V: LVisitor>(v: &mut V, b: &LObjBody) {
2177 match b {
2178 LObjBody::MemberList(m) => v.visit_closure(&m.frame_shape),
2179 LObjBody::StaticMembers(m) => v.visit_closure(&m.frame_shape),
2180 LObjBody::ObjComp(c) => v.visit_closure(&c.frame_shape),
2181 }
2182}
20772183
2078#[cfg(test)]2184#[cfg(test)]
2079mod tests {2185mod tests {
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
3use educe::Educe;3use educe::Educe;
4use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_gcmodule::{Cc, Trace};
5use jrsonnet_interner::IStr;5use jrsonnet_interner::IStr;
6use jrsonnet_ir::Span;6use jrsonnet_ir::{BinaryOpType, Span};
7pub use jrsonnet_macros::builtin;7pub use jrsonnet_macros::builtin;
88
9use self::{9use self::{
12};12};
13use crate::{13use crate::{
14 Context, PackedContextSupThis, Result, Thunk, Val,14 Context, PackedContextSupThis, Result, Thunk, Val,
15 analyze::LFunction,15 analyze::{LDestruct, LExpr, LFunction, LSlot, LVisitor, LocalSlot, visit_lexpr},
16 arr::arridx,16 arr::arridx,
17 ensure_sufficient_stack,17 ensure_sufficient_stack,
18 evaluate::{destructure::destruct, evaluate, evaluate_trivial},18 evaluate::{destructure::destruct, evaluate, evaluate_trivial},
213 }213 }
214}214}
215
216#[derive(Clone, Copy, Debug)]
217pub enum FoldKind {
218 /// `function(acc, x) acc + <expr>`
219 Left,
220 /// `function(x, acc) <expr> + acc`
221 Right,
222}
223
224/// A folding lambda specialised to `acc + <expr>` (or `<expr> + acc`)
225///
226/// `<expr>` does not reference `acc`. Lets the caller skip building an
227/// intermediate accumulator value per step and instead append each
228/// per-element result directly to an output buffer.
229pub struct Folder<'f> {
230 func: &'f FuncDesc,
231 value_destruct: &'f LDestruct,
232 expr: &'f LExpr,
233}
234
235impl Folder<'_> {
236 /// Evaluate `<expr>` with the current element bound to the non-acc param.
237 pub fn eval(&self, x: Thunk<Val>) -> Result<Val> {
238 let body_ctx = self.func.body_captures.clone().enter(|fill, ctx| {
239 destruct(self.value_destruct, fill, x, ctx);
240 });
241 ensure_sufficient_stack(|| evaluate(body_ctx, self.expr))
242 }
243}
244
245struct ReferencesLocal {
246 slot: LocalSlot,
247 found: bool,
248}
249
250impl LVisitor for ReferencesLocal {
251 fn visit_lslot(&mut self, slot: LSlot) {
252 if let LSlot::Local(s) = slot
253 && s.0 == self.slot.0
254 {
255 self.found = true;
256 }
257 }
258 fn visit_lexpr(&mut self, e: &LExpr) {
259 if self.found {
260 return;
261 }
262 visit_lexpr(self, e);
263 }
264}
265
266fn references_local(expr: &LExpr, slot: LocalSlot) -> bool {
267 let mut v = ReferencesLocal { slot, found: false };
268 v.visit_lexpr(expr);
269 v.found
270}
271
272impl FuncVal {
273 pub fn as_folder(&self, kind: FoldKind) -> Option<Folder<'_>> {
274 let Self::Normal(desc) = self else {
275 return None;
276 };
277 let func = &desc.func;
278 if func.params.len() != 2 {
279 return None;
280 }
281 if func.params.iter().any(|p| p.default.is_some()) {
282 return None;
283 }
284
285 let (acc_idx, value_idx) = match kind {
286 FoldKind::Left => (0, 1),
287 FoldKind::Right => (1, 0),
288 };
289
290 let acc_slot = match &func.params[acc_idx].destruct {
291 LDestruct::Full(s) => *s,
292 #[cfg(feature = "exp-destruct")]
293 _ => return None,
294 };
295
296 let LExpr::BinaryOp { lhs, op, rhs } = &*func.body else {
297 return None;
298 };
299 if *op != BinaryOpType::Add {
300 return None;
301 }
302 let (acc_side, expr_side) = match kind {
303 FoldKind::Left => (lhs, rhs),
304 FoldKind::Right => (rhs, lhs),
305 };
306 let LExpr::Slot(LSlot::Local(s)) = &**acc_side else {
307 return None;
308 };
309 if s.0 != acc_slot.0 {
310 return None;
311 }
312 if references_local(expr_side, acc_slot) {
313 return None;
314 }
315
316 Some(Folder {
317 func: desc,
318 value_destruct: &func.params[value_idx].destruct,
319 expr: expr_side,
320 })
321 }
322}
215323
modifiedcrates/jrsonnet-evaluator/src/function/native.rsdiffbeforeafterboth
13#[derive(Debug, Trace, Clone)]13#[derive(Debug, Trace, Clone)]
14pub struct NativeFn<D: 'static>(pub(crate) PreparedFuncVal, PhantomData<D>);14pub struct NativeFn<D: 'static>(pub(crate) PreparedFuncVal, PhantomData<D>);
15
16impl<D: 'static> NativeFn<D> {
17 pub fn func(&self) -> &FuncVal {
18 self.0.func()
19 }
20}
15macro_rules! impl_native_desc {21macro_rules! impl_native_desc {
16 ($i:expr; $($gen:ident)*) => {22 ($i:expr; $($gen:ident)*) => {
17 impl<$($gen,)* O> NativeFn<($($gen,)* O,)>23 impl<$($gen,)* O> NativeFn<($($gen,)* O,)>
modifiedcrates/jrsonnet-evaluator/src/function/prepared.rsdiffbeforeafterboth
21 prepared: Rc::new(prepared),21 prepared: Rc::new(prepared),
22 })22 })
23 }23 }
24 pub fn func(&self) -> &FuncVal {
25 &self.fun
26 }
24 pub fn call(27 pub fn call(
25 &self,28 &self,
26 loc: CallLocation<'_>,29 loc: CallLocation<'_>,
modifiedcrates/jrsonnet-evaluator/src/manifest.rsdiffbeforeafterboth
5};5};
66
7pub trait ManifestFormat {7pub trait ManifestFormat {
8 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()>;8 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()>;
9 fn manifest(&self, val: Val) -> Result<String> {9 fn manifest(&self, val: &Val) -> Result<String> {
10 let mut out = String::new();10 let mut out = String::new();
11 self.manifest_buf(val, &mut out)?;11 self.manifest_buf(val, &mut out)?;
12 Ok(out)12 Ok(out)
16where16where
17 T: ManifestFormat + ?Sized,17 T: ManifestFormat + ?Sized,
18{18{
19 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {19 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()> {
20 let inner = &**self;20 let inner = &**self;
21 inner.manifest_buf(val, buf)21 inner.manifest_buf(val, buf)
22 }22 }
25where25where
26 T: ManifestFormat + ?Sized,26 T: ManifestFormat + ?Sized,
27{27{
28 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {28 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()> {
29 let inner = &**self;29 let inner = &**self;
30 inner.manifest_buf(val, buf)30 inner.manifest_buf(val, buf)
31 }31 }
34pub struct BlackBoxFormat;34pub struct BlackBoxFormat;
35impl ManifestFormat for BlackBoxFormat {35impl ManifestFormat for BlackBoxFormat {
36 #[allow(clippy::only_used_in_recursion)]36 #[allow(clippy::only_used_in_recursion)]
37 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {37 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()> {
38 match val {38 match val {
39 Val::Bool(v) => {39 Val::Bool(v) => {
40 black_box(v);40 black_box(v);
51 Val::Arr(arr_value) => {51 Val::Arr(arr_value) => {
52 for ele in arr_value.iter() {52 for ele in arr_value.iter() {
53 let ele = ele?;53 let ele = ele?;
54 self.manifest_buf(ele, buf)?;54 self.manifest_buf(&ele, buf)?;
55 }55 }
56 }56 }
57 Val::Obj(obj_value) => {57 Val::Obj(obj_value) => {
61 ) {61 ) {
62 black_box(name);62 black_box(name);
63 let value = value?;63 let value = value?;
64 self.manifest_buf(value, buf)?;64 self.manifest_buf(&value, buf)?;
65 }65 }
66 }66 }
67 Val::Func(func_val) => {67 Val::Func(func_val) => {
362}362}
363363
364impl ManifestFormat for JsonFormat<'_> {364impl ManifestFormat for JsonFormat<'_> {
365 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {365 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()> {
366 manifest_json_ex_buf(&val, buf, &mut String::new(), self)366 manifest_json_ex_buf(val, buf, &mut String::new(), self)
367 }367 }
368}368}
369369
370/// Same as [`JsonFormat`] with pre-set options, but top-level string is serialized as-is,370/// Same as [`JsonFormat`] with pre-set options, but top-level string is serialized as-is,
371/// without quoting.371/// without quoting.
372pub struct ToStringFormat;372pub struct ToStringFormat;
373impl ManifestFormat for ToStringFormat {373impl ManifestFormat for ToStringFormat {
374 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {374 fn manifest_buf(&self, val: &Val, out: &mut String) -> Result<()> {
375 const JSON_TO_STRING: JsonFormat = JsonFormat::std_to_string_helper();375 const JSON_TO_STRING: JsonFormat = JsonFormat::std_to_string_helper();
376 if let Some(str) = val.as_str() {376 if let Some(str) = val.as_str() {
377 out.push_str(&str);377 str.into_flat_in(out);
378 return Ok(());378 return Ok(());
379 }379 }
380 #[cfg(feature = "exp-bigint")]380 #[cfg(feature = "exp-bigint")]
387}387}
388pub struct StringFormat;388pub struct StringFormat;
389impl ManifestFormat for StringFormat {389impl ManifestFormat for StringFormat {
390 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {390 fn manifest_buf(&self, val: &Val, out: &mut String) -> Result<()> {
391 let Val::Str(s) = val else {391 let Val::Str(s) = val else {
392 bail!(392 bail!(
393 "output should be string for string manifest format, got {}",393 "output should be string for string manifest format, got {}",
422 }422 }
423}423}
424impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {424impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {
425 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {425 fn manifest_buf(&self, val: &Val, out: &mut String) -> Result<()> {
426 let Val::Arr(arr) = val else {426 let Val::Arr(arr) = val else {
427 bail!(427 bail!(
428 "output should be array for yaml stream format, got {}",428 "output should be array for yaml stream format, got {}",
437 out.push_str("---\n");437 out.push_str("---\n");
438 in_description_frame(438 in_description_frame(
439 || format!("elem <{i}> manifestification"),439 || format!("elem <{i}> manifestification"),
440 || self.inner.manifest_buf(v, out),440 || self.inner.manifest_buf(&v, out),
441 )?;441 )?;
442 }442 }
443 if self.c_document_end {443 if self.c_document_end {
modifiedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
85 let [loc] = span.0.map_source_locations(&[start]);85 let [loc] = span.0.map_source_locations(&[start]);
86 (loc, loc)86 (loc, loc)
87 } else {87 } else {
88 let [s, e] = span.0.map_source_locations(&[start, end]);88 span.0.map_source_locations(&[start, end]).into()
89 (s, e)
90 };89 };
91 write!(path, ":").unwrap();90 write!(path, ":").unwrap();
92 print_code_location(&mut path, &start_loc, &end_loc).unwrap();91 print_code_location(&mut path, &start_loc, &end_loc).unwrap();
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
533 fn into_untyped(typed: Self) -> Result<Val> {533 fn into_untyped(typed: Self) -> Result<Val> {
534 let mut out = ObjValueBuilder::with_capacity(typed.len());534 let mut out = ObjValueBuilder::with_capacity(typed.len());
535 for (k, v) in typed {535 for (k, v) in typed {
536 let Some(key) = K::into_untyped(k)?.as_str() else {536 let key = K::into_untyped(k)?;
537 let Some(key) = key.as_str() else {
537 bail!("map key should serialize to string");538 bail!("map key should serialize to string");
538 };539 };
539 let value = V::into_untyped(v)?;540 let value = V::into_untyped(v)?;
540 out.field(key).value(value);541 out.field(key.into_flat()).value(value);
541 }542 }
542 Ok(Val::Obj(out.build()))543 Ok(Val::Obj(out.build()))
543 }544 }
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
367 }367 }
368 write_buf(self, c);368 write_buf(self, c);
369 }369 }
370 pub fn into_flat(&self) -> IStr {370 pub fn into_flat_in(&self, out: &mut String) {
371 fn write_buf(s: &StrValue, out: &mut String) {371 fn write_buf(s: &StrValue, out: &mut String) {
372 match s {372 match s {
373 StrValue::Flat(f) => out.push_str(f),373 StrValue::Flat(f) => out.push_str(f),
377 }377 }
378 }378 }
379 }379 }
380 match self {
381 Self::Flat(f) => f.clone(),
382 Self::Tree(_) => {
383 let mut buf = String::with_capacity(self.len());380 out.reserve(self.len());
384 write_buf(self, &mut buf);381 write_buf(self, out);
385 buf.into()
386 }
387 }
388 }382 }
383 pub fn into_flat(&self) -> IStr {
384 match self {
385 Self::Flat(f) => f.clone(),
386 Self::Tree(_) => {
387 let mut buf = String::with_capacity(self.len());
388 Self::into_flat_in(self, &mut buf);
389 buf.into()
390 }
391 }
392 }
389 pub fn len(&self) -> usize {393 pub fn len(&self) -> usize {
390 match self {394 match self {
391 Self::Flat(v) => v.len(),395 Self::Flat(v) => v.len(),
492 _ => None,496 _ => None,
493 }497 }
494 }498 }
495 pub fn as_str(&self) -> Option<IStr> {499 pub fn as_str(&self) -> Option<&StrValue> {
496 match self {500 match self {
497 Self::Str(s) => Some(s.clone().into_flat()),501 Self::Str(s) => Some(s),
498 _ => None,502 _ => None,
499 }503 }
500 }504 }
546550
547 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {551 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {
548 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {552 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {
549 manifest.manifest(val.clone())553 manifest.manifest(val)
550 }554 }
551 manifest_dyn(self, &format)555 manifest_dyn(self, &format)
552 }556 }
modifiedcrates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth
22
3use jrsonnet_evaluator::{3use jrsonnet_evaluator::{
4 Either, IStr, ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val, bail, error,4 Either, IStr, ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val, bail, error,
5 function::{NativeFn, builtin},5 function::{FoldKind, NativeFn, builtin},
6 manifest::{ManifestFormat as _, ToStringFormat},
6 typed::{BoundedUsize, Either2, FromUntyped},7 typed::{BoundedUsize, Either2, FromUntyped},
7 val::{ArrValue, IndexableVal, equals},8 val::{ArrValue, IndexableVal, equals},
8};9};
146 arr: Either![ArrValue, IStr],147 arr: Either![ArrValue, IStr],
147 init: Val,148 init: Val,
148) -> Result<Val> {149) -> Result<Val> {
150 if let (Val::Str(init), Either2::A(arr)) = (&init, &arr)
151 && let Some(folder) = func.func().as_folder(FoldKind::Left)
152 {
153 let mut buf = String::new();
154 init.into_flat_in(&mut buf);
155 for i in arr.iter_lazy() {
156 ToStringFormat.manifest_buf(&folder.eval(i)?, &mut buf)?;
157 }
158 return Ok(Val::string(buf));
159 }
160
149 let mut acc = init;161 let mut acc = init;
150 match arr {162 match arr {
168 arr: Either![ArrValue, IStr],180 arr: Either![ArrValue, IStr],
169 init: Val,181 init: Val,
170) -> Result<Val> {182) -> Result<Val> {
183 if let (Val::Str(init), Either2::A(arr)) = (&init, &arr)
184 && let Some(folder) = func.func().as_folder(FoldKind::Right)
185 {
186 let len = arr.len();
187 let mut chunks: Vec<Val> = Vec::with_capacity(len);
188 for i in arr.iter_lazy().rev() {
189 let v = folder.eval(i)?;
190 chunks.push(v);
191 }
192 let mut buf = String::new();
193 for s in chunks.iter().rev() {
194 ToStringFormat.manifest_buf(s, &mut buf)?;
195 }
196 init.into_flat_in(&mut buf);
197 return Ok(Val::string(buf));
198 }
199
171 let mut acc = init;200 let mut acc = init;
172 match arr {201 match arr {
modifiedcrates/jrsonnet-stdlib/src/manifest/ini.rsdiffbeforeafterboth
30}30}
3131
32impl ManifestFormat for IniFormat {32impl ManifestFormat for IniFormat {
33 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {33 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()> {
34 manifest_ini_obj(34 manifest_ini_obj(
35 self,35 self,
36 IniObj::from_untyped(val).description("ini object structure")?,36 IniObj::from_untyped(val.clone()).description("ini object structure")?,
37 buf,37 buf,
38 )38 )
39 }39 }
67 out.push_str(&key);67 out.push_str(&key);
68 out.push_str(" = ");68 out.push_str(" = ");
69 ToStringFormat69 ToStringFormat
70 .manifest_buf(ele, out)70 .manifest_buf(&ele, out)
71 .with_description(manifest_desc)?;71 .with_description(manifest_desc)?;
72 }72 }
73 } else {73 } else {
74 out.push_str(&key);74 out.push_str(&key);
75 out.push_str(" = ");75 out.push_str(" = ");
76 ToStringFormat76 ToStringFormat
77 .manifest_buf(value, out)77 .manifest_buf(&value, out)
78 .with_description(manifest_desc)?;78 .with_description(manifest_desc)?;
79 }79 }
80 }80 }
modifiedcrates/jrsonnet-stdlib/src/manifest/python.rsdiffbeforeafterboth
18}18}
1919
20impl ManifestFormat for PythonFormat {20impl ManifestFormat for PythonFormat {
21 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {21 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()> {
22 match val {22 match val {
23 Val::Bool(true) => buf.push_str("True"),23 Val::Bool(true) => buf.push_str("True"),
24 Val::Bool(false) => buf.push_str("False"),24 Val::Bool(false) => buf.push_str("False"),
34 if i != 0 {34 if i != 0 {
35 buf.push_str(", ");35 buf.push_str(", ");
36 }36 }
37 self.manifest_buf(el, buf)?;37 self.manifest_buf(&el, buf)?;
38 }38 }
39 buf.push(']');39 buf.push(']');
40 }40 }
52 escape_string_json_buf(&field, buf);52 escape_string_json_buf(&field, buf);
53 buf.push_str(": ");53 buf.push_str(": ");
54 let value = obj.get(field)?.expect("field exists");54 let value = obj.get(field)?.expect("field exists");
55 self.manifest_buf(value, buf)?;55 self.manifest_buf(&value, buf)?;
56 }56 }
57 buf.push('}');57 buf.push('}');
58 }58 }
77}77}
7878
79impl ManifestFormat for PythonVarsFormat {79impl ManifestFormat for PythonVarsFormat {
80 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {80 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()> {
81 let inner = PythonFormat {81 let inner = PythonFormat {
82 #[cfg(feature = "exp-preserve-order")]82 #[cfg(feature = "exp-preserve-order")]
83 preserve_order: self.preserve_order,83 preserve_order: self.preserve_order,
96 // Yep, no escaping96 // Yep, no escaping
97 buf.push_str(&field);97 buf.push_str(&field);
98 buf.push_str(" = ");98 buf.push_str(" = ");
99 inner.manifest_buf(obj.get(field)?.expect("field exists"), buf)?;99 inner.manifest_buf(&obj.get(field)?.expect("field exists"), buf)?;
100 buf.push('\n');100 buf.push('\n');
101 }101 }
102 Ok(())102 Ok(())
modifiedcrates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth
312}312}
313313
314impl ManifestFormat for TomlFormat<'_> {314impl ManifestFormat for TomlFormat<'_> {
315 fn manifest_buf(&self, val: Val, buf: &mut String) -> jrsonnet_evaluator::Result<()> {315 fn manifest_buf(&self, val: &Val, buf: &mut String) -> jrsonnet_evaluator::Result<()> {
316 match val {316 match val {
317 Val::Obj(obj) => {317 Val::Obj(obj) => {
318 manifest_table_internal(&obj, &mut Vec::new(), buf, &mut String::new(), self)318 manifest_table_internal(obj, &mut Vec::new(), buf, &mut String::new(), self)
319 }319 }
320 _ => bail!("toml body should be object"),320 _ => bail!("toml body should be object"),
321 }321 }
modifiedcrates/jrsonnet-stdlib/src/manifest/xml.rsdiffbeforeafterboth
75}75}
7676
77impl ManifestFormat for XmlJsonmlFormat {77impl ManifestFormat for XmlJsonmlFormat {
78 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {78 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()> {
79 let val = JSONMLValue::from_untyped(val).with_description(|| "parsing JSONML value")?;79 let val =
80 JSONMLValue::from_untyped(val.clone()).with_description(|| "parsing JSONML value")?;
80 manifest_jsonml(&val, buf, self)81 manifest_jsonml(&val, buf, self)
81 }82 }
82}83}
105 let value = if let Val::Str(s) = value {106 let value = if let Val::Str(s) = value {
106 s.to_string()107 s.to_string()
107 } else {108 } else {
108 ToStringFormat.manifest(value)?109 ToStringFormat.manifest(&value)?
109 };110 };
110 escape_string_xml_buf(&value, buf);111 escape_string_xml_buf(&value, buf);
111 buf.push('"');112 buf.push('"');
modifiedcrates/jrsonnet-stdlib/src/manifest/yaml.rsdiffbeforeafterboth
64 }64 }
65}65}
66impl ManifestFormat for YamlFormat<'_> {66impl ManifestFormat for YamlFormat<'_> {
67 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {67 fn manifest_buf(&self, val: &Val, buf: &mut String) -> Result<()> {
68 manifest_yaml_ex_buf(&val, buf, &mut String::new(), self)68 manifest_yaml_ex_buf(val, buf, &mut String::new(), self)
69 }69 }
70}70}
7171
modifiednix/benchmarks.nixdiffbeforeafterboth
130 cat result.adoc >> $out130 cat result.adoc >> $out
131 '';131 '';
132 in132 in
133 ''133 ''
134 set -oux134 set -oux
135 ulimit -s unlimited135 # ulimit -s unlimited
136136
137 temp=$(mktemp -d)137 temp=$(mktemp -d)
138 cd $temp138 cd $temp
139139
140 touch $out140 touch $out
141 ${optionalString (true) ''141 ${optionalString (true) ''
142 cat ${./benchmarks.adoc} >> $out142 cat ${./benchmarks.adoc} >> $out
143 echo >> $out143 echo >> $out
144144
145 echo "CPU: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs), $(grep -c '^processor' /proc/cpuinfo) threads" >> $out145 echo "CPU: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs), $(grep -c '^processor' /proc/cpuinfo) threads" >> $out
146 echo >> $out146 echo >> $out
147147
148 echo ".Tested versions" >> $out148 echo ".Tested versions" >> $out
149 echo "[%collapsible]" >> $out149 echo "[%collapsible]" >> $out
150 echo "====" >> $out150 echo "====" >> $out
151 echo "* Go: $(go-jsonnet --version)" >> $out151 echo "* Go: $(go-jsonnet --version)" >> $out
152 echo "* C++: $(jsonnet --version)" >> $out152 echo "* C++: $(jsonnet --version)" >> $out
153 echo "* Scala (native/GraalVM): $(sjsonnet-native 2>&1 | grep -oP 'Sjsonnet \S+')" >> $out153 echo "* Scala (native/GraalVM): $(sjsonnet-native 2>&1 | grep -oP 'Sjsonnet \S+')" >> $out
154 echo "* Rust (alternative): rsjsonnet ${rsjsonnet.version} (${rsjsonnet.src.rev})" >> $out154 echo "* Rust (alternative): rsjsonnet ${rsjsonnet.version} (${rsjsonnet.src.rev})" >> $out
155 ${concatStringsSep "\n" (155 ${concatStringsSep "\n" (
156 forEach jrsonnetVariants (156 forEach jrsonnetVariants (
157 variant:157 variant:
158 "echo \"* Rust${158 "echo \"* Rust${
159 if variant.name != "" then " (${variant.name})" else ""159 if variant.name != "" then " (${variant.name})" else ""
160 }: $(${variant.drv}/bin/jrsonnet --version 2>&1)\" >> $out"160 }: $(${variant.drv}/bin/jrsonnet --version 2>&1)\" >> $out"
161 )161 )
162 )}162 )}
163 echo "====" >> $out163 echo "====" >> $out
164 echo >> $out164 echo >> $out
165 ''}165 ''}
166 echo "== Real world" >> $out166 echo "== Real world" >> $out
167 ${mkBench {167 ${mkBench {
168 name = "GitLab runbooks dashboards";168 name = "GitLab runbooks dashboards";
169 path = "${realworldBench}/entry-gitlab-runbooks.jsonnet";169 path = "${realworldBench}/entry-gitlab-runbooks.jsonnet";
170 jpaths = [170 jpaths = [
171 "${realworldBench}/vendor"171 "${realworldBench}/vendor"
172 "${realworldBench}/vendor/runbooks/libsonnet"172 "${realworldBench}/vendor/runbooks/libsonnet"
173 "${realworldBench}/vendor/runbooks/dashboards"173 "${realworldBench}/vendor/runbooks/dashboards"
174 "${realworldBench}/vendor/runbooks/services"174 "${realworldBench}/vendor/runbooks/services"
175 "${realworldBench}/vendor/runbooks/metrics-catalog"175 "${realworldBench}/vendor/runbooks/metrics-catalog"
176 ];176 ];
177 skipCpp = "too slow, takes hours, skews results";177 skipCpp = "too slow, takes hours, skews results";
178 skipGo = skipSlow;178 skipGo = skipSlow;
179 }}179 }}
180 ${mkBench {180 ${mkBench {
181 name = "GraalVM CI";181 name = "GraalVM CI";
182 path = "${realworldBench}/entry-graalvm.jsonnet";182 path = "${realworldBench}/entry-graalvm.jsonnet";
183 jpaths = [183 jpaths = [
184 "${realworldBench}/vendor/graal"184 "${realworldBench}/vendor/graal"
185 ];185 ];
186 skipCpp = "too slow, takes hours, skews results";186 skipCpp = "too slow, takes hours, skews results";
187 skipGo = skipSlow;187 skipGo = skipSlow;
188 }}188 }}
189 ${mkBench {189 ${mkBench {
190 name = "Kube-prometheus";190 name = "Kube-prometheus";
191 path = "${realworldBench}/entry-kube-prometheus.jsonnet";191 path = "${realworldBench}/entry-kube-prometheus.jsonnet";
192 jpaths = [192 jpaths = [
193 "${realworldBench}/vendor"193 "${realworldBench}/vendor"
194 ];194 ];
195 skipCpp = "too slow, takes hours, skews results";195 skipCpp = "too slow, takes hours, skews results";
196 skipGo = skipSlow;196 skipGo = skipSlow;
197 }}197 }}
198 ${mkBench {198 ${mkBench {
199 name = "Loki manifests";199 name = "Loki manifests";
200 path = "${realworldBench}/entry-loki.jsonnet";200 path = "${realworldBench}/entry-loki.jsonnet";
201 jpaths = [201 jpaths = [
202 "${realworldBench}/vendor"202 "${realworldBench}/vendor"
203 "${realworldBench}"203 "${realworldBench}"
204 ];204 ];
205 skipCpp = "too slow, takes hours, skews results";205 skipCpp = "too slow, takes hours, skews results";
206 skipGo = skipSlow;206 skipGo = skipSlow;
207 }}207 }}
208 ${mkBench {208 ${mkBench {
209 name = "Mimir manifests";209 name = "Mimir manifests";
210 path = "${realworldBench}/entry-mimir.jsonnet";210 path = "${realworldBench}/entry-mimir.jsonnet";
211 jpaths = [211 jpaths = [
212 "${realworldBench}/vendor"212 "${realworldBench}/vendor"
213 "${realworldBench}"213 "${realworldBench}"
214 ];214 ];
215 skipCpp = "too slow, takes hours, skews results";215 skipCpp = "too slow, takes hours, skews results";
216 skipGo = skipSlow;216 skipGo = skipSlow;
217 skipScala = "https://github.com/databricks/sjsonnet/issues/829";217 skipScala = "https://github.com/databricks/sjsonnet/issues/829";
218 }}218 }}
219 ${mkBench {219 ${mkBench {
220 name = "Tempo manifests";220 name = "Tempo manifests";
221 path = "${realworldBench}/entry-tempo.jsonnet";221 path = "${realworldBench}/entry-tempo.jsonnet";
222 jpaths = [222 jpaths = [
223 "${realworldBench}/vendor"223 "${realworldBench}/vendor"
224 "${realworldBench}"224 "${realworldBench}"
225 ];225 ];
226 skipCpp = "too slow, takes hours, skews results";226 skipCpp = "too slow, takes hours, skews results";
227 skipGo = skipSlow;227 skipGo = skipSlow;
228 }}228 }}
229229
230 echo >> $out230 echo >> $out
231 echo "== Benchmarks from C++ jsonnet (/perf_tests)" >> $out231 echo "== Benchmarks from C++ jsonnet (/perf_tests)" >> $out
232 ${mkBench {232 ${mkBench {
233 name = "Large string join";233 name = "Large string join";
234 path = "${jsonnetBench}/perf_tests/large_string_join.jsonnet";234 path = "${jsonnetBench}/perf_tests/large_string_join.jsonnet";
235 }}235 }}
236 ${mkBench {236 ${mkBench {
237 name = "Large string template";237 name = "Large string template";
238 omitSource = true;238 omitSource = true;
239 path = "${jsonnetBench}/perf_tests/large_string_template.jsonnet";239 path = "${jsonnetBench}/perf_tests/large_string_template.jsonnet";
240 skipGo = "fails with os stack size exhausion";240 skipGo = "fails with os stack size exhausion";
241 skipCpp = "too slow, takes hours, skews results";241 skipCpp = "too slow, takes hours, skews results";
242 }}242 }}
243 ${mkBench {243 ${mkBench {
244 name = "Realistic 1";244 name = "Realistic 1";
245 path = "${jsonnetBench}/perf_tests/realistic1.jsonnet";245 path = "${jsonnetBench}/perf_tests/realistic1.jsonnet";
246 skipGo = skipSlow;246 skipGo = skipSlow;
247 skipCpp = "too slow, takes hours, skews results";247 skipCpp = "too slow, takes hours, skews results";
248 }}248 }}
249 ${mkBench {249 ${mkBench {
250 name = "Realistic 2";250 name = "Realistic 2";
251 path = "${jsonnetBench}/perf_tests/realistic2.jsonnet";251 path = "${jsonnetBench}/perf_tests/realistic2.jsonnet";
252 skipGo = skipSlow;252 skipGo = skipSlow;
253 skipCpp = "too slow, takes hours, skews results";253 skipCpp = "too slow, takes hours, skews results";
254 }}254 }}
255255
256 echo >> $out256 echo >> $out
257 echo "== Benchmarks from C++ jsonnet (/benchmarks)" >> $out257 echo "== Benchmarks from C++ jsonnet (/benchmarks)" >> $out
258 ${mkBench {258 ${mkBench {
259 name = "Tail call";259 name = "Tail call";
260 path = "${jsonnetBench}/benchmarks/bench.01.jsonnet";260 path = "${jsonnetBench}/benchmarks/bench.01.jsonnet";
261 }}261 }}
262 ${mkBench {262 ${mkBench {
263 name = "Inheritance recursion";263 name = "Inheritance recursion";
264 path = "${jsonnetBench}/benchmarks/bench.02.jsonnet";264 path = "${jsonnetBench}/benchmarks/bench.02.jsonnet";
265 skipCpp = skipSlow;265 skipCpp = skipSlow;
266 skipGo = skipSlow;266 skipGo = skipSlow;
267 }}267 }}
268 ${mkBench {268 ${mkBench {
269 name = "Simple recursive call";269 name = "Simple recursive call";
270 path = "${jsonnetBench}/benchmarks/bench.03.jsonnet";270 path = "${jsonnetBench}/benchmarks/bench.03.jsonnet";
271 skipGo = skipSlow;271 skipGo = skipSlow;
272 }}272 }}
273 ${mkBench {273 ${mkBench {
274 name = "Foldl string concat";274 name = "Foldl string concat";
275 path = "${jsonnetBench}/benchmarks/bench.04.jsonnet";275 path = "${jsonnetBench}/benchmarks/bench.04.jsonnet";
276 skipCpp = skipSlow;276 skipCpp = skipSlow;
277 }}277 }}
278 ${mkBench {278 ${mkBench {
279 name = "Array sorts";279 name = "Array sorts";
280 path = "${jsonnetBench}/benchmarks/bench.06.jsonnet";280 path = "${jsonnetBench}/benchmarks/bench.06.jsonnet";
281 skipCpp = skipSlow;281 skipCpp = skipSlow;
282 }}282 }}
283 ${mkBench {283 ${mkBench {
284 name = "Lazy array";284 name = "Lazy array";
285 path = "${jsonnetBench}/benchmarks/bench.07.jsonnet";285 path = "${jsonnetBench}/benchmarks/bench.07.jsonnet";
286 skipGo = skipSlow;286 skipGo = skipSlow;
287 }}287 }}
288 ${mkBench {288 ${mkBench {
289 name = "Inheritance function recursion";289 name = "Inheritance function recursion";
290 path = "${jsonnetBench}/benchmarks/bench.08.jsonnet";290 path = "${jsonnetBench}/benchmarks/bench.08.jsonnet";
291 skipCpp = skipSlow;291 skipCpp = skipSlow;
292 }}292 }}
293 ${mkBench {293 ${mkBench {
294 name = "String strips";294 name = "String strips";
295 path = "${jsonnetBench}/benchmarks/bench.09.jsonnet";295 path = "${jsonnetBench}/benchmarks/bench.09.jsonnet";
296 skipCpp = "too slow, takes hours, skews results";296 skipCpp = "too slow, takes hours, skews results";
297 }}297 }}
298 ${mkBench {298 ${mkBench {
299 name = "Big object";299 name = "Big object";
300 path = "${jsonnetBench}/benchmarks/gen_big_object.jsonnet";300 path = "${jsonnetBench}/benchmarks/gen_big_object.jsonnet";
301 pathIsGenerator = true;301 pathIsGenerator = true;
302 }}302 }}
303303
304 echo >> $out304 echo >> $out
305 echo "== Benchmarks from Go jsonnet (builtins)" >> $out305 echo "== Benchmarks from Go jsonnet (builtins)" >> $out
306 ${mkBench {306 ${mkBench {
307 name = "std.base64";307 name = "std.base64";
308 path = "${goJsonnetBench}/base64.jsonnet";308 path = "${goJsonnetBench}/base64.jsonnet";
309 skipRustAlternative = skipSlow;309 skipRustAlternative = skipSlow;
310 skipCpp = "too slow, takes hours, skews results";310 skipCpp = "too slow, takes hours, skews results";
311 }}311 }}
312 ${mkBench {312 ${mkBench {
313 name = "std.base64Decode";313 name = "std.base64Decode";
314 path = "${goJsonnetBench}/base64Decode.jsonnet";314 path = "${goJsonnetBench}/base64Decode.jsonnet";
315 skipRustAlternative = skipSlow;315 skipRustAlternative = skipSlow;
316 skipCpp = skipSlow;316 skipCpp = skipSlow;
317 }}317 }}
318 ${mkBench {318 ${mkBench {
319 name = "std.base64DecodeBytes";319 name = "std.base64DecodeBytes";
320 path = "${goJsonnetBench}/base64DecodeBytes.jsonnet";320 path = "${goJsonnetBench}/base64DecodeBytes.jsonnet";
321 skipRustAlternative = skipSlow;321 skipRustAlternative = skipSlow;
322 skipCpp = skipSlow;322 skipCpp = skipSlow;
323 skipGo = skipSlow;323 skipGo = skipSlow;
324 }}324 }}
325 ${mkBench {325 ${mkBench {
326 name = "std.base64 (byte array)";326 name = "std.base64 (byte array)";
327 path = "${goJsonnetBench}/base64_byte_array.jsonnet";327 path = "${goJsonnetBench}/base64_byte_array.jsonnet";
328 skipRustAlternative = skipSlow;328 skipRustAlternative = skipSlow;
329 skipCpp = skipSlow;329 skipCpp = skipSlow;
330 skipGo = skipSlow;330 skipGo = skipSlow;
331 }}331 }}
332 ${mkBench {332 ${mkBench {
333 name = "std.foldl";333 name = "std.foldl";
334 path = "${goJsonnetBench}/foldl.jsonnet";334 path = "${goJsonnetBench}/foldl.jsonnet";
335 }}335 }}
336 ${mkBench {336 ${mkBench {
337 name = "std.manifestJsonEx";337 name = "std.manifestJsonEx";
338 path = "${goJsonnetBench}/manifestJsonEx.jsonnet";338 path = "${goJsonnetBench}/manifestJsonEx.jsonnet";
339 skipCpp = skipSlow;339 skipCpp = skipSlow;
340 }}340 }}
341 ${mkBench {341 ${mkBench {
342 name = "std.manifestTomlEx";342 name = "std.manifestTomlEx";
343 path = "${goJsonnetBench}/manifestTomlEx.jsonnet";343 path = "${goJsonnetBench}/manifestTomlEx.jsonnet";
344 skipCpp = skipSlow;344 skipCpp = skipSlow;
345 }}345 }}
346 ${mkBench {346 ${mkBench {
347 name = "std.parseInt";347 name = "std.parseInt";
348 path = "${goJsonnetBench}/parseInt.jsonnet";348 path = "${goJsonnetBench}/parseInt.jsonnet";
349 skipCpp = skipSlow;349 skipCpp = skipSlow;
350 }}350 }}
351 ${mkBench {351 ${mkBench {
352 name = "std.reverse";352 name = "std.reverse";
353 path = "${goJsonnetBench}/reverse.jsonnet";353 path = "${goJsonnetBench}/reverse.jsonnet";
354 skipCpp = skipSlow;354 skipCpp = skipSlow;
355 skipGo = skipSlow;355 skipGo = skipSlow;
356 }}356 }}
357 ${mkBench {357 ${mkBench {
358 name = "std.substr";358 name = "std.substr";
359 path = "${goJsonnetBench}/substr.jsonnet";359 path = "${goJsonnetBench}/substr.jsonnet";
360 }}360 }}
361 ${mkBench {361 ${mkBench {
362 name = "Comparsion for array";362 name = "Comparsion for array";
363 path = "${goJsonnetBench}/comparison.jsonnet";363 path = "${goJsonnetBench}/comparison.jsonnet";
364 skipCpp = "too slow, takes hours, skews results";364 skipCpp = "too slow, takes hours, skews results";
365 }}365 }}
366 ${mkBench {366 ${mkBench {
367 name = "Comparsion for primitives";367 name = "Comparsion for primitives";
368 path = "${goJsonnetBench}/comparison2.jsonnet";368 path = "${goJsonnetBench}/comparison2.jsonnet";
369 skipRustAlternative = skipSlow;369 skipRustAlternative = skipSlow;
370 skipCpp = "can't run: uses up to 192GB of RAM";370 skipCpp = "can't run: uses up to 192GB of RAM";
371 skipGo = skipSlow;371 skipGo = skipSlow;
372 }}372 }}
373 '';373 '';
374}374}
375375