Implement ConditionalExpression

Also add tests.

--
MOS_MIGRATED_REVID=88474801
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ConditionalExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/ConditionalExpression.java
new file mode 100644
index 0000000..46471b3
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ConditionalExpression.java
@@ -0,0 +1,70 @@
+// Copyright 2014 Google Inc. 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;
+
+/**
+ * Syntax node for an if/else expression.
+ */
+public final class ConditionalExpression extends Expression {
+
+  // Python conditional expressions: $thenCase if $condition else $elseCase
+  // https://docs.python.org/3.5/reference/expressions.html#conditional-expressions
+  private final Expression thenCase;
+  private final Expression condition;
+  private final Expression elseCase;
+
+  public Expression getThenCase() { return thenCase; }
+  public Expression getCondition() { return condition; }
+  public Expression getElseCase() { return elseCase; }
+
+  /**
+   * Constructor for a conditional expression
+   */
+  public ConditionalExpression(
+      Expression thenCase, Expression condition, Expression elseCase) {
+    this.thenCase = thenCase;
+    this.condition = condition;
+    this.elseCase = elseCase;
+  }
+
+  /**
+   * Constructs a string representation of the if expression
+   */
+  @Override
+  public String toString() {
+    return thenCase + " if " + condition + " else " + elseCase;
+  }
+
+  @Override
+  Object eval(Environment env) throws EvalException, InterruptedException {
+    if (EvalUtils.toBoolean(condition.eval(env))) {
+      return thenCase.eval(env);
+    } else {
+      return elseCase.eval(env);
+    }
+  }
+
+  @Override
+  public void accept(SyntaxTreeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  SkylarkType validate(ValidationEnvironment env) throws EvalException {
+    condition.validate(env);
+    return thenCase.validate(env)
+        .infer(elseCase.validate(env), "else case", thenCase.getLocation(), elseCase.getLocation());
+  }
+}
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 97a4bbd..90f79ec 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
@@ -341,6 +341,8 @@
 
   // Convenience method that uses end offset from the last node.
   private <NODE extends ASTNode> NODE setLocation(NODE node, int startOffset, ASTNode lastNode) {
+    Preconditions.checkNotNull(lastNode, "can't extract end offset from a null node");
+    Preconditions.checkNotNull(lastNode.getLocation(), "lastNode doesn't have a location");
     return setLocation(node, startOffset, lastNode.getLocation().getEndOffset());
   }
 
@@ -908,7 +910,23 @@
   }
 
   private Expression parseExpression() {
-    return parseExpression(0);
+    int start = token.left;
+    Expression expr = parseExpression(0);
+    if (token.kind == TokenKind.IF) {
+      nextToken();
+      Expression condition = parseExpression(0);
+      if (token.kind == TokenKind.ELSE) {
+        nextToken();
+        Expression elseClause = parseExpression();
+        return setLocation(new ConditionalExpression(expr, condition, elseClause),
+            start, elseClause);
+      } else {
+        reportError(lexer.createLocation(start, token.left),
+            "missing else clause in conditional expression or semicolon before if");
+        return expr; // Try to recover from error: drop the if and the expression after it. Ouch.
+      }
+    }
+    return expr;
   }
 
   private Expression parseExpression(int prec) {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SyntaxTreeVisitor.java b/src/main/java/com/google/devtools/build/lib/syntax/SyntaxTreeVisitor.java
index e53a7aa..2463d25 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SyntaxTreeVisitor.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SyntaxTreeVisitor.java
@@ -140,4 +140,11 @@
   public void visit(Comment node) {
   }
 
+  public void visit(ConditionalExpression node) {
+    visit(node.getThenCase());
+    visit(node.getCondition());
+    if (node.getElseCase() != null) {
+      visit(node.getElseCase());
+    }
+  }
 }