// Copyright 2018 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.syntax; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.UnmodifiableIterator; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter; import java.util.AbstractList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; /** * A sequence returned by the {@code range} function invocation. * *
Instead 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); } } }