| // 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.common.base.Preconditions; |
| import com.google.devtools.build.lib.events.Location; |
| import java.io.IOException; |
| import java.util.List; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Syntax node for a function argument. |
| * |
| * <p>Argument is a base class for arguments passed in a call (@see Argument.Passed) or defined as |
| * part of a function definition (@see Parameter). It is notably used by some {@link Parser} and |
| * printer functions. |
| */ |
| public abstract class Argument extends Node { |
| |
| public boolean isStar() { |
| return false; |
| } |
| |
| public boolean isStarStar() { |
| return false; |
| } |
| |
| /** |
| * Argument.Passed is the class of arguments passed in a function call |
| * (as opposed to being used in a definition -- @see Parameter for that). |
| * Argument.Passed is usually what we mean when informally say "argument". |
| * |
| * <p>An Argument.Passed can be Positional, Keyword, Star, or StarStar. |
| */ |
| public abstract static class Passed extends Argument { |
| /** the value to be passed by this argument */ |
| protected final Expression value; |
| |
| private Passed(Expression value) { |
| this.value = Preconditions.checkNotNull(value); |
| } |
| |
| public boolean isPositional() { |
| return false; |
| } |
| |
| public boolean isKeyword() { |
| return false; |
| } |
| |
| /** @deprecated Prefer {@link #getIdentifier()} instead. */ |
| @Deprecated |
| @Nullable |
| public String getName() { // only for keyword arguments |
| return null; |
| } |
| |
| @Nullable |
| public Identifier getIdentifier() { |
| return null; |
| } |
| |
| public Expression getValue() { |
| return value; |
| } |
| |
| @Override |
| public void accept(NodeVisitor visitor) { |
| visitor.visit(this); |
| } |
| } |
| |
| /** positional argument: Expression */ |
| public static final class Positional extends Passed { |
| |
| Positional(Expression value) { |
| super(value); |
| } |
| |
| @Override |
| public boolean isPositional() { |
| return true; |
| } |
| |
| @Override |
| public void prettyPrint(Appendable buffer) throws IOException { |
| value.prettyPrint(buffer); |
| } |
| } |
| |
| /** keyword argument: K = Expression */ |
| public static final class Keyword extends Passed { |
| |
| final Identifier identifier; |
| |
| Keyword(Identifier identifier, Expression value) { |
| super(value); |
| this.identifier = identifier; |
| } |
| |
| @Override |
| public String getName() { |
| return identifier.getName(); |
| } |
| |
| @Override |
| public Identifier getIdentifier() { |
| return identifier; |
| } |
| |
| @Override |
| public boolean isKeyword() { |
| return true; |
| } |
| |
| @Override |
| public void prettyPrint(Appendable buffer) throws IOException { |
| buffer.append(identifier.getName()); |
| buffer.append(" = "); |
| value.prettyPrint(buffer); |
| } |
| } |
| |
| /** positional rest (starred) argument: *Expression */ |
| public static final class Star extends Passed { |
| |
| Star(Expression value) { |
| super(value); |
| } |
| |
| @Override |
| public boolean isStar() { |
| return true; |
| } |
| |
| @Override |
| public void prettyPrint(Appendable buffer) throws IOException { |
| buffer.append('*'); |
| value.prettyPrint(buffer); |
| } |
| } |
| |
| /** keyword rest (star_starred) parameter: **Expression */ |
| public static final class StarStar extends Passed { |
| |
| StarStar(Expression value) { |
| super(value); |
| } |
| |
| @Override |
| public boolean isStarStar() { |
| return true; |
| } |
| |
| @Override |
| public void prettyPrint(Appendable buffer) throws IOException { |
| buffer.append("**"); |
| value.prettyPrint(buffer); |
| } |
| } |
| |
| /** Some arguments failed to satisfy python call convention strictures */ |
| static class ArgumentException extends Exception { |
| Location location; |
| |
| /** construct an ArgumentException from a message only */ |
| ArgumentException(Location location, String message) { |
| super(message); |
| this.location = location; |
| } |
| |
| Location getLocation() { |
| return location; |
| } |
| } |
| |
| /** |
| * Validate that the list of Argument's, whether gathered by the Parser or from annotations, |
| * satisfies the requirements: first Positional arguments, then Keyword arguments, then an |
| * optional *arg argument, finally an optional **kwarg argument. |
| */ |
| public static void validateFuncallArguments(List<Passed> arguments) throws ArgumentException { |
| int i = 0; |
| int len = arguments.size(); |
| |
| while (i < len && arguments.get(i).isPositional()) { |
| i++; |
| } |
| |
| while (i < len && arguments.get(i).isKeyword()) { |
| i++; |
| } |
| |
| if (i < len && arguments.get(i).isStar()) { |
| i++; |
| } |
| |
| if (i < len && arguments.get(i).isStarStar()) { |
| i++; |
| } |
| |
| // If there's no argument left, everything is correct. |
| if (i == len) { |
| return; |
| } |
| |
| Location loc = arguments.get(i).getLocation(); |
| if (arguments.get(i).isPositional()) { |
| throw new ArgumentException( |
| loc, "positional argument is misplaced (positional arguments come first)"); |
| } |
| |
| if (arguments.get(i).isKeyword()) { |
| throw new ArgumentException( |
| loc, |
| "keyword argument is misplaced (keyword arguments must be before any *arg or **kwarg)"); |
| } |
| |
| if (i < len && arguments.get(i).isStar()) { |
| throw new ArgumentException(loc, "*arg argument is misplaced"); |
| } |
| |
| if (i < len && arguments.get(i).isStarStar()) { |
| throw new ArgumentException(loc, "**kwarg argument is misplaced (there can be only one)"); |
| } |
| } |
| |
| @Override |
| public final void prettyPrint(Appendable buffer, int indentLevel) throws IOException { |
| prettyPrint(buffer); |
| } |
| |
| @Override |
| public abstract void prettyPrint(Appendable buffer) throws IOException; |
| } |