blob: 02963267a7c71117f2d7bd676fd40158ed125a50 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +00002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.syntax;
15
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000016import com.google.auto.value.AutoValue;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000017import com.google.common.collect.ImmutableList;
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000018import com.google.common.collect.Interner;
19import com.google.common.collect.Interners;
20import com.google.common.collect.Lists;
Klaas Boesche0ec13b92015-11-06 12:16:03 +000021import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
Mark Schaller6df81792015-12-10 18:47:47 +000022import com.google.devtools.build.lib.util.Preconditions;
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000023import com.google.devtools.build.lib.util.StringCanonicalizer;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000024
25import java.io.Serializable;
26import java.util.ArrayList;
27import java.util.Arrays;
28import java.util.HashSet;
29import java.util.List;
Carmi Grushkod5ef2b42016-02-01 18:39:42 +000030import java.util.Map;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000031import java.util.Set;
32
33import javax.annotation.Nullable;
34
35/**
36 * Function Signatures for BUILD language (same as Python)
37 *
38 * <p>Skylark's function signatures are just like Python3's.
39 * A function may have 6 kinds of arguments:
40 * positional mandatory, positional optional, positional rest (aka *star argument),
41 * key-only mandatory, key-only optional, key rest (aka **star_star argument).
42 * A caller may specify all arguments but the *star and **star_star arguments by name,
43 * and thus all mandatory and optional arguments are named arguments.
44 *
45 * <p>To enable various optimizations in the argument processing routine,
46 * we sort arguments according the following constraints, enabling corresponding optimizations:
47 * <ol>
Francois-Rene Rideau012f7892015-03-31 17:27:01 +000048 * <li>The positional mandatories come just before the positional optionals,
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000049 * so they can be filled in one go.
Francois-Rene Rideau012f7892015-03-31 17:27:01 +000050 * <li>Positionals come first, so it's easy to prepend extra positional arguments such as "self"
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000051 * to an argument list, and we optimize for the common case of no key-only mandatory parameters.
52 * key-only parameters are thus grouped together.
53 * positional mandatory and key-only mandatory parameters are separate,
Klaas Boesche0ec13b92015-11-06 12:16:03 +000054 * but there is no loop over a contiguous chunk of them, anyway.
Francois-Rene Rideau012f7892015-03-31 17:27:01 +000055 * <li>The named are all grouped together, with star and star_star rest arguments coming last.
56 * <li>Mandatory arguments in each category (positional and named-only) come before the optional
57 * arguments, for the sake of slightly better clarity to human implementers. This eschews an
58 * optimization whereby grouping optionals together allows to iterate over them in one go instead
59 * of two; however, this relatively minor optimization only matters when keyword arguments are
60 * passed, at which point it is dwarfed by the slowness of keyword processing.
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000061 * </ol>
62 *
Klaas Boesche0ec13b92015-11-06 12:16:03 +000063 * <p>Parameters are thus sorted in the following order:
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000064 * positional mandatory arguments (if any), positional optional arguments (if any),
Francois-Rene Rideau012f7892015-03-31 17:27:01 +000065 * key-only mandatory arguments (if any), key-only optional arguments (if any),
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000066 * then star argument (if any), then star_star argument (if any).
67 */
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000068@AutoValue
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000069public abstract class FunctionSignature implements Serializable {
70
71 /**
72 * The shape of a FunctionSignature, without names
73 */
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000074 @AutoValue
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000075 public abstract static class Shape implements Serializable {
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000076 private static final Interner<Shape> interner = Interners.newWeakInterner();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000077
78 /** Create a function signature */
79 public static Shape create(
80 int mandatoryPositionals,
81 int optionalPositionals,
82 int mandatoryNamedOnly,
83 int optionalNamedOnly,
84 boolean starArg,
85 boolean kwArg) {
86 Preconditions.checkArgument(
87 0 <= mandatoryPositionals && 0 <= optionalPositionals
88 && 0 <= mandatoryNamedOnly && 0 <= optionalNamedOnly);
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000089 return interner.intern(new AutoValue_FunctionSignature_Shape(
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000090 mandatoryPositionals, optionalPositionals,
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000091 mandatoryNamedOnly, optionalNamedOnly, starArg, kwArg));
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000092 }
93
94 // These abstract getters specify the actual argument count fields to be defined by AutoValue.
95 /** number of mandatory positional arguments */
96 public abstract int getMandatoryPositionals();
97
98 /** number of optional positional arguments */
99 public abstract int getOptionalPositionals();
100
101 /** number of mandatory named-only arguments. */
102 public abstract int getMandatoryNamedOnly();
103
104 /** number of optional named-only arguments */
105 public abstract int getOptionalNamedOnly();
106
107 /** indicator for presence of a star argument for extra positional arguments */
108 public abstract boolean hasStarArg();
109
110 /** indicator for presence of a star-star argument for extra keyword arguments */
111 public abstract boolean hasKwArg();
112
113
Klaas Boesche0ec13b92015-11-06 12:16:03 +0000114 // These are computed argument counts
115 /** number of optional and mandatory positional arguments. */
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000116 public int getPositionals() {
117 return getMandatoryPositionals() + getOptionalPositionals();
118 }
119
Klaas Boesche0ec13b92015-11-06 12:16:03 +0000120 /** number of optional and mandatory named-only arguments. */
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000121 public int getNamedOnly() {
122 return getMandatoryNamedOnly() + getOptionalNamedOnly();
123 }
124
125 /** number of optional arguments. */
126 public int getOptionals() {
127 return getOptionalPositionals() + getOptionalNamedOnly();
128 }
129
Klaas Boesche0ec13b92015-11-06 12:16:03 +0000130 /** number of all named parameters: mandatory and optional of positionals and named-only */
131 public int getAllNamed() {
132 return getPositionals() + getNamedOnly();
133 }
134
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000135 /** total number of arguments */
136 public int getArguments() {
Klaas Boesche0ec13b92015-11-06 12:16:03 +0000137 return getAllNamed() + (hasStarArg() ? 1 : 0) + (hasKwArg() ? 1 : 0);
138 }
139
140 /**
141 * @return this signature shape converted to a list of classes
142 */
143 public List<Class<?>> toClasses() {
144 List<Class<?>> parameters = new ArrayList<>();
145
146 for (int i = 0; i < getAllNamed(); i++) {
147 parameters.add(Object.class);
148 }
149 if (hasStarArg()) {
150 parameters.add(Tuple.class);
151 }
152 if (hasKwArg()) {
Carmi Grushkod5ef2b42016-02-01 18:39:42 +0000153 parameters.add(Map.class);
Klaas Boesche0ec13b92015-11-06 12:16:03 +0000154 }
155
156 return parameters;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000157 }
158 }
159
160 /**
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +0000161 * Names of a FunctionSignature
162 */
163 private static Interner<ImmutableList<String>> namesInterner = Interners.newWeakInterner();
164
165 /** Intern a list of names */
166 public static ImmutableList<String> names(List<String> names) {
167 return namesInterner.intern(ImmutableList.<String>copyOf(
168 Lists.transform(names, StringCanonicalizer.INTERN)));
169 }
170
171 /** Intern a list of names */
172 public static ImmutableList<String> names(String... names) {
173 return names(ImmutableList.<String>copyOf(names));
174 }
175
176 // Interner
177 private static Interner<FunctionSignature> signatureInterner = Interners.newWeakInterner();
178
179 /**
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000180 * Signatures proper.
181 *
182 * <p>A signature is a Shape and an ImmutableList of argument variable names
183 * NB: we assume these lists are short, so we may do linear scans.
184 */
185 public static FunctionSignature create(Shape shape, ImmutableList<String> names) {
186 Preconditions.checkArgument(names.size() == shape.getArguments());
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +0000187 return signatureInterner.intern(new AutoValue_FunctionSignature(shape, names(names)));
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000188 }
189
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +0000190
191
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000192 // Field definition (details filled in by AutoValue)
193 /** The shape */
194 public abstract Shape getShape();
195
196 /** The names */
197 public abstract ImmutableList<String> getNames();
198
199 /** append a representation of this signature to a string buffer. */
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000200 public StringBuilder toStringBuilder(StringBuilder sb) {
201 return WithValues.<Object, SkylarkType>create(this).toStringBuilder(sb);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000202 }
203
204 @Override
205 public String toString() {
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000206 StringBuilder sb = new StringBuilder();
207 toStringBuilder(sb);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000208 return sb.toString();
209 }
210
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000211 /**
212 * FunctionSignature.WithValues: also specifies a List of default values and types.
213 *
214 * <p>The lists can be null, which is an optimized path for specifying all null values.
215 *
216 * <p>Note that if some values can be null (for BuiltinFunction, not for UserDefinedFunction),
217 * you should use an ArrayList; otherwise, we recommend an ImmutableList.
218 *
219 * <p>V is the class of defaultValues and T is the class of types.
220 * When parsing a function definition at compile-time, they are &lt;Expression, Expression&gt;;
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000221 * when processing a @SkylarkSignature annotation at build-time, &lt;Object, SkylarkType&gt;.
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000222 */
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +0000223 @AutoValue
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000224 public abstract static class WithValues<V, T> implements Serializable {
225
226 // The fields
227 /** The underlying signature with parameter shape and names */
228 public abstract FunctionSignature getSignature();
229
230 /** The default values (if any) as a List of one per optional parameter.
231 * We might have preferred ImmutableList, but we care about
232 * supporting null's for some BuiltinFunction's, and we don't spit on speed.
233 */
234 @Nullable public abstract List<V> getDefaultValues();
235
236 /** The parameter types (if specified) as a List of one per parameter, including * and **.
237 * We might have preferred ImmutableList, but we care about supporting null's
238 * so we can take shortcut for untyped values.
239 */
240 @Nullable public abstract List<T> getTypes();
241
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +0000242
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000243 /**
244 * Create a signature with (default and type) values.
245 * If you supply mutable List's, we trust that you won't modify them afterwards.
246 */
247 public static <V, T> WithValues<V, T> create(FunctionSignature signature,
248 @Nullable List<V> defaultValues, @Nullable List<T> types) {
249 Shape shape = signature.getShape();
250 Preconditions.checkArgument(defaultValues == null
251 || defaultValues.size() == shape.getOptionals());
252 Preconditions.checkArgument(types == null
253 || types.size() == shape.getArguments());
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +0000254 return new AutoValue_FunctionSignature_WithValues<>(signature, defaultValues, types);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000255 }
256
257 public static <V, T> WithValues<V, T> create(FunctionSignature signature,
258 @Nullable List<V> defaultValues) {
259 return create(signature, defaultValues, null);
260 }
261
262 public static <V, T> WithValues<V, T> create(FunctionSignature signature,
263 @Nullable V[] defaultValues) {
264 return create(signature, Arrays.asList(defaultValues), null);
265 }
266
267 public static <V, T> WithValues<V, T> create(FunctionSignature signature) {
268 return create(signature, null, null);
269 }
270
271 /**
272 * Parse a list of Parameter into a FunctionSignature.
273 *
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000274 * <p>To be used both by the Parser and by the SkylarkSignature annotation processor.
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000275 */
276 public static <V, T> WithValues<V, T> of(Iterable<Parameter<V, T>> parameters)
277 throws SignatureException {
278 int mandatoryPositionals = 0;
279 int optionalPositionals = 0;
280 int mandatoryNamedOnly = 0;
281 int optionalNamedOnly = 0;
282 boolean hasStarStar = false;
283 boolean hasStar = false;
284 @Nullable String star = null;
285 @Nullable String starStar = null;
286 @Nullable T starType = null;
287 @Nullable T starStarType = null;
288 ArrayList<String> params = new ArrayList<>();
289 ArrayList<V> defaults = new ArrayList<>();
290 ArrayList<T> types = new ArrayList<>();
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000291 // optional named-only parameters are kept aside to be spliced after the mandatory ones.
292 ArrayList<String> optionalNamedOnlyParams = new ArrayList<>();
293 ArrayList<T> optionalNamedOnlyTypes = new ArrayList<>();
294 ArrayList<V> optionalNamedOnlyDefaultValues = new ArrayList<>();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000295 boolean defaultRequired = false; // true after mandatory positionals and before star.
296 Set<String> paramNameSet = new HashSet<>(); // set of names, to avoid duplicates
297
298 for (Parameter<V, T> param : parameters) {
299 if (hasStarStar) {
300 throw new SignatureException("illegal parameter after star-star parameter", param);
301 }
302 @Nullable String name = param.getName();
303 @Nullable T type = param.getType();
304 if (param.hasName()) {
305 if (paramNameSet.contains(name)) {
306 throw new SignatureException("duplicate parameter name in function definition", param);
307 }
308 paramNameSet.add(name);
309 }
310 if (param.isStarStar()) {
311 hasStarStar = true;
312 starStar = name;
313 starStarType = type;
314 } else if (param.isStar()) {
315 if (hasStar) {
316 throw new SignatureException(
317 "duplicate star parameter in function definition", param);
318 }
319 hasStar = true;
320 defaultRequired = false;
321 if (param.hasName()) {
322 star = name;
323 starType = type;
324 }
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000325 } else if (hasStar && param.isOptional()) {
326 optionalNamedOnly++;
327 optionalNamedOnlyParams.add(name);
328 optionalNamedOnlyTypes.add(type);
329 optionalNamedOnlyDefaultValues.add(param.getDefaultValue());
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000330 } else {
331 params.add(name);
332 types.add(type);
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000333 if (param.isOptional()) {
334 optionalPositionals++;
335 defaults.add(param.getDefaultValue());
336 defaultRequired = true;
337 } else if (hasStar) {
338 mandatoryNamedOnly++;
339 } else if (defaultRequired) {
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000340 throw new SignatureException(
341 "a mandatory positional parameter must not follow an optional parameter",
342 param);
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000343 } else {
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000344 mandatoryPositionals++;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000345 }
346 }
347 }
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000348 params.addAll(optionalNamedOnlyParams);
349 types.addAll(optionalNamedOnlyTypes);
350 defaults.addAll(optionalNamedOnlyDefaultValues);
351
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000352 if (star != null) {
353 params.add(star);
354 types.add(starType);
355 }
356 if (starStar != null) {
357 params.add(starStar);
358 types.add(starStarType);
359 }
360 return WithValues.<V, T>create(
361 FunctionSignature.create(
362 Shape.create(
363 mandatoryPositionals, optionalPositionals,
364 mandatoryNamedOnly, optionalNamedOnly,
365 star != null, starStar != null),
366 ImmutableList.<String>copyOf(params)),
367 FunctionSignature.<V>valueListOrNull(defaults),
368 FunctionSignature.<T>valueListOrNull(types));
369 }
370
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000371 public StringBuilder toStringBuilder(final StringBuilder sb) {
Florian Weikertee5e5e12015-08-11 16:47:31 +0000372 return toStringBuilder(sb, true, true, true, false);
373 }
374
375 /**
376 * Appends a representation of this signature to a string buffer.
377 * @param sb Output StringBuffer
378 * @param showNames Determines whether the names of arguments should be printed
379 * @param showDefaults Determines whether the default values of arguments should be printed (if
380 * present)
381 * @param skipMissingTypeNames Determines whether missing type names should be omitted (true) or
382 * replaced with "object" (false). If showNames is false, "object" is always used as a type name
383 * to prevent blank spaces.
384 * @param skipFirstMandatory Determines whether the first mandatory parameter should be omitted.
385 */
386 public StringBuilder toStringBuilder(final StringBuilder sb, final boolean showNames,
387 final boolean showDefaults, final boolean skipMissingTypeNames,
388 final boolean skipFirstMandatory) {
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000389 FunctionSignature signature = getSignature();
390 Shape shape = signature.getShape();
391 final ImmutableList<String> names = signature.getNames();
392 @Nullable final List<V> defaultValues = getDefaultValues();
393 @Nullable final List<T> types = getTypes();
394
395 int mandatoryPositionals = shape.getMandatoryPositionals();
396 int optionalPositionals = shape.getOptionalPositionals();
397 int mandatoryNamedOnly = shape.getMandatoryNamedOnly();
398 int optionalNamedOnly = shape.getOptionalNamedOnly();
399 boolean starArg = shape.hasStarArg();
400 boolean kwArg = shape.hasKwArg();
401 int positionals = mandatoryPositionals + optionalPositionals;
402 int namedOnly = mandatoryNamedOnly + optionalNamedOnly;
403 int named = positionals + namedOnly;
404 int args = named + (starArg ? 1 : 0) + (kwArg ? 1 : 0);
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000405 int endMandatoryNamedOnly = positionals + mandatoryNamedOnly;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000406 boolean hasStar = starArg || (namedOnly > 0);
407 int iStarArg = named;
408 int iKwArg = args - 1;
409
410 class Show {
411 private boolean isMore = false;
412 private int j = 0;
413
414 public void comma() {
Francois-Rene Rideauf941d562016-01-29 21:51:19 +0000415 if (isMore) {
416 sb.append(", ");
417 }
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000418 isMore = true;
419 }
420 public void type(int i) {
Florian Weikertee5e5e12015-08-11 16:47:31 +0000421 // We have to assign an artificial type string when the type is null.
422 // This happens when either
423 // a) there is no type defined (such as in user-defined functions) or
424 // b) the type is java.lang.Object.
425 boolean noTypeDefined = (types == null || types.get(i) == null);
426 String typeString = noTypeDefined ? "object" : types.get(i).toString();
427 if (noTypeDefined && showNames && skipMissingTypeNames) {
428 // This is the only case where we don't want to append typeString.
429 // If showNames = false, we ignore skipMissingTypeNames = true and append "object"
430 // in order to prevent blank spaces.
431 } else {
432 // We only append colons when there is a name.
433 if (showNames) {
434 sb.append(": ");
435 }
436 sb.append(typeString);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000437 }
438 }
439 public void mandatory(int i) {
440 comma();
Florian Weikertee5e5e12015-08-11 16:47:31 +0000441 if (showNames) {
442 sb.append(names.get(i));
443 }
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000444 type(i);
445 }
446 public void optional(int i) {
447 mandatory(i);
Florian Weikertee5e5e12015-08-11 16:47:31 +0000448 if (showDefaults) {
449 sb.append(" = ");
450 if (defaultValues == null) {
451 sb.append("?");
452 } else {
453 Printer.write(sb, defaultValues.get(j++));
454 }
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000455 }
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000456 }
Klaas Boesche0ec13b92015-11-06 12:16:03 +0000457 }
458
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000459 Show show = new Show();
460
Florian Weikertee5e5e12015-08-11 16:47:31 +0000461 int i = skipFirstMandatory ? 1 : 0;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000462 for (; i < mandatoryPositionals; i++) {
463 show.mandatory(i);
464 }
465 for (; i < positionals; i++) {
466 show.optional(i);
467 }
468 if (hasStar) {
469 show.comma();
470 sb.append("*");
Florian Weikertee5e5e12015-08-11 16:47:31 +0000471 if (starArg && showNames) {
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000472 sb.append(names.get(iStarArg));
473 }
474 }
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000475 for (; i < endMandatoryNamedOnly; i++) {
476 show.mandatory(i);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000477 }
478 for (; i < named; i++) {
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000479 show.optional(i);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000480 }
481 if (kwArg) {
482 show.comma();
483 sb.append("**");
Florian Weikertee5e5e12015-08-11 16:47:31 +0000484 if (showNames) {
485 sb.append(names.get(iKwArg));
486 }
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000487 }
488
489 return sb;
490 }
491
492 @Override
493 public String toString() {
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000494 StringBuilder sb = new StringBuilder();
495 toStringBuilder(sb);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000496 return sb.toString();
497 }
498 }
499
500 /** The given List, or null if all the list elements are null. */
501 @Nullable public static <E> List<E> valueListOrNull(List<E> list) {
502 if (list == null) {
503 return null;
504 }
505 for (E value : list) {
506 if (value != null) {
507 return list;
508 }
509 }
510 return null;
511 }
512
513 /**
514 * Constructs a function signature (with names) from signature description and names.
515 * This method covers the general case.
516 * The number of optional named-only parameters is deduced from the other arguments.
517 *
518 * @param numMandatoryPositionals an int for the number of mandatory positional parameters
519 * @param numOptionalPositionals an int for the number of optional positional parameters
520 * @param numMandatoryNamedOnly an int for the number of mandatory named-only parameters
521 * @param starArg a boolean for whether there is a starred parameter
522 * @param kwArg a boolean for whether there is a star-starred parameter
523 * @param names an Array of String for the parameter names
524 * @return a FunctionSignature
525 */
526 public static FunctionSignature of(int numMandatoryPositionals, int numOptionalPositionals,
527 int numMandatoryNamedOnly, boolean starArg, boolean kwArg, String... names) {
528 return create(Shape.create(
529 numMandatoryPositionals,
530 numOptionalPositionals,
531 numMandatoryNamedOnly,
532 names.length - (kwArg ? 1 : 0) - (starArg ? 1 : 0)
533 - numMandatoryPositionals - numOptionalPositionals - numMandatoryNamedOnly,
534 starArg, kwArg),
535 ImmutableList.<String>copyOf(names));
536 }
537
538 /**
539 * Constructs a function signature from mandatory positional argument names.
540 *
541 * @param names an Array of String for the positional parameter names
542 * @return a FunctionSignature
543 */
544 public static FunctionSignature of(String... names) {
545 return of(names.length, 0, 0, false, false, names);
546 }
547
548 /**
549 * Constructs a function signature from positional argument names.
550 *
551 * @param numMandatory an int for the number of mandatory positional parameters
552 * @param names an Array of String for the positional parameter names
553 * @return a FunctionSignature
554 */
555 public static FunctionSignature of(int numMandatory, String... names) {
556 return of(numMandatory, names.length - numMandatory, 0, false, false, names);
557 }
558
559 /**
560 * Constructs a function signature from mandatory named-only argument names.
561 *
562 * @param names an Array of String for the mandatory named-only parameter names
563 * @return a FunctionSignature
564 */
565 public static FunctionSignature namedOnly(String... names) {
566 return of(0, 0, names.length, false, false, names);
567 }
568
569 /**
570 * Constructs a function signature from named-only argument names.
571 *
572 * @param numMandatory an int for the number of mandatory named-only parameters
573 * @param names an Array of String for the named-only parameter names
574 * @return a FunctionSignature
575 */
576 public static FunctionSignature namedOnly(int numMandatory, String... names) {
577 return of(0, 0, numMandatory, false, false, names);
578 }
579
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000580 /** Invalid signature from Parser or from SkylarkSignature annotations */
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000581 protected static class SignatureException extends Exception {
582 @Nullable private final Parameter<?, ?> parameter;
583
584 /** SignatureException from a message and a Parameter */
585 public SignatureException(String message, @Nullable Parameter<?, ?> parameter) {
586 super(message);
587 this.parameter = parameter;
588 }
589
590 /** what parameter caused the exception, if identified? */
591 @Nullable public Parameter<?, ?> getParameter() {
592 return parameter;
593 }
594 }
595
596 /** A ready-made signature to allow only keyword arguments and put them in a kwarg parameter */
597 public static final FunctionSignature KWARGS =
598 FunctionSignature.of(0, 0, 0, false, true, "kwargs");
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000599}