difftreelog
feat destruct function arguments
in: master
5 files changed
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)]15fn 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)]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}crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -59,7 +59,7 @@
}
impl FuncDesc {
/// Create body context, but fill arguments without defaults with lazy error
- pub fn default_body_context(&self) -> Context {
+ pub fn default_body_context(&self) -> Result<Context> {
parse_default_function_call(self.ctx.clone(), &self.params)
}
crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -7,6 +7,7 @@
builtin::{BuiltinParam, BuiltinParamName},
};
use crate::{
+ destructure::destruct,
error::{Error::*, Result},
evaluate_named,
gc::GcHashMap,
@@ -50,60 +51,77 @@
throw!(TooManyArgsFunctionHas(params.len()))
}
- let mut filled_args = 0;
+ let mut filled_named = 0;
+ let mut filled_positionals = 0;
args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {
let name = params[id].0.clone();
- passed_args.insert(name, arg);
- filled_args += 1;
+ destruct(&name, arg, &mut passed_args)?;
+ filled_positionals += 1;
Ok(())
})?;
args.named_iter(s, ctx, tailstrict, &mut |name, value| {
// FIXME: O(n) for arg existence check
- if !params.iter().any(|p| &p.0 == name) {
+ if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {
throw!(UnknownFunctionParameter((name as &str).to_owned()));
}
if passed_args.insert(name.clone(), value).is_some() {
throw!(BindingParameterASecondTime(name.clone()));
}
- filled_args += 1;
+ filled_named += 1;
Ok(())
})?;
- if filled_args < params.len() {
+ if filled_named + filled_positionals < params.len() {
// Some args are unset, but maybe we have defaults for them
// Default values should be created in newly created context
let fctx = Context::new_future();
- let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);
+ let mut defaults =
+ GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);
- for param in params.iter().filter(|p| p.1.is_some()) {
- if passed_args.contains_key(¶m.0.clone()) {
+ for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {
+ if let Some(name) = param.0.name() {
+ if passed_args.contains_key(&name) {
+ continue;
+ }
+ } else if idx < filled_positionals {
continue;
}
- defaults.insert(
- param.0.clone(),
+ destruct(
+ ¶m.0,
Thunk::new(tb!(EvaluateNamedThunk {
ctx: fctx.clone(),
- name: param.0.clone(),
+ name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
value: param.1.clone().expect("default exists"),
})),
- );
- filled_args += 1;
+ &mut defaults,
+ )?;
+ if param.0.name().is_some() {
+ filled_named += 1;
+ } else {
+ filled_positionals += 1;
+ }
}
- // Some args still wasn't filled
- if filled_args != params.len() {
+ // Some args still weren't filled
+ if filled_named + filled_positionals != params.len() {
for param in params.iter().skip(args.unnamed_len()) {
let mut found = false;
args.named_names(&mut |name| {
- if name == ¶m.0 {
+ if Some(name) == param.0.name().as_ref() {
found = true;
}
});
if !found {
- throw!(FunctionParameterNotBoundInCall(param.0.clone()));
+ throw!(FunctionParameterNotBoundInCall(
+ param
+ .0
+ .clone()
+ .name()
+ .unwrap_or_else(|| "<destruct>".into())
+ ));
}
}
unreachable!();
@@ -189,7 +207,7 @@
/// Creates Context, which has all argument default values applied
/// and with unbound values causing error to be returned
-pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {
+pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {
#[derive(Trace)]
struct DependsOnUnbound(IStr);
impl ThunkValue for DependsOnUnbound {
@@ -205,23 +223,27 @@
for param in params.iter() {
if let Some(v) = ¶m.1 {
- bindings.insert(
- param.0.clone(),
+ destruct(
+ ¶m.0.clone(),
Thunk::new(tb!(EvaluateNamedThunk {
ctx: fctx.clone(),
- name: param.0.clone(),
+ name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
value: v.clone(),
})),
- );
+ &mut bindings,
+ )?;
} else {
- bindings.insert(
- param.0.clone(),
- Thunk::new(tb!(DependsOnUnbound(param.0.clone()))),
- );
+ destruct(
+ ¶m.0,
+ Thunk::new(tb!(DependsOnUnbound(
+ param.0.name().unwrap_or_else(|| "<destruct>".into())
+ ))),
+ &mut bindings,
+ )?;
}
}
- body_ctx
+ Ok(body_ctx
.extend(bindings, None, None, None)
- .into_future(fctx)
+ .into_future(fctx))
}
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -152,7 +152,7 @@
/// name, default value
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
-pub struct Param(pub IStr, pub Option<LocExpr>);
+pub struct Param(pub Destruct, pub Option<LocExpr>);
/// Defined function parameters
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -206,6 +206,7 @@
},
}
impl Destruct {
+ /// Name of destructure, used for function parameter names
pub fn name(&self) -> Option<IStr> {
match self {
Self::Full(name) => Some(name.clone()),
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -59,7 +59,7 @@
rule keyword(id: &'static str) -> ()
= ##parse_string_literal(id) end_of_ident()
- pub rule param(s: &ParserSettings) -> expr::Param = name:id() expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
+ pub rule param(s: &ParserSettings) -> expr::Param = name:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
pub rule params(s: &ParserSettings) -> expr::ParamsDesc
= params:param(s) ** comma() comma()? { expr::ParamsDesc(Rc::new(params)) }
/ { expr::ParamsDesc(Rc::new(Vec::new())) }