// 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.util.Preconditions; 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 ASTNode { 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; } @Nullable public String getName() { // only for keyword arguments return null; } public Expression getValue() { return value; } @Override public void accept(SyntaxTreeVisitor visitor) { visitor.visit(this); } } /** positional argument: Expression */ public static final class Positional extends Passed { public Positional(Expression value) { super(value); } @Override public boolean isPositional() { return true; } @Override public String toString() { return String.valueOf(value); } } /** keyword argument: K = Expression */ public static final class Keyword extends Passed { final String name; public Keyword(String name, Expression value) { super(value); this.name = name; } @Override public String getName() { return name; } @Override public boolean isKeyword() { return true; } @Override public String toString() { return name + " = " + value; } } /** positional rest (starred) argument: *Expression */ public static final class Star extends Passed { public Star(Expression value) { super(value); } @Override public boolean isStar() { return true; } @Override public String toString() { return "*" + value; } } /** keyword rest (star_starred) parameter: **Expression */ public static final class StarStar extends Passed { public StarStar(Expression value) { super(value); } @Override public boolean isStarStar() { return true; } @Override public String toString() { return "**" + value; } } /** Some arguments failed to satisfy python call convention strictures */ protected static class ArgumentException extends Exception { /** construct an ArgumentException from a message only */ public ArgumentException(String message) { super(message); } } /** * Validate that the list of Argument's, whether gathered by the Parser or from annotations, * satisfies the requirements of the Python calling conventions: all Positional's first, * at most one Star, at most one StarStar, at the end only. */ public static void validateFuncallArguments(List<Passed> arguments) throws ArgumentException { boolean hasNamed = false; boolean hasStar = false; boolean hasKwArg = false; for (Passed arg : arguments) { if (hasKwArg) { throw new ArgumentException("argument after **kwargs"); } if (arg.isPositional()) { if (hasNamed) { throw new ArgumentException("non-keyword arg after keyword arg"); } else if (arg.isStar()) { throw new ArgumentException("only named arguments may follow *expression"); } } else if (arg.isKeyword()) { hasNamed = true; } else if (arg.isStar()) { if (hasStar) { throw new ArgumentException("more than one *stararg"); } hasStar = true; } else { hasKwArg = true; } } } }