Skip to content

Commit

Permalink
change(eval): implement std.mapWithKey as built-in
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardosm committed Nov 5, 2024
1 parent 2976419 commit cebde59
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 1 deletion.
1 change: 1 addition & 0 deletions rsjsonnet-lang/src/program/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ pub(super) enum BuiltInFunc {
Prune,
ObjectHasEx,
ObjectFieldsEx,
MapWithKey,
PrimitiveEquals,
Equals,
Compare,
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 @@ -314,6 +314,13 @@ impl<'p> Evaluator<'_, 'p> {
self.state_stack.push(State::DoThunk(arg1.view()));
self.state_stack.push(State::DoThunk(arg0.view()));
}
BuiltInFunc::MapWithKey => {
let [arg0, arg1] = check_num_args(args);
self.state_stack
.push(State::FnFallible(Self::do_std_map_with_key));
self.state_stack.push(State::DoThunk(arg1.view()));
self.state_stack.push(State::DoThunk(arg0.view()));
}
BuiltInFunc::PrimitiveEquals => {
let [arg0, arg1] = check_num_args(args);
self.state_stack
Expand Down
45 changes: 44 additions & 1 deletion rsjsonnet-lang/src/program/eval/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::{
};
use crate::gc::{Gc, GcView};
use crate::interner::InternedStr;
use crate::{ast, FHashSet};
use crate::{ast, FHashMap, FHashSet};

impl<'p> Evaluator<'_, 'p> {
pub(super) fn do_std_ext_var(&mut self) -> EvalResult<()> {
Expand Down Expand Up @@ -235,6 +235,49 @@ impl<'p> Evaluator<'_, 'p> {
Ok(())
}

pub(super) fn do_std_map_with_key(&mut self) -> EvalResult<()> {
let object = self.value_stack.pop().unwrap();
let func = self.value_stack.pop().unwrap();

let func = self.expect_std_func_arg_func(func, "mapWithKey", 0)?;
let object = self.expect_std_func_arg_object(object, "mapWithKey", 1)?;

let mut mapped_fields = FHashMap::default();
for field_name in object.get_visible_fields_order() {
let field_thunk = self
.program
.find_object_field_thunk(&object, 0, field_name)
.unwrap();

let args_thunks = Box::new([
self.program.gc_alloc(ThunkData::new_done(ValueData::String(
field_name.value().into(),
))),
Gc::from(&field_thunk),
]);

let mapped_field = ObjectField {
base_env: None,
visibility: ast::Visibility::Default,
expr: None,
thunk: OnceCell::from(
self.program
.gc_alloc(ThunkData::new_pending_call(Gc::from(&func), args_thunks)),
),
};

mapped_fields.insert(field_name, mapped_field);
}

let mapped_object = ObjectData::new_simple(mapped_fields);
self.value_stack
.push(ValueData::Object(self.program.gc_alloc(mapped_object)));

self.check_object_asserts(&object);

Ok(())
}

pub(super) fn do_std_primitive_equals(&mut self) -> EvalResult<()> {
let rhs = self.value_stack.pop().unwrap();
let lhs = self.value_stack.pop().unwrap();
Expand Down
1 change: 1 addition & 0 deletions rsjsonnet-lang/src/program/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ impl<'p> Program<'p> {
BuiltInFunc::ObjectFieldsEx,
&["obj", "inc_hidden"],
);
add_simple("mapWithKey", BuiltInFunc::MapWithKey, &["func", "obj"]);
add_simple("primitiveEquals", BuiltInFunc::PrimitiveEquals, &["a", "b"]);
add_simple("equals", BuiltInFunc::Equals, &["a", "b"]);
add_simple("__compare", BuiltInFunc::Compare, &["v1", "v2"]);
Expand Down
1 change: 1 addition & 0 deletions ui-tests/fail/stdlib/mapWithKey/invalid_arg_1.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.mapWithKey(null, {})
8 changes: 8 additions & 0 deletions ui-tests/fail/stdlib/mapWithKey/invalid_arg_1.jsonnet.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: first argument of `std.mapWithKey` is expected to be function, got null
note: while evaluating call to `mapWithKey`
--> invalid_arg_1.jsonnet:1:1
|
1 | std.mapWithKey(null, {})
| ------------------------
note: during top-level value evaluation

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

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.mapWithKey(function(k, v) null, { assert false })
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error: assertion failed
--> object_assert_failed.jsonnet:1:39
|
1 | std.mapWithKey(function(k, v) null, { assert false })
| ^^^^^^^^^^^^
note: while evaluating call to `mapWithKey`
--> object_assert_failed.jsonnet:1:1
|
1 | std.mapWithKey(function(k, v) null, { assert false })
| -----------------------------------------------------
note: during top-level value evaluation

15 changes: 15 additions & 0 deletions ui-tests/pass/stdlib/mapWithKey.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
std.assertEqual(std.mapWithKey(function(k, v) error "err", {}), {}) &&
std.assertEqual(
std.mapWithKey(function(k, v) k + ":" + v, { a: 1, b: 2 }),
{ a: "a:1", b: "b:2" },
) &&
std.assertEqual(
std.mapWithKey(std.format, { "%i": 9, "%x": 10 }),
{ "%i": "9", "%x": "a" },
) &&
std.assertEqual(
std.objectFieldsAll(std.mapWithKey(function(k, v) k + ":" + v, { a: 1, b: 2, c:: 3 })),
["a", "b"],
) &&

true

0 comments on commit cebde59

Please sign in to comment.