Skylark: Allow list slices -- MOS_MIGRATED_REVID=88727892
diff --git a/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java index 8035d08..bee5ca1 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java
@@ -67,18 +67,18 @@ // Convert string index in the same way Python does. // If index is negative, starts from the end. // If index is outside bounds, it is restricted to the valid range. - private static int getPythonStringIndex(int index, int stringLength) { + private static int getClampedIndex(int index, int length) { if (index < 0) { - index += stringLength; + index += length; } - return Math.max(Math.min(index, stringLength), 0); + return Math.max(Math.min(index, length), 0); } // Emulate Python substring function // It converts out of range indices, and never fails private static String getPythonSubstring(String str, int start, int end) { - start = getPythonStringIndex(start, str.length()); - end = getPythonStringIndex(end, str.length()); + start = getClampedIndex(start, str.length()); + end = getClampedIndex(end, str.length()); if (start > end) { return ""; } else { @@ -225,7 +225,7 @@ end = Type.INTEGER.convert(args[3], "'rfind' argument"); } int subpos = getPythonSubstring(thiz, start, end).lastIndexOf(sub); - start = getPythonStringIndex(start, thiz.length()); + start = getClampedIndex(start, thiz.length()); return subpos < 0 ? subpos : subpos + start; } }; @@ -256,7 +256,7 @@ end = Type.INTEGER.convert(args[3], "'find' argument"); } int subpos = getPythonSubstring(thiz, start, end).indexOf(sub); - start = getPythonStringIndex(start, thiz.length()); + start = getClampedIndex(start, thiz.length()); return subpos < 0 ? subpos : subpos + start; } }; @@ -369,17 +369,33 @@ } }; - // substring operator - @SkylarkBuiltin(name = "$substring", hidden = true, - doc = "String[<code>start</code>:<code>end</code>] returns a substring.") - private static Function substring = new MixedModeFunction("$substring", + // slice operator + @SkylarkBuiltin(name = "$slice", hidden = true, + doc = "x[<code>start</code>:<code>end</code>] returns a slice or a list slice.") + private static Function slice = new MixedModeFunction("$slice", ImmutableList.of("this", "start", "end"), 3, false) { @Override - public Object call(Object[] args, FuncallExpression ast) throws ConversionException { - String thiz = Type.STRING.convert(args[0], "substring operand"); - int left = Type.INTEGER.convert(args[1], "substring operand"); - int right = Type.INTEGER.convert(args[2], "substring operand"); - return getPythonSubstring(thiz, left, right); + public Object call(Object[] args, FuncallExpression ast, Environment env) + throws EvalException, ConversionException { + int left = Type.INTEGER.convert(args[1], "start operand"); + int right = Type.INTEGER.convert(args[2], "end operand"); + + // Substring + if (args[0] instanceof String) { + String thiz = Type.STRING.convert(args[0], "substring operand"); + return getPythonSubstring(thiz, left, right); + } + + // List slice + List<Object> list = Type.OBJECT_LIST.convert(args[0], "list operand"); + left = getClampedIndex(left, list.size()); + right = getClampedIndex(right, list.size()); + + List<Object> result = Lists.newArrayList(); + for (int i = left; i < right; i++) { + result.add(list.get(i)); + } + return convert(result, env, ast.getLocation()); } }; @@ -500,6 +516,7 @@ } }; + // TODO(bazel-team): Use the same type for both Skylark and BUILD files. @SuppressWarnings("unchecked") private static Iterable<Object> convert(Collection<?> list, Environment env, Location loc) throws EvalException { @@ -937,10 +954,15 @@ .put(endswith, SkylarkType.BOOL) .put(startswith, SkylarkType.BOOL) .put(strip, SkylarkType.STRING) - .put(substring, SkylarkType.STRING) + .put(slice, SkylarkType.STRING) .put(count, SkylarkType.INT) .build(); + public static final Map<Function, SkylarkType> listPureFunctions = ImmutableMap + .<Function, SkylarkType>builder() + .put(slice, SkylarkType.LIST) + .build(); + public static final List<Function> listFunctions = ImmutableList.of(append, extend); public static final Map<Function, SkylarkType> dictFunctions = ImmutableMap @@ -986,6 +1008,7 @@ setupMethodEnvironment(env, Map.class, dictFunctions.keySet()); env.registerFunction(String.class, index.getName(), index); setupMethodEnvironment(env, String.class, stringFunctions.keySet()); + setupMethodEnvironment(env, List.class, listPureFunctions.keySet()); if (env.isSkylarkEnabled()) { env.registerFunction(SkylarkList.class, index.getName(), index); setupMethodEnvironment(env, skylarkGlobalFunctions.keySet()); @@ -1033,5 +1056,9 @@ Map<String, SkylarkType> string = new HashMap<>(); setupValidationEnvironment(stringFunctions, string); builtIn.put(SkylarkType.STRING, string); + + Map<String, SkylarkType> list = new HashMap<>(); + setupValidationEnvironment(listPureFunctions, string); + builtIn.put(SkylarkType.LIST, list); } }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java index c31d434..41fe332 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
@@ -247,7 +247,7 @@ @Override public String toString() { - if (func.getName().equals("$substring")) { + if (func.getName().equals("$slice")) { return obj + "[" + args.get(0) + ":" + args.get(1) + "]"; } if (func.getName().equals("$index")) {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java index 45eae4c..68a4425 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
@@ -697,7 +697,7 @@ return makeFuncallExpression(receiver, new Ident("$index"), args, start, token.right); } - // This is a substring + // This is a slice (or substring) expect(TokenKind.COLON); int loc2 = token.left; if (token.kind == TokenKind.RBRACKET) { @@ -708,7 +708,7 @@ expect(TokenKind.RBRACKET); args.add(setLocation(new Argument.Positional(endExpr), loc2, endExpr)); - return makeFuncallExpression(receiver, new Ident("$substring"), args, + return makeFuncallExpression(receiver, new Ident("$slice"), args, start, token.right); }