Skip to content

Commit 3961a53

Browse files
committed
feat: destruct function arguments
Signed-off-by: Yaroslav Bolyukin <iam@lach.pw>
1 parent c35e2bd commit 3961a53

5 files changed

Lines changed: 56 additions & 33 deletions

File tree

crates/jrsonnet-evaluator/src/evaluate/destructure.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
};
1313

1414
#[allow(clippy::too_many_lines)]
15-
fn destruct(
15+
pub fn destruct(
1616
d: &Destruct,
1717
parent: Thunk<Val>,
1818
new_bindings: &mut GcHashMap<IStr, Thunk<Val>>,

crates/jrsonnet-evaluator/src/function/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub struct FuncDesc {
5959
}
6060
impl FuncDesc {
6161
/// Create body context, but fill arguments without defaults with lazy error
62-
pub fn default_body_context(&self) -> Context {
62+
pub fn default_body_context(&self) -> Result<Context> {
6363
parse_default_function_call(self.ctx.clone(), &self.params)
6464
}
6565

crates/jrsonnet-evaluator/src/function/parse.rs

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::{
77
builtin::{BuiltinParam, BuiltinParamName},
88
};
99
use crate::{
10+
destructure::destruct,
1011
error::{Error::*, Result},
1112
evaluate_named,
1213
gc::GcHashMap,
@@ -50,60 +51,77 @@ pub fn parse_function_call(
5051
throw!(TooManyArgsFunctionHas(params.len()))
5152
}
5253

53-
let mut filled_args = 0;
54+
let mut filled_named = 0;
55+
let mut filled_positionals = 0;
5456

5557
args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {
5658
let name = params[id].0.clone();
57-
passed_args.insert(name, arg);
58-
filled_args += 1;
59+
destruct(&name, arg, &mut passed_args)?;
60+
filled_positionals += 1;
5961
Ok(())
6062
})?;
6163

6264
args.named_iter(s, ctx, tailstrict, &mut |name, value| {
6365
// FIXME: O(n) for arg existence check
64-
if !params.iter().any(|p| &p.0 == name) {
66+
if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {
6567
throw!(UnknownFunctionParameter((name as &str).to_owned()));
6668
}
6769
if passed_args.insert(name.clone(), value).is_some() {
6870
throw!(BindingParameterASecondTime(name.clone()));
6971
}
70-
filled_args += 1;
72+
filled_named += 1;
7173
Ok(())
7274
})?;
7375

74-
if filled_args < params.len() {
76+
if filled_named + filled_positionals < params.len() {
7577
// Some args are unset, but maybe we have defaults for them
7678
// Default values should be created in newly created context
7779
let fctx = Context::new_future();
78-
let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);
80+
let mut defaults =
81+
GcHashMap::with_capacity(params.len() - filled_named - filled_positionals);
7982

80-
for param in params.iter().filter(|p| p.1.is_some()) {
81-
if passed_args.contains_key(&param.0.clone()) {
83+
for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {
84+
if let Some(name) = param.0.name() {
85+
if passed_args.contains_key(&name) {
86+
continue;
87+
}
88+
} else if idx < filled_positionals {
8289
continue;
8390
}
8491

85-
defaults.insert(
86-
param.0.clone(),
92+
destruct(
93+
&param.0,
8794
Thunk::new(tb!(EvaluateNamedThunk {
8895
ctx: fctx.clone(),
89-
name: param.0.clone(),
96+
name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
9097
value: param.1.clone().expect("default exists"),
9198
})),
92-
);
93-
filled_args += 1;
99+
&mut defaults,
100+
)?;
101+
if param.0.name().is_some() {
102+
filled_named += 1;
103+
} else {
104+
filled_positionals += 1;
105+
}
94106
}
95107

96-
// Some args still wasn't filled
97-
if filled_args != params.len() {
108+
// Some args still weren't filled
109+
if filled_named + filled_positionals != params.len() {
98110
for param in params.iter().skip(args.unnamed_len()) {
99111
let mut found = false;
100112
args.named_names(&mut |name| {
101-
if name == &param.0 {
113+
if Some(name) == param.0.name().as_ref() {
102114
found = true;
103115
}
104116
});
105117
if !found {
106-
throw!(FunctionParameterNotBoundInCall(param.0.clone()));
118+
throw!(FunctionParameterNotBoundInCall(
119+
param
120+
.0
121+
.clone()
122+
.name()
123+
.unwrap_or_else(|| "<destruct>".into())
124+
));
107125
}
108126
}
109127
unreachable!();
@@ -189,7 +207,7 @@ pub fn parse_builtin_call(
189207

190208
/// Creates Context, which has all argument default values applied
191209
/// and with unbound values causing error to be returned
192-
pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {
210+
pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {
193211
#[derive(Trace)]
194212
struct DependsOnUnbound(IStr);
195213
impl ThunkValue for DependsOnUnbound {
@@ -205,23 +223,27 @@ pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Co
205223

206224
for param in params.iter() {
207225
if let Some(v) = &param.1 {
208-
bindings.insert(
209-
param.0.clone(),
226+
destruct(
227+
&param.0.clone(),
210228
Thunk::new(tb!(EvaluateNamedThunk {
211229
ctx: fctx.clone(),
212-
name: param.0.clone(),
230+
name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
213231
value: v.clone(),
214232
})),
215-
);
233+
&mut bindings,
234+
)?;
216235
} else {
217-
bindings.insert(
218-
param.0.clone(),
219-
Thunk::new(tb!(DependsOnUnbound(param.0.clone()))),
220-
);
236+
destruct(
237+
&param.0,
238+
Thunk::new(tb!(DependsOnUnbound(
239+
param.0.name().unwrap_or_else(|| "<destruct>".into())
240+
))),
241+
&mut bindings,
242+
)?;
221243
}
222244
}
223245

224-
body_ctx
246+
Ok(body_ctx
225247
.extend(bindings, None, None, None)
226-
.into_future(fctx)
248+
.into_future(fctx))
227249
}

crates/jrsonnet-parser/src/expr.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ impl Display for BinaryOpType {
152152
/// name, default value
153153
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
154154
#[derive(Debug, PartialEq, Trace)]
155-
pub struct Param(pub IStr, pub Option<LocExpr>);
155+
pub struct Param(pub Destruct, pub Option<LocExpr>);
156156

157157
/// Defined function parameters
158158
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -206,6 +206,7 @@ pub enum Destruct {
206206
},
207207
}
208208
impl Destruct {
209+
/// Name of destructure, used for function parameter names
209210
pub fn name(&self) -> Option<IStr> {
210211
match self {
211212
Self::Full(name) => Some(name.clone()),

crates/jrsonnet-parser/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ parser! {
5959
rule keyword(id: &'static str) -> ()
6060
= ##parse_string_literal(id) end_of_ident()
6161

62-
pub rule param(s: &ParserSettings) -> expr::Param = name:id() expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
62+
pub rule param(s: &ParserSettings) -> expr::Param = name:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
6363
pub rule params(s: &ParserSettings) -> expr::ParamsDesc
6464
= params:param(s) ** comma() comma()? { expr::ParamsDesc(Rc::new(params)) }
6565
/ { expr::ParamsDesc(Rc::new(Vec::new())) }

0 commit comments

Comments
 (0)