pkg/list: add builtins for list concatenation and repetition
These will be used to rewrite from the current use of
list operators.
Change-Id: I2a6461ac7d45649950160bb835cecf2c408593b4
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8064
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/pkg/list/list.go b/pkg/list/list.go
index be8493b..dadc398 100644
--- a/pkg/list/list.go
+++ b/pkg/list/list.go
@@ -123,6 +123,47 @@
return flattenN(xs, depth)
}
+// Repeat returns a new list consisting of count copies of list x.
+//
+// For instance:
+//
+// Repeat([1, 2], 2)
+//
+// results in
+//
+// [1, 2, 1, 2]
+//
+func Repeat(x []cue.Value, count int) ([]cue.Value, error) {
+ if count < 0 {
+ return nil, fmt.Errorf("negative count")
+ }
+ var a []cue.Value
+ for i := 0; i < count; i++ {
+ a = append(a, x...)
+ }
+ return a, nil
+}
+
+// Concat takes a list of lists and concatenates them.
+//
+// Concat([a, b, c]) is equivalent to
+//
+// [ for x in a {x}, for x in b {x}, for x in c {x} ]
+//
+func Concat(a []cue.Value) ([]cue.Value, error) {
+ var res []cue.Value
+ for _, e := range a {
+ iter, err := e.List()
+ if err != nil {
+ return nil, err
+ }
+ for iter.Next() {
+ res = append(res, iter.Value())
+ }
+ }
+ return res, nil
+}
+
// Take reports the prefix of length n of list x, or x itself if n > len(x).
//
// For instance:
diff --git a/pkg/list/pkg.go b/pkg/list/pkg.go
index c5f74e3..f62ae69 100644
--- a/pkg/list/pkg.go
+++ b/pkg/list/pkg.go
@@ -44,6 +44,31 @@
}
},
}, {
+ Name: "Repeat",
+ Params: []internal.Param{
+ {Kind: adt.ListKind},
+ {Kind: adt.IntKind},
+ },
+ Result: adt.ListKind,
+ Func: func(c *internal.CallCtxt) {
+ x, count := c.List(0), c.Int(1)
+ if c.Do() {
+ c.Ret, c.Err = Repeat(x, count)
+ }
+ },
+ }, {
+ Name: "Concat",
+ Params: []internal.Param{
+ {Kind: adt.ListKind},
+ },
+ Result: adt.ListKind,
+ Func: func(c *internal.CallCtxt) {
+ a := c.List(0)
+ if c.Do() {
+ c.Ret, c.Err = Concat(a)
+ }
+ },
+ }, {
Name: "Take",
Params: []internal.Param{
{Kind: adt.ListKind},
diff --git a/pkg/list/testdata/list.txtar b/pkg/list/testdata/list.txtar
new file mode 100644
index 0000000..6291774
--- /dev/null
+++ b/pkg/list/testdata/list.txtar
@@ -0,0 +1,118 @@
+-- in.cue --
+import "list"
+
+repeat: {
+ [string]: {x: _, n: int, v: list.Repeat(x, n)}
+
+ t1: {x: [], n: 0}
+ t2: {x: [1], n: 0}
+
+ t3: {x: [1], n: 1}
+ t4: {x: [1, 2], n: 1}
+
+ t5: {x: [], n: 3}
+ t6: {x: [1, 2], n: 3}
+ t7: {x: [1, 2, 3, 4], n: 3}
+
+ t8: {x: [1], n: -1}
+}
+concat: {
+ [string]: {x: _, v: list.Concat(x)}
+
+ t1: x: []
+ t2: x: [[]]
+
+ t3: x: [[1]]
+ t4: x: [[1, 2]]
+ t5: x: [[1], [2]]
+
+ t6: x: [[1, 2], [3, 4]]
+
+ t7: x: 1
+ t8: x: [1, [2]]
+}
+-- out/list --
+Errors:
+error in call to list.Concat: cannot use value 1 (type int) as list
+error in call to list.Repeat: negative count
+concat.t7.v: cannot use 1 (type int) as list in argument 1 to list.Concat:
+ ./in.cue:30:12
+
+Result:
+repeat: {
+ t1: {
+ x: []
+ n: 0
+ v: []
+ }
+ t2: {
+ x: [1]
+ n: 0
+ v: []
+ }
+ t3: {
+ x: [1]
+ n: 1
+ v: [1]
+ }
+ t4: {
+ x: [1, 2]
+ n: 1
+ v: [1, 2]
+ }
+ t5: {
+ x: []
+ n: 3
+ v: []
+ }
+ t6: {
+ x: [1, 2]
+ n: 3
+ v: [1, 2, 1, 2, 1, 2]
+ }
+ t7: {
+ x: [1, 2, 3, 4]
+ n: 3
+ v: [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
+ }
+ t8: {
+ x: [1]
+ n: -1
+ v: _|_ // error in call to list.Repeat: negative count (and 1 more errors)
+ }
+}
+concat: {
+ t1: {
+ x: []
+ v: []
+ }
+ t2: {
+ x: [[]]
+ v: []
+ }
+ t3: {
+ x: [[1]]
+ v: [1]
+ }
+ t4: {
+ x: [[1, 2]]
+ v: [1, 2]
+ }
+ t5: {
+ x: [[1], [2]]
+ v: [1, 2]
+ }
+ t6: {
+ x: [[1, 2], [3, 4]]
+ v: [1, 2, 3, 4]
+ }
+ t7: {
+ x: 1
+ v: _|_ // concat.t7.v: cannot use 1 (type int) as list in argument 1 to list.Concat
+ }
+ t8: {
+ x: [1, [2]]
+ v: _|_ // error in call to list.Concat: cannot use value 1 (type int) as list (and 1 more errors)
+ }
+}
+