diff options
author | 2015-03-19 10:12:49 +0000 | |
---|---|---|
committer | 2015-03-20 14:34:30 +0000 | |
commit | 024238256de1c2ffa3e75cff5900e9df21322ae7 (patch) | |
tree | 47555ca1a1d51b00d0e2234ad0f85ade2e50e452 /src/main/java/com | |
parent | 5ea2c89608f883ec57ca09d85954847376fe7e0b (diff) |
Refactoring: Introduce a new class LValue.
This CL is just moving code from AssignmentStatement to a new class. The goal
is to share code dealing with LValues.
LValues can be found here:
- lvalue = 2
- [for lvalue in exp]
- {a: b for lvalue in exp]
- for lvalue in exp: pass
The LValue itself can have different forms:
- a
- a, b
- a[0]
- a, (b, c)
- [a[0], (b, c)]
- a[1:5]
Although we may not handle everything, we need to make sure that the same
things can be used in variable assignment and in for loops.
--
MOS_MIGRATED_REVID=89015483
Diffstat (limited to 'src/main/java/com')
4 files changed, 104 insertions, 48 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java index 3e181329e3..c8aaefd704 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java +++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java @@ -1239,7 +1239,7 @@ public final class PackageFactory { EventHandler eventHandler) { for (Statement stmt : ast.getStatements()) { if (stmt instanceof AssignmentStatement) { - Expression lvalue = ((AssignmentStatement) stmt).getLValue(); + Expression lvalue = ((AssignmentStatement) stmt).getLValue().getExpression(); if (!(lvalue instanceof Ident)) { continue; } diff --git a/src/main/java/com/google/devtools/build/lib/syntax/AssignmentStatement.java b/src/main/java/com/google/devtools/build/lib/syntax/AssignmentStatement.java index 0bb584701a..f0063d0325 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/AssignmentStatement.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/AssignmentStatement.java @@ -14,14 +14,12 @@ package com.google.devtools.build.lib.syntax; -import com.google.common.base.Preconditions; - /** * Syntax node for an assignment statement. */ public final class AssignmentStatement extends Statement { - private final Expression lvalue; + private final LValue lvalue; private final Expression expression; @@ -29,14 +27,14 @@ public final class AssignmentStatement extends Statement { * Constructs an assignment: "lvalue := value". */ AssignmentStatement(Expression lvalue, Expression expression) { - this.lvalue = lvalue; + this.lvalue = new LValue(lvalue); this.expression = expression; } /** * Returns the LHS of the assignment. */ - public Expression getLValue() { + public LValue getLValue() { return lvalue; } @@ -54,38 +52,7 @@ public final class AssignmentStatement extends Statement { @Override void exec(Environment env) throws EvalException, InterruptedException { - if (!(lvalue instanceof Ident)) { - throw new EvalException(getLocation(), - "can only assign to variables, not to '" + lvalue + "'"); - } - - Ident ident = (Ident) lvalue; - Object result = expression.eval(env); - Preconditions.checkNotNull(result, "result of %s is null", expression); - - if (env.isSkylarkEnabled()) { - // The variable may have been referenced successfully if a global variable - // with the same name exists. In this case an Exception needs to be thrown. - SkylarkEnvironment skylarkEnv = (SkylarkEnvironment) env; - if (skylarkEnv.hasBeenReadGlobalVariable(ident.getName())) { - throw new EvalException(getLocation(), "Variable '" + ident.getName() - + "' is referenced before assignment." - + "The variable is defined in the global scope."); - } - Class<?> variableType = skylarkEnv.getVariableType(ident.getName()); - Class<?> resultType = EvalUtils.getSkylarkType(result.getClass()); - if (variableType != null && !variableType.equals(resultType) - && !resultType.equals(Environment.NoneType.class) - && !variableType.equals(Environment.NoneType.class)) { - throw new EvalException(getLocation(), String.format("Incompatible variable types, " - + "trying to assign %s (type of %s) to variable %s which is already %s", - EvalUtils.prettyPrintValue(result), - EvalUtils.getDataTypeName(result), - ident.getName(), - EvalUtils.getDataTypeNameFromClass(variableType))); - } - } - env.update(ident.getName(), result); + lvalue.assign(env, getLocation(), expression); } @Override @@ -95,14 +62,6 @@ public final class AssignmentStatement extends Statement { @Override void validate(ValidationEnvironment env) throws EvalException { - // TODO(bazel-team): Implement other validations. - if (lvalue instanceof Ident) { - Ident ident = (Ident) lvalue; - SkylarkType resultType = expression.validate(env); - env.update(ident.getName(), resultType, getLocation()); - } else { - throw new EvalException(getLocation(), - "can only assign to variables, not to '" + lvalue + "'"); - } + lvalue.validate(env, getLocation(), expression.validate(env)); } } diff --git a/src/main/java/com/google/devtools/build/lib/syntax/LValue.java b/src/main/java/com/google/devtools/build/lib/syntax/LValue.java new file mode 100644 index 0000000000..a328118a6e --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/syntax/LValue.java @@ -0,0 +1,97 @@ +// 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; + +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.events.Location; + +import java.io.Serializable; + +/** + * Class representing an LValue. + * It appears in assignment, for loop and comprehensions, e.g. + * lvalue = 2 + * [for lvalue in exp] + * for lvalue in exp: pass + * An LValue can be a simple variable or something more complex like a tuple. + */ +public class LValue implements Serializable { + // Currently, expr can only be an Ident, but we plan to support more. + private final Expression expr; + + public LValue(Expression expr) { + this.expr = expr; + } + + public Expression getExpression() { + return expr; + } + + /** + * Assign a value to an LValue and update the environment. + */ + public void assign(Environment env, Location loc, Expression rvalue) + throws EvalException, InterruptedException { + if (!(expr instanceof Ident)) { + throw new EvalException(loc, + "can only assign to variables, not to '" + expr + "'"); + } + + Ident ident = (Ident) expr; + Object result = rvalue.eval(env); + Preconditions.checkNotNull(result, "result of %s is null", rvalue); + + if (env.isSkylarkEnabled()) { + // The variable may have been referenced successfully if a global variable + // with the same name exists. In this case an Exception needs to be thrown. + SkylarkEnvironment skylarkEnv = (SkylarkEnvironment) env; + if (skylarkEnv.hasBeenReadGlobalVariable(ident.getName())) { + throw new EvalException(loc, "Variable '" + ident.getName() + + "' is referenced before assignment." + + "The variable is defined in the global scope."); + } + Class<?> variableType = skylarkEnv.getVariableType(ident.getName()); + Class<?> resultType = EvalUtils.getSkylarkType(result.getClass()); + if (variableType != null && !variableType.equals(resultType) + && !resultType.equals(Environment.NoneType.class) + && !variableType.equals(Environment.NoneType.class)) { + throw new EvalException(loc, String.format("Incompatible variable types, " + + "trying to assign %s (type of %s) to variable %s which is already %s", + EvalUtils.prettyPrintValue(result), + EvalUtils.getDataTypeName(result), + ident.getName(), + EvalUtils.getDataTypeNameFromClass(variableType))); + } + } + env.update(ident.getName(), result); + } + + void validate(ValidationEnvironment env, Location loc, SkylarkType rvalueType) + throws EvalException { + // TODO(bazel-team): Implement other validations. + if (expr instanceof Ident) { + Ident ident = (Ident) expr; + env.update(ident.getName(), rvalueType, loc); + return; + } + throw new EvalException(loc, + "can only assign to variables, not to '" + expr + "'"); + } + + @Override + public String toString() { + return expr.toString(); + } +} 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 2463d25600..439a4b197e 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 @@ -85,7 +85,7 @@ public class SyntaxTreeVisitor { } public void visit(AssignmentStatement node) { - visit(node.getLValue()); + visit(node.getLValue().getExpression()); visit(node.getExpression()); } |