Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/builtins.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ In future, each builtin will be associated with a feature (many builtins could b
| [x - y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-minus) | _ |
| [x * y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-mul) | _ |
| [numbers.range](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-numbersrange) | _ |
| [numbers.range_step](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-numbersrange_step) | _ |
| [numbers.range_step](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-numbersrange_step) | Supported. When `start > stop`, step is negated automatically to produce a descending range, matching OPA behavior. |
| [x + y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-plus) | _ |
| [rand.intn](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-randintn) | _ |
| [x % y](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-numbers-rem) | _ |
Expand Down
22 changes: 12 additions & 10 deletions src/builtins/numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,22 +140,24 @@ fn range_step(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -
_ => (),
}

if strict && (!incr.is_integer() || incr <= Number::from(0u64)) {
bail!(params[2].span().error("step must be a positive integer"))
if !incr.is_integer() || incr <= Number::from(0u64) {
if strict {
bail!(params[2].span().error("step must be a positive integer"))
}
return Ok(Value::Undefined);
}

let (incr, num_elements) = match (v2.sub(&v1)?.as_i64(), incr.as_i64()) {
(Some(v), Some(incr)) if v >= 0 => (incr, v / incr + 1),
(Some(v), Some(incr)) => (-incr, -v / incr + 1),
_ => bail!(span.error("could not determine number of elements")),
let step = if v1 <= v2 {
incr
} else {
Number::from(0u64).sub(&incr)?
};

let mut values = Vec::with_capacity(num_elements as usize);
let incr = Number::from(incr);
let mut values = Vec::new();
let mut v = v1;
while (v <= v2 && incr.is_positive()) || (v >= v2 && !incr.is_positive()) {
while (v <= v2 && step.is_positive()) || (v >= v2 && !step.is_positive()) {
values.push(Value::from(v.clone()));
v.add_assign(&incr)?;
v.add_assign(&step)?;
// Guard vector growth as the stepped range accumulates.
enforce_limit()?;
}
Expand Down
59 changes: 59 additions & 0 deletions tests/interpreter/cases/builtins/numbers/range_step.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

cases:
- note: ascending
data: {}
modules:
- |
package test
x = numbers.range_step(1, 5, 1)
query: data.test.x
want_result: [1, 2, 3, 4, 5]

- note: step-2
data: {}
modules:
- |
package test
x = numbers.range_step(0, 10, 2)
query: data.test.x
want_result: [0, 2, 4, 6, 8, 10]

- note: descending
data: {}
modules:
- |
package test
x = numbers.range_step(5, 1, 1)
query: data.test.x
want_result: [5, 4, 3, 2, 1]

- note: single-element
data: {}
modules:
- |
package test
x = numbers.range_step(3, 3, 1)
query: data.test.x
want_result: [3]

- note: non-integer-step-undefined
strict: false
data: {}
modules:
- |
package test
x = numbers.range_step(1, 5, 0.5)
query: data.test.x
no_result: true

- note: zero-step-undefined
strict: false
data: {}
modules:
- |
package test
x = numbers.range_step(1, 5, 0)
query: data.test.x
no_result: true
Loading