Skylark: Slice operations now accept a step argument.

--
MOS_MIGRATED_REVID=110446625
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 1214d8d..c5f612f 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
@@ -713,16 +713,15 @@
     return receiver;
   }
 
-  // substring_suffix ::= '[' expression? ':' expression? ']'
+  // substring_suffix ::= '[' expression? ':' expression?  ':' expression? ']'
   private Expression parseSubstringSuffix(int start, Expression receiver) {
     List<Argument.Passed> args = new ArrayList<>();
     Expression startExpr;
-    Expression endExpr;
 
     expect(TokenKind.LBRACKET);
     int loc1 = token.left;
     if (token.kind == TokenKind.COLON) {
-      startExpr = setLocation(new IntegerLiteral(0), token.left, token.right);
+      startExpr = setLocation(new Identifier("None"), token.left, token.right);
     } else {
       startExpr = parseExpression();
     }
@@ -734,20 +733,38 @@
                                    start, token.right);
     }
     // This is a slice (or substring)
-    expect(TokenKind.COLON);
-    int loc2 = token.left;
-    if (token.kind == TokenKind.RBRACKET) {
-      endExpr = setLocation(new IntegerLiteral(Integer.MAX_VALUE), token.left, token.right);
-    } else {
-      endExpr = parseNonTupleExpression();
-    }
+    args.add(parseSliceArgument(new Identifier("None")));
+    args.add(parseSliceArgument(new IntegerLiteral(1)));
     expect(TokenKind.RBRACKET);
-
-    args.add(setLocation(new Argument.Positional(endExpr), loc2, endExpr));
     return makeFuncallExpression(receiver, new Identifier("$slice"), args,
                                  start, token.right);
   }
 
+  /**
+   * Parses {@code [':' [expr]]} which can either be the end or the step argument of a slice
+   * operation. If no such expression is found, this method returns an argument that represents
+   * {@code defaultValue}.
+   */
+  private Argument.Positional parseSliceArgument(Expression defaultValue) {
+    Expression explicitArg = getSliceEndOrStepExpression();
+    Expression argValue =
+        (explicitArg == null) ? setLocation(defaultValue, token.left, token.right) : explicitArg;
+    return setLocation(new Argument.Positional(argValue), token.left, argValue);
+  }
+
+  private Expression getSliceEndOrStepExpression() {
+    // There has to be a colon before any end or slice argument.
+    // However, if the next token thereafter is another colon or a right bracket, no argument value
+    // was specified.
+    if (token.kind == TokenKind.COLON) {
+      expect(TokenKind.COLON);
+      if (token.kind != TokenKind.COLON && token.kind != TokenKind.RBRACKET) {
+        return parseNonTupleExpression();
+      }
+    }
+    return null;
+  }
+
   // Equivalent to 'exprlist' rule in Python grammar.
   // loop_variables ::= primary_with_suffix ( ',' primary_with_suffix )* ','?
   private Expression parseForLoopVariables() {