Skip to content

Commit

Permalink
change(eval): implement std.filterMap as built-in
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardosm committed Nov 6, 2024
1 parent d9a8146 commit 3d0b273
Show file tree
Hide file tree
Showing 21 changed files with 121 additions and 24 deletions.
1 change: 1 addition & 0 deletions rsjsonnet-lang/src/program/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,7 @@ pub(super) enum BuiltInFunc {
Find,
Map,
MapWithIndex,
FilterMap,
FlatMap,
Filter,
Foldl,
Expand Down
7 changes: 7 additions & 0 deletions rsjsonnet-lang/src/program/eval/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,13 @@ impl<'p> Evaluator<'_, 'p> {
self.state_stack.push(State::DoThunk(arg1.view()));
self.state_stack.push(State::DoThunk(arg0.view()));
}
BuiltInFunc::FilterMap => {
let [arg0, arg1, arg2] = check_num_args(args);
self.state_stack.push(State::StdFilterMap);
self.state_stack.push(State::DoThunk(arg2.view()));
self.state_stack.push(State::DoThunk(arg1.view()));
self.state_stack.push(State::DoThunk(arg0.view()));
}
BuiltInFunc::FlatMap => {
let [arg0, arg1] = check_num_args(args);
self.state_stack
Expand Down
4 changes: 4 additions & 0 deletions rsjsonnet-lang/src/program/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,10 @@ impl<'p, 'a> Evaluator<'a, 'p> {
} => self.do_std_count_inner(array, index, count),
State::StdFind { value } => self.do_std_find(value)?,
State::StdFindInner { array, index } => self.do_std_find_inner(array, index),
State::StdFilterMap => self.do_std_filter_map()?,
State::StdFilterMapCheck { item, map_func } => {
self.do_std_filter_map_check(item, map_func)?
}
State::StdFilter => self.do_std_filter()?,
State::StdFilterCheck { item } => self.do_std_filter_check(item)?,
State::StdFoldl { init } => self.do_std_foldl(init)?,
Expand Down
5 changes: 5 additions & 0 deletions rsjsonnet-lang/src/program/eval/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ pub(super) enum State<'a, 'p> {
array: GcView<ArrayData<'p>>,
index: usize,
},
StdFilterMap,
StdFilterMapCheck {
item: GcView<ThunkData<'p>>,
map_func: GcView<FuncData<'p>>,
},
StdFilter,
StdFilterCheck {
item: GcView<ThunkData<'p>>,
Expand Down
53 changes: 53 additions & 0 deletions rsjsonnet-lang/src/program/eval/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,59 @@ impl<'p> Evaluator<'_, 'p> {
}
}

pub(super) fn do_std_filter_map(&mut self) -> EvalResult<()> {
let array = self.value_stack.pop().unwrap();
let map_func = self.value_stack.pop().unwrap();
let filter_func = self.value_stack.pop().unwrap();

let filter_func = self.expect_std_func_arg_func(filter_func, "filterMap", 0)?;
let map_func = self.expect_std_func_arg_func(map_func, "filterMap", 1)?;
let array = self.expect_std_func_arg_array(array, "filterMap", 2)?;

self.state_stack.push(State::ArrayToValue);
self.array_stack.push(Vec::new());

for item in array.iter().rev() {
self.state_stack.push(State::StdFilterMapCheck {
item: item.view(),
map_func: map_func.clone(),
});
self.check_thunk_args_and_execute_call(&filter_func, &[item.view()], &[], None)?;
}

Ok(())
}

pub(super) fn do_std_filter_map_check(
&mut self,
item: GcView<ThunkData<'p>>,
map_func: GcView<FuncData<'p>>,
) -> EvalResult<()> {
let cond_value = self.value_stack.pop().unwrap();
let ValueData::Bool(cond_value) = cond_value else {
return Err(self.report_error(EvalErrorKind::Other {
span: None,
message: format!(
"filter function must return a boolean, got {}",
EvalErrorValueType::from_value(&cond_value).to_str(),
),
}));
};

if cond_value {
let array = self.array_stack.last_mut().unwrap();

let args_thunks = Box::new([Gc::from(&item)]);

array.push(self.program.gc_alloc(ThunkData::new_pending_call(
Gc::from(&map_func),
args_thunks,
)));
}

Ok(())
}

pub(super) fn do_std_flat_map(&mut self) -> EvalResult<()> {
let arr = self.value_stack.pop().unwrap();
let func = self.value_stack.pop().unwrap();
Expand Down
10 changes: 0 additions & 10 deletions rsjsonnet-lang/src/program/std.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,6 @@ limitations under the License.
lines(arr)::
std.join('\n', arr + ['']),

filterMap(filter_func, map_func, arr)::
if !std.isFunction(filter_func) then
error ('std.filterMap first param must be function, got ' + std.type(filter_func))
else if !std.isFunction(map_func) then
error ('std.filterMap second param must be function, got ' + std.type(map_func))
else if !std.isArray(arr) then
error ('std.filterMap third param must be array, got ' + std.type(arr))
else
std.map(map_func, std.filter(filter_func, arr)),

abs(n)::
if !std.isNumber(n) then
error 'std.abs expected number, got ' + std.type(n)
Expand Down
5 changes: 5 additions & 0 deletions rsjsonnet-lang/src/program/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ impl<'p> Program<'p> {
add_simple("find", BuiltInFunc::Find, &["value", "find"]);
add_simple("map", BuiltInFunc::Map, &["func", "arr"]);
add_simple("mapWithIndex", BuiltInFunc::MapWithIndex, &["func", "arr"]);
add_simple(
"filterMap",
BuiltInFunc::FilterMap,
&["filter_func", "map_func", "arr"],
);
add_simple("flatMap", BuiltInFunc::FlatMap, &["func", "arr"]);
add_simple("filter", BuiltInFunc::Filter, &["func", "arr"]);
add_simple("foldl", BuiltInFunc::Foldl, &["func", "arr", "init"]);
Expand Down
1 change: 1 addition & 0 deletions ui-tests/fail/stdlib/filterMap/invalid_arg_1.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.filterMap(null, function(x) null, [])
8 changes: 8 additions & 0 deletions ui-tests/fail/stdlib/filterMap/invalid_arg_1.jsonnet.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: first argument of `std.filterMap` is expected to be function, got null
note: while evaluating call to `filterMap`
--> invalid_arg_1.jsonnet:1:1
|
1 | std.filterMap(null, function(x) null, [])
| -----------------------------------------
note: during top-level value evaluation

1 change: 1 addition & 0 deletions ui-tests/fail/stdlib/filterMap/invalid_arg_2.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.filterMap(function(x) false, null, [])
8 changes: 8 additions & 0 deletions ui-tests/fail/stdlib/filterMap/invalid_arg_2.jsonnet.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: second argument of `std.filterMap` is expected to be function, got null
note: while evaluating call to `filterMap`
--> invalid_arg_2.jsonnet:1:1
|
1 | std.filterMap(function(x) false, null, [])
| ------------------------------------------
note: during top-level value evaluation

1 change: 1 addition & 0 deletions ui-tests/fail/stdlib/filterMap/invalid_arg_3.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.filterMap(function(x) false, function(x) null, null)
8 changes: 8 additions & 0 deletions ui-tests/fail/stdlib/filterMap/invalid_arg_3.jsonnet.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: third argument of `std.filterMap` is expected to be array, got null
note: while evaluating call to `filterMap`
--> invalid_arg_3.jsonnet:1:1
|
1 | std.filterMap(function(x) false, function(x) null, null)
| --------------------------------------------------------
note: during top-level value evaluation

4 changes: 2 additions & 2 deletions ui-tests/fail/stdlib/manifestToml/inner_error.jsonnet.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ note: while manifesting array item 1
note: while manifesting object field "y"
note: while manifesting object field "x"
note: while evaluating call to `manifestTomlEx`
--> <stdlib>:75:25
--> <stdlib>:65:25
|
75 | manifestToml(value):: std.manifestTomlEx(value, ' '),
65 | manifestToml(value):: std.manifestTomlEx(value, ' '),
| -------------------------------
note: while evaluating call to function
--> inner_error.jsonnet:1:1
Expand Down
4 changes: 2 additions & 2 deletions ui-tests/fail/stdlib/manifestToml/invalid_arg.jsonnet.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: first argument of `std.manifestTomlEx` is expected to be object, got null
note: while evaluating call to `manifestTomlEx`
--> <stdlib>:75:25
--> <stdlib>:65:25
|
75 | manifestToml(value):: std.manifestTomlEx(value, ' '),
65 | manifestToml(value):: std.manifestTomlEx(value, ' '),
| -------------------------------
note: while evaluating call to function
--> invalid_arg.jsonnet:1:1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
error: functions cannot be manifested
note: while manifesting object field "x"
note: while evaluating call to `manifestTomlEx`
--> <stdlib>:75:25
--> <stdlib>:65:25
|
75 | manifestToml(value):: std.manifestTomlEx(value, ' '),
65 | manifestToml(value):: std.manifestTomlEx(value, ' '),
| -------------------------------
note: while evaluating call to function
--> manifest_function.jsonnet:1:1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
error: cannot manifest null in TOML
note: while manifesting object field "x"
note: while evaluating call to `manifestTomlEx`
--> <stdlib>:75:25
--> <stdlib>:65:25
|
75 | manifestToml(value):: std.manifestTomlEx(value, ' '),
65 | manifestToml(value):: std.manifestTomlEx(value, ' '),
| -------------------------------
note: while evaluating call to function
--> manifest_null.jsonnet:1:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ error: assertion failed
1 | std.manifestToml({ assert false })
| ^^^^^^^^^^^^
note: while evaluating call to `manifestTomlEx`
--> <stdlib>:75:25
--> <stdlib>:65:25
|
75 | manifestToml(value):: std.manifestTomlEx(value, ' '),
65 | manifestToml(value):: std.manifestTomlEx(value, ' '),
| -------------------------------
note: while evaluating call to function
--> object_assert_failed_root.jsonnet:1:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ error: assertion failed
| ^^^^^^^^^^^^
note: while manifesting object field "x"
note: while evaluating call to `manifestTomlEx`
--> <stdlib>:75:25
--> <stdlib>:65:25
|
75 | manifestToml(value):: std.manifestTomlEx(value, ' '),
65 | manifestToml(value):: std.manifestTomlEx(value, ' '),
| -------------------------------
note: while evaluating call to function
--> object_assert_failed_sub_table.jsonnet:1:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ error: assertion failed
note: while manifesting array item 0
note: while manifesting object field "x"
note: while evaluating call to `manifestTomlEx`
--> <stdlib>:75:25
--> <stdlib>:65:25
|
75 | manifestToml(value):: std.manifestTomlEx(value, ' '),
65 | manifestToml(value):: std.manifestTomlEx(value, ' '),
| -------------------------------
note: while evaluating call to function
--> object_assert_failed_value.jsonnet:1:1
Expand Down
5 changes: 5 additions & 0 deletions ui-tests/pass/stdlib/filterMap.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
std.assertEqual(std.filterMap(function(x) error "err", function(x) error "err", []), []) &&
std.assertEqual(std.filterMap(function(x) false, function(x) error "err", [4, 1, 8, 7]), []) &&
std.assertEqual(std.filterMap(function(x) x % 2 == 0, function(x) x * 10, [6, 3, 9, 2]), [60, 20]) &&

true

0 comments on commit 3d0b273

Please sign in to comment.