difftreelog
feat object destructuring defaults
in: master
5 files changed
crates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/dynamic.rs
+++ b/crates/jrsonnet-evaluator/src/dynamic.rs
@@ -8,6 +8,9 @@
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) {
crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth1use gcmodule::Trace;2use jrsonnet_interner::IStr;3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};45use crate::{6 error::{Error::*, Result},7 evaluate, evaluate_method, evaluate_named,8 gc::GcHashMap,9 tb, throw,10 val::ThunkValue,11 Context, Pending, State, Thunk, Val,12};1314#[allow(clippy::too_many_lines)]15pub fn destruct(16 d: &Destruct,17 parent: Thunk<Val>,18 new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,19) -> Result<()> {20 match d {21 Destruct::Full(v) => {22 let old = new_bindings.insert(v.clone(), parent);23 if old.is_some() {24 throw!(DuplicateLocalVar(v.clone()))25 }26 }27 #[cfg(feature = "exp-destruct")]28 Destruct::Skip => {}29 #[cfg(feature = "exp-destruct")]30 Destruct::Array { start, rest, end } => {31 use jrsonnet_parser::DestructRest;3233 use crate::{throw_runtime, val::ArrValue};3435 #[derive(Trace)]36 struct DataThunk {37 parent: Thunk<Val>,38 min_len: usize,39 has_rest: bool,40 }41 impl ThunkValue for DataThunk {42 type Output = ArrValue;4344 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {45 let v = self.parent.evaluate(s)?;46 let arr = match v {47 Val::Arr(a) => a,48 _ => throw_runtime!("expected array"),49 };50 if !self.has_rest {51 if arr.len() != self.min_len {52 throw_runtime!("expected {} elements, got {}", self.min_len, arr.len())53 }54 } else if arr.len() < self.min_len {55 throw_runtime!(56 "expected at least {} elements, but array was only {}",57 self.min_len,58 arr.len()59 )60 }61 Ok(arr)62 }63 }6465 let full = Thunk::new(tb!(DataThunk {66 min_len: start.len() + end.len(),67 has_rest: rest.is_some(),68 parent,69 }));7071 {72 #[derive(Trace)]73 struct BaseThunk {74 full: Thunk<ArrValue>,75 index: usize,76 }77 impl ThunkValue for BaseThunk {78 type Output = Val;7980 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {81 let full = self.full.evaluate(s.clone())?;82 Ok(full.get(s, self.index)?.expect("length is checked"))83 }84 }85 for (i, d) in start.iter().enumerate() {86 destruct(87 d,88 Thunk::new(tb!(BaseThunk {89 full: full.clone(),90 index: i,91 })),92 new_bindings,93 )?;94 }95 }9697 match rest {98 Some(DestructRest::Keep(v)) => {99 #[derive(Trace)]100 struct RestThunk {101 full: Thunk<ArrValue>,102 start: usize,103 end: usize,104 }105 impl ThunkValue for RestThunk {106 type Output = Val;107108 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {109 let full = self.full.evaluate(s)?;110 let to = full.len() - self.end;111 Ok(Val::Arr(full.slice(Some(self.start), Some(to), None)))112 }113 }114115 destruct(116 &Destruct::Full(v.clone()),117 Thunk::new(tb!(RestThunk {118 full: full.clone(),119 start: start.len(),120 end: end.len(),121 })),122 new_bindings,123 )?;124 }125 Some(DestructRest::Drop) => {}126 None => {}127 }128129 {130 #[derive(Trace)]131 struct EndThunk {132 full: Thunk<ArrValue>,133 index: usize,134 end: usize,135 }136 impl ThunkValue for EndThunk {137 type Output = Val;138139 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {140 let full = self.full.evaluate(s.clone())?;141 Ok(full142 .get(s, full.len() - self.end + self.index)?143 .expect("length is checked"))144 }145 }146 for (i, d) in end.iter().enumerate() {147 destruct(148 d,149 Thunk::new(tb!(EndThunk {150 full: full.clone(),151 index: i,152 end: end.len(),153 })),154 new_bindings,155 )?;156 }157 }158 }159 #[cfg(feature = "exp-destruct")]160 Destruct::Object { fields, rest } => {161 use crate::{obj::ObjValue, throw_runtime};162163 #[derive(Trace)]164 struct DataThunk {165 parent: Thunk<Val>,166 field_names: Vec<IStr>,167 has_rest: bool,168 }169 impl ThunkValue for DataThunk {170 type Output = ObjValue;171172 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {173 let v = self.parent.evaluate(s)?;174 let obj = match v {175 Val::Obj(o) => o,176 _ => throw_runtime!("expected object"),177 };178 for field in &self.field_names {179 if !obj.has_field_ex(field.clone(), true) {180 throw_runtime!("missing field: {}", field);181 }182 }183 if !self.has_rest {184 let len = obj.len();185 if len != self.field_names.len() {186 throw_runtime!("too many fields, and rest not found");187 }188 }189 Ok(obj)190 }191 }192 let field_names: Vec<_> = fields.iter().map(|f| f.0.clone()).collect();193 let full = Thunk::new(tb!(DataThunk {194 parent,195 field_names: field_names.clone(),196 has_rest: rest.is_some()197 }));198199 for (field, d) in fields {200 #[derive(Trace)]201 struct FieldThunk {202 full: Thunk<ObjValue>,203 field: IStr,204 }205 impl ThunkValue for FieldThunk {206 type Output = Val;207208 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {209 let full = self.full.evaluate(s.clone())?;210 let field = full.get(s, self.field)?.expect("shape is checked");211 Ok(field)212 }213 }214 let value = Thunk::new(tb!(FieldThunk {215 full: full.clone(),216 field: field.clone()217 }));218 if let Some(d) = d {219 destruct(d, value, new_bindings)?;220 } else {221 destruct(&Destruct::Full(field.clone()), value, new_bindings)?;222 }223 }224 }225 }226 Ok(())227}228229pub fn evaluate_dest(230 d: &BindSpec,231 fctx: Pending<Context>,232 new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,233) -> Result<()> {234 match d {235 BindSpec::Field { into, value } => {236 #[derive(Trace)]237 struct EvaluateThunkValue {238 name: Option<IStr>,239 fctx: Pending<Context>,240 expr: LocExpr,241 }242 impl ThunkValue for EvaluateThunkValue {243 type Output = Val;244 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {245 if let Some(name) = self.name {246 evaluate_named(s, self.fctx.unwrap(), &self.expr, name)247 } else {248 evaluate(s, self.fctx.unwrap(), &self.expr)249 }250 }251 }252 let data = Thunk::new(tb!(EvaluateThunkValue {253 name: into.name(),254 fctx,255 expr: value.clone(),256 }));257 destruct(into, data, new_bindings)?;258 }259 BindSpec::Function {260 name,261 params,262 value,263 } => {264 #[derive(Trace)]265 struct MethodThunk {266 fctx: Pending<Context>,267 name: IStr,268 params: ParamsDesc,269 value: LocExpr,270 }271 impl ThunkValue for MethodThunk {272 type Output = Val;273274 fn get(self: Box<Self>, _s: State) -> Result<Self::Output> {275 Ok(evaluate_method(276 self.fctx.unwrap(),277 self.name,278 self.params,279 self.value,280 ))281 }282 }283284 let old = new_bindings.insert(285 name.clone(),286 Thunk::new(tb!(MethodThunk {287 fctx,288 name: name.clone(),289 params: params.clone(),290 value: value.clone()291 })),292 );293 if old.is_some() {294 throw!(DuplicateLocalVar(name.clone()))295 }296 }297 }298 Ok(())299}1use gcmodule::Trace;2use jrsonnet_interner::IStr;3use jrsonnet_parser::{BindSpec, Destruct, LocExpr, ParamsDesc};45use crate::{6 error::{Error::*, Result},7 evaluate, evaluate_method, evaluate_named,8 gc::GcHashMap,9 tb, throw,10 val::ThunkValue,11 Context, Pending, State, Thunk, Val,12};1314#[allow(clippy::too_many_lines)]15#[allow(unused_variables)]16pub fn destruct(17 d: &Destruct,18 parent: Thunk<Val>,19 fctx: Pending<Context>,20 new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,21) -> Result<()> {22 match d {23 Destruct::Full(v) => {24 let old = new_bindings.insert(v.clone(), parent);25 if old.is_some() {26 throw!(DuplicateLocalVar(v.clone()))27 }28 }29 #[cfg(feature = "exp-destruct")]30 Destruct::Skip => {}31 #[cfg(feature = "exp-destruct")]32 Destruct::Array { start, rest, end } => {33 use jrsonnet_parser::DestructRest;3435 use crate::{throw_runtime, val::ArrValue};3637 #[derive(Trace)]38 struct DataThunk {39 parent: Thunk<Val>,40 min_len: usize,41 has_rest: bool,42 }43 impl ThunkValue for DataThunk {44 type Output = ArrValue;4546 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {47 let v = self.parent.evaluate(s)?;48 let arr = match v {49 Val::Arr(a) => a,50 _ => throw_runtime!("expected array"),51 };52 if !self.has_rest {53 if arr.len() != self.min_len {54 throw_runtime!("expected {} elements, got {}", self.min_len, arr.len())55 }56 } else if arr.len() < self.min_len {57 throw_runtime!(58 "expected at least {} elements, but array was only {}",59 self.min_len,60 arr.len()61 )62 }63 Ok(arr)64 }65 }6667 let full = Thunk::new(tb!(DataThunk {68 min_len: start.len() + end.len(),69 has_rest: rest.is_some(),70 parent,71 }));7273 {74 #[derive(Trace)]75 struct BaseThunk {76 full: Thunk<ArrValue>,77 index: usize,78 }79 impl ThunkValue for BaseThunk {80 type Output = Val;8182 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {83 let full = self.full.evaluate(s.clone())?;84 Ok(full.get(s, self.index)?.expect("length is checked"))85 }86 }87 for (i, d) in start.iter().enumerate() {88 destruct(89 d,90 Thunk::new(tb!(BaseThunk {91 full: full.clone(),92 index: i,93 })),94 fctx.clone(),95 new_bindings,96 )?;97 }98 }99100 match rest {101 Some(DestructRest::Keep(v)) => {102 #[derive(Trace)]103 struct RestThunk {104 full: Thunk<ArrValue>,105 start: usize,106 end: usize,107 }108 impl ThunkValue for RestThunk {109 type Output = Val;110111 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {112 let full = self.full.evaluate(s)?;113 let to = full.len() - self.end;114 Ok(Val::Arr(full.slice(Some(self.start), Some(to), None)))115 }116 }117118 destruct(119 &Destruct::Full(v.clone()),120 Thunk::new(tb!(RestThunk {121 full: full.clone(),122 start: start.len(),123 end: end.len(),124 })),125 fctx.clone(),126 new_bindings,127 )?;128 }129 Some(DestructRest::Drop) => {}130 None => {}131 }132133 {134 #[derive(Trace)]135 struct EndThunk {136 full: Thunk<ArrValue>,137 index: usize,138 end: usize,139 }140 impl ThunkValue for EndThunk {141 type Output = Val;142143 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {144 let full = self.full.evaluate(s.clone())?;145 Ok(full146 .get(s, full.len() - self.end + self.index)?147 .expect("length is checked"))148 }149 }150 for (i, d) in end.iter().enumerate() {151 destruct(152 d,153 Thunk::new(tb!(EndThunk {154 full: full.clone(),155 index: i,156 end: end.len(),157 })),158 fctx.clone(),159 new_bindings,160 )?;161 }162 }163 }164 #[cfg(feature = "exp-destruct")]165 Destruct::Object { fields, rest } => {166 use crate::{obj::ObjValue, throw_runtime};167168 #[derive(Trace)]169 struct DataThunk {170 parent: Thunk<Val>,171 field_names: Vec<IStr>,172 has_rest: bool,173 }174 impl ThunkValue for DataThunk {175 type Output = ObjValue;176177 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {178 let v = self.parent.evaluate(s)?;179 let obj = match v {180 Val::Obj(o) => o,181 _ => throw_runtime!("expected object"),182 };183 for field in &self.field_names {184 if !obj.has_field_ex(field.clone(), true) {185 throw_runtime!("missing field: {}", field);186 }187 }188 if !self.has_rest {189 let len = obj.len();190 if len != self.field_names.len() {191 throw_runtime!("too many fields, and rest not found");192 }193 }194 Ok(obj)195 }196 }197 let field_names: Vec<_> = fields198 .iter()199 .filter(|f| f.2.is_none())200 .map(|f| f.0.clone())201 .collect();202 let full = Thunk::new(tb!(DataThunk {203 parent,204 field_names: field_names.clone(),205 has_rest: rest.is_some()206 }));207208 for (field, d, default) in fields {209 #[derive(Trace)]210 struct FieldThunk {211 full: Thunk<ObjValue>,212 field: IStr,213 default: Option<(Pending<Context>, LocExpr)>,214 }215 impl ThunkValue for FieldThunk {216 type Output = Val;217218 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {219 let full = self.full.evaluate(s.clone())?;220 if let Some(field) = full.get(s.clone(), self.field)? {221 Ok(field)222 } else {223 let (fctx, expr) = self.default.as_ref().expect("shape is checked");224 Ok(evaluate(s, fctx.clone().unwrap(), &expr)?)225 }226 }227 }228 let value = Thunk::new(tb!(FieldThunk {229 full: full.clone(),230 field: field.clone(),231 default: default.clone().map(|e| (fctx.clone(), e)),232 }));233 if let Some(d) = d {234 destruct(d, value, fctx.clone(), new_bindings)?;235 } else {236 destruct(237 &Destruct::Full(field.clone()),238 value,239 fctx.clone(),240 new_bindings,241 )?;242 }243 }244 }245 }246 Ok(())247}248249pub fn evaluate_dest(250 d: &BindSpec,251 fctx: Pending<Context>,252 new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,253) -> Result<()> {254 match d {255 BindSpec::Field { into, value } => {256 #[derive(Trace)]257 struct EvaluateThunkValue {258 name: Option<IStr>,259 fctx: Pending<Context>,260 expr: LocExpr,261 }262 impl ThunkValue for EvaluateThunkValue {263 type Output = Val;264 fn get(self: Box<Self>, s: State) -> Result<Self::Output> {265 if let Some(name) = self.name {266 evaluate_named(s, self.fctx.unwrap(), &self.expr, name)267 } else {268 evaluate(s, self.fctx.unwrap(), &self.expr)269 }270 }271 }272 let data = Thunk::new(tb!(EvaluateThunkValue {273 name: into.name(),274 fctx: fctx.clone(),275 expr: value.clone(),276 }));277 destruct(into, data, fctx, new_bindings)?;278 }279 BindSpec::Function {280 name,281 params,282 value,283 } => {284 #[derive(Trace)]285 struct MethodThunk {286 fctx: Pending<Context>,287 name: IStr,288 params: ParamsDesc,289 value: LocExpr,290 }291 impl ThunkValue for MethodThunk {292 type Output = Val;293294 fn get(self: Box<Self>, _s: State) -> Result<Self::Output> {295 Ok(evaluate_method(296 self.fctx.unwrap(),297 self.name,298 self.params,299 self.value,300 ))301 }302 }303304 let old = new_bindings.insert(305 name.clone(),306 Thunk::new(tb!(MethodThunk {307 fctx,308 name: name.clone(),309 params: params.clone(),310 value: value.clone()311 })),312 );313 if old.is_some() {314 throw!(DuplicateLocalVar(name.clone()))315 }316 }317 }318 Ok(())319}crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -56,7 +56,12 @@
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 @@
name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
value: param.1.clone().expect("default exists"),
})),
+ fctx.clone(),
&mut defaults,
)?;
if param.0.name().is_some() {
@@ -230,6 +236,7 @@
name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
value: v.clone(),
})),
+ fctx.clone(),
&mut bindings,
)?;
} else {
@@ -238,6 +245,7 @@
Thunk::new(tb!(DependsOnUnbound(
param.0.name().unwrap_or_else(|| "<destruct>".into())
))),
+ fctx.clone(),
&mut bindings,
)?;
}
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -188,7 +188,7 @@
}
#[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 @@
},
#[cfg(feature = "exp-destruct")]
Object {
- fields: Vec<(IStr, Option<Destruct>)>,
+ fields: Vec<(IStr, Option<Destruct>, Option<LocExpr>)>,
rest: Option<DestructRest>,
},
}
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- 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 @@
}
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}