| // 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; |
| |
| import com.google.devtools.build.lib.events.Location; |
| import java.io.IOException; |
| |
| /** |
| * An index expression ({@code obj[field]}). Not to be confused with a slice expression ({@code |
| * obj[from:to]}). The object may be either a sequence or an associative mapping (most commonly |
| * lists and dictionaries). |
| */ |
| public final class IndexExpression extends Expression { |
| |
| private final Expression object; |
| |
| private final Expression key; |
| |
| public IndexExpression(Expression object, Expression key) { |
| this.object = object; |
| this.key = key; |
| } |
| |
| public Expression getObject() { |
| return object; |
| } |
| |
| public Expression getKey() { |
| return key; |
| } |
| |
| @Override |
| public void prettyPrint(Appendable buffer) throws IOException { |
| object.prettyPrint(buffer); |
| buffer.append('['); |
| key.prettyPrint(buffer); |
| buffer.append(']'); |
| } |
| |
| @Override |
| Object doEval(Environment env) throws EvalException, InterruptedException { |
| return evaluate(object.eval(env), key.eval(env), env, getLocation()); |
| } |
| |
| /** |
| * Retrieves the value associated with a key in the given object. |
| * |
| * @throws EvalException if {@code object} is not a list or dictionary |
| */ |
| public static Object evaluate(Object object, Object key, Environment env, Location loc) |
| throws EvalException, InterruptedException { |
| if (object instanceof SkylarkIndexable) { |
| Object result = ((SkylarkIndexable) object).getIndex(key, loc); |
| // TODO(bazel-team): We shouldn't have this convertToSkylark call here. If it's needed at all, |
| // it should go in the implementations of SkylarkIndexable#getIndex that produce non-Skylark |
| // values. |
| return SkylarkType.convertToSkylark(result, env); |
| } else if (object instanceof String) { |
| String string = (String) object; |
| int index = EvalUtils.getSequenceIndex(key, string.length(), loc); |
| return string.substring(index, index + 1); |
| } else { |
| throw new EvalException( |
| loc, |
| String.format( |
| "type '%s' has no operator [](%s)", |
| EvalUtils.getDataTypeName(object), EvalUtils.getDataTypeName(key))); |
| } |
| } |
| |
| @Override |
| public void accept(SyntaxTreeVisitor visitor) { |
| visitor.visit(this); |
| } |
| |
| @Override |
| public Kind kind() { |
| return Kind.INDEX; |
| } |
| } |