pkg/list: adding range function as a builtin
this commit adds a range function which allows to generate a sequence
of numbers from a start point adding steps till a limit is reached.
For instance:
Range(0, 5, 2)
results in
[0, 2, 4]
Change-Id: I29eddca25c4c5206c226d518d617b7b69ab8852e
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3483
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index 0610c90..a9838bb 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -189,6 +189,30 @@
test("list", `list.Product("foo")`),
`_|_(cannot use "foo" (type string) as list in argument 1 to list.Product)`,
}, {
+ test("list", `list.Range(0, 5, 0)`),
+ `_|_(error in call to list.Range: step must be non zero)`,
+ }, {
+ test("list", `list.Range(5, 0, 1)`),
+ `_|_(error in call to list.Range: end must be greater than start when step is positive)`,
+ }, {
+ test("list", `list.Range(0, 5, -1)`),
+ `_|_(error in call to list.Range: end must be less than start when step is negative)`,
+ }, {
+ test("list", `list.Range(0, 5, 1)`),
+ `[0,1,2,3,4]`,
+ }, {
+ test("list", `list.Range(0, 1, 1)`),
+ `[0]`,
+ }, {
+ test("list", `list.Range(0, 5, 2)`),
+ `[0,2,4]`,
+ }, {
+ test("list", `list.Range(5, 0, -1)`),
+ `[5,4,3,2,1]`,
+ }, {
+ test("list", `list.Range(0, 5, 0.5)`),
+ `[0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5]`,
+ }, {
test("list", `list.Slice([1, 2, 3, 4], 1, 3)`),
`[2,3]`,
}, {
diff --git a/cue/builtins.go b/cue/builtins.go
index 037fa33..7337780 100644
--- a/cue/builtins.go
+++ b/cue/builtins.go
@@ -904,6 +904,47 @@
}
},
}, {
+ Name: "Range",
+ Params: []kind{numKind, numKind, numKind},
+ Result: listKind,
+ Func: func(c *callCtxt) {
+ start, limit, step := c.decimal(0), c.decimal(1), c.decimal(2)
+ c.ret, c.err = func() (interface{}, error) {
+ if step.IsZero() {
+ return nil, fmt.Errorf("step must be non zero")
+ }
+
+ if !step.Negative && +1 == start.Cmp(limit) {
+ return nil, fmt.Errorf("end must be greater than start when step is positive")
+ }
+
+ if step.Negative && -1 == start.Cmp(limit) {
+ return nil, fmt.Errorf("end must be less than start when step is negative")
+ }
+
+ var vals []*internal.Decimal
+ num := start
+ for {
+ if !step.Negative && -1 != num.Cmp(limit) {
+ break
+ }
+
+ if step.Negative && +1 != num.Cmp(limit) {
+ break
+ }
+
+ vals = append(vals, num)
+ d := apd.New(0, 0)
+ _, err := internal.BaseContext.Add(d, step, num)
+ if err != nil {
+ return nil, err
+ }
+ num = d
+ }
+ return vals, nil
+ }()
+ },
+ }, {
Name: "Sum",
Params: []kind{listKind},
Result: numKind,
diff --git a/pkg/list/math.go b/pkg/list/math.go
index 4ce4ba3..d91adb5 100644
--- a/pkg/list/math.go
+++ b/pkg/list/math.go
@@ -86,6 +86,52 @@
return d, nil
}
+// Range generates a list of numbers using a start value, a limit value, and a
+// step value.
+//
+// For instance:
+//
+// Range(0, 5, 2)
+//
+// results in
+//
+// [0, 2, 4]
+//
+func Range(start, limit, step *internal.Decimal) ([]*internal.Decimal, error) {
+ if step.IsZero() {
+ return nil, fmt.Errorf("step must be non zero")
+ }
+
+ if !step.Negative && +1 == start.Cmp(limit) {
+ return nil, fmt.Errorf("end must be greater than start when step is positive")
+ }
+
+ if step.Negative && -1 == start.Cmp(limit) {
+ return nil, fmt.Errorf("end must be less than start when step is negative")
+ }
+
+ var vals []*internal.Decimal
+ num := start
+ for {
+ if !step.Negative && -1 != num.Cmp(limit) {
+ break
+ }
+
+ if step.Negative && +1 != num.Cmp(limit) {
+ break
+ }
+
+ vals = append(vals, num)
+ d := apd.New(0, 0)
+ _, err := internal.BaseContext.Add(d, step, num)
+ if err != nil {
+ return nil, err
+ }
+ num = d
+ }
+ return vals, nil
+}
+
// Sum returns the sum of a list non empty xs.
func Sum(xs []*internal.Decimal) (*internal.Decimal, error) {
d := apd.New(0, 0)