pkg/list: adding slice function

atm. cue only allows to retrieve slices by using [low:high] expression
similar to golang. since the slice expression is going to be deprecated
/ removed in future version this function adds the same functionality.

Change-Id: Ibd6e0c6e9a151d9eebc3763645b03b0a00478291
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3160
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index 6e63183..d9f8748 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -171,6 +171,21 @@
 		test("list", `list.Product("foo")`),
 		`_|_(cannot use "foo" (type string) as list in argument 1 to list.Product)`,
 	}, {
+		test("list", `list.Slice([1, 2, 3, 4], 1, 3)`),
+		`[2,3]`,
+	}, {
+		test("list", `list.Slice([1, 2, 3, 4], -1, 3)`),
+		`_|_(error in call to list.Slice: negative slice index)`,
+	}, {
+		test("list", `list.Slice([1, 2, 3, 4], 3, 1)`),
+		`_|_(error in call to list.Slice: invalid slice index: 3 > 1)`,
+	}, {
+		test("list", `list.Slice([1, 2, 3, 4], 5, 5)`),
+		`_|_(error in call to list.Slice: slice bounds out of range)`,
+	}, {
+		test("list", `list.Slice([1, 2, 3, 4], 1, 5)`),
+		`_|_(error in call to list.Slice: slice bounds out of range)`,
+	}, {
 		test("list", `list.Sum([1, 2, 3, 4])`),
 		`10`,
 	}, {
diff --git a/cue/builtins.go b/cue/builtins.go
index efc4ffd..a2e2ff9 100644
--- a/cue/builtins.go
+++ b/cue/builtins.go
@@ -663,6 +663,32 @@
 	},
 	"list": &builtinPkg{
 		native: []*builtin{{
+			Name:   "Slice",
+			Params: []kind{listKind, intKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				a, i, j := c.list(0), c.int(1), c.int(2)
+				c.ret, c.err = func() (interface{}, error) {
+					if i < 0 || j < 0 {
+						return nil, fmt.Errorf("negative slice index")
+					}
+
+					if i > j {
+						return nil, fmt.Errorf("invalid slice index: %v > %v", i, j)
+					}
+
+					if i > len(a) {
+						return nil, fmt.Errorf("slice bounds out of range")
+					}
+
+					if j > len(a) {
+						return nil, fmt.Errorf("slice bounds out of range")
+					}
+
+					return a[i:j], nil
+				}()
+			},
+		}, {
 			Name:   "MinItems",
 			Params: []kind{listKind, intKind},
 			Result: boolKind,
diff --git a/pkg/list/list.go b/pkg/list/list.go
index 8955103..f06f94f 100644
--- a/pkg/list/list.go
+++ b/pkg/list/list.go
@@ -22,6 +22,37 @@
 	"cuelang.org/go/cue"
 )
 
+// Slice extracts the consecutive elements from a list starting from position i
+// up till, but not including, position j, where 0 <= i < j <= len(a).
+//
+// For instance:
+//
+//    Slice([1, 2, 3, 4], 1, 3)
+//
+// results in
+//
+//    [2, 3]
+//
+func Slice(a []cue.Value, i, j int) ([]cue.Value, error) {
+	if i < 0 {
+		return nil, fmt.Errorf("negative slice index")
+	}
+
+	if i > j {
+		return nil, fmt.Errorf("invalid slice index: %v > %v", i, j)
+	}
+
+	if i > len(a) {
+		return nil, fmt.Errorf("slice bounds out of range")
+	}
+
+	if j > len(a) {
+		return nil, fmt.Errorf("slice bounds out of range")
+	}
+
+	return a[i:j], nil
+}
+
 // MinItems reports whether a has at least n items.
 func MinItems(a []cue.Value, n int) bool {
 	return len(a) <= n