| // 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; | |
| import java.util.Collection; | |
| /** | |
| * 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, Object result) | |
| throws EvalException, InterruptedException { | |
| assign(env, loc, expr, result); | |
| } | |
| private static void assign(Environment env, Location loc, Expression lvalue, Object result) | |
| throws EvalException, InterruptedException { | |
| if (lvalue instanceof Ident) { | |
| assign(env, loc, (Ident) lvalue, result); | |
| return; | |
| } | |
| if (lvalue instanceof ListLiteral) { | |
| ListLiteral variables = (ListLiteral) lvalue; | |
| Collection<?> rvalue = EvalUtils.toCollection(result, loc); | |
| int len = variables.getElements().size(); | |
| if (len != rvalue.size()) { | |
| throw new EvalException(loc, String.format( | |
| "lvalue has length %d, but rvalue has has length %d", len, rvalue.size())); | |
| } | |
| int i = 0; | |
| for (Object o : rvalue) { | |
| assign(env, loc, variables.getElements().get(i), o); | |
| i++; | |
| } | |
| return; | |
| } | |
| throw new EvalException(loc, | |
| "can only assign to variables and tuples, not to '" + lvalue + "'"); | |
| } | |
| /** | |
| * Assign value to a single variable. | |
| */ | |
| private static void assign(Environment env, Location loc, Ident ident, Object result) | |
| throws EvalException, InterruptedException { | |
| Preconditions.checkNotNull(result, "trying to assign null to %s", ident); | |
| 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 { | |
| validate(env, loc, expr, rvalueType); | |
| } | |
| private static void validate(ValidationEnvironment env, Location loc, Expression expr, | |
| SkylarkType rvalueType) throws EvalException { | |
| if (expr instanceof Ident) { | |
| Ident ident = (Ident) expr; | |
| env.update(ident.getName(), rvalueType, loc); | |
| return; | |
| } | |
| if (expr instanceof ListLiteral) { | |
| for (Expression e : ((ListLiteral) expr).getElements()) { | |
| validate(env, loc, e, SkylarkType.UNKNOWN); | |
| } | |
| return; | |
| } | |
| throw new EvalException(loc, | |
| "can only assign to variables, not to '" + expr + "'"); | |
| } | |
| @Override | |
| public String toString() { | |
| return expr.toString(); | |
| } | |
| } |