difftreelog
refactor remove manual ThunkValue implementations
in: master
3 files changed
crates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -3,23 +3,11 @@
use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, LocExpr};
-use crate::{evaluate, gc::GcHashMap, typed::Typed, val::ThunkValue, Context, Result, Thunk, Val};
+use crate::{evaluate, gc::GcHashMap, typed::Typed, Context, Result, Thunk, Val};
/// Marker for arguments, which can be evaluated with context set to None
pub trait OptionalContext {}
-#[derive(Trace)]
-struct EvaluateThunk {
- ctx: Context,
- expr: LocExpr,
-}
-impl ThunkValue for EvaluateThunk {
- type Output = Val;
- fn get(self: Box<Self>) -> Result<Val> {
- evaluate(self.ctx, &self.expr)
- }
-}
-
pub trait ArgLike {
fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;
}
@@ -29,10 +17,8 @@
Ok(if tailstrict {
Thunk::evaluated(evaluate(ctx, self)?)
} else {
- Thunk::new(EvaluateThunk {
- ctx,
- expr: (*self).clone(),
- })
+ let expr = (*self).clone();
+ Thunk!(move || evaluate(ctx, &expr))
})
}
}
@@ -65,10 +51,8 @@
Self::Code(code) => Ok(if tailstrict {
Thunk::evaluated(evaluate(ctx, code)?)
} else {
- Thunk::new(EvaluateThunk {
- ctx,
- expr: code.clone(),
- })
+ let code = code.clone();
+ Thunk!(move || evaluate(ctx, &code))
}),
Self::Val(val) => Ok(Thunk::evaluated(val.clone())),
Self::Lazy(lazy) => Ok(lazy.clone()),
@@ -140,10 +124,10 @@
if tailstrict {
Thunk::evaluated(evaluate(ctx.clone(), arg)?)
} else {
- Thunk::new(EvaluateThunk {
- ctx: ctx.clone(),
- expr: arg.clone(),
- })
+ let ctx = ctx.clone();
+ let arg = arg.clone();
+
+ Thunk!(move || evaluate(ctx, &arg))
},
)?;
}
@@ -162,10 +146,10 @@
if tailstrict {
Thunk::evaluated(evaluate(ctx.clone(), arg)?)
} else {
- Thunk::new(EvaluateThunk {
- ctx: ctx.clone(),
- expr: arg.clone(),
- })
+ let ctx = ctx.clone();
+ let arg = arg.clone();
+
+ Thunk!(move || evaluate(ctx, &arg))
},
)?;
}
crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth1use std::mem::replace;23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{LocExpr, ParamsDesc};67use super::{arglike::ArgsLike, builtin::BuiltinParam};8use crate::{9 bail,10 destructure::destruct,11 error::{ErrorKind::*, Result},12 evaluate_named,13 function::builtin::ParamDefault,14 gc::GcHashMap,15 val::ThunkValue,16 Context, Pending, Thunk, Val,17};1819#[derive(Trace)]20struct EvaluateNamedThunk {21 ctx: Pending<Context>,22 name: IStr,23 value: LocExpr,24}2526impl ThunkValue for EvaluateNamedThunk {27 type Output = Val;28 fn get(self: Box<Self>) -> Result<Val> {29 evaluate_named(self.ctx.unwrap(), &self.value, self.name)30 }31}3233/// Creates correct [context](Context) for function body evaluation returning error on invalid call.34///35/// ## Parameters36/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)37/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)38/// * `params`: function parameters' definition39/// * `args`: passed function arguments40/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily41pub fn parse_function_call(42 ctx: Context,43 body_ctx: Context,44 params: &ParamsDesc,45 args: &dyn ArgsLike,46 tailstrict: bool,47) -> Result<Context> {48 let mut passed_args =49 GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());50 if args.unnamed_len() > params.len() {51 bail!(TooManyArgsFunctionHas(52 params.len(),53 params54 .iter()55 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))56 .collect()57 ))58 }5960 let mut filled_named = 0;61 let mut filled_positionals = 0;6263 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {64 let name = params[id].0.clone();65 destruct(66 &name,67 arg,68 Pending::new_filled(ctx.clone()),69 &mut passed_args,70 )?;71 filled_positionals += 1;72 Ok(())73 })?;7475 args.named_iter(ctx, tailstrict, &mut |name, value| {76 // FIXME: O(n) for arg existence check77 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {78 bail!(UnknownFunctionParameter((name as &str).to_owned()));79 }80 if passed_args.insert(name.clone(), value).is_some() {81 bail!(BindingParameterASecondTime(name.clone()));82 }83 filled_named += 1;84 Ok(())85 })?;8687 if filled_named + filled_positionals < params.len() {88 // Some args are unset, but maybe we have defaults for them89 // Default values should be created in newly created context90 let fctx = Context::new_future();91 let mut defaults = GcHashMap::with_capacity(92 params.iter().map(|p| p.0.capacity_hint()).sum::<usize>()93 - filled_named94 - filled_positionals,95 );9697 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {98 if let Some(name) = param.0.name() {99 if passed_args.contains_key(&name) {100 continue;101 }102 } else if idx < filled_positionals {103 continue;104 }105106 destruct(107 ¶m.0,108 Thunk::new(EvaluateNamedThunk {109 ctx: fctx.clone(),110 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),111 value: param.1.clone().expect("default exists"),112 }),113 fctx.clone(),114 &mut defaults,115 )?;116 if param.0.name().is_some() {117 filled_named += 1;118 } else {119 filled_positionals += 1;120 }121 }122123 // Some args still weren't filled124 if filled_named + filled_positionals != params.len() {125 for param in params.iter().skip(args.unnamed_len()) {126 let mut found = false;127 args.named_names(&mut |name| {128 if Some(name) == param.0.name().as_ref() {129 found = true;130 }131 });132 if !found {133 bail!(FunctionParameterNotBoundInCall(134 param.0.clone().name(),135 params136 .iter()137 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))138 .collect()139 ));140 }141 }142 unreachable!();143 }144145 Ok(body_ctx146 .extend(passed_args, None, None, None)147 .extend(defaults, None, None, None)148 .into_future(fctx))149 } else {150 let body_ctx = body_ctx.extend(passed_args, None, None, None);151 Ok(body_ctx)152 }153}154155/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead156///157/// ## Parameters158/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)159/// * `params`: function parameters' definition160/// * `args`: passed function arguments161/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily162pub fn parse_builtin_call(163 ctx: Context,164 params: &[BuiltinParam],165 args: &dyn ArgsLike,166 tailstrict: bool,167) -> Result<Vec<Option<Thunk<Val>>>> {168 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];169 if args.unnamed_len() > params.len() {170 bail!(TooManyArgsFunctionHas(171 params.len(),172 params173 .iter()174 .map(|p| (p.name().as_str().map(IStr::from), p.default()))175 .collect()176 ))177 }178179 let mut filled_args = 0;180181 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {182 passed_args[id] = Some(arg);183 filled_args += 1;184 Ok(())185 })?;186187 args.named_iter(ctx, tailstrict, &mut |name, arg| {188 // FIXME: O(n) for arg existence check189 let id = params190 .iter()191 .position(|p| p.name() == name)192 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;193 if replace(&mut passed_args[id], Some(arg)).is_some() {194 bail!(BindingParameterASecondTime(name.clone()));195 }196 filled_args += 1;197 Ok(())198 })?;199200 if filled_args < params.len() {201 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {202 if passed_args[id].is_some() {203 continue;204 }205 filled_args += 1;206 }207208 // Some args still wasn't filled209 if filled_args != params.len() {210 for param in params.iter().skip(args.unnamed_len()) {211 let mut found = false;212 args.named_names(&mut |name| {213 if param.name() == name {214 found = true;215 }216 });217 if !found {218 bail!(FunctionParameterNotBoundInCall(219 param.name().as_str().map(IStr::from),220 params221 .iter()222 .map(|p| (p.name().as_str().map(IStr::from), p.default()))223 .collect()224 ));225 }226 }227 unreachable!();228 }229 }230 Ok(passed_args)231}232233/// Creates Context, which has all argument default values applied234/// and with unbound values causing error to be returned235pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {236 let fctx = Context::new_future();237238 let mut bindings = GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());239240 for param in params.iter() {241 if let Some(v) = ¶m.1 {242 destruct(243 ¶m.0.clone(),244 Thunk::new(EvaluateNamedThunk {245 ctx: fctx.clone(),246 name: param.0.name().unwrap_or_else(|| "<destruct>".into()),247 value: v.clone(),248 }),249 fctx.clone(),250 &mut bindings,251 )?;252 } else {253 destruct(254 ¶m.0,255 {256 let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());257 let params = params.clone();258 Thunk!(move || Err(FunctionParameterNotBoundInCall(259 Some(param_name),260 params261 .iter()262 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))263 .collect(),264 )265 .into()))266 },267 fctx.clone(),268 &mut bindings,269 )?;270 }271 }272273 Ok(body_ctx274 .extend(bindings, None, None, None)275 .into_future(fctx))276}1use std::mem::replace;23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{LocExpr, ParamsDesc};67use super::{arglike::ArgsLike, builtin::BuiltinParam};8use crate::{9 bail,10 destructure::destruct,11 error::{ErrorKind::*, Result},12 evaluate_named,13 function::builtin::ParamDefault,14 gc::GcHashMap,15 Context, Pending, Thunk, Val,16};1718/// Creates correct [context](Context) for function body evaluation returning error on invalid call.19///20/// ## Parameters21/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)22/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)23/// * `params`: function parameters' definition24/// * `args`: passed function arguments25/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily26pub fn parse_function_call(27 ctx: Context,28 body_ctx: Context,29 params: &ParamsDesc,30 args: &dyn ArgsLike,31 tailstrict: bool,32) -> Result<Context> {33 let mut passed_args =34 GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());35 if args.unnamed_len() > params.len() {36 bail!(TooManyArgsFunctionHas(37 params.len(),38 params39 .iter()40 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))41 .collect()42 ))43 }4445 let mut filled_named = 0;46 let mut filled_positionals = 0;4748 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {49 let name = params[id].0.clone();50 destruct(51 &name,52 arg,53 Pending::new_filled(ctx.clone()),54 &mut passed_args,55 )?;56 filled_positionals += 1;57 Ok(())58 })?;5960 args.named_iter(ctx, tailstrict, &mut |name, value| {61 // FIXME: O(n) for arg existence check62 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {63 bail!(UnknownFunctionParameter((name as &str).to_owned()));64 }65 if passed_args.insert(name.clone(), value).is_some() {66 bail!(BindingParameterASecondTime(name.clone()));67 }68 filled_named += 1;69 Ok(())70 })?;7172 if filled_named + filled_positionals < params.len() {73 // Some args are unset, but maybe we have defaults for them74 // Default values should be created in newly created context75 let fctx = Context::new_future();76 let mut defaults = GcHashMap::with_capacity(77 params.iter().map(|p| p.0.capacity_hint()).sum::<usize>()78 - filled_named79 - filled_positionals,80 );8182 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {83 if let Some(name) = param.0.name() {84 if passed_args.contains_key(&name) {85 continue;86 }87 } else if idx < filled_positionals {88 continue;89 }9091 destruct(92 ¶m.0,93 {94 let ctx = fctx.clone();95 let name = param.0.name().unwrap_or_else(|| "<destruct>".into());96 let value = param.1.clone().expect("default exists");97 Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))98 },99 fctx.clone(),100 &mut defaults,101 )?;102 if param.0.name().is_some() {103 filled_named += 1;104 } else {105 filled_positionals += 1;106 }107 }108109 // Some args still weren't filled110 if filled_named + filled_positionals != params.len() {111 for param in params.iter().skip(args.unnamed_len()) {112 let mut found = false;113 args.named_names(&mut |name| {114 if Some(name) == param.0.name().as_ref() {115 found = true;116 }117 });118 if !found {119 bail!(FunctionParameterNotBoundInCall(120 param.0.clone().name(),121 params122 .iter()123 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))124 .collect()125 ));126 }127 }128 unreachable!();129 }130131 Ok(body_ctx132 .extend(passed_args, None, None, None)133 .extend(defaults, None, None, None)134 .into_future(fctx))135 } else {136 let body_ctx = body_ctx.extend(passed_args, None, None, None);137 Ok(body_ctx)138 }139}140141/// You shouldn't probally use this function, use `jrsonnet_macros::builtin` instead142///143/// ## Parameters144/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)145/// * `params`: function parameters' definition146/// * `args`: passed function arguments147/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily148pub fn parse_builtin_call(149 ctx: Context,150 params: &[BuiltinParam],151 args: &dyn ArgsLike,152 tailstrict: bool,153) -> Result<Vec<Option<Thunk<Val>>>> {154 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];155 if args.unnamed_len() > params.len() {156 bail!(TooManyArgsFunctionHas(157 params.len(),158 params159 .iter()160 .map(|p| (p.name().as_str().map(IStr::from), p.default()))161 .collect()162 ))163 }164165 let mut filled_args = 0;166167 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {168 passed_args[id] = Some(arg);169 filled_args += 1;170 Ok(())171 })?;172173 args.named_iter(ctx, tailstrict, &mut |name, arg| {174 // FIXME: O(n) for arg existence check175 let id = params176 .iter()177 .position(|p| p.name() == name)178 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;179 if replace(&mut passed_args[id], Some(arg)).is_some() {180 bail!(BindingParameterASecondTime(name.clone()));181 }182 filled_args += 1;183 Ok(())184 })?;185186 if filled_args < params.len() {187 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {188 if passed_args[id].is_some() {189 continue;190 }191 filled_args += 1;192 }193194 // Some args still wasn't filled195 if filled_args != params.len() {196 for param in params.iter().skip(args.unnamed_len()) {197 let mut found = false;198 args.named_names(&mut |name| {199 if param.name() == name {200 found = true;201 }202 });203 if !found {204 bail!(FunctionParameterNotBoundInCall(205 param.name().as_str().map(IStr::from),206 params207 .iter()208 .map(|p| (p.name().as_str().map(IStr::from), p.default()))209 .collect()210 ));211 }212 }213 unreachable!();214 }215 }216 Ok(passed_args)217}218219/// Creates Context, which has all argument default values applied220/// and with unbound values causing error to be returned221pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {222 let fctx = Context::new_future();223224 let mut bindings = GcHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());225226 for param in params.iter() {227 if let Some(v) = ¶m.1 {228 destruct(229 ¶m.0.clone(),230 {231 let ctx = fctx.clone();232 let name = param.0.name().unwrap_or_else(|| "<destruct>".into());233 let value = v.clone();234 Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))235 },236 fctx.clone(),237 &mut bindings,238 )?;239 } else {240 destruct(241 ¶m.0,242 {243 let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());244 let params = params.clone();245 Thunk!(move || Err(FunctionParameterNotBoundInCall(246 Some(param_name),247 params248 .iter()249 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))250 .collect(),251 )252 .into()))253 },254 fctx.clone(),255 &mut bindings,256 )?;257 }258 }259260 Ok(body_ctx261 .extend(bindings, None, None, None)262 .into_future(fctx))263}nix/benchmarks.nixdiffbeforeafterboth--- a/nix/benchmarks.nix
+++ b/nix/benchmarks.nix
@@ -52,6 +52,7 @@
mkdir -p $out
cp -r ${src}/* $out/
cd $out
+ chmod u+w jsonnetfile.lock.json
mkdir vendor
${jsonnet-bundler}/bin/jb install
'';
@@ -125,7 +126,7 @@
go-jsonnet $path > generated.jsonnet
path=generated.jsonnet
''}
- hyperfine -N -w4 -m20 --output=pipe --style=basic --export-markdown result.md \
+ hyperfine -N -w4 -m20 --output=pipe --style=basic --export-asciidoc result.adoc \
${concatStringsSep " " (forEach jrsonnetVariants (
variant: "\"${variant.drv}/bin/jrsonnet $path${optionalString (vendor != "") " -J${vendor}"}\" -n \"Rust${
if variant.name != ""
@@ -137,7 +138,7 @@
${optionalString (skipGo == "") "\"go-jsonnet $path${optionalString (vendor != "") " -J ${vendor}"}\" -n \"Go\""} \
${optionalString (skipScala == "") "\"sjsonnet $path${optionalString (vendor != "") " -J ${vendor}"}\" -n \"Scala\""} \
${optionalString (skipCpp == "") "\"jsonnet $path${optionalString (vendor != "") " -J ${vendor}"}\" -n \"C++\""}
- cat result.md >> $out
+ cat result.adoc >> $out
'';
in ''
set -oux