xref: /aosp_15_r20/art/cmdline/detail/cmdline_parse_argument_detail.h (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
18*795d594fSAndroid Build Coastguard Worker #define ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
19*795d594fSAndroid Build Coastguard Worker 
20*795d594fSAndroid Build Coastguard Worker #include <assert.h>
21*795d594fSAndroid Build Coastguard Worker #include <algorithm>
22*795d594fSAndroid Build Coastguard Worker #include <functional>
23*795d594fSAndroid Build Coastguard Worker #include <memory>
24*795d594fSAndroid Build Coastguard Worker #include <numeric>
25*795d594fSAndroid Build Coastguard Worker #include <string_view>
26*795d594fSAndroid Build Coastguard Worker #include <type_traits>
27*795d594fSAndroid Build Coastguard Worker #include <vector>
28*795d594fSAndroid Build Coastguard Worker 
29*795d594fSAndroid Build Coastguard Worker #include "android-base/strings.h"
30*795d594fSAndroid Build Coastguard Worker 
31*795d594fSAndroid Build Coastguard Worker #include "base/indenter.h"
32*795d594fSAndroid Build Coastguard Worker #include "cmdline_parse_result.h"
33*795d594fSAndroid Build Coastguard Worker #include "cmdline_types.h"
34*795d594fSAndroid Build Coastguard Worker #include "token_range.h"
35*795d594fSAndroid Build Coastguard Worker #include "unit.h"
36*795d594fSAndroid Build Coastguard Worker 
37*795d594fSAndroid Build Coastguard Worker namespace art {
38*795d594fSAndroid Build Coastguard Worker // Implementation details for the parser. Do not look inside if you hate templates.
39*795d594fSAndroid Build Coastguard Worker namespace detail {
40*795d594fSAndroid Build Coastguard Worker 
41*795d594fSAndroid Build Coastguard Worker // A non-templated base class for argument parsers. Used by the general parser
42*795d594fSAndroid Build Coastguard Worker // to parse arguments, without needing to know the argument type at compile time.
43*795d594fSAndroid Build Coastguard Worker //
44*795d594fSAndroid Build Coastguard Worker // This is an application of the type erasure idiom.
45*795d594fSAndroid Build Coastguard Worker struct CmdlineParseArgumentAny {
~CmdlineParseArgumentAnyCmdlineParseArgumentAny46*795d594fSAndroid Build Coastguard Worker   virtual ~CmdlineParseArgumentAny() {}
47*795d594fSAndroid Build Coastguard Worker 
48*795d594fSAndroid Build Coastguard Worker   // Attempt to parse this argument starting at arguments[position].
49*795d594fSAndroid Build Coastguard Worker   // If the parsing succeeds, the parsed value will be saved as a side-effect.
50*795d594fSAndroid Build Coastguard Worker   //
51*795d594fSAndroid Build Coastguard Worker   // In most situations, the parsing will not match by returning kUnknown. In this case,
52*795d594fSAndroid Build Coastguard Worker   // no tokens were consumed and the position variable will not be updated.
53*795d594fSAndroid Build Coastguard Worker   //
54*795d594fSAndroid Build Coastguard Worker   // At other times, parsing may fail due to validation but the initial token was still matched
55*795d594fSAndroid Build Coastguard Worker   // (for example an out of range value, or passing in a string where an int was expected).
56*795d594fSAndroid Build Coastguard Worker   // In this case the tokens are still consumed, and the position variable will get incremented
57*795d594fSAndroid Build Coastguard Worker   // by all the consumed tokens.
58*795d594fSAndroid Build Coastguard Worker   //
59*795d594fSAndroid Build Coastguard Worker   // The # of tokens consumed by the parse attempt will be set as an out-parameter into
60*795d594fSAndroid Build Coastguard Worker   // consumed_tokens. The parser should skip this many tokens before parsing the next
61*795d594fSAndroid Build Coastguard Worker   // argument.
62*795d594fSAndroid Build Coastguard Worker   virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) = 0;
63*795d594fSAndroid Build Coastguard Worker   // How many tokens should be taken off argv for parsing this argument.
64*795d594fSAndroid Build Coastguard Worker   // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
65*795d594fSAndroid Build Coastguard Worker   //
66*795d594fSAndroid Build Coastguard Worker   // A [min,max] range is returned to represent argument definitions with multiple
67*795d594fSAndroid Build Coastguard Worker   // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
68*795d594fSAndroid Build Coastguard Worker   virtual std::pair<size_t, size_t> GetNumTokens() const = 0;
69*795d594fSAndroid Build Coastguard Worker   // Get the run-time typename of the argument type.
70*795d594fSAndroid Build Coastguard Worker   virtual const char* GetTypeName() const = 0;
71*795d594fSAndroid Build Coastguard Worker   // Try to do a close match, returning how many tokens were matched against this argument
72*795d594fSAndroid Build Coastguard Worker   // definition. More tokens is better.
73*795d594fSAndroid Build Coastguard Worker   //
74*795d594fSAndroid Build Coastguard Worker   // Do a quick match token-by-token, and see if they match.
75*795d594fSAndroid Build Coastguard Worker   // Any tokens with a wildcard in them are only matched up until the wildcard.
76*795d594fSAndroid Build Coastguard Worker   // If this is true, then the wildcard matching later on can still fail, so this is not
77*795d594fSAndroid Build Coastguard Worker   // a guarantee that the argument is correct, it's more of a strong hint that the
78*795d594fSAndroid Build Coastguard Worker   // user-provided input *probably* was trying to match this argument.
79*795d594fSAndroid Build Coastguard Worker   //
80*795d594fSAndroid Build Coastguard Worker   // Returns how many tokens were either matched (or ignored because there was a
81*795d594fSAndroid Build Coastguard Worker   // wildcard present). 0 means no match. If the Size() tokens are returned.
82*795d594fSAndroid Build Coastguard Worker   virtual size_t MaybeMatches(const TokenRange& tokens) = 0;
83*795d594fSAndroid Build Coastguard Worker 
84*795d594fSAndroid Build Coastguard Worker   virtual void DumpHelp(VariableIndentationOutputStream& os) = 0;
85*795d594fSAndroid Build Coastguard Worker 
86*795d594fSAndroid Build Coastguard Worker   virtual const std::optional<const char*>& GetCategory() = 0;
87*795d594fSAndroid Build Coastguard Worker };
88*795d594fSAndroid Build Coastguard Worker 
89*795d594fSAndroid Build Coastguard Worker template <typename T>
90*795d594fSAndroid Build Coastguard Worker using EnableIfNumeric = std::enable_if<std::is_arithmetic<T>::value>;
91*795d594fSAndroid Build Coastguard Worker 
92*795d594fSAndroid Build Coastguard Worker template <typename T>
93*795d594fSAndroid Build Coastguard Worker using DisableIfNumeric = std::enable_if<!std::is_arithmetic<T>::value>;
94*795d594fSAndroid Build Coastguard Worker 
95*795d594fSAndroid Build Coastguard Worker // Argument definition information, created by an ArgumentBuilder and an UntypedArgumentBuilder.
96*795d594fSAndroid Build Coastguard Worker template <typename TArg>
97*795d594fSAndroid Build Coastguard Worker struct CmdlineParserArgumentInfo {
98*795d594fSAndroid Build Coastguard Worker   // This version will only be used if TArg is arithmetic and thus has the <= operators.
99*795d594fSAndroid Build Coastguard Worker   template <typename T = TArg>  // Necessary to get SFINAE to kick in.
100*795d594fSAndroid Build Coastguard Worker   bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = nullptr) {
101*795d594fSAndroid Build Coastguard Worker     if (has_range_) {
102*795d594fSAndroid Build Coastguard Worker       return min_ <= value && value <= max_;
103*795d594fSAndroid Build Coastguard Worker     }
104*795d594fSAndroid Build Coastguard Worker     return true;
105*795d594fSAndroid Build Coastguard Worker   }
106*795d594fSAndroid Build Coastguard Worker 
107*795d594fSAndroid Build Coastguard Worker   // This version will be used at other times when TArg is not arithmetic.
108*795d594fSAndroid Build Coastguard Worker   template <typename T = TArg>
109*795d594fSAndroid Build Coastguard Worker   bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = nullptr) {
110*795d594fSAndroid Build Coastguard Worker     assert(!has_range_);
111*795d594fSAndroid Build Coastguard Worker     return true;
112*795d594fSAndroid Build Coastguard Worker   }
113*795d594fSAndroid Build Coastguard Worker 
114*795d594fSAndroid Build Coastguard Worker   // Do a quick match token-by-token, and see if they match.
115*795d594fSAndroid Build Coastguard Worker   // Any tokens with a wildcard in them only match the prefix up until the wildcard.
116*795d594fSAndroid Build Coastguard Worker   //
117*795d594fSAndroid Build Coastguard Worker   // If this is true, then the wildcard matching later on can still fail, so this is not
118*795d594fSAndroid Build Coastguard Worker   // a guarantee that the argument is correct, it's more of a strong hint that the
119*795d594fSAndroid Build Coastguard Worker   // user-provided input *probably* was trying to match this argument.
MaybeMatchesCmdlineParserArgumentInfo120*795d594fSAndroid Build Coastguard Worker   size_t MaybeMatches(const TokenRange& token_list) const {
121*795d594fSAndroid Build Coastguard Worker     auto best_match = FindClosestMatch(token_list);
122*795d594fSAndroid Build Coastguard Worker 
123*795d594fSAndroid Build Coastguard Worker     return best_match.second;
124*795d594fSAndroid Build Coastguard Worker   }
125*795d594fSAndroid Build Coastguard Worker 
126*795d594fSAndroid Build Coastguard Worker   // Attempt to find the closest match (see MaybeMatches).
127*795d594fSAndroid Build Coastguard Worker   //
128*795d594fSAndroid Build Coastguard Worker   // Returns the token range that was the closest match and the # of tokens that
129*795d594fSAndroid Build Coastguard Worker   // this range was matched up until.
FindClosestMatchCmdlineParserArgumentInfo130*795d594fSAndroid Build Coastguard Worker   std::pair<const TokenRange*, size_t> FindClosestMatch(const TokenRange& token_list) const {
131*795d594fSAndroid Build Coastguard Worker     const TokenRange* best_match_ptr = nullptr;
132*795d594fSAndroid Build Coastguard Worker 
133*795d594fSAndroid Build Coastguard Worker     size_t best_match = 0;
134*795d594fSAndroid Build Coastguard Worker     for (auto&& token_range : tokenized_names_) {
135*795d594fSAndroid Build Coastguard Worker       size_t this_match = token_range.MaybeMatches(token_list, std::string("_"));
136*795d594fSAndroid Build Coastguard Worker 
137*795d594fSAndroid Build Coastguard Worker       if (this_match > best_match) {
138*795d594fSAndroid Build Coastguard Worker         best_match_ptr = &token_range;
139*795d594fSAndroid Build Coastguard Worker         best_match = this_match;
140*795d594fSAndroid Build Coastguard Worker       }
141*795d594fSAndroid Build Coastguard Worker     }
142*795d594fSAndroid Build Coastguard Worker 
143*795d594fSAndroid Build Coastguard Worker     return std::make_pair(best_match_ptr, best_match);
144*795d594fSAndroid Build Coastguard Worker   }
145*795d594fSAndroid Build Coastguard Worker 
146*795d594fSAndroid Build Coastguard Worker   template <typename T = TArg>  // Necessary to get SFINAE to kick in.
DumpHelpCmdlineParserArgumentInfo147*795d594fSAndroid Build Coastguard Worker   void DumpHelp(VariableIndentationOutputStream& vios) {
148*795d594fSAndroid Build Coastguard Worker     // Separate arguments
149*795d594fSAndroid Build Coastguard Worker     vios.Stream() << std::endl;
150*795d594fSAndroid Build Coastguard Worker     for (auto cname : names_) {
151*795d594fSAndroid Build Coastguard Worker       std::string_view name = cname;
152*795d594fSAndroid Build Coastguard Worker       if (using_blanks_) {
153*795d594fSAndroid Build Coastguard Worker         name = name.substr(0, name.find('_'));
154*795d594fSAndroid Build Coastguard Worker       }
155*795d594fSAndroid Build Coastguard Worker       auto& os = vios.Stream();
156*795d594fSAndroid Build Coastguard Worker       auto print_once = [&]() {
157*795d594fSAndroid Build Coastguard Worker         os << name;
158*795d594fSAndroid Build Coastguard Worker         if (using_blanks_) {
159*795d594fSAndroid Build Coastguard Worker           if (has_value_map_) {
160*795d594fSAndroid Build Coastguard Worker             bool first = true;
161*795d594fSAndroid Build Coastguard Worker             for (auto [val, unused] : value_map_) {
162*795d594fSAndroid Build Coastguard Worker               os << (first ? "{" : "|") << val;
163*795d594fSAndroid Build Coastguard Worker               first = false;
164*795d594fSAndroid Build Coastguard Worker             }
165*795d594fSAndroid Build Coastguard Worker             os << "}";
166*795d594fSAndroid Build Coastguard Worker           } else if (metavar_.has_value()) {
167*795d594fSAndroid Build Coastguard Worker             os << *metavar_;
168*795d594fSAndroid Build Coastguard Worker           } else {
169*795d594fSAndroid Build Coastguard Worker             os << "{" << CmdlineType<T>::DescribeType() << "}";
170*795d594fSAndroid Build Coastguard Worker           }
171*795d594fSAndroid Build Coastguard Worker         }
172*795d594fSAndroid Build Coastguard Worker       };
173*795d594fSAndroid Build Coastguard Worker       print_once();
174*795d594fSAndroid Build Coastguard Worker       if (appending_values_) {
175*795d594fSAndroid Build Coastguard Worker         os << " [";
176*795d594fSAndroid Build Coastguard Worker         print_once();
177*795d594fSAndroid Build Coastguard Worker         os << "...]";
178*795d594fSAndroid Build Coastguard Worker       }
179*795d594fSAndroid Build Coastguard Worker       os << std::endl;
180*795d594fSAndroid Build Coastguard Worker     }
181*795d594fSAndroid Build Coastguard Worker     if (help_.has_value()) {
182*795d594fSAndroid Build Coastguard Worker       ScopedIndentation si(&vios);
183*795d594fSAndroid Build Coastguard Worker       vios.Stream() << *help_ << std::endl;
184*795d594fSAndroid Build Coastguard Worker     }
185*795d594fSAndroid Build Coastguard Worker   }
186*795d594fSAndroid Build Coastguard Worker 
187*795d594fSAndroid Build Coastguard Worker 
188*795d594fSAndroid Build Coastguard Worker   // Mark the argument definition as completed, do not mutate the object anymore after this
189*795d594fSAndroid Build Coastguard Worker   // call is done.
190*795d594fSAndroid Build Coastguard Worker   //
191*795d594fSAndroid Build Coastguard Worker   // Performs several checks of the validity and token calculations.
CompleteArgumentCmdlineParserArgumentInfo192*795d594fSAndroid Build Coastguard Worker   void CompleteArgument() {
193*795d594fSAndroid Build Coastguard Worker     assert(names_.size() >= 1);
194*795d594fSAndroid Build Coastguard Worker     assert(!is_completed_);
195*795d594fSAndroid Build Coastguard Worker 
196*795d594fSAndroid Build Coastguard Worker     is_completed_ = true;
197*795d594fSAndroid Build Coastguard Worker 
198*795d594fSAndroid Build Coastguard Worker     size_t blank_count = 0;
199*795d594fSAndroid Build Coastguard Worker     size_t token_count = 0;
200*795d594fSAndroid Build Coastguard Worker 
201*795d594fSAndroid Build Coastguard Worker     size_t global_blank_count = 0;
202*795d594fSAndroid Build Coastguard Worker     size_t global_token_count = 0;
203*795d594fSAndroid Build Coastguard Worker     for (auto&& name : names_) {
204*795d594fSAndroid Build Coastguard Worker       std::string s(name);
205*795d594fSAndroid Build Coastguard Worker 
206*795d594fSAndroid Build Coastguard Worker       size_t local_blank_count = std::count(s.begin(), s.end(), '_');
207*795d594fSAndroid Build Coastguard Worker       size_t local_token_count = std::count(s.begin(), s.end(), ' ');
208*795d594fSAndroid Build Coastguard Worker 
209*795d594fSAndroid Build Coastguard Worker       if (global_blank_count != 0) {
210*795d594fSAndroid Build Coastguard Worker         assert(local_blank_count == global_blank_count
211*795d594fSAndroid Build Coastguard Worker                && "Every argument descriptor string must have same amount of blanks (_)");
212*795d594fSAndroid Build Coastguard Worker       }
213*795d594fSAndroid Build Coastguard Worker 
214*795d594fSAndroid Build Coastguard Worker       if (local_blank_count != 0) {
215*795d594fSAndroid Build Coastguard Worker         global_blank_count = local_blank_count;
216*795d594fSAndroid Build Coastguard Worker         blank_count++;
217*795d594fSAndroid Build Coastguard Worker 
218*795d594fSAndroid Build Coastguard Worker         assert(local_blank_count == 1 && "More than one blank is not supported");
219*795d594fSAndroid Build Coastguard Worker         assert(s.back() == '_' && "The blank character must only be at the end of the string");
220*795d594fSAndroid Build Coastguard Worker       }
221*795d594fSAndroid Build Coastguard Worker 
222*795d594fSAndroid Build Coastguard Worker       if (global_token_count != 0) {
223*795d594fSAndroid Build Coastguard Worker         assert(local_token_count == global_token_count
224*795d594fSAndroid Build Coastguard Worker                && "Every argument descriptor string must have same amount of tokens (spaces)");
225*795d594fSAndroid Build Coastguard Worker       }
226*795d594fSAndroid Build Coastguard Worker 
227*795d594fSAndroid Build Coastguard Worker       if (local_token_count != 0) {
228*795d594fSAndroid Build Coastguard Worker         global_token_count = local_token_count;
229*795d594fSAndroid Build Coastguard Worker         token_count++;
230*795d594fSAndroid Build Coastguard Worker       }
231*795d594fSAndroid Build Coastguard Worker 
232*795d594fSAndroid Build Coastguard Worker       // Tokenize every name, turning it from a string to a token list.
233*795d594fSAndroid Build Coastguard Worker       tokenized_names_.clear();
234*795d594fSAndroid Build Coastguard Worker       for (auto&& name1 : names_) {
235*795d594fSAndroid Build Coastguard Worker         // Split along ' ' only, removing any duplicated spaces.
236*795d594fSAndroid Build Coastguard Worker         tokenized_names_.push_back(
237*795d594fSAndroid Build Coastguard Worker             TokenRange::Split(name1, {' '}).RemoveToken(" "));
238*795d594fSAndroid Build Coastguard Worker       }
239*795d594fSAndroid Build Coastguard Worker 
240*795d594fSAndroid Build Coastguard Worker       // remove the _ character from each of the token ranges
241*795d594fSAndroid Build Coastguard Worker       // we will often end up with an empty token (i.e. ["-XX", "_"] -> ["-XX", ""]
242*795d594fSAndroid Build Coastguard Worker       // and this is OK because we still need an empty token to simplify
243*795d594fSAndroid Build Coastguard Worker       // range comparisons
244*795d594fSAndroid Build Coastguard Worker       simple_names_.clear();
245*795d594fSAndroid Build Coastguard Worker 
246*795d594fSAndroid Build Coastguard Worker       for (auto&& tokenized_name : tokenized_names_) {
247*795d594fSAndroid Build Coastguard Worker         simple_names_.push_back(tokenized_name.RemoveCharacter('_'));
248*795d594fSAndroid Build Coastguard Worker       }
249*795d594fSAndroid Build Coastguard Worker     }
250*795d594fSAndroid Build Coastguard Worker 
251*795d594fSAndroid Build Coastguard Worker     if (token_count != 0) {
252*795d594fSAndroid Build Coastguard Worker       assert(("Every argument descriptor string must have equal amount of tokens (spaces)" &&
253*795d594fSAndroid Build Coastguard Worker           token_count == names_.size()));
254*795d594fSAndroid Build Coastguard Worker     }
255*795d594fSAndroid Build Coastguard Worker 
256*795d594fSAndroid Build Coastguard Worker     if (blank_count != 0) {
257*795d594fSAndroid Build Coastguard Worker       assert(("Every argument descriptor string must have an equal amount of blanks (_)" &&
258*795d594fSAndroid Build Coastguard Worker           blank_count == names_.size()));
259*795d594fSAndroid Build Coastguard Worker     }
260*795d594fSAndroid Build Coastguard Worker 
261*795d594fSAndroid Build Coastguard Worker     using_blanks_ = blank_count > 0;
262*795d594fSAndroid Build Coastguard Worker     {
263*795d594fSAndroid Build Coastguard Worker       size_t smallest_name_token_range_size =
264*795d594fSAndroid Build Coastguard Worker           std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), ~(0u),
265*795d594fSAndroid Build Coastguard Worker                           [](size_t min, const TokenRange& cur) {
266*795d594fSAndroid Build Coastguard Worker                             return std::min(min, cur.Size());
267*795d594fSAndroid Build Coastguard Worker                           });
268*795d594fSAndroid Build Coastguard Worker       size_t largest_name_token_range_size =
269*795d594fSAndroid Build Coastguard Worker           std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), 0u,
270*795d594fSAndroid Build Coastguard Worker                           [](size_t max, const TokenRange& cur) {
271*795d594fSAndroid Build Coastguard Worker                             return std::max(max, cur.Size());
272*795d594fSAndroid Build Coastguard Worker                           });
273*795d594fSAndroid Build Coastguard Worker 
274*795d594fSAndroid Build Coastguard Worker       token_range_size_ = std::make_pair(smallest_name_token_range_size,
275*795d594fSAndroid Build Coastguard Worker                                          largest_name_token_range_size);
276*795d594fSAndroid Build Coastguard Worker     }
277*795d594fSAndroid Build Coastguard Worker 
278*795d594fSAndroid Build Coastguard Worker     if (has_value_list_) {
279*795d594fSAndroid Build Coastguard Worker       assert(names_.size() == value_list_.size()
280*795d594fSAndroid Build Coastguard Worker              && "Number of arg descriptors must match number of values");
281*795d594fSAndroid Build Coastguard Worker       assert(!has_value_map_);
282*795d594fSAndroid Build Coastguard Worker     }
283*795d594fSAndroid Build Coastguard Worker     if (has_value_map_) {
284*795d594fSAndroid Build Coastguard Worker       if (!using_blanks_) {
285*795d594fSAndroid Build Coastguard Worker         assert(names_.size() == value_map_.size() &&
286*795d594fSAndroid Build Coastguard Worker                "Since no blanks were specified, each arg is mapped directly into a mapped "
287*795d594fSAndroid Build Coastguard Worker                "value without parsing; sizes must match");
288*795d594fSAndroid Build Coastguard Worker       }
289*795d594fSAndroid Build Coastguard Worker 
290*795d594fSAndroid Build Coastguard Worker       assert(!has_value_list_);
291*795d594fSAndroid Build Coastguard Worker     }
292*795d594fSAndroid Build Coastguard Worker 
293*795d594fSAndroid Build Coastguard Worker     if (!using_blanks_ && !CmdlineType<TArg>::kCanParseBlankless) {
294*795d594fSAndroid Build Coastguard Worker       assert((has_value_map_ || has_value_list_) &&
295*795d594fSAndroid Build Coastguard Worker              "Arguments without a blank (_) must provide either a value map or a value list");
296*795d594fSAndroid Build Coastguard Worker     }
297*795d594fSAndroid Build Coastguard Worker 
298*795d594fSAndroid Build Coastguard Worker     TypedCheck();
299*795d594fSAndroid Build Coastguard Worker   }
300*795d594fSAndroid Build Coastguard Worker 
301*795d594fSAndroid Build Coastguard Worker   // List of aliases for a single argument definition, e.g. {"-Xdex2oat", "-Xnodex2oat"}.
302*795d594fSAndroid Build Coastguard Worker   std::vector<const char*> names_;
303*795d594fSAndroid Build Coastguard Worker   // Is there at least 1 wildcard '_' in the argument definition?
304*795d594fSAndroid Build Coastguard Worker   bool using_blanks_ = false;
305*795d594fSAndroid Build Coastguard Worker   // [min, max] token counts in each arg def
306*795d594fSAndroid Build Coastguard Worker   std::pair<size_t, size_t> token_range_size_;
307*795d594fSAndroid Build Coastguard Worker 
308*795d594fSAndroid Build Coastguard Worker   // contains all the names in a tokenized form, i.e. as a space-delimited list
309*795d594fSAndroid Build Coastguard Worker   std::vector<TokenRange> tokenized_names_;
310*795d594fSAndroid Build Coastguard Worker 
311*795d594fSAndroid Build Coastguard Worker   // contains the tokenized names, but with the _ character stripped
312*795d594fSAndroid Build Coastguard Worker   std::vector<TokenRange> simple_names_;
313*795d594fSAndroid Build Coastguard Worker 
314*795d594fSAndroid Build Coastguard Worker   // For argument definitions created with '.AppendValues()'
315*795d594fSAndroid Build Coastguard Worker   // Meaning that parsing should mutate the existing value in-place if possible.
316*795d594fSAndroid Build Coastguard Worker   bool appending_values_ = false;
317*795d594fSAndroid Build Coastguard Worker 
318*795d594fSAndroid Build Coastguard Worker   // For argument definitions created with '.WithRange(min, max)'
319*795d594fSAndroid Build Coastguard Worker   bool has_range_ = false;
320*795d594fSAndroid Build Coastguard Worker   TArg min_;
321*795d594fSAndroid Build Coastguard Worker   TArg max_;
322*795d594fSAndroid Build Coastguard Worker 
323*795d594fSAndroid Build Coastguard Worker   // For argument definitions created with '.WithValueMap'
324*795d594fSAndroid Build Coastguard Worker   bool has_value_map_ = false;
325*795d594fSAndroid Build Coastguard Worker   std::vector<std::pair<const char*, TArg>> value_map_;
326*795d594fSAndroid Build Coastguard Worker 
327*795d594fSAndroid Build Coastguard Worker   // For argument definitions created with '.WithValues'
328*795d594fSAndroid Build Coastguard Worker   bool has_value_list_ = false;
329*795d594fSAndroid Build Coastguard Worker   std::vector<TArg> value_list_;
330*795d594fSAndroid Build Coastguard Worker 
331*795d594fSAndroid Build Coastguard Worker   std::optional<const char*> help_;
332*795d594fSAndroid Build Coastguard Worker   std::optional<const char*> category_;
333*795d594fSAndroid Build Coastguard Worker   std::optional<const char*> metavar_;
334*795d594fSAndroid Build Coastguard Worker 
335*795d594fSAndroid Build Coastguard Worker   // Make sure there's a default constructor.
336*795d594fSAndroid Build Coastguard Worker   CmdlineParserArgumentInfo() = default;
337*795d594fSAndroid Build Coastguard Worker 
338*795d594fSAndroid Build Coastguard Worker   // Ensure there's a default move constructor.
339*795d594fSAndroid Build Coastguard Worker   CmdlineParserArgumentInfo(CmdlineParserArgumentInfo&&) noexcept = default;
340*795d594fSAndroid Build Coastguard Worker 
341*795d594fSAndroid Build Coastguard Worker  private:
342*795d594fSAndroid Build Coastguard Worker   // Perform type-specific checks at runtime.
343*795d594fSAndroid Build Coastguard Worker   template <typename T = TArg>
344*795d594fSAndroid Build Coastguard Worker   void TypedCheck(typename std::enable_if<std::is_same<Unit, T>::value>::type* = 0) {
345*795d594fSAndroid Build Coastguard Worker     assert(!using_blanks_ &&
346*795d594fSAndroid Build Coastguard Worker            "Blanks are not supported in Unit arguments; since a Unit has no parse-able value");
347*795d594fSAndroid Build Coastguard Worker   }
348*795d594fSAndroid Build Coastguard Worker 
TypedCheckCmdlineParserArgumentInfo349*795d594fSAndroid Build Coastguard Worker   void TypedCheck() {}
350*795d594fSAndroid Build Coastguard Worker 
351*795d594fSAndroid Build Coastguard Worker   bool is_completed_ = false;
352*795d594fSAndroid Build Coastguard Worker };
353*795d594fSAndroid Build Coastguard Worker 
354*795d594fSAndroid Build Coastguard Worker // A virtual-implementation of the necessary argument information in order to
355*795d594fSAndroid Build Coastguard Worker // be able to parse arguments.
356*795d594fSAndroid Build Coastguard Worker template <typename TArg>
357*795d594fSAndroid Build Coastguard Worker struct CmdlineParseArgument : CmdlineParseArgumentAny {
CmdlineParseArgumentCmdlineParseArgument358*795d594fSAndroid Build Coastguard Worker   CmdlineParseArgument(CmdlineParserArgumentInfo<TArg>&& argument_info,
359*795d594fSAndroid Build Coastguard Worker                        std::function<void(TArg&)>&& save_argument,
360*795d594fSAndroid Build Coastguard Worker                        std::function<TArg&(void)>&& load_argument)
361*795d594fSAndroid Build Coastguard Worker       : argument_info_(std::forward<decltype(argument_info)>(argument_info)),
362*795d594fSAndroid Build Coastguard Worker         save_argument_(std::forward<decltype(save_argument)>(save_argument)),
363*795d594fSAndroid Build Coastguard Worker         load_argument_(std::forward<decltype(load_argument)>(load_argument)) {
364*795d594fSAndroid Build Coastguard Worker   }
365*795d594fSAndroid Build Coastguard Worker 
366*795d594fSAndroid Build Coastguard Worker   using UserTypeInfo = CmdlineType<TArg>;
367*795d594fSAndroid Build Coastguard Worker 
ParseArgumentCmdlineParseArgument368*795d594fSAndroid Build Coastguard Worker   virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) {
369*795d594fSAndroid Build Coastguard Worker     assert(arguments.Size() > 0);
370*795d594fSAndroid Build Coastguard Worker     assert(consumed_tokens != nullptr);
371*795d594fSAndroid Build Coastguard Worker 
372*795d594fSAndroid Build Coastguard Worker     auto closest_match_res = argument_info_.FindClosestMatch(arguments);
373*795d594fSAndroid Build Coastguard Worker     size_t best_match_size = closest_match_res.second;
374*795d594fSAndroid Build Coastguard Worker     const TokenRange* best_match_arg_def = closest_match_res.first;
375*795d594fSAndroid Build Coastguard Worker 
376*795d594fSAndroid Build Coastguard Worker     if (best_match_size > arguments.Size()) {
377*795d594fSAndroid Build Coastguard Worker       // The best match has more tokens than were provided.
378*795d594fSAndroid Build Coastguard Worker       // Shouldn't happen in practice since the outer parser does this check.
379*795d594fSAndroid Build Coastguard Worker       return CmdlineResult(CmdlineResult::kUnknown, "Size mismatch");
380*795d594fSAndroid Build Coastguard Worker     }
381*795d594fSAndroid Build Coastguard Worker 
382*795d594fSAndroid Build Coastguard Worker     assert(best_match_arg_def != nullptr);
383*795d594fSAndroid Build Coastguard Worker     *consumed_tokens = best_match_arg_def->Size();
384*795d594fSAndroid Build Coastguard Worker 
385*795d594fSAndroid Build Coastguard Worker     if (!argument_info_.using_blanks_) {
386*795d594fSAndroid Build Coastguard Worker       return ParseArgumentSingle(arguments.Join(' '));
387*795d594fSAndroid Build Coastguard Worker     }
388*795d594fSAndroid Build Coastguard Worker 
389*795d594fSAndroid Build Coastguard Worker     // Extract out the blank value from arguments
390*795d594fSAndroid Build Coastguard Worker     // e.g. for a def of "foo:_" and input "foo:bar", blank_value == "bar"
391*795d594fSAndroid Build Coastguard Worker     std::string blank_value = "";
392*795d594fSAndroid Build Coastguard Worker     size_t idx = 0;
393*795d594fSAndroid Build Coastguard Worker     for (auto&& def_token : *best_match_arg_def) {
394*795d594fSAndroid Build Coastguard Worker       auto&& arg_token = arguments[idx];
395*795d594fSAndroid Build Coastguard Worker 
396*795d594fSAndroid Build Coastguard Worker       // Does this definition-token have a wildcard in it?
397*795d594fSAndroid Build Coastguard Worker       if (def_token.find('_') == std::string::npos) {
398*795d594fSAndroid Build Coastguard Worker         // No, regular token. Match 1:1 against the argument token.
399*795d594fSAndroid Build Coastguard Worker         bool token_match = def_token == arg_token;
400*795d594fSAndroid Build Coastguard Worker 
401*795d594fSAndroid Build Coastguard Worker         if (!token_match) {
402*795d594fSAndroid Build Coastguard Worker           return CmdlineResult(CmdlineResult::kFailure,
403*795d594fSAndroid Build Coastguard Worker                                std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
404*795d594fSAndroid Build Coastguard Worker                                + " at token " + std::to_string(idx));
405*795d594fSAndroid Build Coastguard Worker         }
406*795d594fSAndroid Build Coastguard Worker       } else {
407*795d594fSAndroid Build Coastguard Worker         // This is a wild-carded token.
408*795d594fSAndroid Build Coastguard Worker         TokenRange def_split_wildcards = TokenRange::Split(def_token, {'_'});
409*795d594fSAndroid Build Coastguard Worker 
410*795d594fSAndroid Build Coastguard Worker         // Extract the wildcard contents out of the user-provided arg_token.
411*795d594fSAndroid Build Coastguard Worker         std::unique_ptr<TokenRange> arg_matches =
412*795d594fSAndroid Build Coastguard Worker             def_split_wildcards.MatchSubstrings(arg_token, "_");
413*795d594fSAndroid Build Coastguard Worker         if (arg_matches == nullptr) {
414*795d594fSAndroid Build Coastguard Worker           return CmdlineResult(CmdlineResult::kFailure,
415*795d594fSAndroid Build Coastguard Worker                                std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
416*795d594fSAndroid Build Coastguard Worker                                + ", with a wildcard pattern " + def_token
417*795d594fSAndroid Build Coastguard Worker                                + " at token " + std::to_string(idx));
418*795d594fSAndroid Build Coastguard Worker         }
419*795d594fSAndroid Build Coastguard Worker 
420*795d594fSAndroid Build Coastguard Worker         // Get the corresponding wildcard tokens from arg_matches,
421*795d594fSAndroid Build Coastguard Worker         // and concatenate it to blank_value.
422*795d594fSAndroid Build Coastguard Worker         for (size_t sub_idx = 0;
423*795d594fSAndroid Build Coastguard Worker             sub_idx < def_split_wildcards.Size() && sub_idx < arg_matches->Size(); ++sub_idx) {
424*795d594fSAndroid Build Coastguard Worker           if (def_split_wildcards[sub_idx] == "_") {
425*795d594fSAndroid Build Coastguard Worker             blank_value += arg_matches->GetToken(sub_idx);
426*795d594fSAndroid Build Coastguard Worker           }
427*795d594fSAndroid Build Coastguard Worker         }
428*795d594fSAndroid Build Coastguard Worker       }
429*795d594fSAndroid Build Coastguard Worker 
430*795d594fSAndroid Build Coastguard Worker       ++idx;
431*795d594fSAndroid Build Coastguard Worker     }
432*795d594fSAndroid Build Coastguard Worker 
433*795d594fSAndroid Build Coastguard Worker     return ParseArgumentSingle(blank_value);
434*795d594fSAndroid Build Coastguard Worker   }
435*795d594fSAndroid Build Coastguard Worker 
DumpHelpCmdlineParseArgument436*795d594fSAndroid Build Coastguard Worker   virtual void DumpHelp(VariableIndentationOutputStream& os) {
437*795d594fSAndroid Build Coastguard Worker     argument_info_.DumpHelp(os);
438*795d594fSAndroid Build Coastguard Worker   }
439*795d594fSAndroid Build Coastguard Worker 
GetCategoryCmdlineParseArgument440*795d594fSAndroid Build Coastguard Worker   virtual const std::optional<const char*>& GetCategory() {
441*795d594fSAndroid Build Coastguard Worker     return argument_info_.category_;
442*795d594fSAndroid Build Coastguard Worker   }
443*795d594fSAndroid Build Coastguard Worker 
444*795d594fSAndroid Build Coastguard Worker  private:
ParseArgumentSingleCmdlineParseArgument445*795d594fSAndroid Build Coastguard Worker   virtual CmdlineResult ParseArgumentSingle(const std::string& argument) {
446*795d594fSAndroid Build Coastguard Worker     // TODO: refactor to use LookupValue for the value lists/maps
447*795d594fSAndroid Build Coastguard Worker 
448*795d594fSAndroid Build Coastguard Worker     // Handle the 'WithValueMap(...)' argument definition
449*795d594fSAndroid Build Coastguard Worker     if (argument_info_.has_value_map_) {
450*795d594fSAndroid Build Coastguard Worker       for (auto&& value_pair : argument_info_.value_map_) {
451*795d594fSAndroid Build Coastguard Worker         const char* name = value_pair.first;
452*795d594fSAndroid Build Coastguard Worker 
453*795d594fSAndroid Build Coastguard Worker         if (argument == name) {
454*795d594fSAndroid Build Coastguard Worker           return SaveArgument(value_pair.second);
455*795d594fSAndroid Build Coastguard Worker         }
456*795d594fSAndroid Build Coastguard Worker       }
457*795d594fSAndroid Build Coastguard Worker 
458*795d594fSAndroid Build Coastguard Worker       // Error case: Fail, telling the user what the allowed values were.
459*795d594fSAndroid Build Coastguard Worker       std::vector<std::string> allowed_values;
460*795d594fSAndroid Build Coastguard Worker       for (auto&& value_pair : argument_info_.value_map_) {
461*795d594fSAndroid Build Coastguard Worker         const char* name = value_pair.first;
462*795d594fSAndroid Build Coastguard Worker         allowed_values.push_back(name);
463*795d594fSAndroid Build Coastguard Worker       }
464*795d594fSAndroid Build Coastguard Worker 
465*795d594fSAndroid Build Coastguard Worker       std::string allowed_values_flat = android::base::Join(allowed_values, ',');
466*795d594fSAndroid Build Coastguard Worker       return CmdlineResult(CmdlineResult::kFailure,
467*795d594fSAndroid Build Coastguard Worker                            "Argument value '" + argument + "' does not match any of known valid "
468*795d594fSAndroid Build Coastguard Worker                             "values: {" + allowed_values_flat + "}");
469*795d594fSAndroid Build Coastguard Worker     }
470*795d594fSAndroid Build Coastguard Worker 
471*795d594fSAndroid Build Coastguard Worker     // Handle the 'WithValues(...)' argument definition
472*795d594fSAndroid Build Coastguard Worker     if (argument_info_.has_value_list_) {
473*795d594fSAndroid Build Coastguard Worker       size_t arg_def_idx = 0;
474*795d594fSAndroid Build Coastguard Worker       for (auto&& value : argument_info_.value_list_) {
475*795d594fSAndroid Build Coastguard Worker         auto&& arg_def_token = argument_info_.names_[arg_def_idx];
476*795d594fSAndroid Build Coastguard Worker 
477*795d594fSAndroid Build Coastguard Worker         if (arg_def_token == argument) {
478*795d594fSAndroid Build Coastguard Worker           return SaveArgument(value);
479*795d594fSAndroid Build Coastguard Worker         }
480*795d594fSAndroid Build Coastguard Worker         ++arg_def_idx;
481*795d594fSAndroid Build Coastguard Worker       }
482*795d594fSAndroid Build Coastguard Worker 
483*795d594fSAndroid Build Coastguard Worker       assert(arg_def_idx + 1 == argument_info_.value_list_.size() &&
484*795d594fSAndroid Build Coastguard Worker              "Number of named argument definitions must match number of values defined");
485*795d594fSAndroid Build Coastguard Worker 
486*795d594fSAndroid Build Coastguard Worker       // Error case: Fail, telling the user what the allowed values were.
487*795d594fSAndroid Build Coastguard Worker       std::vector<std::string> allowed_values;
488*795d594fSAndroid Build Coastguard Worker       allowed_values.reserve(argument_info_.names_.size());
489*795d594fSAndroid Build Coastguard Worker       for (auto&& arg_name : argument_info_.names_) {
490*795d594fSAndroid Build Coastguard Worker         allowed_values.push_back(arg_name);
491*795d594fSAndroid Build Coastguard Worker       }
492*795d594fSAndroid Build Coastguard Worker 
493*795d594fSAndroid Build Coastguard Worker       std::string allowed_values_flat = android::base::Join(allowed_values, ',');
494*795d594fSAndroid Build Coastguard Worker       return CmdlineResult(CmdlineResult::kFailure,
495*795d594fSAndroid Build Coastguard Worker                            "Argument value '" + argument + "' does not match any of known valid"
496*795d594fSAndroid Build Coastguard Worker                             "values: {" + allowed_values_flat + "}");
497*795d594fSAndroid Build Coastguard Worker     }
498*795d594fSAndroid Build Coastguard Worker 
499*795d594fSAndroid Build Coastguard Worker     // Handle the regular case where we parsed an unknown value from a blank.
500*795d594fSAndroid Build Coastguard Worker     UserTypeInfo type_parser;
501*795d594fSAndroid Build Coastguard Worker 
502*795d594fSAndroid Build Coastguard Worker     if (argument_info_.appending_values_) {
503*795d594fSAndroid Build Coastguard Worker       TArg& existing = load_argument_();
504*795d594fSAndroid Build Coastguard Worker       CmdlineParseResult<TArg> result = type_parser.ParseAndAppend(argument, existing);
505*795d594fSAndroid Build Coastguard Worker 
506*795d594fSAndroid Build Coastguard Worker       assert(!argument_info_.has_range_);
507*795d594fSAndroid Build Coastguard Worker 
508*795d594fSAndroid Build Coastguard Worker       return std::move(result);
509*795d594fSAndroid Build Coastguard Worker     }
510*795d594fSAndroid Build Coastguard Worker 
511*795d594fSAndroid Build Coastguard Worker     CmdlineParseResult<TArg> result = type_parser.Parse(argument);
512*795d594fSAndroid Build Coastguard Worker 
513*795d594fSAndroid Build Coastguard Worker     if (result.IsSuccess()) {
514*795d594fSAndroid Build Coastguard Worker       TArg& value = result.GetValue();
515*795d594fSAndroid Build Coastguard Worker 
516*795d594fSAndroid Build Coastguard Worker       // Do a range check for 'WithRange(min,max)' argument definition.
517*795d594fSAndroid Build Coastguard Worker       if (!argument_info_.CheckRange(value)) {
518*795d594fSAndroid Build Coastguard Worker         return CmdlineParseResult<TArg>::OutOfRange(
519*795d594fSAndroid Build Coastguard Worker             value, argument_info_.min_, argument_info_.max_);
520*795d594fSAndroid Build Coastguard Worker       }
521*795d594fSAndroid Build Coastguard Worker 
522*795d594fSAndroid Build Coastguard Worker       return SaveArgument(value);
523*795d594fSAndroid Build Coastguard Worker     }
524*795d594fSAndroid Build Coastguard Worker 
525*795d594fSAndroid Build Coastguard Worker     // Some kind of type-specific parse error. Pass the result as-is.
526*795d594fSAndroid Build Coastguard Worker     CmdlineResult raw_result = std::move(result);
527*795d594fSAndroid Build Coastguard Worker     return raw_result;
528*795d594fSAndroid Build Coastguard Worker   }
529*795d594fSAndroid Build Coastguard Worker 
530*795d594fSAndroid Build Coastguard Worker  public:
GetTypeNameCmdlineParseArgument531*795d594fSAndroid Build Coastguard Worker   virtual const char* GetTypeName() const {
532*795d594fSAndroid Build Coastguard Worker     // TODO: Obviate the need for each type specialization to hardcode the type name
533*795d594fSAndroid Build Coastguard Worker     return UserTypeInfo::Name();
534*795d594fSAndroid Build Coastguard Worker   }
535*795d594fSAndroid Build Coastguard Worker 
536*795d594fSAndroid Build Coastguard Worker   // How many tokens should be taken off argv for parsing this argument.
537*795d594fSAndroid Build Coastguard Worker   // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
538*795d594fSAndroid Build Coastguard Worker   //
539*795d594fSAndroid Build Coastguard Worker   // A [min,max] range is returned to represent argument definitions with multiple
540*795d594fSAndroid Build Coastguard Worker   // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
GetNumTokensCmdlineParseArgument541*795d594fSAndroid Build Coastguard Worker   virtual std::pair<size_t, size_t> GetNumTokens() const {
542*795d594fSAndroid Build Coastguard Worker     return argument_info_.token_range_size_;
543*795d594fSAndroid Build Coastguard Worker   }
544*795d594fSAndroid Build Coastguard Worker 
545*795d594fSAndroid Build Coastguard Worker   // See if this token range might begin the same as the argument definition.
MaybeMatchesCmdlineParseArgument546*795d594fSAndroid Build Coastguard Worker   virtual size_t MaybeMatches(const TokenRange& tokens) {
547*795d594fSAndroid Build Coastguard Worker     return argument_info_.MaybeMatches(tokens);
548*795d594fSAndroid Build Coastguard Worker   }
549*795d594fSAndroid Build Coastguard Worker 
550*795d594fSAndroid Build Coastguard Worker  private:
SaveArgumentCmdlineParseArgument551*795d594fSAndroid Build Coastguard Worker   CmdlineResult SaveArgument(const TArg& value) {
552*795d594fSAndroid Build Coastguard Worker     assert(!argument_info_.appending_values_
553*795d594fSAndroid Build Coastguard Worker            && "If the values are being appended, then the updated parse value is "
554*795d594fSAndroid Build Coastguard Worker                "updated by-ref as a side effect and shouldn't be stored directly");
555*795d594fSAndroid Build Coastguard Worker     TArg val = value;
556*795d594fSAndroid Build Coastguard Worker     save_argument_(val);
557*795d594fSAndroid Build Coastguard Worker     return CmdlineResult(CmdlineResult::kSuccess);
558*795d594fSAndroid Build Coastguard Worker   }
559*795d594fSAndroid Build Coastguard Worker 
560*795d594fSAndroid Build Coastguard Worker   CmdlineParserArgumentInfo<TArg> argument_info_;
561*795d594fSAndroid Build Coastguard Worker   std::function<void(TArg&)> save_argument_;
562*795d594fSAndroid Build Coastguard Worker   std::function<TArg&(void)> load_argument_;
563*795d594fSAndroid Build Coastguard Worker };
564*795d594fSAndroid Build Coastguard Worker }  // namespace detail  // NOLINT [readability/namespace] [5]
565*795d594fSAndroid Build Coastguard Worker }  // namespace art
566*795d594fSAndroid Build Coastguard Worker 
567*795d594fSAndroid Build Coastguard Worker #endif  // ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
568