start
to stop
, using a "
+ "step
increment. If a single argument is provided, items will "
@@ -658,7 +658,7 @@ public Integer invoke(String value) throws EvalException {
)
private static final BuiltinFunction range =
new BuiltinFunction("range") {
- public MutableList> invoke(
+ public SkylarkListInstead of eagerly allocating an array with all elements of the sequence, this class uses + * simple math to compute a value at each index. This is particularly useful when range is huge or + * only a few elements from it are used. + * + *
Eventually {@code range} function should produce an instance of the {@code range} type as is
+ * the case in Python 3, but for now to preserve backwards compatibility with Python 2, {@code list}
+ * is returned.
+ */
+@SkylarkModule(
+ name = "range",
+ category = SkylarkModuleCategory.BUILTIN,
+ doc =
+ "A language built-in type to support ranges. Example of range literal:
"
+ + "
x = range(1, 10, 3)" + + "Accessing elements is possible using indexing (starts from
0
):e = x[1] # e == 2" + + "Ranges do not support the
+
operator for concatenation."
+ + "Similar to strings, ranges support slice operations:"
+ + "range(10)[1:3] # range(1, 3)\n" + + "range(10)[::2] # range(0, 10, 2)\n" + + "range(10)[3:0:-1] # range(3, 0, -1)" + + "Ranges are immutable, as in Python 3.") +public final class RangeList extends SkylarkList
Usage of this method is not recommended, since it completely defeats the purpose of lazy + * computation by eagerly computing the result. + * + * @return A materialized version of the range that can be used as a + *
list+ * type. + */ + MutableList
{@code start} and {@code stop} define a half-open interval + * + *
[start, stop)+ */ + private static class Slice { + + private final int start; + private final int stop; + private final int step; + + private Slice(int start, int stop, int step) { + this.start = start; + this.stop = stop; + this.step = step; + } + + /** + * Computes slice indices for the requested range slice. + * + *
The implementation is based on CPython
+ * https://github.com/python/cpython/blob/09bb918a61031377d720f1a0fa1fe53c962791b6/Objects/sliceobject.c#L366-L509
+ */
+ public static Slice from(
+ int length, Object startObj, Object endObj, Object stepObj, Location loc)
+ throws EvalException {
+ int start;
+ int stop;
+ int step;
+
+ if (stepObj == Runtime.NONE) {
+ step = 1;
+ } else if (stepObj instanceof Integer) {
+ step = (Integer) stepObj;
+ } else {
+ throw new EvalException(
+ loc, String.format("slice step must be an integer, not '%s'", stepObj));
+ }
+ if (step == 0) {
+ throw new EvalException(loc, "slice step cannot be zero");
+ }
+
+ int upper; // upper bound for stop (exclusive)
+ int lower; // lower bound for start (inclusive)
+ if (step < 0) {
+ lower = -1;
+ upper = length - 1;
+ } else {
+ lower = 0;
+ upper = length;
+ }
+
+ if (startObj == Runtime.NONE) {
+ start = step < 0 ? upper : lower;
+ } else if (startObj instanceof Integer) {
+ start = (Integer) startObj;
+ if (start < 0) {
+ start += length;
+ start = Math.max(start, lower);
+ } else {
+ start = Math.min(start, upper);
+ }
+ } else {
+ throw new EvalException(
+ loc, String.format("slice start must be an integer, not '%s'", startObj));
+ }
+ if (endObj == Runtime.NONE) {
+ stop = step < 0 ? lower : upper;
+ } else if (endObj instanceof Integer) {
+ stop = (Integer) endObj;
+ if (stop < 0) {
+ stop += length;
+ stop = Math.max(stop, lower);
+ } else {
+ stop = Math.min(stop, upper);
+ }
+ } else {
+ throw new EvalException(
+ loc, String.format("slice end must be an integer, not '%s'", endObj));
+ }
+ return new Slice(start, stop, step);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
index 6be38d5f8fd5eb..023a87d1d08079 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import com.google.common.collect.UnmodifiableIterator;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.skylarkinterface.Param;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
@@ -25,9 +26,12 @@
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
import com.google.devtools.build.lib.syntax.SkylarkMutable.BaseMutableList;
+import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.RandomAccess;
import javax.annotation.Nullable;
@@ -116,7 +120,8 @@ public String toString() {
@Override
public boolean equals(Object object) {
return (this == object)
- || ((object != null) && (this.getClass() == object.getClass())
+ || ((object != null)
+ && (this.getClass() == object.getClass())
&& getContentsUnsafe().equals(((SkylarkList) object).getContentsUnsafe()));
}
@@ -345,7 +350,7 @@ public static