From d76beb28c515063ecf8f3bed35499d7c983d8c81 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Fri, 3 Jun 2022 18:36:46 +0200 Subject: [PATCH] feat: object destructuring defaults Signed-off-by: Yaroslav Bolyukin --- crates/jrsonnet-evaluator/src/dynamic.rs | 3 ++ .../src/evaluate/destructure.rs | 38 ++++++++++++++----- .../jrsonnet-evaluator/src/function/parse.rs | 10 ++++- crates/jrsonnet-parser/src/expr.rs | 4 +- crates/jrsonnet-parser/src/lib.rs | 4 +- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/crates/jrsonnet-evaluator/src/dynamic.rs b/crates/jrsonnet-evaluator/src/dynamic.rs index c02edd37..a6fea3a9 100644 --- a/crates/jrsonnet-evaluator/src/dynamic.rs +++ b/crates/jrsonnet-evaluator/src/dynamic.rs @@ -8,6 +8,9 @@ impl Pending { pub fn new() -> Self { Self(Cc::new(RefCell::new(None))) } + pub fn new_filled(v: T) -> Self { + Self(Cc::new(RefCell::new(Some(v)))) + } /// # Panics /// If wrapper is filled already pub fn fill(self, value: T) { diff --git a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs index 55d36356..fddf13a0 100644 --- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs +++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs @@ -12,9 +12,11 @@ use crate::{ }; #[allow(clippy::too_many_lines)] +#[allow(unused_variables)] pub fn destruct( d: &Destruct, parent: Thunk, + fctx: Pending, new_bindings: &mut GcHashMap>, ) -> Result<()> { match d { @@ -89,6 +91,7 @@ pub fn destruct( full: full.clone(), index: i, })), + fctx.clone(), new_bindings, )?; } @@ -119,6 +122,7 @@ pub fn destruct( start: start.len(), end: end.len(), })), + fctx.clone(), new_bindings, )?; } @@ -151,6 +155,7 @@ pub fn destruct( index: i, end: end.len(), })), + fctx.clone(), new_bindings, )?; } @@ -189,36 +194,51 @@ pub fn destruct( Ok(obj) } } - let field_names: Vec<_> = fields.iter().map(|f| f.0.clone()).collect(); + let field_names: Vec<_> = fields + .iter() + .filter(|f| f.2.is_none()) + .map(|f| f.0.clone()) + .collect(); let full = Thunk::new(tb!(DataThunk { parent, field_names: field_names.clone(), has_rest: rest.is_some() })); - for (field, d) in fields { + for (field, d, default) in fields { #[derive(Trace)] struct FieldThunk { full: Thunk, field: IStr, + default: Option<(Pending, LocExpr)>, } impl ThunkValue for FieldThunk { type Output = Val; fn get(self: Box, s: State) -> Result { let full = self.full.evaluate(s.clone())?; - let field = full.get(s, self.field)?.expect("shape is checked"); - Ok(field) + if let Some(field) = full.get(s.clone(), self.field)? { + Ok(field) + } else { + let (fctx, expr) = self.default.as_ref().expect("shape is checked"); + Ok(evaluate(s, fctx.clone().unwrap(), &expr)?) + } } } let value = Thunk::new(tb!(FieldThunk { full: full.clone(), - field: field.clone() + field: field.clone(), + default: default.clone().map(|e| (fctx.clone(), e)), })); if let Some(d) = d { - destruct(d, value, new_bindings)?; + destruct(d, value, fctx.clone(), new_bindings)?; } else { - destruct(&Destruct::Full(field.clone()), value, new_bindings)?; + destruct( + &Destruct::Full(field.clone()), + value, + fctx.clone(), + new_bindings, + )?; } } } @@ -251,10 +271,10 @@ pub fn evaluate_dest( } let data = Thunk::new(tb!(EvaluateThunkValue { name: into.name(), - fctx, + fctx: fctx.clone(), expr: value.clone(), })); - destruct(into, data, new_bindings)?; + destruct(into, data, fctx, new_bindings)?; } BindSpec::Function { name, diff --git a/crates/jrsonnet-evaluator/src/function/parse.rs b/crates/jrsonnet-evaluator/src/function/parse.rs index ee769b44..cbddd3e1 100644 --- a/crates/jrsonnet-evaluator/src/function/parse.rs +++ b/crates/jrsonnet-evaluator/src/function/parse.rs @@ -56,7 +56,12 @@ pub fn parse_function_call( args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| { let name = params[id].0.clone(); - destruct(&name, arg, &mut passed_args)?; + destruct( + &name, + arg, + Pending::new_filled(ctx.clone()), + &mut passed_args, + )?; filled_positionals += 1; Ok(()) })?; @@ -96,6 +101,7 @@ pub fn parse_function_call( name: param.0.name().unwrap_or_else(|| "".into()), value: param.1.clone().expect("default exists"), })), + fctx.clone(), &mut defaults, )?; if param.0.name().is_some() { @@ -230,6 +236,7 @@ pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Re name: param.0.name().unwrap_or_else(|| "".into()), value: v.clone(), })), + fctx.clone(), &mut bindings, )?; } else { @@ -238,6 +245,7 @@ pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Re Thunk::new(tb!(DependsOnUnbound( param.0.name().unwrap_or_else(|| "".into()) ))), + fctx.clone(), &mut bindings, )?; } diff --git a/crates/jrsonnet-parser/src/expr.rs b/crates/jrsonnet-parser/src/expr.rs index a99b898e..a13a9847 100644 --- a/crates/jrsonnet-parser/src/expr.rs +++ b/crates/jrsonnet-parser/src/expr.rs @@ -188,7 +188,7 @@ pub enum DestructRest { } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq, Trace)] +#[derive(Debug, Clone, PartialEq, Trace)] pub enum Destruct { Full(IStr), #[cfg(feature = "exp-destruct")] @@ -201,7 +201,7 @@ pub enum Destruct { }, #[cfg(feature = "exp-destruct")] Object { - fields: Vec<(IStr, Option)>, + fields: Vec<(IStr, Option, Option)>, rest: Option, }, } diff --git a/crates/jrsonnet-parser/src/lib.rs b/crates/jrsonnet-parser/src/lib.rs index 5c2b105b..9f39f2dd 100644 --- a/crates/jrsonnet-parser/src/lib.rs +++ b/crates/jrsonnet-parser/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::redundant_closure_call)] +#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)] use std::rc::Rc; @@ -109,7 +109,7 @@ parser! { } pub rule destruct_object(s: &ParserSettings) -> expr::Destruct = "{" _ - fields:(name:id() _ into:(":" _ into:destruct(s) {into})? {(name, into)})**comma() + fields:(name:id() into:(_ ":" _ into:destruct(s) {into})? default:(_ "=" _ v:expr(s) {v})? {(name, into, default)})**comma() rest:( comma() rest:destruct_rest()? {rest} / comma()? {None}