Skip to content

Commit

Permalink
change(eval): implement std.repeat as built-in
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardosm committed Nov 4, 2024
1 parent f27897d commit 3c5485c
Show file tree
Hide file tree
Showing 21 changed files with 120 additions and 21 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 @@ -744,6 +744,7 @@ pub(super) enum BuiltInFunc {
Foldl,
Foldr,
Range,
Repeat,
Slice,
Join,
Reverse,
Expand Down
6 changes: 6 additions & 0 deletions rsjsonnet-lang/src/program/eval/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,12 @@ impl<'p> Evaluator<'_, 'p> {
self.state_stack.push(State::DoThunk(arg1.view()));
self.state_stack.push(State::DoThunk(arg0.view()));
}
BuiltInFunc::Repeat => {
let [arg0, arg1] = check_num_args(args);
self.state_stack.push(State::StdRepeat);
self.state_stack.push(State::DoThunk(arg1.view()));
self.state_stack.push(State::DoThunk(arg0.view()));
}
BuiltInFunc::Slice => {
let [arg0, arg1, arg2, arg3] = check_num_args(args);
self.state_stack.push(State::StdSlice);
Expand Down
1 change: 1 addition & 0 deletions rsjsonnet-lang/src/program/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,7 @@ impl<'p, 'a> Evaluator<'a, 'p> {
State::StdFoldr { init } => self.do_std_foldr(init)?,
State::StdFoldrItem { func, item } => self.do_std_foldr_item(func, item)?,
State::StdRange => self.do_std_range()?,
State::StdRepeat => self.do_std_repeat()?,
State::StdSlice => self.do_std_slice()?,
State::StdJoin => self.do_std_join()?,
State::StdJoinStrItem { sep } => self.do_std_join_str_item(sep)?,
Expand Down
1 change: 1 addition & 0 deletions rsjsonnet-lang/src/program/eval/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ pub(super) enum State<'p> {
item: GcView<ThunkData<'p>>,
},
StdRange,
StdRepeat,
StdSlice,
StdJoin,
StdJoinStrItem {
Expand Down
42 changes: 42 additions & 0 deletions rsjsonnet-lang/src/program/eval/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1839,6 +1839,48 @@ impl<'p> Evaluator<'_, 'p> {
Ok(())
}

pub(super) fn do_std_repeat(&mut self) -> EvalResult<()> {
let count = self.value_stack.pop().unwrap();
let value = self.value_stack.pop().unwrap();

let count = self.expect_std_func_arg_number(count, "repeat", 1)?;

let Some(count) = float::try_to_i32_exact(count).and_then(|v| usize::try_from(v).ok())
else {
return Err(self.report_error(EvalErrorKind::Other {
span: None,
message: format!("invalid count value {count}"),
}));
};

match value {
ValueData::String(s) => {
self.value_stack
.push(ValueData::String(s.repeat(count).into()));
Ok(())
}
ValueData::Array(array) => {
let array = array.view();
self.value_stack.push(ValueData::Array(
self.program.gc_alloc(
std::iter::repeat(&array)
.take(count)
.flat_map(|a| a.iter())
.cloned()
.collect(),
),
));
Ok(())
}
_ => Err(self.report_error(EvalErrorKind::InvalidStdFuncArgType {
func_name: "repeat".into(),
arg_index: 0,
expected_types: vec![EvalErrorValueType::String, EvalErrorValueType::Array],
got_type: EvalErrorValueType::from_value(&value),
})),
}
}

pub(super) fn do_std_slice(&mut self) -> EvalResult<()> {
let step = self.value_stack.pop().unwrap();
let end = self.value_stack.pop().unwrap();
Expand Down
7 changes: 0 additions & 7 deletions rsjsonnet-lang/src/program/std.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ limitations under the License.
{
local std = self,

repeat(what, count)::
local joiner =
if std.isString(what) then ''
else if std.isArray(what) then []
else error 'std.repeat first argument must be an array or a string';
std.join(joiner, std.makeArray(count, function(i) what)),

mapWithKey(func, obj)::
if !std.isFunction(func) then
error ('std.mapWithKey first param must be function, got ' + std.type(func))
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 @@ -186,6 +186,7 @@ impl<'p> Program<'p> {
add_simple("foldl", BuiltInFunc::Foldl, &["func", "arr", "init"]);
add_simple("foldr", BuiltInFunc::Foldr, &["func", "arr", "init"]);
add_simple("range", BuiltInFunc::Range, &["from", "to"]);
add_simple("repeat", BuiltInFunc::Repeat, &["what", "count"]);
add_simple(
"slice",
BuiltInFunc::Slice,
Expand Down
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>:110:25
--> <stdlib>:103:25
|
110 | manifestToml(value):: std.manifestTomlEx(value, ' '),
103 | 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>:110:25
--> <stdlib>:103:25
|
110 | manifestToml(value):: std.manifestTomlEx(value, ' '),
103 | 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>:110:25
--> <stdlib>:103:25
|
110 | manifestToml(value):: std.manifestTomlEx(value, ' '),
103 | 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>:110:25
--> <stdlib>:103:25
|
110 | manifestToml(value):: std.manifestTomlEx(value, ' '),
103 | 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>:110:25
--> <stdlib>:103:25
|
110 | manifestToml(value):: std.manifestTomlEx(value, ' '),
103 | 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>:110:25
--> <stdlib>:103:25
|
110 | manifestToml(value):: std.manifestTomlEx(value, ' '),
103 | 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>:110:25
--> <stdlib>:103:25
|
110 | manifestToml(value):: std.manifestTomlEx(value, ' '),
103 | manifestToml(value):: std.manifestTomlEx(value, ' '),
| -------------------------------
note: while evaluating call to function
--> object_assert_failed_value.jsonnet:1:1
Expand Down
1 change: 1 addition & 0 deletions ui-tests/fail/stdlib/repeat/invalid_arg_1.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.repeat(null, 0)
8 changes: 8 additions & 0 deletions ui-tests/fail/stdlib/repeat/invalid_arg_1.jsonnet.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: first argument of `std.repeat` is expected to be string or array, got null
note: while evaluating call to `repeat`
--> invalid_arg_1.jsonnet:1:1
|
1 | std.repeat(null, 0)
| -------------------
note: during top-level value evaluation

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

1 change: 1 addition & 0 deletions ui-tests/fail/stdlib/repeat/non_integer_count.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.repeat("", 1.5)
8 changes: 8 additions & 0 deletions ui-tests/fail/stdlib/repeat/non_integer_count.jsonnet.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: invalid count value 1.5
note: while evaluating call to `repeat`
--> non_integer_count.jsonnet:1:1
|
1 | std.repeat("", 1.5)
| -------------------
note: during top-level value evaluation

27 changes: 27 additions & 0 deletions ui-tests/pass/stdlib/repeat.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
std.assertEqual(std.repeat("", 0), "") &&
std.assertEqual(std.repeat("", 1), "") &&
std.assertEqual(std.repeat("", 2), "") &&
std.assertEqual(std.repeat("", 1000), "") &&
std.assertEqual(std.repeat("a", 0), "") &&
std.assertEqual(std.repeat("a", 1), "a") &&
std.assertEqual(std.repeat("a", 2), "aa") &&
std.assertEqual(std.repeat("a", 5), "aaaaa") &&
std.assertEqual(std.repeat("ab", 0), "") &&
std.assertEqual(std.repeat("ab", 1), "ab") &&
std.assertEqual(std.repeat("ab", 2), "abab") &&
std.assertEqual(std.repeat("ab", 5), "ababababab") &&

std.assertEqual(std.repeat([], 0), []) &&
std.assertEqual(std.repeat([], 1), []) &&
std.assertEqual(std.repeat([], 2), []) &&
std.assertEqual(std.repeat([], 1000), []) &&
std.assertEqual(std.repeat([1], 0), []) &&
std.assertEqual(std.repeat([1], 1), [1]) &&
std.assertEqual(std.repeat([1], 2), [1, 1]) &&
std.assertEqual(std.repeat([1], 5), [1, 1, 1, 1, 1]) &&
std.assertEqual(std.repeat(["a", "b"], 0), []) &&
std.assertEqual(std.repeat(["a", "b"], 1), ["a", "b"]) &&
std.assertEqual(std.repeat(["a", "b"], 2), ["a", "b", "a", "b"]) &&
std.assertEqual(std.repeat(["a", "b"], 5), ["a", "b", "a", "b", "a", "b", "a", "b", "a", "b"]) &&

true

0 comments on commit 3c5485c

Please sign in to comment.