blob: 034ef3fbc2d2acef3a6c04c26f7b5a8b63dd6352 [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
laurentlb3d2a68c2017-06-30 00:32:04 +020016
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000017import com.google.auto.value.AutoValue;
tomlua155b532017-11-08 20:12:47 +010018import com.google.common.base.Preconditions;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000019import com.google.common.collect.ImmutableList;
janakr76de1ad2018-03-03 11:12:36 -080020import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000021
22/**
23 * Function Signatures for BUILD language (same as Python)
24 *
Googler886c0a42019-10-02 12:41:55 -070025 * <p>Starlark's function signatures are just like Python3's. A function may have 6 kinds of
26 * parameters: positional mandatory, positional optional, positional rest (aka *args or variadic
27 * parameter), keyword-only mandatory, keyword-only optional, keyword rest (aka **kwargs parameter).
28 * A caller may specify all arguments but the *args and **kwargs arguments by name, and thus all
29 * mandatory and optional parameters are named parameters.
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000030 *
Googler886c0a42019-10-02 12:41:55 -070031 * <p>To enable various optimizations in the argument processing routine, we sort parameters
janakr76de1ad2018-03-03 11:12:36 -080032 * according the following constraints, enabling corresponding optimizations:
33 *
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000034 * <ol>
janakr76de1ad2018-03-03 11:12:36 -080035 * <li>The positional mandatories come just before the positional optionals, so they can be filled
36 * in one go.
37 * <li>Positionals come first, so it's easy to prepend extra positional arguments such as "self"
38 * to an argument list, and we optimize for the common case of no key-only mandatory
39 * parameters. key-only parameters are thus grouped together. positional mandatory and
40 * key-only mandatory parameters are separate, but there is no loop over a contiguous chunk of
41 * them, anyway.
Googler886c0a42019-10-02 12:41:55 -070042 * <li>The named are all grouped together, with star and star_star rest parameters coming last.
43 * <li>Mandatory parameters in each category (positional and named-only) come before the optional
44 * parameters, for the sake of slightly better clarity to human implementers. This eschews an
janakr76de1ad2018-03-03 11:12:36 -080045 * optimization whereby grouping optionals together allows to iterate over them in one go
46 * instead of two; however, this relatively minor optimization only matters when keyword
47 * arguments are passed, at which point it is dwarfed by the slowness of keyword processing.
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000048 * </ol>
49 *
Googler886c0a42019-10-02 12:41:55 -070050 * <p>Parameters are thus sorted in the following order: positional mandatory parameters (if any),
51 * positional optional parameters (if any), key-only mandatory parameters (if any), key-only
52 * optional parameters (if any), then star parameter (if any), then star_star parameter (if any).
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000053 */
janakr76de1ad2018-03-03 11:12:36 -080054@AutoCodec
Francois-Rene Rideaubf7b4362015-04-07 06:10:26 +000055@AutoValue
adonovancac48fe2020-04-23 12:31:21 -070056abstract class FunctionSignature {
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000057
Googler886c0a42019-10-02 12:41:55 -070058 // These abstract getters specify the actual parameter count fields to be defined by AutoValue.
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000059
Googler886c0a42019-10-02 12:41:55 -070060 /** Number of mandatory positional parameters */
adonovancac48fe2020-04-23 12:31:21 -070061 abstract int numMandatoryPositionals();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000062
Googler886c0a42019-10-02 12:41:55 -070063 /** Number of optional positional parameters */
adonovancac48fe2020-04-23 12:31:21 -070064 abstract int numOptionalPositionals();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000065
Googler886c0a42019-10-02 12:41:55 -070066 /** Number of mandatory named-only parameters. */
adonovancac48fe2020-04-23 12:31:21 -070067 abstract int numMandatoryNamedOnly();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000068
Googler886c0a42019-10-02 12:41:55 -070069 /** Number of optional named-only parameters */
adonovancac48fe2020-04-23 12:31:21 -070070 abstract int numOptionalNamedOnly();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000071
Googler886c0a42019-10-02 12:41:55 -070072 /** True if function has variadic parameter, {@code def f(*args)}. */
adonovancac48fe2020-04-23 12:31:21 -070073 abstract boolean hasVarargs();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000074
Googler886c0a42019-10-02 12:41:55 -070075 /** True if function has residual keyword-argument parameter, {@code def f(**kwargs)}. */
adonovancac48fe2020-04-23 12:31:21 -070076 abstract boolean hasKwargs();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000077
Googler886c0a42019-10-02 12:41:55 -070078 /** Parameter names. */
adonovancac48fe2020-04-23 12:31:21 -070079 abstract ImmutableList<String> getParameterNames();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000080
Googler886c0a42019-10-02 12:41:55 -070081 // computed parameter counts
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000082
Googler886c0a42019-10-02 12:41:55 -070083 /** Number of optional and mandatory positional parameters. */
adonovancac48fe2020-04-23 12:31:21 -070084 int numPositionals() {
Googler886c0a42019-10-02 12:41:55 -070085 return numMandatoryPositionals() + numOptionalPositionals();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000086 }
87
Googler886c0a42019-10-02 12:41:55 -070088 /** Number of optional and mandatory named-only parameters. */
adonovancac48fe2020-04-23 12:31:21 -070089 int numNamedOnly() {
Googler886c0a42019-10-02 12:41:55 -070090 return numMandatoryNamedOnly() + numOptionalNamedOnly();
91 }
92
93 /** number of optional parameters. */
adonovancac48fe2020-04-23 12:31:21 -070094 int numOptionals() {
Googler886c0a42019-10-02 12:41:55 -070095 return numOptionalPositionals() + numOptionalNamedOnly();
96 }
97
laurentlb5e5556b2020-01-03 11:32:43 -080098 private boolean hasStar() {
99 return hasVarargs() || (numNamedOnly() > 0);
Googler886c0a42019-10-02 12:41:55 -0700100 }
101
102 /** total number of parameters */
adonovancac48fe2020-04-23 12:31:21 -0700103 int numParameters() {
laurentlb5e5556b2020-01-03 11:32:43 -0800104 return numPositionals() + numNamedOnly() + (hasStar() ? 1 : 0) + (hasKwargs() ? 1 : 0);
Googler886c0a42019-10-02 12:41:55 -0700105 }
106
Googler886c0a42019-10-02 12:41:55 -0700107 // TODO(adonovan): not a user-friendly API. Provide external callers with this function:
108 // FunctionSignature.parse("a, b=1, *, c, d=2, *args, **kwargs")
109 // implemented by invoking the Starlark parser. (Most uses are in tests.)
janakr76de1ad2018-03-03 11:12:36 -0800110 @AutoCodec.Instantiator
adonovancac48fe2020-04-23 12:31:21 -0700111 static FunctionSignature create(
Googler886c0a42019-10-02 12:41:55 -0700112 int numMandatoryPositionals,
113 int numOptionalPositionals,
114 int numMandatoryNamedOnly,
115 int numOptionalNamedOnly,
116 boolean hasVarargs,
117 boolean hasKwargs,
118 ImmutableList<String> parameterNames) {
119 Preconditions.checkArgument(
120 0 <= numMandatoryPositionals
121 && 0 <= numOptionalPositionals
122 && 0 <= numMandatoryNamedOnly
123 && 0 <= numOptionalNamedOnly);
124
adonovan459eae72020-04-03 09:42:52 -0700125 return new AutoValue_FunctionSignature(
126 numMandatoryPositionals,
127 numOptionalPositionals,
128 numMandatoryNamedOnly,
129 numOptionalNamedOnly,
130 hasVarargs,
131 hasKwargs,
132 parameterNames);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000133 }
134
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000135 @Override
Googler28c1a2a2019-10-07 11:28:42 -0700136 public final String toString() {
adonovan76708192020-04-23 13:09:52 -0700137 StringBuilder printer = new StringBuilder();
Googler28c1a2a2019-10-07 11:28:42 -0700138 final ImmutableList<String> names = getParameterNames();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000139
Googler28c1a2a2019-10-07 11:28:42 -0700140 int mandatoryPositionals = numMandatoryPositionals();
141 int optionalPositionals = numOptionalPositionals();
142 int mandatoryNamedOnly = numMandatoryNamedOnly();
143 int optionalNamedOnly = numOptionalNamedOnly();
144 boolean hasVarargs = hasVarargs();
145 boolean hasKwargs = hasKwargs();
146 int positionals = mandatoryPositionals + optionalPositionals;
147 int namedOnly = mandatoryNamedOnly + optionalNamedOnly;
148 int named = positionals + namedOnly;
149 int args = named + (hasVarargs ? 1 : 0) + (hasKwargs ? 1 : 0);
150 int endMandatoryNamedOnly = positionals + mandatoryNamedOnly;
Googler28c1a2a2019-10-07 11:28:42 -0700151 int iStarArg = named;
152 int iKwArg = args - 1;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000153
Googler28c1a2a2019-10-07 11:28:42 -0700154 class Show {
155 private boolean isMore = false;
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000156
adonovancac48fe2020-04-23 12:31:21 -0700157 void comma() {
Googler28c1a2a2019-10-07 11:28:42 -0700158 if (isMore) {
159 printer.append(", ");
160 }
161 isMore = true;
brandjon540aac62017-06-12 23:08:09 +0200162 }
janakr76de1ad2018-03-03 11:12:36 -0800163
adonovancac48fe2020-04-23 12:31:21 -0700164 void mandatory(int i) {
Googler28c1a2a2019-10-07 11:28:42 -0700165 comma();
166 printer.append(names.get(i));
Googler28c1a2a2019-10-07 11:28:42 -0700167 }
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000168
adonovancac48fe2020-04-23 12:31:21 -0700169 void optional(int i) {
Googler28c1a2a2019-10-07 11:28:42 -0700170 mandatory(i);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000171 }
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000172 }
173
Googler28c1a2a2019-10-07 11:28:42 -0700174 Show show = new Show();
175
Googler275cc6b2019-11-12 11:22:19 -0800176 int i = 0;
Googler28c1a2a2019-10-07 11:28:42 -0700177 for (; i < mandatoryPositionals; i++) {
178 show.mandatory(i);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000179 }
Googler28c1a2a2019-10-07 11:28:42 -0700180 for (; i < positionals; i++) {
181 show.optional(i);
182 }
laurentlb5e5556b2020-01-03 11:32:43 -0800183 if (hasStar()) {
Googler28c1a2a2019-10-07 11:28:42 -0700184 show.comma();
185 printer.append("*");
186 if (hasVarargs) {
187 printer.append(names.get(iStarArg));
188 }
189 }
190 for (; i < endMandatoryNamedOnly; i++) {
191 show.mandatory(i);
192 }
193 for (; i < named; i++) {
194 show.optional(i);
195 }
196 if (hasKwargs) {
197 show.comma();
198 printer.append("**");
199 printer.append(names.get(iKwArg));
200 }
201
adonovan76708192020-04-23 13:09:52 -0700202 return printer.toString();
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000203 }
204
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000205 /**
Googler886c0a42019-10-02 12:41:55 -0700206 * Constructs a function signature (with names) from signature description and names. This method
207 * covers the general case. The number of optional named-only parameters is deduced from the other
208 * arguments.
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000209 *
210 * @param numMandatoryPositionals an int for the number of mandatory positional parameters
211 * @param numOptionalPositionals an int for the number of optional positional parameters
212 * @param numMandatoryNamedOnly an int for the number of mandatory named-only parameters
Googler886c0a42019-10-02 12:41:55 -0700213 * @param hasVarargs whether function is variadic parameter
214 * @param hasKwargs whether function accepts arbitrary named arguments
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000215 * @param names an Array of String for the parameter names
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000216 */
adonovan9e614922020-04-21 06:41:34 -0700217 private static FunctionSignature of(
Googler886c0a42019-10-02 12:41:55 -0700218 int numMandatoryPositionals,
219 int numOptionalPositionals,
220 int numMandatoryNamedOnly,
221 boolean hasVarargs,
222 boolean hasKwargs,
223 String... names) {
224 return create(
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000225 numMandatoryPositionals,
226 numOptionalPositionals,
227 numMandatoryNamedOnly,
Googler886c0a42019-10-02 12:41:55 -0700228 names.length
229 - (hasKwargs ? 1 : 0)
230 - (hasVarargs ? 1 : 0)
231 - numMandatoryPositionals
232 - numOptionalPositionals
233 - numMandatoryNamedOnly,
234 hasVarargs,
235 hasKwargs,
brandjonc06e7462017-07-11 20:54:58 +0200236 ImmutableList.copyOf(names));
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000237 }
238
adonovanc3327992020-01-08 09:09:11 -0800239 /** A ready-made signature that accepts no arguments. */
adonovan9e614922020-04-21 06:41:34 -0700240 static final FunctionSignature NOARGS = of(0, 0, 0, false, false);
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +0000241}