| // Copyright 2014 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 net.starlark.java.eval; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.ObjectArrays; |
| import java.util.AbstractCollection; |
| import java.util.AbstractList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import net.starlark.java.annot.StarlarkBuiltin; |
| |
| /** A Tuple is an immutable finite sequence of values. */ |
| @StarlarkBuiltin( |
| name = "tuple", |
| category = "core", |
| doc = |
| "The built-in tuple type. Example tuple expressions:<br>" |
| + "<pre class=language-python>x = (1, 2, 3)</pre>" |
| + "Accessing elements is possible using indexing (starts from <code>0</code>):<br>" |
| + "<pre class=language-python>e = x[1] # e == 2</pre>" |
| + "Lists support the <code>+</code> operator to concatenate two tuples. Example:<br>" |
| + "<pre class=language-python>x = (1, 2) + (3, 4) # x == (1, 2, 3, 4)\n" |
| + "x = (\"a\", \"b\")\n" |
| + "x += (\"c\",) # x == (\"a\", \"b\", \"c\")</pre>" |
| + "Similar to lists, tuples support slice operations:" |
| + "<pre class=language-python>('a', 'b', 'c', 'd')[1:3] # ('b', 'c')\n" |
| + "('a', 'b', 'c', 'd')[::2] # ('a', 'c')\n" |
| + "('a', 'b', 'c', 'd')[3:0:-1] # ('d', 'c', 'b')</pre>" |
| + "Tuples are immutable, therefore <code>x[1] = \"a\"</code> is not supported.") |
| public abstract class Tuple extends AbstractList<Object> |
| implements Sequence<Object>, Comparable<Tuple> { |
| |
| // Prohibit instantiation outside of package. |
| Tuple() {} |
| |
| // The shared (sole) empty tuple. |
| private static final Tuple EMPTY = new RegularTuple(new Object[] {}); |
| |
| /** Returns the empty tuple. */ |
| public static Tuple empty() { |
| return EMPTY; |
| } |
| |
| /** Returns a Tuple that wraps the specified array, which must not be subsequently modified. */ |
| static Tuple wrap(Object[] array) { |
| switch (array.length) { |
| case 0: |
| return EMPTY; |
| case 1: |
| return new SingletonTuple(array[0]); |
| default: |
| return new RegularTuple(array); |
| } |
| } |
| |
| /** Returns a tuple containing the given elements. */ |
| public static Tuple copyOf(Iterable<?> seq) { |
| if (seq instanceof Tuple) { |
| return (Tuple) seq; |
| } |
| return wrap(Iterables.toArray(seq, Object.class)); |
| } |
| |
| /** Returns a tuple containing the given elements. */ |
| public static Tuple of(Object... elems) { |
| return wrap(Arrays.copyOf(elems, elems.length)); |
| } |
| |
| /** Returns a two-element tuple. */ |
| public static Tuple pair(Object a, Object b) { |
| // Equivalent to of(a, b) but avoids variadic array allocation. |
| return wrap(new Object[] {a, b}); |
| } |
| |
| /** Returns a three-element tuple. */ |
| public static Tuple triple(Object a, Object b, Object c) { |
| // Equivalent to of(a, b, c) but avoids variadic array allocation. |
| return wrap(new Object[] {a, b, c}); |
| } |
| |
| /** Returns a tuple that is the concatenation of two tuples. */ |
| public static Tuple concat(Tuple x, Tuple y) { |
| if (x.isEmpty()) { |
| return y; |
| } else if (y.isEmpty()) { |
| return x; |
| } else { |
| Object[] xelems = |
| x instanceof SingletonTuple |
| ? new Object[] {((SingletonTuple) x).elem} |
| : ((RegularTuple) x).elems; |
| Object[] yelems = |
| y instanceof SingletonTuple |
| ? new Object[] {((SingletonTuple) y).elem} |
| : ((RegularTuple) y).elems; |
| return wrap(ObjectArrays.concat(xelems, yelems, Object.class)); |
| } |
| } |
| |
| @Override |
| public int compareTo(Tuple that) { |
| return Sequence.compare(this, that); |
| } |
| |
| @Override |
| public boolean equals(Object that) { |
| // This slightly violates the java.util.List equivalence contract |
| // because it considers the class, not just the elements. |
| // This is needed because in Starlark tuples are never equal to lists, however in Java they both |
| // implement List interface. |
| return this == that || (that instanceof Tuple && Sequence.sameElems(this, ((Tuple) that))); |
| } |
| |
| // TODO(adonovan): StarlarkValue has 3 String methods yet still we need this fourth. Why? |
| @Override |
| public String toString() { |
| return Starlark.repr(this); |
| } |
| |
| /** |
| * Returns a new ImmutableList<T> backed by {@code array}, which must not be subsequently |
| * modified. |
| */ |
| // TODO(adonovan): move this somewhere more appropriate. |
| static <T> ImmutableList<T> wrapImmutable(Object[] array) { |
| // Construct an ImmutableList that shares the array. |
| // ImmutableList relies on the implementation of Collection.toArray |
| // not subsequently modifying the returned array. |
| return ImmutableList.copyOf( |
| new AbstractCollection<T>() { |
| @Override |
| public Object[] toArray() { |
| return array; |
| } |
| |
| @Override |
| public int size() { |
| return array.length; |
| } |
| |
| @Override |
| public Iterator<T> iterator() { |
| throw new UnsupportedOperationException(); |
| } |
| }); |
| } |
| |
| /** Returns a Tuple containing n consecutive repeats of this tuple. */ |
| abstract Tuple repeat(StarlarkInt n) throws EvalException; |
| } |