bazel syntax: eliminate Operator; use TokenKind
Also:
- rename TokenKind.getPrettyName -> toString.
No-one wants to see internal enum names in error messages.
- use shorter names (op, x, y) throughout {Bi,U}naryOperatorExpression.
PiperOrigin-RevId: 255981802
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/AugmentedAssignmentStatement.java b/src/main/java/com/google/devtools/build/lib/syntax/AugmentedAssignmentStatement.java
index addc790..fab391f 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/AugmentedAssignmentStatement.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/AugmentedAssignmentStatement.java
@@ -21,19 +21,19 @@
public final class AugmentedAssignmentStatement extends Statement {
private final Expression lhs; // same constraint as AssignmentStatement
- private final Operator operator;
+ private final TokenKind op;
private final Expression rhs;
/** Constructs an augmented assignment. */
- public AugmentedAssignmentStatement(Operator operator, Expression lhs, Expression rhs) {
+ public AugmentedAssignmentStatement(TokenKind op, Expression lhs, Expression rhs) {
this.lhs = lhs;
- this.operator = operator;
+ this.op = op;
this.rhs = rhs;
}
/** Returns the operator of the assignment. */
- public Operator getOperator() {
- return operator;
+ public TokenKind getOperator() {
+ return op;
}
/** Returns the LHS of the assignment. */
@@ -51,7 +51,7 @@
printIndent(buffer, indentLevel);
lhs.prettyPrint(buffer);
buffer.append(' ');
- buffer.append(operator.toString());
+ buffer.append(op.toString());
buffer.append("= ");
rhs.prettyPrint(buffer);
buffer.append('\n');
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
index ac6cce3..e231416 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
@@ -19,48 +19,68 @@
import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
import java.io.IOException;
+import java.util.EnumSet;
import java.util.IllegalFormatException;
-/** Syntax node for a binary operator expression. */
+/** A BinaryExpression represents a binary operator expression 'x op y'. */
public final class BinaryOperatorExpression extends Expression {
- private final Expression lhs;
+ private final Expression x;
+ private final TokenKind op; // one of 'operators'
+ private final Expression y;
- private final Expression rhs;
+ /** operators is the set of valid binary operators. */
+ public static final EnumSet<TokenKind> operators =
+ EnumSet.of(
+ TokenKind.AND,
+ TokenKind.EQUALS_EQUALS,
+ TokenKind.GREATER,
+ TokenKind.GREATER_EQUALS,
+ TokenKind.IN,
+ TokenKind.LESS,
+ TokenKind.LESS_EQUALS,
+ TokenKind.MINUS,
+ TokenKind.NOT_EQUALS,
+ TokenKind.NOT_IN,
+ TokenKind.OR,
+ TokenKind.PERCENT,
+ TokenKind.SLASH,
+ TokenKind.SLASH_SLASH,
+ TokenKind.PLUS,
+ TokenKind.PIPE,
+ TokenKind.STAR);
- private final Operator operator;
-
- public BinaryOperatorExpression(Operator operator, Expression lhs, Expression rhs) {
- this.lhs = lhs;
- this.rhs = rhs;
- this.operator = operator;
+ public BinaryOperatorExpression(Expression x, TokenKind op, Expression y) {
+ this.x = x;
+ this.op = op;
+ this.y = y;
}
- public Expression getLhs() {
- return lhs;
+ /** getX returns the left operand. */
+ public Expression getX() {
+ return x;
}
- public Expression getRhs() {
- return rhs;
+ /** getOperator returns the operator. */
+ public TokenKind getOperator() {
+ return op;
}
- /**
- * Returns the operator kind for this binary operation.
- */
- public Operator getOperator() {
- return operator;
+ /** getY returns the right operand. */
+ public Expression getY() {
+ return y;
}
@Override
public void prettyPrint(Appendable buffer) throws IOException {
- // TODO(bazel-team): Possibly omit parentheses when they are not needed according to operator
- // precedence rules. This requires passing down more contextual information.
+ // TODO(bazel-team): retain parentheses in the syntax tree so we needn't
+ // conservatively emit them here.
buffer.append('(');
- lhs.prettyPrint(buffer);
+ x.prettyPrint(buffer);
buffer.append(' ');
- buffer.append(operator.toString());
+ buffer.append(op.toString());
buffer.append(' ');
- rhs.prettyPrint(buffer);
+ y.prettyPrint(buffer);
buffer.append(')');
}
@@ -68,47 +88,47 @@
public String toString() {
// This omits the parentheses for brevity, but is not correct in general due to operator
// precedence rules.
- return lhs + " " + operator + " " + rhs;
+ return x + " " + op + " " + y;
}
/** Implements comparison operators. */
- private static int compare(Object lval, Object rval, Location location) throws EvalException {
+ private static int compare(Object x, Object y, Location location) throws EvalException {
try {
- return EvalUtils.SKYLARK_COMPARATOR.compare(lval, rval);
+ return EvalUtils.SKYLARK_COMPARATOR.compare(x, y);
} catch (EvalUtils.ComparisonException e) {
throw new EvalException(location, e);
}
}
- /** Implements the "in" operator. */
- private static boolean in(Object lval, Object rval, Environment env, Location location)
+ /** Implements 'x in y'. */
+ private static boolean in(Object x, Object y, Environment env, Location location)
throws EvalException {
- if (env.getSemantics().incompatibleDepsetIsNotIterable() && rval instanceof SkylarkNestedSet) {
+ if (env.getSemantics().incompatibleDepsetIsNotIterable() && y instanceof SkylarkNestedSet) {
throw new EvalException(
location,
"argument of type '"
- + EvalUtils.getDataTypeName(rval)
+ + EvalUtils.getDataTypeName(y)
+ "' is not iterable. "
+ "in operator only works on lists, tuples, dicts and strings. "
+ "Use --incompatible_depset_is_not_iterable=false to temporarily disable "
+ "this check.");
- } else if (rval instanceof SkylarkQueryable) {
- return ((SkylarkQueryable) rval).containsKey(lval, location, env.getStarlarkContext());
- } else if (rval instanceof String) {
- if (lval instanceof String) {
- return ((String) rval).contains((String) lval);
+ } else if (y instanceof SkylarkQueryable) {
+ return ((SkylarkQueryable) y).containsKey(x, location, env.getStarlarkContext());
+ } else if (y instanceof String) {
+ if (x instanceof String) {
+ return ((String) y).contains((String) x);
} else {
throw new EvalException(
location,
"'in <string>' requires string as left operand, not '"
- + EvalUtils.getDataTypeName(lval)
+ + EvalUtils.getDataTypeName(x)
+ "'");
}
} else {
throw new EvalException(
location,
"argument of type '"
- + EvalUtils.getDataTypeName(rval)
+ + EvalUtils.getDataTypeName(y)
+ "' is not iterable. "
+ "in operator only works on lists, tuples, dicts and strings.");
}
@@ -121,111 +141,98 @@
* side expression is evaluated exactly once, and the right-hand side expression is evaluated
* either once or not at all.
*
- * @throws IllegalArgumentException if {@code operator} is not {@link Operator#AND} or
- * {@link Operator#OR}.
+ * @throws IllegalArgumentException if {@code op} is not {@link Operator#AND} or {@link
+ * Operator#OR}.
*/
public static Object evaluateWithShortCircuiting(
- Operator operator,
- Expression lhs,
- Expression rhs,
- Environment env,
- Location loc)
+ TokenKind op, Expression x, Expression y, Environment env, Location loc)
throws EvalException, InterruptedException {
- Object lval = lhs.eval(env);
- if (operator == Operator.AND) {
- return EvalUtils.toBoolean(lval) ? rhs.eval(env) : lval;
- } else if (operator == Operator.OR) {
- return EvalUtils.toBoolean(lval) ? lval : rhs.eval(env);
- } else {
- throw new IllegalArgumentException("Not a short-circuiting operator: " + operator);
+ Object xval = x.eval(env);
+ switch (op) {
+ case AND:
+ return EvalUtils.toBoolean(xval) ? y.eval(env) : xval;
+ case OR:
+ return EvalUtils.toBoolean(xval) ? xval : y.eval(env);
+ default:
+ throw new IllegalArgumentException("Not a short-circuiting operator: " + op);
}
}
/**
- * Evaluates {@code lhs @ rhs}, where {@code @} is the operator, and returns the result.
+ * Evaluates {@code x @ y}, where {@code @} is the operator, and returns the result.
*
* <p>This method does not implement any short-circuiting logic for boolean operations, as the
* parameters are already evaluated.
*/
- public static Object evaluate(
- Operator operator,
- Object lhs,
- Object rhs,
- Environment env,
- Location loc)
+ public static Object evaluate(TokenKind op, Object x, Object y, Environment env, Location loc)
throws EvalException, InterruptedException {
- return evaluate(operator, lhs, rhs, env, loc, /*isAugmented=*/false);
+ return evaluate(op, x, y, env, loc, /*isAugmented=*/ false);
}
private static Object evaluate(
- Operator operator,
- Object lhs,
- Object rhs,
- Environment env,
- Location location,
- boolean isAugmented)
+ TokenKind op, Object x, Object y, Environment env, Location location, boolean isAugmented)
throws EvalException, InterruptedException {
try {
- switch (operator) {
+ switch (op) {
// AND and OR are included for completeness, but should normally be handled using
// evaluateWithShortCircuiting() instead of this method.
case AND:
- return EvalUtils.toBoolean(lhs) ? rhs : lhs;
+ return EvalUtils.toBoolean(x) ? y : x;
case OR:
- return EvalUtils.toBoolean(lhs) ? lhs : rhs;
+ return EvalUtils.toBoolean(x) ? x : y;
case PLUS:
- return plus(lhs, rhs, env, location, isAugmented);
+ return plus(x, y, env, location, isAugmented);
case PIPE:
- return pipe(lhs, rhs, env, location);
+ return pipe(x, y, env, location);
case MINUS:
- return minus(lhs, rhs, location);
+ return minus(x, y, location);
- case MULT:
- return mult(lhs, rhs, env, location);
+ case STAR:
+ return mult(x, y, env, location);
- case DIVIDE:
+ case SLASH:
throw new EvalException(
location,
"The `/` operator is not allowed. Please use the `//` operator for integer "
+ "division.");
- case FLOOR_DIVIDE:
- return divide(lhs, rhs, location);
+ case SLASH_SLASH:
+ return divide(x, y, location);
case PERCENT:
- return percent(lhs, rhs, location);
+ return percent(x, y, location);
case EQUALS_EQUALS:
- return lhs.equals(rhs);
+ return x.equals(y);
case NOT_EQUALS:
- return !lhs.equals(rhs);
+ return !x.equals(y);
case LESS:
- return compare(lhs, rhs, location) < 0;
+ return compare(x, y, location) < 0;
case LESS_EQUALS:
- return compare(lhs, rhs, location) <= 0;
+ return compare(x, y, location) <= 0;
case GREATER:
- return compare(lhs, rhs, location) > 0;
+ return compare(x, y, location) > 0;
case GREATER_EQUALS:
- return compare(lhs, rhs, location) >= 0;
+ return compare(x, y, location) >= 0;
case IN:
- return in(lhs, rhs, env, location);
+ return in(x, y, env, location);
case NOT_IN:
- return !in(lhs, rhs, env, location);
+ return !in(x, y, env, location);
default:
- throw new AssertionError("Unsupported binary operator: " + operator);
+ throw new AssertionError("Unsupported binary operator: " + op);
} // endswitch
} catch (ArithmeticException e) {
throw new EvalException(location, e.getMessage());
@@ -233,23 +240,23 @@
}
/**
- * Evaluates {@code lhs @= rhs} and returns the result, possibly mutating {@code lhs}.
+ * Evaluates {@code x @= y} and returns the result, possibly mutating {@code x}.
*
- * <p>Whether or not {@code lhs} is mutated depends on its type. If it is mutated, then it is also
+ * <p>Whether or not {@code x} is mutated depends on its type. If it is mutated, then it is also
* the return value.
*/
public static Object evaluateAugmented(
- Operator operator, Object lhs, Object rhs, Environment env, Location loc)
+ TokenKind op, Object x, Object y, Environment env, Location loc)
throws EvalException, InterruptedException {
- return evaluate(operator, lhs, rhs, env, loc, /*isAugmented=*/ true);
+ return evaluate(op, x, y, env, loc, /*isAugmented=*/ true);
}
@Override
Object doEval(Environment env) throws EvalException, InterruptedException {
- if (operator == Operator.AND || operator == Operator.OR) {
- return evaluateWithShortCircuiting(operator, lhs, rhs, env, getLocation());
+ if (op == TokenKind.AND || op == TokenKind.OR) {
+ return evaluateWithShortCircuiting(op, x, y, env, getLocation());
} else {
- return evaluate(operator, lhs.eval(env), rhs.eval(env), env, getLocation());
+ return evaluate(op, x.eval(env), y.eval(env), env, getLocation());
}
}
@@ -263,42 +270,43 @@
return Kind.BINARY_OPERATOR;
}
- /** Implements Operator.PLUS. */
+ /** Implements 'x + y'. */
private static Object plus(
- Object lval, Object rval, Environment env, Location location, boolean isAugmented)
+ Object x, Object y, Environment env, Location location, boolean isAugmented)
throws EvalException {
// int + int
- if (lval instanceof Integer && rval instanceof Integer) {
- return Math.addExact((Integer) lval, (Integer) rval);
+ if (x instanceof Integer && y instanceof Integer) {
+ return Math.addExact((Integer) x, (Integer) y);
}
// string + string
- if (lval instanceof String && rval instanceof String) {
- return (String) lval + (String) rval;
+ if (x instanceof String && y instanceof String) {
+ return (String) x + (String) y;
}
- if (lval instanceof SelectorValue || rval instanceof SelectorValue
- || lval instanceof SelectorList
- || rval instanceof SelectorList) {
- return SelectorList.concat(location, lval, rval);
+ if (x instanceof SelectorValue
+ || y instanceof SelectorValue
+ || x instanceof SelectorList
+ || y instanceof SelectorList) {
+ return SelectorList.concat(location, x, y);
}
- if ((lval instanceof Tuple) && (rval instanceof Tuple)) {
- return Tuple.concat((Tuple<?>) lval, (Tuple<?>) rval);
+ if (x instanceof Tuple && y instanceof Tuple) {
+ return Tuple.concat((Tuple<?>) x, (Tuple<?>) y);
}
- if ((lval instanceof MutableList) && (rval instanceof MutableList)) {
+ if (x instanceof MutableList && y instanceof MutableList) {
if (isAugmented) {
@SuppressWarnings("unchecked")
- MutableList<Object> list = (MutableList) lval;
- list.addAll((MutableList<?>) rval, location, env.mutability());
+ MutableList<Object> list = (MutableList) x;
+ list.addAll((MutableList<?>) y, location, env.mutability());
return list;
} else {
- return MutableList.concat((MutableList<?>) lval, (MutableList<?>) rval, env.mutability());
+ return MutableList.concat((MutableList<?>) x, (MutableList<?>) y, env.mutability());
}
}
- if (lval instanceof SkylarkDict && rval instanceof SkylarkDict) {
+ if (x instanceof SkylarkDict && y instanceof SkylarkDict) {
if (env.getSemantics().incompatibleDisallowDictPlus()) {
throw new EvalException(
location,
@@ -306,22 +314,22 @@
+ "`update` method instead. You can temporarily enable the `+` operator by passing "
+ "the flag --incompatible_disallow_dict_plus=false");
}
- return SkylarkDict.plus((SkylarkDict<?, ?>) lval, (SkylarkDict<?, ?>) rval, env);
+ return SkylarkDict.plus((SkylarkDict<?, ?>) x, (SkylarkDict<?, ?>) y, env);
}
- if (lval instanceof Concatable && rval instanceof Concatable) {
- Concatable lobj = (Concatable) lval;
- Concatable robj = (Concatable) rval;
+ if (x instanceof Concatable && y instanceof Concatable) {
+ Concatable lobj = (Concatable) x;
+ Concatable robj = (Concatable) y;
Concatter concatter = lobj.getConcatter();
if (concatter != null && concatter.equals(robj.getConcatter())) {
return concatter.concat(lobj, robj, location);
} else {
- throw typeException(lval, rval, Operator.PLUS, location);
+ throw typeException(x, y, TokenKind.PLUS, location);
}
}
// TODO(bazel-team): Remove deprecated operator.
- if (lval instanceof SkylarkNestedSet) {
+ if (x instanceof SkylarkNestedSet) {
if (env.getSemantics().incompatibleDepsetUnion()) {
throw new EvalException(
location,
@@ -330,15 +338,15 @@
+ "recommendations. Use --incompatible_depset_union=false "
+ "to temporarily disable this check.");
}
- return SkylarkNestedSet.of((SkylarkNestedSet) lval, rval, location);
+ return SkylarkNestedSet.of((SkylarkNestedSet) x, y, location);
}
- throw typeException(lval, rval, Operator.PLUS, location);
+ throw typeException(x, y, TokenKind.PLUS, location);
}
- /** Implements Operator.PIPE. */
- private static Object pipe(Object lval, Object rval, Environment env, Location location)
+ /** Implements 'x | y'. */
+ private static Object pipe(Object x, Object y, Environment env, Location location)
throws EvalException {
- if (lval instanceof SkylarkNestedSet) {
+ if (x instanceof SkylarkNestedSet) {
if (env.getSemantics().incompatibleDepsetUnion()) {
throw new EvalException(
location,
@@ -347,31 +355,31 @@
+ "recommendations. Use --incompatible_depset_union=false "
+ "to temporarily disable this check.");
}
- return SkylarkNestedSet.of((SkylarkNestedSet) lval, rval, location);
+ return SkylarkNestedSet.of((SkylarkNestedSet) x, y, location);
}
- throw typeException(lval, rval, Operator.PIPE, location);
+ throw typeException(x, y, TokenKind.PIPE, location);
}
- /** Implements Operator.MINUS. */
- private static Object minus(Object lval, Object rval, Location location) throws EvalException {
- if (lval instanceof Integer && rval instanceof Integer) {
- return Math.subtractExact((Integer) lval, (Integer) rval);
+ /** Implements 'x - y'. */
+ private static Object minus(Object x, Object y, Location location) throws EvalException {
+ if (x instanceof Integer && y instanceof Integer) {
+ return Math.subtractExact((Integer) x, (Integer) y);
}
- throw typeException(lval, rval, Operator.MINUS, location);
+ throw typeException(x, y, TokenKind.MINUS, location);
}
- /** Implements Operator.MULT. */
- private static Object mult(Object lval, Object rval, Environment env, Location location)
+ /** Implements 'x * y'. */
+ private static Object mult(Object x, Object y, Environment env, Location location)
throws EvalException {
Integer number = null;
Object otherFactor = null;
- if (lval instanceof Integer) {
- number = (Integer) lval;
- otherFactor = rval;
- } else if (rval instanceof Integer) {
- number = (Integer) rval;
- otherFactor = lval;
+ if (x instanceof Integer) {
+ number = (Integer) x;
+ otherFactor = y;
+ } else if (y instanceof Integer) {
+ number = (Integer) y;
+ otherFactor = x;
}
if (number != null) {
@@ -385,14 +393,14 @@
return ((SkylarkList<?>) otherFactor).repeat(number, env.mutability());
}
}
- throw typeException(lval, rval, Operator.MULT, location);
+ throw typeException(x, y, TokenKind.STAR, location);
}
- /** Implements Operator.DIVIDE. */
- private static Object divide(Object lval, Object rval, Location location) throws EvalException {
+ /** Implements 'x // y'. */
+ private static Object divide(Object x, Object y, Location location) throws EvalException {
// int / int
- if (lval instanceof Integer && rval instanceof Integer) {
- if (rval.equals(0)) {
+ if (x instanceof Integer && y instanceof Integer) {
+ if (y.equals(0)) {
throw new EvalException(location, "integer division by zero");
}
// Integer division doesn't give the same result in Java and in Python 2 with
@@ -400,23 +408,22 @@
// Java: -7/3 = -2
// Python: -7/3 = -3
// We want to follow Python semantics, so we use float division and round down.
- return (int) Math.floor(Double.valueOf((Integer) lval) / (Integer) rval);
+ return (int) Math.floor(Double.valueOf((Integer) x) / (Integer) y);
}
- throw typeException(lval, rval, Operator.FLOOR_DIVIDE, location);
+ throw typeException(x, y, TokenKind.SLASH_SLASH, location);
}
- /** Implements Operator.PERCENT. */
- private static Object percent(Object lval, Object rval, Location location)
- throws EvalException {
+ /** Implements 'x % y'. */
+ private static Object percent(Object x, Object y, Location location) throws EvalException {
// int % int
- if (lval instanceof Integer && rval instanceof Integer) {
- if (rval.equals(0)) {
+ if (x instanceof Integer && y instanceof Integer) {
+ if (y.equals(0)) {
throw new EvalException(location, "integer modulo by zero");
}
// Python and Java implement division differently, wrt negative numbers.
// In Python, sign of the result is the sign of the divisor.
- int div = (Integer) rval;
- int result = ((Integer) lval).intValue() % Math.abs(div);
+ int div = (Integer) y;
+ int result = ((Integer) x).intValue() % Math.abs(div);
if (result > 0 && div < 0) {
result += div; // make the result negative
} else if (result < 0 && div > 0) {
@@ -426,25 +433,22 @@
}
// string % tuple, string % dict, string % anything-else
- if (lval instanceof String) {
- String pattern = (String) lval;
+ if (x instanceof String) {
+ String pattern = (String) x;
try {
- if (rval instanceof Tuple) {
- return Printer.formatWithList(pattern, (Tuple) rval);
+ if (y instanceof Tuple) {
+ return Printer.formatWithList(pattern, (Tuple) y);
}
- return Printer.format(pattern, rval);
+ return Printer.format(pattern, y);
} catch (IllegalFormatException e) {
throw new EvalException(location, e.getMessage());
}
}
- throw typeException(lval, rval, Operator.PERCENT, location);
+ throw typeException(x, y, TokenKind.PERCENT, location);
}
- /**
- * Throws an exception signifying incorrect types for the given operator.
- */
- private static EvalException typeException(
- Object lval, Object rval, Operator operator, Location location) {
+ /** Throws an exception signifying incorrect types for the given operator. */
+ private static EvalException typeException(Object x, Object y, TokenKind op, Location location) {
// NB: this message format is identical to that used by CPython 2.7.6 or 3.4.0,
// though python raises a TypeError.
// For more details, we'll hopefully have usable stack traces at some point.
@@ -452,8 +456,6 @@
location,
String.format(
"unsupported operand type(s) for %s: '%s' and '%s'",
- operator,
- EvalUtils.getDataTypeName(lval),
- EvalUtils.getDataTypeName(rval)));
+ op, EvalUtils.getDataTypeName(x), EvalUtils.getDataTypeName(y)));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Eval.java b/src/main/java/com/google/devtools/build/lib/syntax/Eval.java
index 036210d..3199952 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Eval.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Eval.java
@@ -325,12 +325,11 @@
* value).
*/
private static void assignAugmented(
- Expression expr, Operator operator, Expression rhs, Environment env, Location loc)
+ Expression expr, TokenKind op, Expression rhs, Environment env, Location loc)
throws EvalException, InterruptedException {
if (expr instanceof Identifier) {
Object result =
- BinaryOperatorExpression.evaluateAugmented(
- operator, expr.eval(env), rhs.eval(env), env, loc);
+ BinaryOperatorExpression.evaluateAugmented(op, expr.eval(env), rhs.eval(env), env, loc);
assignIdentifier((Identifier) expr, result, env);
} else if (expr instanceof IndexExpression) {
IndexExpression indexExpression = (IndexExpression) expr;
@@ -340,8 +339,7 @@
Object oldValue = IndexExpression.evaluate(object, key, env, loc);
// Evaluate rhs after lhs.
Object rhsValue = rhs.eval(env);
- Object result =
- BinaryOperatorExpression.evaluateAugmented(operator, oldValue, rhsValue, env, loc);
+ Object result = BinaryOperatorExpression.evaluateAugmented(op, oldValue, rhsValue, env, loc);
assignItem(object, key, result, env, loc);
} else if (expr instanceof ListLiteral) {
throw new EvalException(loc, "cannot perform augmented assignment on a list literal");
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Operator.java b/src/main/java/com/google/devtools/build/lib/syntax/Operator.java
deleted file mode 100644
index c0740e9..0000000
--- a/src/main/java/com/google/devtools/build/lib/syntax/Operator.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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 com.google.devtools.build.lib.syntax;
-
-/** Infix binary operators. */
-public enum Operator {
-
- AND("and"),
- DIVIDE("/"),
- EQUALS_EQUALS("=="),
- FLOOR_DIVIDE("//"),
- GREATER(">"),
- GREATER_EQUALS(">="),
- IN("in"),
- LESS("<"),
- LESS_EQUALS("<="),
- MINUS("-"),
- MULT("*"),
- NOT("not"),
- NOT_EQUALS("!="),
- NOT_IN("not in"),
- OR("or"),
- PERCENT("%"),
- PIPE("|"),
- PLUS("+");
-
- private final String name;
-
- Operator(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name;
- }
-}
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 a1aa1e5..9daa9a3 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
@@ -126,49 +126,38 @@
private final Lexer lexer;
private final EventHandler eventHandler;
- private static final Map<TokenKind, Operator> binaryOperators =
- new ImmutableMap.Builder<TokenKind, Operator>()
- .put(TokenKind.AND, Operator.AND)
- .put(TokenKind.EQUALS_EQUALS, Operator.EQUALS_EQUALS)
- .put(TokenKind.GREATER, Operator.GREATER)
- .put(TokenKind.GREATER_EQUALS, Operator.GREATER_EQUALS)
- .put(TokenKind.IN, Operator.IN)
- .put(TokenKind.LESS, Operator.LESS)
- .put(TokenKind.LESS_EQUALS, Operator.LESS_EQUALS)
- .put(TokenKind.MINUS, Operator.MINUS)
- .put(TokenKind.NOT_EQUALS, Operator.NOT_EQUALS)
- .put(TokenKind.NOT_IN, Operator.NOT_IN)
- .put(TokenKind.OR, Operator.OR)
- .put(TokenKind.PERCENT, Operator.PERCENT)
- .put(TokenKind.SLASH, Operator.DIVIDE)
- .put(TokenKind.SLASH_SLASH, Operator.FLOOR_DIVIDE)
- .put(TokenKind.PLUS, Operator.PLUS)
- .put(TokenKind.PIPE, Operator.PIPE)
- .put(TokenKind.STAR, Operator.MULT)
+ // TODO(adonovan): opt: compute this by subtraction.
+ private static final Map<TokenKind, TokenKind> augmentedAssignmentMethods =
+ new ImmutableMap.Builder<TokenKind, TokenKind>()
+ .put(TokenKind.PLUS_EQUALS, TokenKind.PLUS)
+ .put(TokenKind.MINUS_EQUALS, TokenKind.MINUS)
+ .put(TokenKind.STAR_EQUALS, TokenKind.STAR)
+ .put(TokenKind.SLASH_EQUALS, TokenKind.SLASH)
+ .put(TokenKind.SLASH_SLASH_EQUALS, TokenKind.SLASH_SLASH)
+ .put(TokenKind.PERCENT_EQUALS, TokenKind.PERCENT)
.build();
- private static final Map<TokenKind, Operator> augmentedAssignmentMethods =
- new ImmutableMap.Builder<TokenKind, Operator>()
- .put(TokenKind.PLUS_EQUALS, Operator.PLUS)
- .put(TokenKind.MINUS_EQUALS, Operator.MINUS)
- .put(TokenKind.STAR_EQUALS, Operator.MULT)
- .put(TokenKind.SLASH_EQUALS, Operator.DIVIDE)
- .put(TokenKind.SLASH_SLASH_EQUALS, Operator.FLOOR_DIVIDE)
- .put(TokenKind.PERCENT_EQUALS, Operator.PERCENT)
- .build();
-
- /** Highest precedence goes last.
- * Based on: http://docs.python.org/2/reference/expressions.html#operator-precedence
- **/
- private static final List<EnumSet<Operator>> operatorPrecedence = ImmutableList.of(
- EnumSet.of(Operator.OR),
- EnumSet.of(Operator.AND),
- EnumSet.of(Operator.NOT),
- EnumSet.of(Operator.EQUALS_EQUALS, Operator.NOT_EQUALS, Operator.LESS, Operator.LESS_EQUALS,
- Operator.GREATER, Operator.GREATER_EQUALS, Operator.IN, Operator.NOT_IN),
- EnumSet.of(Operator.PIPE),
- EnumSet.of(Operator.MINUS, Operator.PLUS),
- EnumSet.of(Operator.DIVIDE, Operator.FLOOR_DIVIDE, Operator.MULT, Operator.PERCENT));
+ /**
+ * Highest precedence goes last. Based on:
+ * http://docs.python.org/2/reference/expressions.html#operator-precedence
+ */
+ private static final List<EnumSet<TokenKind>> operatorPrecedence =
+ ImmutableList.of(
+ EnumSet.of(TokenKind.OR),
+ EnumSet.of(TokenKind.AND),
+ EnumSet.of(TokenKind.NOT),
+ EnumSet.of(
+ TokenKind.EQUALS_EQUALS,
+ TokenKind.NOT_EQUALS,
+ TokenKind.LESS,
+ TokenKind.LESS_EQUALS,
+ TokenKind.GREATER,
+ TokenKind.GREATER_EQUALS,
+ TokenKind.IN,
+ TokenKind.NOT_IN),
+ EnumSet.of(TokenKind.PIPE),
+ EnumSet.of(TokenKind.MINUS, TokenKind.PLUS),
+ EnumSet.of(TokenKind.SLASH, TokenKind.SLASH_SLASH, TokenKind.STAR, TokenKind.PERCENT));
private int errorsCount;
private boolean recoveryMode; // stop reporting errors until next statement
@@ -335,7 +324,7 @@
private boolean expect(TokenKind kind) {
boolean expected = token.kind == kind;
if (!expected) {
- syntaxError("expected " + kind.getPrettyName());
+ syntaxError("expected " + kind);
}
nextToken();
return expected;
@@ -425,7 +414,9 @@
case RAISE: error = "'raise' not supported, use 'fail' instead"; break;
case TRY: error = "'try' not supported, all exceptions are fatal"; break;
case WHILE: error = "'while' not supported, use 'for' instead"; break;
- default: error = "keyword '" + token.kind.getPrettyName() + "' not supported"; break;
+ default:
+ error = "keyword '" + token.kind + "' not supported";
+ break;
}
reportError(lexer.createLocation(token.left, token.right), error);
}
@@ -676,7 +667,7 @@
{
nextToken();
Expression expr = parsePrimaryWithSuffix();
- UnaryOperatorExpression minus = new UnaryOperatorExpression(UnaryOperator.MINUS, expr);
+ UnaryOperatorExpression minus = new UnaryOperatorExpression(TokenKind.MINUS, expr);
return setLocation(minus, start, expr);
}
default:
@@ -802,7 +793,7 @@
nextToken();
return expr;
} else {
- syntaxError("expected '" + closingBracket.getPrettyName() + "', 'for' or 'if'");
+ syntaxError("expected '" + closingBracket + "', 'for' or 'if'");
syncPast(LIST_TERMINATOR_SET);
return makeErrorExpression(comprehensionStartOffset, token.right);
}
@@ -927,9 +918,8 @@
Expression expr = parseNonTupleExpression(prec + 1);
// The loop is not strictly needed, but it prevents risks of stack overflow. Depth is
// limited to number of different precedence levels (operatorPrecedence.size()).
- Operator lastOp = null;
+ TokenKind lastOp = null;
for (;;) {
-
if (token.kind == TokenKind.NOT) {
// If NOT appears when we expect a binary operator, it must be followed by IN.
// Since the code expects every operator to be a single token, we push a NOT_IN token.
@@ -940,44 +930,40 @@
token.kind = TokenKind.NOT_IN;
}
- if (!binaryOperators.containsKey(token.kind)) {
- return expr;
- }
- Operator operator = binaryOperators.get(token.kind);
- if (!operatorPrecedence.get(prec).contains(operator)) {
+ TokenKind op = token.kind;
+ if (!operatorPrecedence.get(prec).contains(op)) {
return expr;
}
// Operator '==' and other operators of the same precedence (e.g. '<', 'in')
// are not associative.
- if (lastOp != null && operatorPrecedence.get(prec).contains(Operator.EQUALS_EQUALS)) {
+ if (lastOp != null && operatorPrecedence.get(prec).contains(TokenKind.EQUALS_EQUALS)) {
reportError(
lexer.createLocation(token.left, token.right),
- String.format("Operator '%s' is not associative with operator '%s'. Use parens.",
- lastOp, operator));
+ String.format(
+ "Operator '%s' is not associative with operator '%s'. Use parens.", lastOp, op));
}
nextToken();
Expression secondary = parseNonTupleExpression(prec + 1);
- expr = optimizeBinOpExpression(operator, expr, secondary);
+ expr = optimizeBinOpExpression(expr, op, secondary);
setLocation(expr, start, secondary);
- lastOp = operator;
+ lastOp = op;
}
}
// Optimize binary expressions.
// string literal + string literal can be concatenated into one string literal
// so we don't have to do the expensive string concatenation at runtime.
- private Expression optimizeBinOpExpression(
- Operator operator, Expression expr, Expression secondary) {
- if (operator == Operator.PLUS) {
- if (expr instanceof StringLiteral && secondary instanceof StringLiteral) {
- StringLiteral left = (StringLiteral) expr;
- StringLiteral right = (StringLiteral) secondary;
+ private Expression optimizeBinOpExpression(Expression x, TokenKind op, Expression y) {
+ if (op == TokenKind.PLUS) {
+ if (x instanceof StringLiteral && y instanceof StringLiteral) {
+ StringLiteral left = (StringLiteral) x;
+ StringLiteral right = (StringLiteral) y;
return new StringLiteral(stringInterner.intern(left.getValue() + right.getValue()));
}
}
- return new BinaryOperatorExpression(operator, expr, secondary);
+ return new BinaryOperatorExpression(x, op, y);
}
// Equivalent to 'test' rule in Python grammar.
@@ -1005,7 +991,7 @@
if (prec >= operatorPrecedence.size()) {
return parsePrimaryWithSuffix();
}
- if (token.kind == TokenKind.NOT && operatorPrecedence.get(prec).contains(Operator.NOT)) {
+ if (token.kind == TokenKind.NOT && operatorPrecedence.get(prec).contains(TokenKind.NOT)) {
return parseNotExpression(prec);
}
return parseBinOpExpression(prec);
@@ -1016,9 +1002,8 @@
int start = token.left;
expect(TokenKind.NOT);
Expression expression = parseNonTupleExpression(prec);
- UnaryOperatorExpression notExpression =
- new UnaryOperatorExpression(UnaryOperator.NOT, expression);
- return setLocation(notExpression, start, expression);
+ UnaryOperatorExpression not = new UnaryOperatorExpression(TokenKind.NOT, expression);
+ return setLocation(not, start, expression);
}
// file_input ::= ('\n' | stmt)* EOF
@@ -1171,11 +1156,10 @@
Expression rhs = parseExpression();
return setLocation(new AssignmentStatement(expression, rhs), start, rhs);
} else if (augmentedAssignmentMethods.containsKey(token.kind)) {
- Operator operator = augmentedAssignmentMethods.get(token.kind);
+ TokenKind op = augmentedAssignmentMethods.get(token.kind);
nextToken();
Expression operand = parseExpression();
- return setLocation(
- new AugmentedAssignmentStatement(operator, expression, operand), start, operand);
+ return setLocation(new AugmentedAssignmentStatement(op, expression, operand), start, operand);
} else {
return setLocation(new ExpressionStatement(expression), start, expression);
}
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 1f5d6e0..751d551 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
@@ -64,8 +64,8 @@
}
public void visit(BinaryOperatorExpression node) {
- visit(node.getLhs());
- visit(node.getRhs());
+ visit(node.getX());
+ visit(node.getY());
}
public void visit(FuncallExpression node) {
@@ -160,7 +160,7 @@
}
public void visit(UnaryOperatorExpression node) {
- visit(node.getOperand());
+ visit(node.getX());
}
public void visit(DotExpression node) {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Token.java b/src/main/java/com/google/devtools/build/lib/syntax/Token.java
index 733b538..e836f992 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Token.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Token.java
@@ -53,9 +53,9 @@
@Override
public String toString() {
// TODO(bazel-team): do proper escaping of string literals
- return kind == TokenKind.STRING ? ("\"" + value + "\"")
- : value == null ? kind.getPrettyName()
- : value.toString();
+ return kind == TokenKind.STRING
+ ? ("\"" + value + "\"")
+ : value == null ? kind.toString() : value.toString();
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java b/src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java
index e2a4dcd..b85743c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java
@@ -48,7 +48,7 @@
IMPORT("import"),
IN("in"),
INDENT("indent"),
- INT("integer"),
+ INT("integer literal"),
IS("is"),
LAMBDA("lambda"),
LBRACE("{"),
@@ -85,22 +85,20 @@
STAR("*"),
STAR_EQUALS("*="),
STAR_STAR("**"),
- STRING("string"),
+ STRING("string literal"),
TRY("try"),
WHILE("while"),
WITH("with"),
YIELD("yield");
- private final String prettyName;
+ private final String name;
- private TokenKind(String prettyName) {
- this.prettyName = prettyName;
+ private TokenKind(String name) {
+ this.name = name;
}
- /**
- * Returns the pretty name for this token, for use in error messages for the user.
- */
- public String getPrettyName() {
- return prettyName;
+ @Override
+ public String toString() {
+ return name;
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/UnaryOperator.java b/src/main/java/com/google/devtools/build/lib/syntax/UnaryOperator.java
deleted file mode 100644
index 85fe921..0000000
--- a/src/main/java/com/google/devtools/build/lib/syntax/UnaryOperator.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 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 com.google.devtools.build.lib.syntax;
-
-/** Unary operators. */
-public enum UnaryOperator {
-
- // Include trailing whitespace in name for non-symbolic operators (for pretty printing).
- NOT("not "),
- MINUS("-");
-
- private final String name;
-
- UnaryOperator(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name;
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/UnaryOperatorExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/UnaryOperatorExpression.java
index 824333a..7341708 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/UnaryOperatorExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/UnaryOperatorExpression.java
@@ -16,53 +16,49 @@
import com.google.devtools.build.lib.events.Location;
import java.io.IOException;
-/** Syntax node for a unary operator expression. */
+/** A UnaryOperatorExpression represents a unary operator expression, 'op x'. */
public final class UnaryOperatorExpression extends Expression {
- private final UnaryOperator operator;
+ private final TokenKind op; // NOT or MINUS
+ private final Expression x;
- private final Expression operand;
-
- public UnaryOperatorExpression(UnaryOperator operator, Expression operand) {
- this.operator = operator;
- this.operand = operand;
+ public UnaryOperatorExpression(TokenKind op, Expression x) {
+ this.op = op;
+ this.x = x;
}
- public UnaryOperator getOperator() {
- return operator;
+ /** getOperator returns the operator. */
+ public TokenKind getOperator() {
+ return op;
}
- public Expression getOperand() {
- return operand;
+ /** getX returns the operand. */
+ public Expression getX() {
+ return x;
}
@Override
public void prettyPrint(Appendable buffer) throws IOException {
- // TODO(bazel-team): Possibly omit parentheses when they are not needed according to operator
- // precedence rules. This requires passing down more contextual information.
- buffer.append(operator.toString());
+ // TODO(bazel-team): retain parentheses in the syntax tree so we needn't
+ // conservatively emit them here.
+ buffer.append(op == TokenKind.NOT ? "not " : op.toString());
buffer.append('(');
- operand.prettyPrint(buffer);
+ x.prettyPrint(buffer);
buffer.append(')');
}
@Override
public String toString() {
- // All current and planned unary operators happen to be prefix operators.
- // Non-symbolic operators have trailing whitespace built into their name.
- //
// Note that this omits the parentheses for brevity, but is not correct in general due to
// operator precedence rules. For example, "(not False) in mylist" prints as
// "not False in mylist", which evaluates to opposite results in the case that mylist is empty.
- return operator.toString() + operand;
+ // TODO(adonovan): record parentheses explicitly in syntax tree.
+ return (op == TokenKind.NOT ? "not " : op.toString()) + x;
}
- private static Object evaluate(
- UnaryOperator operator,
- Object value,
- Location loc)
+ private static Object evaluate(TokenKind op, Object value, Location loc)
throws EvalException, InterruptedException {
- switch (operator) {
+ switch (op) {
case NOT:
return !EvalUtils.toBoolean(value);
@@ -81,13 +77,13 @@
}
default:
- throw new AssertionError("Unsupported unary operator: " + operator);
+ throw new AssertionError("Unsupported unary operator: " + op);
}
}
@Override
Object doEval(Environment env) throws EvalException, InterruptedException {
- return evaluate(operator, operand.eval(env), getLocation());
+ return evaluate(op, x.eval(env), getLocation());
}
@Override
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java b/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
index 6cc5fef..cfc9641 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/ParserTest.java
@@ -105,35 +105,35 @@
BinaryOperatorExpression e =
(BinaryOperatorExpression) parseExpression("'%sx' % 'foo' + 'bar'");
- assertThat(e.getOperator()).isEqualTo(Operator.PLUS);
+ assertThat(e.getOperator()).isEqualTo(TokenKind.PLUS);
}
@Test
public void testPrecedence2() throws Exception {
BinaryOperatorExpression e =
(BinaryOperatorExpression) parseExpression("('%sx' % 'foo') + 'bar'");
- assertThat(e.getOperator()).isEqualTo(Operator.PLUS);
+ assertThat(e.getOperator()).isEqualTo(TokenKind.PLUS);
}
@Test
public void testPrecedence3() throws Exception {
BinaryOperatorExpression e =
(BinaryOperatorExpression) parseExpression("'%sx' % ('foo' + 'bar')");
- assertThat(e.getOperator()).isEqualTo(Operator.PERCENT);
+ assertThat(e.getOperator()).isEqualTo(TokenKind.PERCENT);
}
@Test
public void testPrecedence4() throws Exception {
BinaryOperatorExpression e =
(BinaryOperatorExpression) parseExpression("1 + - (2 - 3)");
- assertThat(e.getOperator()).isEqualTo(Operator.PLUS);
+ assertThat(e.getOperator()).isEqualTo(TokenKind.PLUS);
}
@Test
public void testPrecedence5() throws Exception {
BinaryOperatorExpression e =
(BinaryOperatorExpression) parseExpression("2 * x | y + 1");
- assertThat(e.getOperator()).isEqualTo(Operator.PIPE);
+ assertThat(e.getOperator()).isEqualTo(TokenKind.PIPE);
}
@Test
@@ -170,9 +170,9 @@
UnaryOperatorExpression e = (UnaryOperatorExpression) parseExpression("-5");
UnaryOperatorExpression e2 = (UnaryOperatorExpression) parseExpression("- 5");
- IntegerLiteral i = (IntegerLiteral) e.getOperand();
+ IntegerLiteral i = (IntegerLiteral) e.getX();
assertThat(i.getValue()).isEqualTo(5);
- IntegerLiteral i2 = (IntegerLiteral) e2.getOperand();
+ IntegerLiteral i2 = (IntegerLiteral) e2.getX();
assertThat(i2.getValue()).isEqualTo(5);
assertLocation(0, 2, e.getLocation());
assertLocation(0, 3, e2.getLocation());