From 9d4ac63b28b234a8ae43a45b5be042c1fbd82d90 Mon Sep 17 00:00:00 2001 From: Mahmoud Abdelkader Date: Tue, 9 Feb 2021 14:56:00 -0800 Subject: [PATCH] Update Starlark to Tip (https://github.com/bazelbuild/bazel/commit/be96ade1cb6033d68d30ef9d20b063b87b86e646#diff-37cd73cae488b2421089c2b94c9a869cfb4a5f109753f60602b1dd20959bc573) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changelog for (Master)[https://github.com/bazelbuild/bazel/commit/be96ade1cb6033d68d30ef9d20b063b87b86e646#diff-37cd73cae488b2421089c2b94c9a869cfb4a5f109753f60602b1dd20959bc573]: - Builtins injection: Rename _internal to _builtins and add functionality … This object is used in @_builtins .bzl files but is not accessible to user code. The `toplevel` and `native` fields give access to the *native* (pre-injected) values of symbols whose post-injected values are available to user .bzl files. For instance, -`_builtins.toplevel.CcInfo` in @_builtins code gives the original CcInfo definition from the Java code, even if `CcInfo` in a regular .bzl file refers to an injected value. (To avoid ambiguity, `CcInfo` itself is not a valid top-level symbol for @_builtins .bzl files.) The `internal` field contains any value registered via ConfiguredRuleClassProvider.Builder#addStarlarkBuiltinsInternal(). The `getFlag()` method can retrieve the values of StarlarkSemantics flags. Because of how flags are stored, it requires that a default value be given. - Starlark: better errors on integer overflow … Before: ``` >> [1] * (1 << 30) * (1 << 5) Exception in thread "main" net.starlark.java.eval.Starlark$UncheckedEvalException: java.lang.ArrayIndexOutOfBoundsException: arraycopy: last destination index 1073741824 out of bounds for object array[0] (Starlark stack: [@:1:1]) net.starlark.java.eval.Starlark.fastcall(Starlark.java:621) net.starlark.java.eval.Starlark.execFileProgram(Starlark.java:892) ... exit 1 ``` Now: ``` >> [1] * (1 << 30) * (1 << 5) Traceback (most recent call last): File "", line 1, column 21, in Error: got 34359738368 for repeat, want value in signed 32-bit range ``` - starlark: int: ignore base prefix unless matches explicit base … Previously, when int was called with an explicit base, it would report an error if the digit string starts with a base prefix for a different base, such as int("0b101", 16). Now, it uses the base prefix only if it matches the requested base, so the example above would return 0x0b101, as would int("0x0b101", 16). See github.com/google/starlark-go/pull/344 - starlark: delete StarlarkFile.parseWithPrelude … It was an obstacle to interpreter optimizations, as it caused a single StarlarkFile to contain statements whose locations come from different files. The previous workspace logic used parseWithPrelude to concatenate all the statements of the WORKSPACE files (prefix + main + suffix) and then split them into chunks, ignoring---crucially, several days work revealed---file boundaries. The splitChunks logic now achieves the same effect by representing chunks as lists of nonempty partial files, and calling execute() for each partial file in the chunk. The chunk splitting tests have been rewritten, clarified, and expanded to exercise the chunk-spans-file-boundaries case. Many thanks to Jon Brandvein for hours of help trying to figure out what the workspace logic does. It is not our team's finest work. Also: - minor consequent simplifications to parser and lexer. - narrow the scope of various try blocks (sorry, messy diff). - starlark: remove redundant pattern validity check in Printer … - starlark: support %x, %X, and %o conversions in 'string % number' … Also, improve operand type error message to show type, not value. See github.com/bazelbuild/starlark/pull/154 for spec change. - Starlark: StarlarkInt.{floordiv,mod} now work with 64-bit integers … * No special case for 32-bit integers, division is slow anyway * Switch `mod` to use `Math.floorMod` for simplicity/safety Closes #12667. - starlark: delete deprecated EvalException(Location) constructor … ...as there are, at long last, no further uses within Bazel. Also, clarify doc comments regarding EvalException.callstack. - bazel analysis: preparatory cleanup to SRCTU error reporting … This change causes the error handling logic in StarlarkRuleConfiguredTargetUtil (SRCTU) to avoid the EvalException(Location) constructor. Instead, the auxiliary location, if any, of provider instantiation is simply prepended to the error message (see the infoError function) in anticipation of being printed after a newline. For example: ERROR p/BUILD:1:1:\n foo.bzl:1:2: createTarget uses a distinct exception, BadImplementationFunction, for errors that arise not from the Starlark implementation function but from the post-processing of the providers. A follow-up change will replace this exception by directly reporting events to the handler, thus permitting both higher quality structured errors capable of reporting more than one relevant location, and multiple errors in a single pass. However, that change is too tricky to accomplish in this CL. Also: - move "advertised provider" and "orphaned file" checks into createTarget. - add comments to document this particularly painful code. - delete EvalException.getDeprecatedLocation. - Starlark: long StarlarkInt multiply without BigInteger … Use Hacker's Delight 8-2. Closes #12643. - bazel packages: use EventHandler not EvalException in .bzl "export" o… … …peration This allows us to delete one of the deprecated EvalException(Location,...) constructors. Similar follow-up changes (events not exceptions) will be required to eliminate the remaining such constructor. As a bonus, this approach allows multiple errors to be reported at once. --- .tmp/bazel | 2 +- .../java/net/starlark/java/eval/Printer.java | 59 +++-- .../net/starlark/java/eval/StarlarkInt.java | 40 ++-- .../net/starlark/java/eval/StarlarkList.java | 30 ++- .../starlark/java/eval/StarlarkSemantics.java | 19 ++ .../java/net/starlark/java/eval/Tuple.java | 7 +- .../java/net/starlark/java/syntax/Lexer.java | 9 +- .../java/net/starlark/java/syntax/Parser.java | 4 +- .../starlark/java/syntax/StarlarkFile.java | 42 +--- .../net/starlark/java/eval/Benchmarks.java | 5 +- .../starlark/java/eval/EvaluationTest.java | 120 +++++----- .../net/starlark/java/eval/PrinterTest.java | 11 +- .../starlark/java/eval/StarlarkListTest.java | 225 +----------------- .../starlark/java/eval/testdata/float.star | 33 +++ .../net/starlark/java/eval/testdata/int.star | 209 +++++++++------- .../java/eval/testdata/int_constructor.star | 115 +++++---- .../net/starlark/java/eval/testdata/list.star | 65 +++++ .../java/eval/testdata/list_mutation.star | 15 +- .../java/eval/testdata/list_slices.star | 30 ++- .../java/eval/testdata/string_misc.star | 7 + .../starlark/java/eval/testdata/tuple.star | 20 ++ 21 files changed, 536 insertions(+), 531 deletions(-) create mode 100644 libstarlark/src/test/java/net/starlark/java/eval/testdata/list.star diff --git a/.tmp/bazel b/.tmp/bazel index 0d56ebfd3..09ff98533 160000 --- a/.tmp/bazel +++ b/.tmp/bazel @@ -1 +1 @@ -Subproject commit 0d56ebfd3d74eb1567cbada9aaa563e6e85054b3 +Subproject commit 09ff9853345f66ffa5c2fa351b150fbae0b3408e diff --git a/libstarlark/src/main/java/net/starlark/java/eval/Printer.java b/libstarlark/src/main/java/net/starlark/java/eval/Printer.java index a0fbe2326..c520ae534 100644 --- a/libstarlark/src/main/java/net/starlark/java/eval/Printer.java +++ b/libstarlark/src/main/java/net/starlark/java/eval/Printer.java @@ -298,6 +298,7 @@ public static void format(Printer printer, String format, Object... arguments) { } /** Same as {@link #format}, but with a list instead of variadic args. */ + @SuppressWarnings("FormatString") // see b/178189609 public static void formatWithList(Printer printer, String pattern, List arguments) { // N.B. MissingFormatWidthException is the only kind of IllegalFormatException // whose constructor can take and display arbitrary error message, hence its use below. @@ -330,16 +331,6 @@ public static void formatWithList(Printer printer, String pattern, List argum continue; } - // valid? - if ("drsefgEFG".indexOf(conv) < 0) { - throw new MissingFormatWidthException( - // The call to Starlark.repr doesn't cause an infinite recursion because it's - // only used to format a string properly. - String.format( - "unsupported format character \"%s\" at index %s in %s", - String.valueOf(conv), p + 1, Starlark.repr(pattern))); - } - // get argument if (a >= argLength) { throw new MissingFormatWidthException( @@ -352,22 +343,32 @@ public static void formatWithList(Printer printer, String pattern, List argum switch (conv) { case 'd': - if (arg instanceof StarlarkInt || arg instanceof Integer) { - printer.repr(arg); - } else if (arg instanceof StarlarkFloat) { - double d = ((StarlarkFloat) arg).toDouble(); - StarlarkInt rounded; - try { - rounded = StarlarkInt.ofFiniteDouble(d); - } catch (IllegalArgumentException unused) { - throw new MissingFormatWidthException("got " + arg + ", want a finite number"); + case 'o': + case 'x': + case 'X': + { + Number n; + if (arg instanceof StarlarkInt) { + n = ((StarlarkInt) arg).toNumber(); + } else if (arg instanceof Integer) { + n = (Number) arg; + } else if (arg instanceof StarlarkFloat) { + double d = ((StarlarkFloat) arg).toDouble(); + try { + n = StarlarkInt.ofFiniteDouble(d).toNumber(); + } catch (IllegalArgumentException unused) { + throw new MissingFormatWidthException("got " + arg + ", want a finite number"); + } + } else { + throw new MissingFormatWidthException( + String.format( + "got %s for '%%%c' format, want int or float", Starlark.type(arg), conv)); } - printer.repr(rounded); - } else { - throw new MissingFormatWidthException( - "invalid argument " + Starlark.repr(arg) + " for format pattern %d"); + printer.str( + String.format( + conv == 'd' ? "%d" : conv == 'o' ? "%o" : conv == 'x' ? "%x" : "%X", n)); + continue; } - continue; case 'e': case 'f': @@ -384,7 +385,8 @@ public static void formatWithList(Printer printer, String pattern, List argum v = ((StarlarkFloat) arg).toDouble(); } else { throw new MissingFormatWidthException( - "invalid argument " + Starlark.repr(arg) + " for format pattern %d"); + String.format( + "got %s for '%%%c' format, want int or float", Starlark.type(arg), conv)); } printer.str(StarlarkFloat.format(v, conv)); continue; @@ -398,7 +400,12 @@ public static void formatWithList(Printer printer, String pattern, List argum continue; default: - throw new IllegalStateException("unreachable"); + // The call to Starlark.repr doesn't cause an infinite recursion + // because it's only used to format a string properly. + throw new MissingFormatWidthException( + String.format( + "unsupported format character \"%s\" at index %s in %s", + String.valueOf(conv), p + 1, Starlark.repr(pattern))); } } if (a < argLength) { diff --git a/libstarlark/src/main/java/net/starlark/java/eval/StarlarkInt.java b/libstarlark/src/main/java/net/starlark/java/eval/StarlarkInt.java index bba669a0c..bf3bcb29d 100644 --- a/libstarlark/src/main/java/net/starlark/java/eval/StarlarkInt.java +++ b/libstarlark/src/main/java/net/starlark/java/eval/StarlarkInt.java @@ -121,14 +121,9 @@ public static StarlarkInt parse(String s, int base) { prefixBase = 16; } if (prefixBase != 0) { - digits = s.substring(2); // strip prefix - if (base == 0) { + if (base == 0 || base == prefixBase) { base = prefixBase; - } else if (base != prefixBase) { - throw new NumberFormatException( - String.format( - "invalid base-%d literal: %s (%s prefix wants base %d)", - base, Starlark.repr(stringForErrors), s.substring(0, 2), prefixBase)); + digits = s.substring(2); // strip prefix } } } @@ -577,12 +572,19 @@ public static StarlarkInt floordiv(StarlarkInt x, StarlarkInt y) throws EvalExce if (y == ZERO) { throw Starlark.errorf("integer division by zero"); } - if (x instanceof Int32 && y instanceof Int32) { - long xl = ((Int32) x).v; - long yl = ((Int32) y).v; + try { + long xl = x.toLongFast(); + long yl = y.toLongFast(); // http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html - long quo = Math.floorDiv(xl, yl); - return StarlarkInt.of(quo); + if (xl == Long.MIN_VALUE && yl == -1) { + /* sole case in which quotient doesn't fit in long */ + } else { + long quo = Math.floorDiv(xl, yl); + return StarlarkInt.of(quo); + } + /* overflow */ + } catch (Overflow unused) { + /* fall through */ } BigInteger xbig = x.toBigInteger(); @@ -599,15 +601,13 @@ public static StarlarkInt mod(StarlarkInt x, StarlarkInt y) throws EvalException if (y == ZERO) { throw Starlark.errorf("integer modulo by zero"); } - if (x instanceof Int32 && y instanceof Int32) { - long xl = ((Int32) x).v; - long yl = ((Int32) y).v; + try { + long xl = x.toLongFast(); + long yl = y.toLongFast(); // In Starlark, the sign of the result is the sign of the divisor. - long z = xl % yl; - if ((xl < 0) != (yl < 0) && z != 0) { - z += yl; - } - return StarlarkInt.of(z); + return StarlarkInt.of(Math.floorMod(xl, yl)); + } catch (Overflow unused) { + /* fall through */ } BigInteger xbig = x.toBigInteger(); diff --git a/libstarlark/src/main/java/net/starlark/java/eval/StarlarkList.java b/libstarlark/src/main/java/net/starlark/java/eval/StarlarkList.java index 6844eb992..6fce805f5 100644 --- a/libstarlark/src/main/java/net/starlark/java/eval/StarlarkList.java +++ b/libstarlark/src/main/java/net/starlark/java/eval/StarlarkList.java @@ -74,6 +74,10 @@ public final class StarlarkList extends AbstractList implements Sequence, StarlarkValue, Mutability.Freezable, Comparable> { + // It's always possible to overeat in small bites but we'll + // try to stop someone swallowing the world in one gulp. + static final int MAX_ALLOC = 1 << 30; + // The implementation strategy is similar to ArrayList, // but without the extra indirection of using ArrayList. @@ -284,9 +288,12 @@ public StarlarkList repeat(StarlarkInt n, Mutability mutability) throws EvalE return wrap(mutability, EMPTY_ARRAY); } - // TODO(adonovan): reject unreasonably large n. int ni = n.toInt("repeat"); - Object[] res = new Object[ni * size]; + long sz = (long) ni * size; + if (sz > MAX_ALLOC) { + throw Starlark.errorf("excessive repeat (%d * %d elements)", size, ni); + } + Object[] res = new Object[(int) sz]; for (int i = 0; i < ni; i++) { System.arraycopy(elems, 0, res, i * size, size); } @@ -334,6 +341,15 @@ private void grow(int mincap) { } } + // Grow capacity enough to insert given number of elements + private void growAdditional(int additional) throws EvalException { + int mincap = size + additional; + if (mincap < 0 || mincap > MAX_ALLOC) { + throw Starlark.errorf("excessive capacity requested (%d + %d elements)", size, additional); + } + grow(mincap); + } + /** * Appends an element to the end of the list, after validating that mutation is allowed. * @@ -341,7 +357,7 @@ private void grow(int mincap) { */ public void addElement(E element) throws EvalException { Starlark.checkMutable(this); - grow(size + 1); + growAdditional(1); elems[size++] = element; } @@ -353,7 +369,7 @@ public void addElement(E element) throws EvalException { */ public void addElementAt(int index, E element) throws EvalException { Starlark.checkMutable(this); - grow(size + 1); + growAdditional(1); System.arraycopy(elems, index, elems, index + 1, size - index); elems[index] = element; size++; @@ -369,20 +385,20 @@ public void addElements(Iterable elements) throws EvalException { if (elements instanceof StarlarkList) { StarlarkList that = (StarlarkList) elements; // (safe even if this == that) - grow(this.size + that.size); + growAdditional(that.size); System.arraycopy(that.elems, 0, this.elems, this.size, that.size); this.size += that.size; } else if (elements instanceof Collection) { // collection of known size Collection that = (Collection) elements; - grow(size + that.size()); + growAdditional(that.size()); for (Object x : that) { elems[size++] = x; } } else { // iterable for (Object x : elements) { - grow(size + 1); + growAdditional(1); elems[size++] = x; } } diff --git a/libstarlark/src/main/java/net/starlark/java/eval/StarlarkSemantics.java b/libstarlark/src/main/java/net/starlark/java/eval/StarlarkSemantics.java index f4e0b55a0..3e937b92d 100644 --- a/libstarlark/src/main/java/net/starlark/java/eval/StarlarkSemantics.java +++ b/libstarlark/src/main/java/net/starlark/java/eval/StarlarkSemantics.java @@ -72,6 +72,25 @@ public T get(Key key) { return v != null ? v : key.defaultValue; } + // TODO(bazel-team): This exists solely for BuiltinsInternalModule#getFlag, which allows a + // (privileged) Starlark caller to programmatically retrieve a flag's value without knowing its + // schema and default value. Reconsider whether we should support that use case from this class. + /** + * Returns the value of the option with the given name, or the default value if it is not set or + * does not exist. + */ + public Object getGeneric(String name, Object defaultValue) { + Object v = map.get(name); + // Try boolean prefixes if that didn't work. + if (v == null) { + v = map.get("+" + name); + } + if (v == null) { + v = map.get("-" + name); + } + return v != null ? v : defaultValue; + } + /** A Key identifies an option, providing its name, type, and default value. */ public static class Key { public final String name; diff --git a/libstarlark/src/main/java/net/starlark/java/eval/Tuple.java b/libstarlark/src/main/java/net/starlark/java/eval/Tuple.java index 5e0daa72b..c6c6d6623 100644 --- a/libstarlark/src/main/java/net/starlark/java/eval/Tuple.java +++ b/libstarlark/src/main/java/net/starlark/java/eval/Tuple.java @@ -245,9 +245,12 @@ Tuple repeat(StarlarkInt n) throws EvalException { return empty(); } - // TODO(adonovan): reject unreasonably large n. int ni = n.toInt("repeat"); - Object[] res = new Object[ni * elems.length]; + long sz = (long) ni * elems.length; + if (sz > StarlarkList.MAX_ALLOC) { + throw Starlark.errorf("excessive repeat (%d * %d elements)", elems.length, ni); + } + Object[] res = new Object[(int) sz]; for (int i = 0; i < ni; i++) { System.arraycopy(elems, 0, res, i * elems.length, elems.length); } diff --git a/libstarlark/src/main/java/net/starlark/java/syntax/Lexer.java b/libstarlark/src/main/java/net/starlark/java/syntax/Lexer.java index d344b43f3..efc3c98e5 100644 --- a/libstarlark/src/main/java/net/starlark/java/syntax/Lexer.java +++ b/libstarlark/src/main/java/net/starlark/java/syntax/Lexer.java @@ -15,8 +15,8 @@ package net.starlark.java.syntax; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -53,7 +53,7 @@ final class Lexer { // The first (outermost) element is always zero. private final Stack indentStack = new Stack<>(); - private final List comments; + private final ImmutableList.Builder comments = ImmutableList.builder(); // The number of unclosed open-parens ("(", '{', '[') at the current point in // the stream. Whitespace is handled differently when this is nonzero. @@ -93,14 +93,13 @@ final class Lexer { this.pos = 0; this.errors = errors; this.checkIndentation = true; - this.comments = new ArrayList<>(); this.dents = 0; indentStack.push(0); } - List getComments() { - return comments; + ImmutableList getComments() { + return comments.build(); } /** diff --git a/libstarlark/src/main/java/net/starlark/java/syntax/Parser.java b/libstarlark/src/main/java/net/starlark/java/syntax/Parser.java index 3683c5f02..0ad46ba92 100644 --- a/libstarlark/src/main/java/net/starlark/java/syntax/Parser.java +++ b/libstarlark/src/main/java/net/starlark/java/syntax/Parser.java @@ -38,7 +38,7 @@ static final class ParseResult { final ImmutableList statements; /** The comments from the parsed file. */ - final List comments; + final ImmutableList comments; // Errors encountered during scanning or parsing. // These lists are ultimately owned by StarlarkFile. @@ -47,7 +47,7 @@ static final class ParseResult { private ParseResult( FileLocations locs, ImmutableList statements, - List comments, + ImmutableList comments, List errors) { this.locs = locs; // No need to copy here; when the object is created, the parser instance is just about to go diff --git a/libstarlark/src/main/java/net/starlark/java/syntax/StarlarkFile.java b/libstarlark/src/main/java/net/starlark/java/syntax/StarlarkFile.java index 8b93a82a5..ffd872fe1 100644 --- a/libstarlark/src/main/java/net/starlark/java/syntax/StarlarkFile.java +++ b/libstarlark/src/main/java/net/starlark/java/syntax/StarlarkFile.java @@ -57,26 +57,22 @@ private StarlarkFile( this.errors = errors; } - // Creates a StarlarkFile from the given effective list of statements, - // which may include the prelude. - private static StarlarkFile create( - FileLocations locs, - ImmutableList statements, - FileOptions options, - Parser.ParseResult result) { - return new StarlarkFile( - locs, statements, options, ImmutableList.copyOf(result.comments), result.errors); - } - - /** Extract a subtree containing only statements from i (included) to j (excluded). */ - public StarlarkFile subTree(int i, int j) { + /** + * Returns a new StarlarkFile whose statements are {@code getStatements().subList(start, end)}, + * and no comments. + * + * @deprecated This is a hack to support Bazel WORKSPACE files. + */ + @Deprecated + public StarlarkFile subTree(int start, int end) { return new StarlarkFile( this.locs, - this.statements.subList(i, j), + this.statements.subList(start, end), this.options, /*comments=*/ ImmutableList.of(), errors); } + /** * Returns an unmodifiable view of the list of scanner, parser, and (perhaps) resolver errors * accumulated in this Starlark file. @@ -123,21 +119,6 @@ public Resolver.Function getResolvedFunction() { return resolved; } - /** - * Parse the specified file, returning its syntax tree with the prelude statements inserted at the - * front of its statement list. - */ - public static StarlarkFile parseWithPrelude( - ParserInput input, List prelude, FileOptions options) { - Parser.ParseResult result = Parser.parseFile(input, options); - - ImmutableList.Builder stmts = ImmutableList.builder(); - stmts.addAll(prelude); - stmts.addAll(result.statements); - - return create(result.locs, stmts.build(), options, result); - } - /** * Parse a Starlark file. * @@ -154,7 +135,8 @@ public static StarlarkFile parseWithPrelude( */ public static StarlarkFile parse(ParserInput input, FileOptions options) { Parser.ParseResult result = Parser.parseFile(input, options); - return create(result.locs, ImmutableList.copyOf(result.statements), options, result); + return new StarlarkFile( + result.locs, result.statements, options, result.comments, result.errors); } /** Parse a Starlark file with default options. */ diff --git a/libstarlark/src/test/java/net/starlark/java/eval/Benchmarks.java b/libstarlark/src/test/java/net/starlark/java/eval/Benchmarks.java index f577cffa1..484ab6550 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/Benchmarks.java +++ b/libstarlark/src/test/java/net/starlark/java/eval/Benchmarks.java @@ -19,6 +19,7 @@ import com.sun.management.ThreadMXBean; import java.io.File; import java.lang.management.ManagementFactory; +import java.util.Arrays; import java.util.Map; import java.util.TreeMap; import java.util.regex.Pattern; @@ -144,7 +145,9 @@ public static void main(String[] args) throws Exception { src = new File("src"); // bazel } File testdata = new File(src, "test/java/net/starlark/java/eval/testdata"); - for (File file : testdata.listFiles()) { + File[] files = testdata.listFiles(); + Arrays.sort(files); // for determinism + for (File file : files) { String basename = file.getName(); if (!(basename.startsWith("bench_") && basename.endsWith(".star"))) { continue; diff --git a/libstarlark/src/test/java/net/starlark/java/eval/EvaluationTest.java b/libstarlark/src/test/java/net/starlark/java/eval/EvaluationTest.java index b936ba26b..3b98a72d1 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/EvaluationTest.java +++ b/libstarlark/src/test/java/net/starlark/java/eval/EvaluationTest.java @@ -299,17 +299,6 @@ ev.new Scenario() .testIfExactError("integer modulo by zero", "5 % 0"); } - @Test - public void testMult() throws Exception { - ev.new Scenario() - .testExpression("6 * 7", StarlarkInt.of(42)) - .testExpression("3 * 'ab'", "ababab") - .testExpression("0 * 'ab'", "") - .testExpression("'1' + '0' * 5", "100000") - .testExpression("'ab' * -4", "") - .testExpression("-1 * ''", ""); - } - @Test public void testFloorDivision() throws Exception { ev.new Scenario() @@ -516,40 +505,6 @@ ev.new Scenario() .testIfExactError("unsupported binary operation: tuple + list", "(1, 2) + [3, 4]"); } - @Test - public void testListMultiply() throws Exception { - ev.new Scenario() - .testEval("[1, 2, 3] * 1", "[1, 2, 3]") - .testEval("[1, 2] * 2", "[1, 2, 1, 2]") - .testEval("[1, 2] * 3", "[1, 2, 1, 2, 1, 2]") - .testEval("[1, 2] * 4", "[1, 2, 1, 2, 1, 2, 1, 2]") - .testEval("[8] * 5", "[8, 8, 8, 8, 8]") - .testEval("[ ] * 10", "[]") - .testEval("[1, 2] * 0", "[]") - .testEval("[1, 2] * -4", "[]") - .testEval("2 * [1, 2]", "[1, 2, 1, 2]") - .testEval("10 * []", "[]") - .testEval("0 * [1, 2]", "[]") - .testEval("-4 * [1, 2]", "[]"); - } - - @Test - public void testTupleMultiply() throws Exception { - ev.new Scenario() - .testEval("(1, 2, 3) * 1", "(1, 2, 3)") - .testEval("(1, 2) * 2", "(1, 2, 1, 2)") - .testEval("(1, 2) * 3", "(1, 2, 1, 2, 1, 2)") - .testEval("(1, 2) * 4", "(1, 2, 1, 2, 1, 2, 1, 2)") - .testEval("(8,) * 5", "(8, 8, 8, 8, 8)") - .testEval("( ) * 10", "()") - .testEval("(1, 2) * 0", "()") - .testEval("(1, 2) * -4", "()") - .testEval("2 * (1, 2)", "(1, 2, 1, 2)") - .testEval("10 * ()", "()") - .testEval("0 * (1, 2)", "()") - .testEval("-4 * (1, 2)", "()"); - } - @Test public void testListComprehensionFailsOnNonSequence() throws Exception { ev.new Scenario().testIfErrorContains("type 'int' is not iterable", "[x + 1 for x in 123]"); @@ -665,20 +620,16 @@ public void testInCompositeForPrecedence() throws Exception { ev.new Scenario().testExpression("not 'a' in ['a'] or 0", StarlarkInt.of(0)); } - private static StarlarkValue createObjWithStr() { - return new StarlarkValue() { - @Override - public void repr(Printer printer) { - printer.append(""); - } - }; - } - @Test - public void testPercentOnObjWithStr() throws Exception { - ev.new Scenario() - .update("obj", createObjWithStr()) - .testExpression("'%s' % obj", ""); + public void testPercentOnValueWithRepr() throws Exception { + Object obj = + new StarlarkValue() { + @Override + public void repr(Printer printer) { + printer.append(""); + } + }; + ev.new Scenario().update("obj", obj).testExpression("'%s' % obj", ""); } private static class Dummy implements StarlarkValue {} @@ -697,8 +648,15 @@ ev.new Scenario() @Test public void testPercentOnTupleOfDummyValues() throws Exception { + Object obj = + new StarlarkValue() { + @Override + public void repr(Printer printer) { + printer.append(""); + } + }; ev.new Scenario() - .update("obj", createObjWithStr()) + .update("obj", obj) .testExpression("'%s %s' % (obj, obj)", " "); ev.new Scenario() .update("unknown", new Dummy()) @@ -708,13 +666,6 @@ ev.new Scenario() + " object net.starlark.java.eval.EvaluationTest$Dummy>"); } - @Test - public void testPercOnObjectInvalidFormat() throws Exception { - ev.new Scenario() - .update("obj", createObjWithStr()) - .testIfExactError("invalid argument for format pattern %d", "'%d' % obj"); - } - @Test public void testDictKeys() throws Exception { ev.new Scenario().testExactOrder("{'a': 1}.keys() + ['b', 'c']", "a", "b", "c"); @@ -773,7 +724,42 @@ public void testExec() throws Exception { "foo1")); } - // TODO(adonovan): add more tests of load. + @Test + public void testLoadsBindLocally() throws Exception { + Module a = Module.create(); + Starlark.execFile( + ParserInput.fromString("x = 1", "a.bzl"), + FileOptions.DEFAULT, + a, + new StarlarkThread(Mutability.create(), StarlarkSemantics.DEFAULT)); + + StarlarkThread bThread = new StarlarkThread(Mutability.create(), StarlarkSemantics.DEFAULT); + bThread.setLoader( + module -> { + assertThat(module).isEqualTo("a.bzl"); + return a; + }); + Module b = Module.create(); + Starlark.execFile( + ParserInput.fromString("load('a.bzl', 'x')", "b.bzl"), FileOptions.DEFAULT, b, bThread); + + StarlarkThread cThread = new StarlarkThread(Mutability.create(), StarlarkSemantics.DEFAULT); + cThread.setLoader( + module -> { + assertThat(module).isEqualTo("b.bzl"); + return b; + }); + EvalException ex = + assertThrows( + EvalException.class, + () -> + Starlark.execFile( + ParserInput.fromString("load('b.bzl', 'x')", "c.bzl"), + FileOptions.DEFAULT, + Module.create(), + cThread)); + assertThat(ex).hasMessageThat().contains("file 'b.bzl' does not contain symbol 'x'"); + } @Test public void testTopLevelRebinding() throws Exception { diff --git a/libstarlark/src/test/java/net/starlark/java/eval/PrinterTest.java b/libstarlark/src/test/java/net/starlark/java/eval/PrinterTest.java index 92957a64e..cc3583a45 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/PrinterTest.java +++ b/libstarlark/src/test/java/net/starlark/java/eval/PrinterTest.java @@ -125,15 +125,14 @@ public void testFormatPositional() throws Exception { assertThat(Starlark.format("%% %d %r %s", StarlarkInt.of(1), "2", "3")) .isEqualTo("% 1 \"2\" 3"); + checkFormatPositionalFails("got string for '%d' format, want int or float", "%d", "1"); checkFormatPositionalFails( - "invalid argument \"1\" for format pattern %d", - "%d", "1"); - checkFormatPositionalFails("unsupported format character \".\" at index 1 in \"%.3g\"", - "%.3g"); + "unsupported format character \".\" at index 1 in \"%.3g\"", "%.3g", 1); checkFormatPositionalFails("unsupported format character \".\" at index 1 in \"%.3g\"", "%.3g", 1, 2); - checkFormatPositionalFails("unsupported format character \".\" at index 1 in \"%.s\"", - "%.s"); + checkFormatPositionalFails( + "unsupported format character \".\" at index 1 in \"%.s\"", "%.s", 1); + checkFormatPositionalFails("not enough arguments for format pattern \"%.s\": ()", "%.s"); } private StarlarkValue createObjWithStr() { diff --git a/libstarlark/src/test/java/net/starlark/java/eval/StarlarkListTest.java b/libstarlark/src/test/java/net/starlark/java/eval/StarlarkListTest.java index ca78cb0c6..f4e883a1a 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/StarlarkListTest.java +++ b/libstarlark/src/test/java/net/starlark/java/eval/StarlarkListTest.java @@ -27,233 +27,16 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Tests for StarlarkList. */ +/** Tests of StarlarkList's Java API. */ // TODO(adonovan): duplicate/share these tests for Tuple where applicable. @RunWith(JUnit4.class) public final class StarlarkListTest { - private final EvaluationTestCase ev = new EvaluationTestCase(); - - @Test - public void testIndex() throws Exception { - ev.exec("l = [1, '2', 3]"); - assertThat(ev.eval("l[0]")).isEqualTo(StarlarkInt.of(1)); - assertThat(ev.eval("l[1]")).isEqualTo("2"); - assertThat(ev.eval("l[2]")).isEqualTo(StarlarkInt.of(3)); - - ev.exec("t = (1, '2', 3)"); - assertThat(ev.eval("t[0]")).isEqualTo(StarlarkInt.of(1)); - assertThat(ev.eval("t[1]")).isEqualTo("2"); - assertThat(ev.eval("t[2]")).isEqualTo(StarlarkInt.of(3)); - } - - @Test - public void testIndexOutOfBounds() throws Exception { - ev.checkEvalError( - "index out of range (index is 3, but sequence has 3 elements)", "['a', 'b', 'c'][3]"); - ev.checkEvalError( - "index out of range (index is 10, but sequence has 3 elements)", "['a', 'b', 'c'][10]"); - ev.checkEvalError("index out of range (index is 0, but sequence has 0 elements)", "[][0]"); - } - - @Test - public void testNegativeIndices() throws Exception { - ev.exec("l = ['a', 'b', 'c']"); - assertThat(ev.eval("l[0]")).isEqualTo("a"); - assertThat(ev.eval("l[-1]")).isEqualTo("c"); - assertThat(ev.eval("l[-2]")).isEqualTo("b"); - assertThat(ev.eval("l[-3]")).isEqualTo("a"); - ev.checkEvalError("index out of range (index is -4, but sequence has 3 elements)", "l[-4]"); - ev.checkEvalError("index out of range (index is -1, but sequence has 0 elements)", "[][-1]"); - } - - @SuppressWarnings("unchecked") - private Sequence listEval(String... input) throws Exception { - return (Sequence) ev.eval(input); - } - - @Test - public void testSlice() throws Exception { - ev.exec("l = ['a', 'b', 'c']"); - assertThat(listEval("l[0:3]")).containsExactly("a", "b", "c").inOrder(); - assertThat(listEval("l[0:2]")).containsExactly("a", "b").inOrder(); - assertThat(listEval("l[0:1]")).containsExactly("a").inOrder(); - assertThat(listEval("l[0:0]")).isEmpty(); - assertThat(listEval("l[1:3]")).containsExactly("b", "c").inOrder(); - assertThat(listEval("l[2:3]")).containsExactly("c").inOrder(); - assertThat(listEval("l[3:3]")).isEmpty(); - assertThat(listEval("l[2:1]")).isEmpty(); - assertThat(listEval("l[3:0]")).isEmpty(); - - ev.exec("t = ('a', 'b', 'c')"); - assertThat(listEval("t[0:3]")).containsExactly("a", "b", "c").inOrder(); - assertThat(listEval("t[1:2]")).containsExactly("b").inOrder(); - } - - @Test - public void testSliceDefault() throws Exception { - ev.exec("l = ['a', 'b', 'c']"); - assertThat(listEval("l[:]")).containsExactly("a", "b", "c").inOrder(); - assertThat(listEval("l[:2]")).containsExactly("a", "b").inOrder(); - assertThat(listEval("l[2:]")).containsExactly("c").inOrder(); - } - - @Test - public void testSliceNegative() throws Exception { - ev.exec("l = ['a', 'b', 'c']"); - assertThat(listEval("l[-2:-1]")).containsExactly("b").inOrder(); - assertThat(listEval("l[-2:]")).containsExactly("b", "c").inOrder(); - assertThat(listEval("l[0:-1]")).containsExactly("a", "b").inOrder(); - assertThat(listEval("l[-1:1]")).isEmpty(); - } - - @Test - public void testSliceBounds() throws Exception { - ev.exec("l = ['a', 'b', 'c']"); - assertThat(listEval("l[0:5]")).containsExactly("a", "b", "c").inOrder(); - assertThat(listEval("l[-10:2]")).containsExactly("a", "b").inOrder(); - assertThat(listEval("l[3:10]")).isEmpty(); - assertThat(listEval("l[-10:-9]")).isEmpty(); - } - - @Test - public void testSliceSkip() throws Exception { - ev.exec("l = ['a', 'b', 'c', 'd', 'e', 'f', 'g']"); - assertThat(listEval("l[0:6:2]")).containsExactly("a", "c", "e").inOrder(); - assertThat(listEval("l[0:7:2]")).containsExactly("a", "c", "e", "g").inOrder(); - assertThat(listEval("l[0:10:2]")).containsExactly("a", "c", "e", "g").inOrder(); - assertThat(listEval("l[-6:10:2]")).containsExactly("b", "d", "f").inOrder(); - assertThat(listEval("l[1:5:3]")).containsExactly("b", "e").inOrder(); - assertThat(listEval("l[-10:3:2]")).containsExactly("a", "c").inOrder(); - assertThat(listEval("l[-10:10:1]")).containsExactly( - "a", "b", "c", "d", "e", "f", "g").inOrder(); - } - - @Test - public void testSliceNegativeSkip() throws Exception { - ev.exec("l = ['a', 'b', 'c', 'd', 'e', 'f', 'g']"); - assertThat(listEval("l[5:2:-1]")).containsExactly("f", "e", "d").inOrder(); - assertThat(listEval("l[5:2:-2]")).containsExactly("f", "d").inOrder(); - assertThat(listEval("l[5:3:-2]")).containsExactly("f").inOrder(); - assertThat(listEval("l[6::-4]")).containsExactly("g", "c").inOrder(); - assertThat(listEval("l[7::-4]")).containsExactly("g", "c").inOrder(); - assertThat(listEval("l[-1::-4]")).containsExactly("g", "c").inOrder(); - assertThat(listEval("l[-1:-10:-4]")).containsExactly("g", "c").inOrder(); - assertThat(listEval("l[-1:-3:-4]")).containsExactly("g").inOrder(); - assertThat(listEval("l[2:5:-1]")).isEmpty(); - assertThat(listEval("l[-10:5:-1]")).isEmpty(); - assertThat(listEval("l[1:-8:-1]")).containsExactly("b", "a").inOrder(); - - ev.checkEvalError("slice step cannot be zero", "l[2:5:0]"); - } - - @Test - public void testListSize() throws Exception { - assertThat(ev.eval("len([42, 'hello, world', []])")).isEqualTo(StarlarkInt.of(3)); - } - - @Test - public void testListEmpty() throws Exception { - assertThat(ev.eval("8 if [1, 2, 3] else 9")).isEqualTo(StarlarkInt.of(8)); - assertThat(ev.eval("8 if [] else 9")).isEqualTo(StarlarkInt.of(9)); - } - - @Test - public void testListConcat() throws Exception { - assertThat(ev.eval("[1, 2] + [3, 4]")) - .isEqualTo( - StarlarkList.of( - /*mutability=*/ null, - StarlarkInt.of(1), - StarlarkInt.of(2), - StarlarkInt.of(3), - StarlarkInt.of(4))); - } - - @Test - public void testConcatListIndex() throws Exception { - ev.exec( - "l = [1, 2] + [3, 4]", // - "e0 = l[0]", - "e1 = l[1]", - "e2 = l[2]", - "e3 = l[3]"); - assertThat(ev.lookup("e0")).isEqualTo(StarlarkInt.of(1)); - assertThat(ev.lookup("e1")).isEqualTo(StarlarkInt.of(2)); - assertThat(ev.lookup("e2")).isEqualTo(StarlarkInt.of(3)); - assertThat(ev.lookup("e3")).isEqualTo(StarlarkInt.of(4)); - } - - @Test - public void testConcatListHierarchicalIndex() throws Exception { - ev.exec( - "l = [1] + (([2] + [3, 4]) + [5])", // - "e0 = l[0]", - "e1 = l[1]", - "e2 = l[2]", - "e3 = l[3]", - "e4 = l[4]"); - assertThat(ev.lookup("e0")).isEqualTo(StarlarkInt.of(1)); - assertThat(ev.lookup("e1")).isEqualTo(StarlarkInt.of(2)); - assertThat(ev.lookup("e2")).isEqualTo(StarlarkInt.of(3)); - assertThat(ev.lookup("e3")).isEqualTo(StarlarkInt.of(4)); - assertThat(ev.lookup("e4")).isEqualTo(StarlarkInt.of(5)); - } - - @Test - public void testConcatListSize() throws Exception { - assertThat(ev.eval("len([1, 2] + [3, 4])")).isEqualTo(StarlarkInt.of(4)); - } - - @Test - public void testAppend() throws Exception { - ev.exec("l = [1, 2]"); - assertThat(Starlark.NONE).isEqualTo(ev.eval("l.append([3, 4])")); - assertThat(ev.eval("[1, 2, [3, 4]]")).isEqualTo(ev.lookup("l")); - } - - @Test - public void testExtend() throws Exception { - ev.exec("l = [1, 2]"); - assertThat(Starlark.NONE).isEqualTo(ev.eval("l.extend([3, 4])")); - assertThat(ev.eval("[1, 2, 3, 4]")).isEqualTo(ev.lookup("l")); - } - @Test public void listAfterRemoveHasExpectedEqualsAndHashCode() throws Exception { - ev.exec("l = [1, 2, 3]"); - ev.exec("l.remove(3)"); - assertThat(ev.lookup("l")).isEqualTo(ev.eval("[1, 2]")); - assertThat(ev.lookup("l").hashCode()).isEqualTo(ev.eval("[1, 2]").hashCode()); - } - - @Test - public void testConcatListToString() throws Exception { - assertThat(ev.eval("str([1, 2] + [3, 4])")).isEqualTo("[1, 2, 3, 4]"); - } - - @Test - public void testConcatListNotEmpty() throws Exception { - ev.exec("l = [1, 2] + [3, 4]", "v = 1 if l else 0"); - assertThat(ev.lookup("v")).isEqualTo(StarlarkInt.of(1)); - } - - @Test - public void testConcatListEmpty() throws Exception { - ev.exec("l = [] + []", "v = 1 if l else 0"); - assertThat(ev.lookup("v")).isEqualTo(StarlarkInt.of(0)); - } - - @Test - public void testListComparison() throws Exception { - assertThat(ev.eval("(1, 'two', [3, 4]) == (1, 'two', [3, 4])")).isEqualTo(true); - assertThat(ev.eval("[1, 2, 3, 4] == [1, 2] + [3, 4]")).isEqualTo(true); - assertThat(ev.eval("[1, 2, 3, 4] == (1, 2, 3, 4)")).isEqualTo(false); - assertThat(ev.eval("[1, 2] == [1, 2, 3]")).isEqualTo(false); - assertThat(ev.eval("[] == []")).isEqualTo(true); - assertThat(ev.eval("() == ()")).isEqualTo(true); - assertThat(ev.eval("() == (1,)")).isEqualTo(false); - assertThat(ev.eval("(1) == (1,)")).isEqualTo(false); + StarlarkList l = StarlarkList.of(Mutability.create(), "1", "2", "3"); + l.removeElement("3"); + assertThat(l.hashCode()).isEqualTo(StarlarkList.immutableOf("1", "2").hashCode()); } @Test diff --git a/libstarlark/src/test/java/net/starlark/java/eval/testdata/float.star b/libstarlark/src/test/java/net/starlark/java/eval/testdata/float.star index 06ae2f5ce..039f7c3bc 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/testdata/float.star +++ b/libstarlark/src/test/java/net/starlark/java/eval/testdata/float.star @@ -190,6 +190,39 @@ assert_fails(lambda: "%d" % float("NaN"), "got nan, want a finite number") assert_fails(lambda: "%d" % float("+Inf"), "got [+]inf, want a finite number") assert_fails(lambda: "%d" % float("-Inf"), "got -inf, want a finite number") +# %x +assert_eq("%x" % 0, "0") +assert_eq("%x" % 0.0, "0") +assert_eq("%x" % 123, "7b") +assert_eq("%x" % 123.0, "7b") +assert_eq("%x" % 1.23e45, "3727b520f7a148000000000000000000000000") +assert_eq("%x" % negzero, "0") +assert_fails(lambda: "%x" % float("NaN"), "got nan, want a finite number") +assert_fails(lambda: "%x" % float("+Inf"), "got [+]inf, want a finite number") +assert_fails(lambda: "%x" % float("-Inf"), "got -inf, want a finite number") + +# %X +assert_eq("%X" % 0, "0") +assert_eq("%X" % 0.0, "0") +assert_eq("%X" % 123, "7B") +assert_eq("%X" % 123.0, "7B") +assert_eq("%X" % 1.23e45, "3727B520F7A148000000000000000000000000") +assert_eq("%X" % negzero, "0") +assert_fails(lambda: "%X" % float("NaN"), "got nan, want a finite number") +assert_fails(lambda: "%X" % float("+Inf"), "got [+]inf, want a finite number") +assert_fails(lambda: "%X" % float("-Inf"), "got -inf, want a finite number") + +# %o +assert_eq("%o" % 0, "0") +assert_eq("%o" % 0.0, "0") +assert_eq("%o" % 123, "173") +assert_eq("%o" % 123.0, "173") +assert_eq("%o" % 1.23e45, "67117324407572051000000000000000000000000000000000") +assert_eq("%o" % negzero, "0") +assert_fails(lambda: "%o" % float("NaN"), "got nan, want a finite number") +assert_fails(lambda: "%o" % float("+Inf"), "got [+]inf, want a finite number") +assert_fails(lambda: "%o" % float("-Inf"), "got -inf, want a finite number") + # %e assert_eq("%e" % 0, "0.000000e+00") assert_eq("%e" % 0.0, "0.000000e+00") diff --git a/libstarlark/src/test/java/net/starlark/java/eval/testdata/int.star b/libstarlark/src/test/java/net/starlark/java/eval/testdata/int.star index c503afdee..57206fad9 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/testdata/int.star +++ b/libstarlark/src/test/java/net/starlark/java/eval/testdata/int.star @@ -17,10 +17,10 @@ assert_eq(-1 << 128, -0x100000000000000000000000000000000) assert_eq((1 << 128) // (1 << 127), 2) # Not using << to define constants because we are testing << -maxint = 0x7fffffff # (1<<31) - 1 -maxlong = 0x7fffffffffffffff # (1<<63) - 1 -minint = -0x80000000 # -1 << 31 -minlong = -0x8000000000000000 # -1 << 63 +maxint = 0x7fffffff # (1<<31) - 1 +maxlong = 0x7fffffffffffffff # (1<<63) - 1 +minint = -0x80000000 # -1 << 31 +minlong = -0x8000000000000000 # -1 << 63 # size boundaries assert_eq(maxint + 1, 0x80000000) @@ -31,27 +31,51 @@ assert_eq(minlong - 1, -0x8000000000000001) # str(int) assert_eq(str(0), "0") assert_eq(str(1), "1") -assert_eq(str(1<<24), "16777216") -assert_eq(str(1<<48), "281474976710656") -assert_eq(str(1<<96), "79228162514264337593543950336") -assert_eq(str(-1<<24), "-16777216") -assert_eq(str(-1<<48), "-281474976710656") -assert_eq(str(-1<<96), "-79228162514264337593543950336") +assert_eq(str(1 << 24), "16777216") +assert_eq(str(1 << 48), "281474976710656") +assert_eq(str(1 << 96), "79228162514264337593543950336") +assert_eq(str(-1 << 24), "-16777216") +assert_eq(str(-1 << 48), "-281474976710656") +assert_eq(str(-1 << 96), "-79228162514264337593543950336") # %d formatting -# TODO(adonovan): implement %x. +assert_eq("%d" % 255, "255") assert_eq("%d" % (1 << 32), "4294967296") assert_eq("%d" % (1 << 64), "18446744073709551616") assert_eq("%d" % (1 << 128), "340282366920938463463374607431768211456") assert_eq("%d" % (-1 << 128), "-340282366920938463463374607431768211456") +# %x formatting +assert_eq("%x" % 255, "ff") +assert_eq("%x" % (1 << 32), "100000000") +assert_eq("%x" % (1 << 64), "10000000000000000") +assert_eq("%x" % (1 << 128), "100000000000000000000000000000000") +assert_eq("%x" % (-1 << 128), "-100000000000000000000000000000000") +assert_fails(lambda: "%x" % "1", "got string for '%x' format, want int or float") + +# %X formatting +assert_eq("%X" % 255, "FF") +assert_eq("%X" % (1 << 32), "100000000") +assert_eq("%X" % (1 << 64), "10000000000000000") +assert_eq("%X" % (1 << 128), "100000000000000000000000000000000") +assert_eq("%X" % (-1 << 128), "-100000000000000000000000000000000") +assert_fails(lambda: "%X" % "1", "got string for '%X' format, want int or float") + +# %o formatting +assert_eq("%o" % 255, "377") +assert_eq("%o" % (1 << 32), "40000000000") +assert_eq("%o" % (1 << 64), "2000000000000000000000") +assert_eq("%o" % (1 << 128), "4000000000000000000000000000000000000000000") +assert_eq("%o" % (-1 << 128), "-4000000000000000000000000000000000000000000") +assert_fails(lambda: "%o" % "1", "got string for '%o' format, want int or float") + # truth assert_(not 0) assert_(1) assert_(-1) -assert_(1<<24) # int32 -assert_(1<<48) # int64 -assert_(1<<100) # big +assert_(1 << 24) # int32 +assert_(1 << 48) # int64 +assert_(1 << 100) # big # comparisons assert_(5 > 2) @@ -73,39 +97,53 @@ assert_eq(1111 * 1111, 1234321) assert_eq(1111 * 1, 1111) assert_eq(1111 * -1, -1111) assert_eq(1111 * 0, 0) -p1, p2 = 0x316c5239, 0x67c4a7d5 # 32-bit primes +p1, p2 = 0x316c5239, 0x67c4a7d5 # 32-bit primes product = p1 * p2 assert_eq(product, p2 * p1) assert_eq(product // p1, p2) assert_eq(product % p1, 0) assert_eq(maxint, 0x7fffffff) assert_eq(maxint * maxint, 0x3fffffff00000001) -assert_eq((1<<62) - (2<<31) + 1, 0x3fffffff00000001) +assert_eq((1 << 62) - (2 << 31) + 1, 0x3fffffff00000001) assert_eq(111111111 * 111111111, 12345678987654321) assert_eq(-(111111111 * 111111111), -12345678987654321) assert_eq((111111111 * 111111111) // 111111111, 111111111) # 7 * 1317624576693539401 = maxlong special_ints = [ - 0, 1, 2, 7, maxint - 1, maxint, maxint + 1, maxint + 2, 0xffffffff, 0x1ffffffff, - 1317624576693539401, maxlong - 1, maxlong, maxlong + 1, maxlong + 2, + 0, + 1, + 2, + 7, + maxint - 1, + maxint, + maxint + 1, + maxint + 2, + 0xffffffff, + 0x1ffffffff, + 1317624576693539401, + maxlong - 1, + maxlong, + maxlong + 1, + maxlong + 2, ] + def test_mul(): - for i_abs in special_ints: - for j_abs in special_ints: - for i_sign in [1, -1]: - for j_sign in [1, -1]: - i = i_abs if i_sign > 0 else -i_abs - j = j_abs if j_sign > 0 else -j_abs - assert_eq(i * j, int_mul_slow(i, j)) + for i_abs in special_ints: + for j_abs in special_ints: + for i_sign in [1, -1]: + for j_sign in [1, -1]: + i = i_abs if i_sign > 0 else -i_abs + j = j_abs if j_sign > 0 else -j_abs + assert_eq(i * j, int_mul_slow(i, j)) test_mul() # floored division assert_eq(100 // 7, 14) assert_eq(100 // -7, -15) -assert_eq(-100 // 7, -15) # NB: different from Go / Java -assert_eq(-100 // -7, 14) # NB: different from Go / Java +assert_eq(-100 // 7, -15) # NB: different from Go / Java +assert_eq(-100 // -7, 14) # NB: different from Go / Java assert_eq(98 // 7, 14) assert_eq(98 // -7, -14) assert_eq(-98 // 7, -14) @@ -116,13 +154,13 @@ assert_eq(-1 // 7, -1) assert_eq(-1 // -7, 0) assert_eq(0 // 3, 0) assert_eq(0 // -3, 0) -assert_eq( product // 1234567, 1169282890553) -assert_eq( product // -1234567, -1169282890553-1) -assert_eq(-product // 1234567, -1169282890553-1) -assert_eq(-product // -1234567, 1169282890553) -assert_eq(((-1) << 31) // -1, 1 << 31) # sole case of int // int that causes int overflow -assert_eq(((-1) << 63) // -1, 1 << 63) # ditto, long overflow -assert_fails(lambda: 1 // 0 , "integer division by zero") +assert_eq(product // 1234567, 1169282890553) +assert_eq(product // -1234567, -1169282890553 - 1) +assert_eq(-product // 1234567, -1169282890553 - 1) +assert_eq(-product // -1234567, 1169282890553) +assert_eq(((-1) << 31) // -1, 1 << 31) # sole case of int // int that causes int overflow +assert_eq(((-1) << 63) // -1, 1 << 63) # ditto, long overflow +assert_fails(lambda: 1 // 0, "integer division by zero") # floating-point division of int operands assert_eq(str(100 / 7), "14.285714285714286") @@ -134,58 +172,61 @@ assert_eq(98 / 7, 14.0) assert_eq(98 / -7, -14.0) assert_eq(-98 / 7, -14.0) assert_eq(-98 / -7, 14.0) -assert_eq(type(product / 1234567), "float") -assert_eq(int( product / 1234567), 1169282890553) -assert_eq(int( product / -1234567), -1169282890553) -assert_eq(int(-product / 1234567), -1169282890553) -assert_eq(int(-product / -1234567), 1169282890553) -assert_eq(((-1) << 31) / -1, 1 << 31) # sole case of int / int that causes int overflow -assert_eq(((-1) << 63) / -1, 1 << 63) # ditto, long overflow +assert_eq(type(product / 1234567), "float") +assert_eq(int(product / 1234567), 1169282890553) +assert_eq(int(product / -1234567), -1169282890553) +assert_eq(int(-product / 1234567), -1169282890553) +assert_eq(int(-product / -1234567), 1169282890553) +assert_eq(((-1) << 31) / -1, 1 << 31) # sole case of int / int that causes int overflow +assert_eq(((-1) << 63) / -1, 1 << 63) # ditto, long overflow # remainder assert_eq(100 % 7, 2) -assert_eq(100 % -7, -5) # NB: different from Go / Java -assert_eq(-100 % 7, 5) # NB: different from Go / Java +assert_eq(100 % -7, -5) # NB: different from Go / Java +assert_eq(-100 % 7, 5) # NB: different from Go / Java assert_eq(-100 % -7, -2) assert_eq(98 % 7, 0) assert_eq(98 % -7, 0) assert_eq(-98 % 7, 0) assert_eq(-98 % -7, 0) -assert_eq( product % 1234567, 1013598) -assert_eq( product % -1234567, -220969) # ditto -assert_eq(-product % 1234567, 220969) # ditto +assert_eq(product % 1234567, 1013598) +assert_eq(product % -1234567, -220969) # ditto +assert_eq(-product % 1234567, 220969) # ditto assert_eq(-product % -1234567, -1013598) -assert_fails(lambda: 1 % 0 , "integer modulo by zero") +assert_eq(1 % maxlong, 1) +assert_eq((-1) % maxlong, maxlong - 1) +assert_eq(1 % minlong, -maxlong) +assert_eq((-1) % minlong, -1) +assert_fails(lambda: 1 % 0, "integer modulo by zero") # precedence assert_eq(5 - 7 * 2 + 3, -6) assert_eq(4 * 5 // 2 + 5 // 2 * 4, 18) -assert_eq(1<<8 - 1, 1 << (8-1)) # confusingly... +assert_eq(1 << 8 - 1, 1 << (8 - 1)) # confusingly... assert_eq(8 | 3 ^ 4 & -2, 15) assert_eq(~8 >> 1 | 3 ^ 4 & -2 << 2 * 3 + 4 // -2, -5) # compound assignment def compound(): - x = 1 - x += 1 - assert_eq(x, 2) - x -= 3 - assert_eq(x, -1) - x *= 10 - assert_eq(x, -10) - x //= -2 - assert_eq(x, 5) - x %= 3 - assert_eq(x, 2) + x = 1 + x += 1 + assert_eq(x, 2) + x -= 3 + assert_eq(x, -1) + x *= 10 + assert_eq(x, -10) + x //= -2 + assert_eq(x, 5) + x %= 3 + assert_eq(x, 2) compound() - # unary operators assert_eq(+0, 0) assert_eq(+4, 4) -assert_eq(+-4, -4) +assert_eq(+(-4), -4) assert_eq(-0, 0) assert_eq(-4, 0 - 4) @@ -195,25 +236,25 @@ assert_eq(-maxint, 0 - maxint) assert_eq(-minlong, 0 - minlong) assert_eq(-maxlong, 0 - maxlong) -assert_eq(++4, 4) -assert_eq(+-4, 0 - 4) -assert_eq(-+-4, 4) +assert_eq(+(+4), 4) +assert_eq(+(-4), 0 - 4) +assert_eq(-(+(-4)), 4) # bitwise def f(): - x = 2 - x &= 1 - assert_eq(x, 0) - x = 0 - x |= 2 - assert_eq(x, 2) - x ^= 3 - assert_eq(x, 1) - x <<= 2 - assert_eq(x, 4) - x >>=2 - assert_eq(x, 1) + x = 2 + x &= 1 + assert_eq(x, 0) + x = 0 + x |= 2 + assert_eq(x, 2) + x ^= 3 + assert_eq(x, 1) + x <<= 2 + assert_eq(x, 4) + x >>= 2 + assert_eq(x, 1) f() @@ -237,32 +278,36 @@ assert_eq(~maxint, minint) assert_eq(~minlong, maxlong) assert_eq(~maxlong, minlong) assert_eq(~(1 << 100), -(1 << 100) - 1) -assert_eq(~-(1 << 100), (1 << 100) - 1) +assert_eq(~(-(1 << 100)), (1 << 100) - 1) # | assert_eq(1 | 2, 3) assert_eq(3 | 6, 7) assert_eq(7 | 0, 7) + # & assert_eq(7 & 0, 0) assert_eq(7 & 7, 7) assert_eq(7 & 2, 2) -assert_eq((1|2) & (2|4), 2) +assert_eq((1 | 2) & (2 | 4), 2) assert_fails(lambda: 1 & False, "unsupported binary operation: int & bool") + # ^ assert_eq(1 ^ 2, 3) assert_eq(2 ^ 2, 0) assert_eq(-6 ^ 0, -6) -assert_eq(1 | 0 ^ 1, 1) # check | and ^ operators precedence +assert_eq(1 | 0 ^ 1, 1) # check | and ^ operators precedence assert_fails(lambda: "a" ^ 5, "unsupported binary operation: string \\^ int") + # ~ assert_eq(~1, -2) -assert_eq(~-2, 1) +assert_eq(~(-2), 1) assert_eq(~0, -1) assert_eq(~6, -7) assert_eq(~0, -1) -assert_eq(~2147483647, -2147483647 - 1); +assert_eq(~2147483647, -2147483647 - 1) assert_fails(lambda: ~False, "unsupported unary operation: ~bool") + # << assert_eq(1 << 2, 4) assert_eq(7 << 0, 7) @@ -277,6 +322,7 @@ assert_eq(-1 << 63, minlong) assert_eq(-1 << 64, minlong * 2) assert_fails(lambda: 1 << 520, "shift count too large: 520") assert_fails(lambda: 1 << -4, "negative shift count: -4") + # >> assert_eq(2 >> 1, 1) assert_eq(7 >> 0, 7) @@ -296,6 +342,7 @@ assert_eq(maxlong >> 62, 1) assert_eq(1000 >> 100, 0) assert_eq(-10 >> 1000, -1) assert_fails(lambda: 2 >> -1, "negative shift count: -1") + # << and >> assert_eq(1 << 500 >> 499, 2) assert_eq(1 << 32, 0x10000 * 0x10000) diff --git a/libstarlark/src/test/java/net/starlark/java/eval/testdata/int_constructor.star b/libstarlark/src/test/java/net/starlark/java/eval/testdata/int_constructor.star index ff0ec9963..2090f5747 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/testdata/int_constructor.star +++ b/libstarlark/src/test/java/net/starlark/java/eval/testdata/int_constructor.star @@ -20,50 +20,52 @@ assert_fails(lambda: int(None), "got NoneType, want string, int, float, or bool" assert_fails(lambda: int(""), "empty string") # no base -assert_eq(int('0'), 0) -assert_eq(int('1'), 1) -assert_eq(int('42'), 42) -assert_eq(int('-1'), -1) -assert_eq(int('-1234'), -1234) -assert_eq(int('2147483647'), 2147483647) -assert_eq(int('-2147483648'), -2147483647 - 1) -assert_eq(int('123456789012345678901234567891234567890'), 123456789012345678901234567891234567890) -assert_eq(int('-123456789012345678901234567891234567890'), -123456789012345678901234567891234567890) -assert_eq(int('-0xabcdefabcdefabcdefabcdefabcdef', 0), -0xabcdefabcdefabcdefabcdefabcdef) -assert_eq(int('1111111111111', 2), 8191) -assert_eq(int('1111111111111', 5), 305175781) -assert_eq(int('1111111111111', 8), 78536544841) -assert_eq(int('1111111111111', 10), 1111111111111) -assert_eq(int('1111111111111', 16), 300239975158033) -assert_eq(int('1111111111111', 36), 4873763662273663093) -assert_eq(int('016'), 16) # zero ok when base != 0. -assert_eq(int('+42'), 42) # '+' prefix ok +assert_eq(int("0"), 0) +assert_eq(int("1"), 1) +assert_eq(int("42"), 42) +assert_eq(int("-1"), -1) +assert_eq(int("-1234"), -1234) +assert_eq(int("2147483647"), 2147483647) +assert_eq(int("-2147483648"), -2147483647 - 1) +assert_eq(int("123456789012345678901234567891234567890"), 123456789012345678901234567891234567890) +assert_eq(int("-123456789012345678901234567891234567890"), -123456789012345678901234567891234567890) +assert_eq(int("-0xabcdefabcdefabcdefabcdefabcdef", 0), -0xabcdefabcdefabcdefabcdefabcdef) +assert_eq(int("1111111111111", 2), 8191) +assert_eq(int("1111111111111", 5), 305175781) +assert_eq(int("1111111111111", 8), 78536544841) +assert_eq(int("1111111111111", 10), 1111111111111) +assert_eq(int("1111111111111", 16), 300239975158033) +assert_eq(int("1111111111111", 36), 4873763662273663093) +assert_eq(int("016"), 16) # zero ok when base != 0. +assert_eq(int("+42"), 42) # '+' prefix ok + # with base, no prefix -assert_eq(int('11', 2), 3) -assert_eq(int('11', 9), 10) -assert_eq(int('AF', 16), 175) -assert_eq(int('11', 36), 37) -assert_eq(int('az', 36), 395) -assert_eq(int('11', 10), 11) -assert_eq(int('11', 0), 11) +assert_eq(int("11", 2), 3) +assert_eq(int("11", 9), 10) +assert_eq(int("AF", 16), 175) +assert_eq(int("11", 36), 37) +assert_eq(int("az", 36), 395) +assert_eq(int("11", 10), 11) +assert_eq(int("11", 0), 11) + # base and prefix -assert_eq(int('0b11', 0), 3) -assert_eq(int('0B11', 2), 3) -assert_eq(int('0o11', 0), 9) -assert_eq(int('0O11', 8), 9) -assert_eq(int('0XFF', 0), 255) -assert_eq(int('0xFF', 16), 255) -assert_eq(int('0b11', 0), 3) -assert_eq(int('-0b11', 0), -3) -assert_eq(int('+0b11', 0), 3) -assert_eq(int('0B11', 2), 3) -assert_eq(int('0o11', 0), 9) -assert_eq(int('0O11', 8), 9) -assert_eq(int('-11', 2), -3) -assert_eq(int('016', 8), 14) -assert_eq(int('016', 16), 22) -assert_eq(int('0', 0), 0) -assert_eq(int('0x0b10', 16), 0x0b10) +assert_eq(int("0b11", 0), 3) +assert_eq(int("0B11", 2), 3) +assert_eq(int("0o11", 0), 9) +assert_eq(int("0O11", 8), 9) +assert_eq(int("0XFF", 0), 255) +assert_eq(int("0xFF", 16), 255) +assert_eq(int("0b11", 0), 3) +assert_eq(int("-0b11", 0), -3) +assert_eq(int("+0b11", 0), 3) +assert_eq(int("0B11", 2), 3) +assert_eq(int("0o11", 0), 9) +assert_eq(int("0O11", 8), 9) +assert_eq(int("-11", 2), -3) +assert_eq(int("016", 8), 14) +assert_eq(int("016", 16), 22) +assert_eq(int("0", 0), 0) +assert_eq(int("0x0b10", 16), 0x0b10) assert_fails(lambda: int("0xFF", 8), 'invalid base-8 literal: "0xFF"') assert_fails(lambda: int("016", 0), 'cannot infer base when string begins with a 0: "016"') assert_fails(lambda: int("123", 3), 'invalid base-3 literal: "123"') @@ -76,16 +78,27 @@ assert_fails(lambda: int(True, 2), "can't convert non-string with explicit base" assert_fails(lambda: int(True, 10), "can't convert non-string with explicit base") assert_fails(lambda: int(1, 2), "can't convert non-string with explicit base") +# Base prefix is honored only if base=0, or if the prefix matches the explicit base. +# See https://github.com/google/starlark-go/issues/337 +assert_fails(lambda: int("0b0"), "invalid base-10 literal") +assert_eq(int("0b0", 0), 0) +assert_eq(int("0b0", 2), 0) +assert_eq(int("0b0", 16), 0xb0) +assert_eq(int("0x0b0", 16), 0xb0) +assert_eq(int("0x0b0", 0), 0xb0) +assert_eq(int("0x0b0101", 16), 0x0b0101) +assert_eq(int("0b0101", 2), 5) # prefix is redundant with explicit base + # This case is allowed in Python but not Skylark assert_fails(lambda: int(), "missing 1 required positional argument: x") # Unlike Python, leading and trailing whitespace is not allowed. Use int(s.strip()). -assert_fails(lambda: int(' 1'), 'invalid base-10 literal: " 1"') -assert_fails(lambda: int('1 '), 'invalid base-10 literal: "1 "') -assert_fails(lambda: int('-'), 'invalid base-10 literal: "-"') -assert_fails(lambda: int('+'), 'invalid base-10 literal: "[+]"') -assert_fails(lambda: int('0x'), 'invalid base-10 literal: "0x"') -assert_fails(lambda: int('1.5'), 'invalid base-10 literal: "1.5"') -assert_fails(lambda: int('ab'), 'invalid base-10 literal: "ab"') -assert_fails(lambda: int('--1'), 'invalid base-10 literal: "--1"') -assert_fails(lambda: int('-0x-10', 16), 'invalid base-16 literal: "-0x-10"') +assert_fails(lambda: int(" 1"), 'invalid base-10 literal: " 1"') +assert_fails(lambda: int("1 "), 'invalid base-10 literal: "1 "') +assert_fails(lambda: int("-"), 'invalid base-10 literal: "-"') +assert_fails(lambda: int("+"), 'invalid base-10 literal: "[+]"') +assert_fails(lambda: int("0x"), 'invalid base-10 literal: "0x"') +assert_fails(lambda: int("1.5"), 'invalid base-10 literal: "1.5"') +assert_fails(lambda: int("ab"), 'invalid base-10 literal: "ab"') +assert_fails(lambda: int("--1"), 'invalid base-10 literal: "--1"') +assert_fails(lambda: int("-0x-10", 16), 'invalid base-16 literal: "-0x-10"') diff --git a/libstarlark/src/test/java/net/starlark/java/eval/testdata/list.star b/libstarlark/src/test/java/net/starlark/java/eval/testdata/list.star new file mode 100644 index 000000000..c0f4370cc --- /dev/null +++ b/libstarlark/src/test/java/net/starlark/java/eval/testdata/list.star @@ -0,0 +1,65 @@ +# list tests +# TODO(adonovan): duplicate/share these tests for tuple where applicable. + +# index +l = [1, "2", 3] +assert_eq(l[0], 1) +assert_eq(l[1], "2") +assert_eq(l[2], 3) + +# index out of bounds +assert_fails( + lambda: ["a", "b", "c"][3], + "index out of range \\(index is 3, but sequence has 3 elements\\)", +) +assert_fails( + lambda: ["a", "b", "c"][10], + "index out of range \\(index is 10, but sequence has 3 elements\\)", +) +assert_fails( + lambda: [][0], + "index out of range \\(index is 0, but sequence has 0 elements\\)", +) + +# negative indices +m = ["a", "b", "c"] +assert_eq(m[0], "a") +assert_eq(m[-1], "c") +assert_eq(m[-2], "b") +assert_eq(m[-3], "a") +assert_fails(lambda: m[-4], "index out of range \\(index is -4, but sequence has 3 elements\\)") +assert_fails(lambda: [][-1], "index out of range \\(index is -1, but sequence has 0 elements\\)") + +# len +assert_eq(len([42, "hello, world", []]), 3) + +# truth +assert_eq(8 if [1, 2, 3] else 9, 8) +assert_eq(8 if [] else 9, 9) + +# concat +assert_eq([1, 2] + [3, 4], [1, 2, 3, 4]) +assert_eq(len([1, 2] + [3, 4]), 4) +assert_eq(str([1, 2] + [3, 4]), "[1, 2, 3, 4]") +assert_eq(1 if ([1, 2] + [3, 4]) else 0, 1) +assert_eq(1 if ([] + []) else 0, 0) + +h = [1] + (([2] + [3, 4]) + [5]) +assert_eq(h[0], 1) +assert_eq(h[1], 2) +assert_eq(h[2], 3) +assert_eq(h[3], 4) +assert_eq(h[4], 5) + +# comparison +assert_eq([1, 2, 3, 4], [1, 2] + [3, 4]) +assert_([1, 2, 3, 4] != (1, 2, 3, 4)) +assert_([1, 2] != [1, 2, 3]) +assert_eq([], []) + +# repeat +assert_eq([1, 2, 3] * 3, [1, 2, 3, 1, 2, 3, 1, 2, 3]) +assert_eq(3 * [1, 2, 3], [1, 2, 3, 1, 2, 3, 1, 2, 3]) +assert_eq([1, 2, 3] * -1, []) +assert_eq([1, 2, 3] * 0, []) +assert_fails(lambda: [1] * (1 << 35), "got 34359738368 for repeat, want value in signed 32-bit range") diff --git a/libstarlark/src/test/java/net/starlark/java/eval/testdata/list_mutation.star b/libstarlark/src/test/java/net/starlark/java/eval/testdata/list_mutation.star index 73291ec1e..b11d5a3b6 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/testdata/list_mutation.star +++ b/libstarlark/src/test/java/net/starlark/java/eval/testdata/list_mutation.star @@ -27,7 +27,6 @@ assert_eq(b, []) assert_fails(lambda: (1, 2).insert(3), "'tuple' value has no field or method 'insert'") --- - # append foo = ["a", "b"] @@ -36,21 +35,25 @@ assert_eq(foo, ["a", "b", "c"]) foo.append("d") assert_eq(foo, ["a", "b", "c", "d"]) +bar = [1, 2] +assert_eq(bar.append([3, 4]), None) +assert_eq(bar, [1, 2, [3, 4]]) + assert_fails(lambda: (1, 2).append(3), "'tuple' value has no field or method 'append'") --- - # extend foo = ["a", "b"] -foo.extend(["c", "d"]) -foo.extend(("e", "f")) -foo.extend({"g": None}) +assert_eq(foo.extend(["c", "d"]), None) +assert_eq(foo.extend(("e", "f")), None) +assert_eq(foo.extend({"g": None}), None) assert_eq(foo, ["a", "b", "c", "d", "e", "f", "g"]) assert_fails(lambda: (1, 2).extend([3, 4]), "'tuple' value has no field or method 'extend'") assert_fails(lambda: [1, 2].extend(3), "type 'int' is not iterable") ---- +assert_fails(lambda: [].extend(range((1 << 31) - 1)), "excessive capacity requested") +--- # remove foo = ["a", "b", "c", "b"] diff --git a/libstarlark/src/test/java/net/starlark/java/eval/testdata/list_slices.star b/libstarlark/src/test/java/net/starlark/java/eval/testdata/list_slices.star index dc8542040..c07777f00 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/testdata/list_slices.star +++ b/libstarlark/src/test/java/net/starlark/java/eval/testdata/list_slices.star @@ -1,9 +1,27 @@ +# Tests of list and tuple slices, x[a:b:c] + +abc = ["a", "b", "c"] + # Without step -assert_eq([0, 1, 2, 3][0:-1], [0, 1, 2]) -assert_eq([0, 1, 2, 3, 4, 5][2:4], [2, 3]) -assert_eq([0, 1, 2, 3, 4, 5][-2:-1], [4]) -assert_eq([][1:2], []) -assert_eq([0, 1, 2, 3][-10:10], [0, 1, 2, 3]) +assert_eq(abc[1:2], ["b"]) +assert_eq(abc[1:1], []) +assert_eq(abc[-10:10], abc) +assert_eq(abc[:2], ["a", "b"]) +assert_eq(abc[:-1], ["a", "b"]) +assert_eq(abc[:], abc) +assert_eq(abc[:2], ["a", "b"]) +assert_eq(abc[2:], ["c"]) + +# Negative bounds +assert_eq(abc[-2:-1], ["b"]) +assert_eq(abc[-2:], ["b", "c"]) +assert_eq(abc[0:-1], ["a", "b"]) +assert_eq(abc[-1:1], []) + +assert_eq(abc[0:5], abc) +assert_eq(abc[-10:2], ["a", "b"]) +assert_eq(abc[3:10], []) +assert_eq(abc[-10:-9], []) # With step assert_eq([1, 2, 3, 4, 5][::1], [1, 2, 3, 4, 5]) @@ -17,6 +35,8 @@ assert_eq([][::1], []) assert_eq([][::-1], []) assert_eq([1, 2, 3, 4, 5, 6, 7][::3], [1, 4, 7]) assert_eq([1, 2, 3, 4, 5, 6, 7, 8, 9][1:7:3], [2, 5]) +assert_eq([1, 2, 3, 4, 5, 6, 7, 8, 9][-8:-3:2], [2, 4, 6]) +assert_eq([1, 2, 3, 4, 5, 6, 7, 8, 9][3:-3:2], [4, 6]) assert_eq([1, 2, 3][3:1:1], []) assert_eq([1, 2, 3][1:3:-1], []) diff --git a/libstarlark/src/test/java/net/starlark/java/eval/testdata/string_misc.star b/libstarlark/src/test/java/net/starlark/java/eval/testdata/string_misc.star index ec27f1473..96de437e5 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/testdata/string_misc.star +++ b/libstarlark/src/test/java/net/starlark/java/eval/testdata/string_misc.star @@ -169,3 +169,10 @@ assert_eq(enumerate({"a": 0, 2: 1, "ab": 3}), [(0, "a"), (1, 2), (2, "ab")]) assert_eq(enumerate({}), []) assert_eq(enumerate([False, True, None], 42), [(42, False), (43, True), (44, None)]) assert_fails(lambda: enumerate("ab"), "type 'string' is not iterable") + +# repeat +assert_eq("abc" * 3, "abcabcabc") +assert_eq(3 * "abc", "abcabcabc") +assert_eq("abc" * 0, "") +assert_eq("abc" * -1, "") +assert_fails(lambda: "abc" * (1 << 35), "got 34359738368 for repeat, want value in signed 32-bit range") diff --git a/libstarlark/src/test/java/net/starlark/java/eval/testdata/tuple.star b/libstarlark/src/test/java/net/starlark/java/eval/testdata/tuple.star index a7ac3206b..0a6a0efe3 100644 --- a/libstarlark/src/test/java/net/starlark/java/eval/testdata/tuple.star +++ b/libstarlark/src/test/java/net/starlark/java/eval/testdata/tuple.star @@ -1,5 +1,25 @@ # tests of tuples +# index +t = (1, "2", 3) +assert_eq(t[0], 1) +assert_eq(t[1], "2") +assert_eq(t[2], 3) + +# concatenation assert_eq(() + (1, "a"), (1, "a")) assert_eq((1, "a") + (), (1, "a")) assert_eq((1,) + ("a",), (1, "a")) + +# comparison +assert_eq((1, "two", [3, 4]), (1, "two", [3, 4])) +assert_eq((), ()) +assert_(() != (1,)) +assert_((1) != (1,)) + +# repeat +assert_eq((1, 2, 3) * 3, (1, 2, 3, 1, 2, 3, 1, 2, 3)) +assert_eq(3 * (1, 2, 3), (1, 2, 3, 1, 2, 3, 1, 2, 3)) +assert_eq((1, 2, 3) * -1, ()) +assert_eq((1, 2, 3) * 0, ()) +assert_fails(lambda: (1,) * (1 << 35), "got 34359738368 for repeat, want value in signed 32-bit range")