difftreelog
feat function folder abstraction
in: master
15 files changed
crates/jrsonnet-evaluator/src/analyze.rsdiffbeforeafterboth2075 pub errored: bool,2075 pub errored: bool,2076}2076}20772078/// Only useful for slot reference analysis2079pub 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}20902091pub 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::IdentityFunction2099 | 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_shape2106 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}21752176fn 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}207721832078#[cfg(test)]2184#[cfg(test)]2079mod tests {2185mod tests {crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth3use 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;889use 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}215216#[derive(Clone, Copy, Debug)]217pub enum FoldKind {218 /// `function(acc, x) acc + <expr>`219 Left,220 /// `function(x, acc) <expr> + acc`221 Right,222}223224/// A folding lambda specialised to `acc + <expr>` (or `<expr> + acc`)225///226/// `<expr>` does not reference `acc`. Lets the caller skip building an227/// intermediate accumulator value per step and instead append each228/// 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}234235impl 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}244245struct ReferencesLocal {246 slot: LocalSlot,247 found: bool,248}249250impl LVisitor for ReferencesLocal {251 fn visit_lslot(&mut self, slot: LSlot) {252 if let LSlot::Local(s) = slot253 && s.0 == self.slot.0254 {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}265266fn references_local(expr: &LExpr, slot: LocalSlot) -> bool {267 let mut v = ReferencesLocal { slot, found: false };268 v.visit_lexpr(expr);269 v.found270}271272impl 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 }284285 let (acc_idx, value_idx) = match kind {286 FoldKind::Left => (0, 1),287 FoldKind::Right => (1, 0),288 };289290 let acc_slot = match &func.params[acc_idx].destruct {291 LDestruct::Full(s) => *s,292 #[cfg(feature = "exp-destruct")]293 _ => return None,294 };295296 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 }315316 Some(Folder {317 func: desc,318 value_destruct: &func.params[value_idx].destruct,319 expr: expr_side,320 })321 }322}215323crates/jrsonnet-evaluator/src/function/native.rsdiffbeforeafterboth13#[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>);1516impl<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,)>crates/jrsonnet-evaluator/src/function/prepared.rsdiffbeforeafterboth21 prepared: Rc::new(prepared),21 prepared: Rc::new(prepared),22 })22 })23 }23 }24 pub fn func(&self) -> &FuncVal {25 &self.fun26 }24 pub fn call(27 pub fn call(25 &self,28 &self,26 loc: CallLocation<'_>,29 loc: CallLocation<'_>,crates/jrsonnet-evaluator/src/manifest.rsdiffbeforeafterboth5};5};667pub 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)16where16where17 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 }25where25where26 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}363363364impl 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}369369370/// 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 {crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth85 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();crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth533 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 }crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth367 }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 }546550547 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 }crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth223use 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 }160149 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 }199171 let mut acc = init;200 let mut acc = init;172 match arr {201 match arr {crates/jrsonnet-stdlib/src/manifest/ini.rsdiffbeforeafterboth30}30}313132impl 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 ToStringFormat70 .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 ToStringFormat77 .manifest_buf(value, out)77 .manifest_buf(&value, out)78 .with_description(manifest_desc)?;78 .with_description(manifest_desc)?;79 }79 }80 }80 }crates/jrsonnet-stdlib/src/manifest/python.rsdiffbeforeafterboth18}18}191920impl 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}787879impl 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 escaping97 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(())crates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth312}312}313313314impl 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 }crates/jrsonnet-stdlib/src/manifest/xml.rsdiffbeforeafterboth75}75}767677impl 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('"');crates/jrsonnet-stdlib/src/manifest/yaml.rsdiffbeforeafterboth64 }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}7171nix/benchmarks.nixdiffbeforeafterboth130 cat result.adoc >> $out130 cat result.adoc >> $out131 '';131 '';132 in132 in133 ''133 ''134 set -oux134 set -oux135 ulimit -s unlimited135 # ulimit -s unlimited136136137 temp=$(mktemp -d)137 temp=$(mktemp -d)138 cd $temp138 cd $temp139139140 touch $out140 touch $out141 ${optionalString (true) ''141 ${optionalString (true) ''142 cat ${./benchmarks.adoc} >> $out142 cat ${./benchmarks.adoc} >> $out143 echo >> $out143 echo >> $out144144145 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" >> $out146 echo >> $out146 echo >> $out147147148 echo ".Tested versions" >> $out148 echo ".Tested versions" >> $out149 echo "[%collapsible]" >> $out149 echo "[%collapsible]" >> $out150 echo "====" >> $out150 echo "====" >> $out151 echo "* Go: $(go-jsonnet --version)" >> $out151 echo "* Go: $(go-jsonnet --version)" >> $out152 echo "* C++: $(jsonnet --version)" >> $out152 echo "* C++: $(jsonnet --version)" >> $out153 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+')" >> $out154 echo "* Rust (alternative): rsjsonnet ${rsjsonnet.version} (${rsjsonnet.src.rev})" >> $out154 echo "* Rust (alternative): rsjsonnet ${rsjsonnet.version} (${rsjsonnet.src.rev})" >> $out155 ${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 "====" >> $out164 echo >> $out164 echo >> $out165 ''}165 ''}166 echo "== Real world" >> $out166 echo "== Real world" >> $out167 ${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 }}229229230 echo >> $out230 echo >> $out231 echo "== Benchmarks from C++ jsonnet (/perf_tests)" >> $out231 echo "== Benchmarks from C++ jsonnet (/perf_tests)" >> $out232 ${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 }}255255256 echo >> $out256 echo >> $out257 echo "== Benchmarks from C++ jsonnet (/benchmarks)" >> $out257 echo "== Benchmarks from C++ jsonnet (/benchmarks)" >> $out258 ${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 }}303303304 echo >> $out304 echo >> $out305 echo "== Benchmarks from Go jsonnet (builtins)" >> $out305 echo "== Benchmarks from Go jsonnet (builtins)" >> $out306 ${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