1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
5 #include "cmConfigure.h" // IWYU pragma: keep
15 #include <cm/optional>
16 #include <cm/string_view>
17 #include <cm/type_traits>
18 #include <cmext/string_view>
20 #include "cmArgumentParserTypes.h" // IWYU pragma: keep
22 template <typename Result>
23 class cmArgumentParser; // IWYU pragma: keep
27 namespace ArgumentParser {
31 std::map<cm::string_view, std::string> KeywordErrors;
34 explicit operator bool() const { return this->KeywordErrors.empty(); }
36 void AddKeywordError(cm::string_view key, cm::string_view text)
39 this->KeywordErrors[key] += text;
42 std::map<cm::string_view, std::string> const& GetKeywordErrors() const
44 return this->KeywordErrors;
47 bool MaybeReportError(cmMakefile& mf) const;
50 template <typename Result>
51 typename std::enable_if<std::is_base_of<ParseResult, Result>::value,
53 AsParseResultPtr(Result& result)
58 template <typename Result>
59 typename std::enable_if<!std::is_base_of<ParseResult, Result>::value,
61 AsParseResultPtr(Result&)
74 std::size_t Count = 0;
76 ExpectAtLeast(std::size_t count)
83 using KeywordAction = std::function<void(Instance&)>;
84 using KeywordNameAction = std::function<void(Instance&, cm::string_view)>;
85 using PositionAction =
86 std::function<void(Instance&, std::size_t, cm::string_view)>;
88 // using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>;
89 class KeywordActionMap
90 : public std::vector<std::pair<cm::string_view, KeywordAction>>
93 std::pair<iterator, bool> Emplace(cm::string_view name,
94 KeywordAction action);
95 const_iterator Find(cm::string_view name) const;
98 // using PositionActionMap = cm::flat_map<cm::string_view, PositionAction>;
99 class PositionActionMap
100 : public std::vector<std::pair<std::size_t, PositionAction>>
103 std::pair<iterator, bool> Emplace(std::size_t pos, PositionAction action);
104 const_iterator Find(std::size_t pos) const;
110 KeywordActionMap Keywords;
111 KeywordNameAction KeywordMissingValue;
112 KeywordNameAction ParsedKeyword;
113 PositionActionMap Positions;
119 using ExpectAtLeast = ArgumentParser::ExpectAtLeast;
120 using Continue = ArgumentParser::Continue;
121 using Instance = ArgumentParser::Instance;
122 using ParseResult = ArgumentParser::ParseResult;
124 ArgumentParser::ActionMap Bindings;
126 bool MaybeBind(cm::string_view name, KeywordAction action)
128 return this->Bindings.Keywords.Emplace(name, std::move(action)).second;
131 void Bind(cm::string_view name, KeywordAction action)
133 bool const inserted = this->MaybeBind(name, std::move(action));
135 static_cast<void>(inserted);
138 void BindParsedKeyword(KeywordNameAction action)
140 assert(!this->Bindings.ParsedKeyword);
141 this->Bindings.ParsedKeyword = std::move(action);
144 void BindKeywordMissingValue(KeywordNameAction action)
146 assert(!this->Bindings.KeywordMissingValue);
147 this->Bindings.KeywordMissingValue = std::move(action);
150 void Bind(std::size_t pos, PositionAction action)
152 bool const inserted =
153 this->Bindings.Positions.Emplace(pos, std::move(action)).second;
155 static_cast<void>(inserted);
162 Instance(ActionMap const& bindings, ParseResult* parseResult,
163 std::vector<std::string>* unparsedArguments, void* result = nullptr)
165 , ParseResults(parseResult)
166 , UnparsedArguments(unparsedArguments)
171 void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect);
172 void Bind(bool& val);
173 void Bind(std::string& val);
174 void Bind(NonEmpty<std::string>& val);
175 void Bind(Maybe<std::string>& val);
176 void Bind(MaybeEmpty<std::vector<std::string>>& val);
177 void Bind(NonEmpty<std::vector<std::string>>& val);
178 void Bind(std::vector<std::vector<std::string>>& val);
180 // cm::optional<> records the presence the keyword to which it binds.
181 template <typename T>
182 void Bind(cm::optional<T>& optVal)
190 template <typename Range>
191 void Parse(Range const& args, std::size_t pos = 0)
193 for (cm::string_view arg : args) {
194 this->Consume(pos++, arg);
196 this->FinishKeyword();
200 ActionMap const& Bindings;
201 ParseResult* ParseResults = nullptr;
202 std::vector<std::string>* UnparsedArguments = nullptr;
203 void* Result = nullptr;
205 cm::string_view Keyword;
206 std::size_t KeywordValuesSeen = 0;
207 std::size_t KeywordValuesExpected = 0;
208 std::function<Continue(cm::string_view)> KeywordValueFunc;
209 bool DoneWithPositional = false;
211 void Consume(std::size_t pos, cm::string_view arg);
212 void FinishKeyword();
214 template <typename Result>
215 friend class ::cmArgumentParser;
218 } // namespace ArgumentParser
220 template <typename Result>
221 class cmArgumentParser : private ArgumentParser::Base
224 // I *think* this function could be made `constexpr` when the code is
225 // compiled as C++20. This would allow building a parser at compile time.
226 template <typename T>
227 cmArgumentParser& Bind(cm::static_string_view name, T Result::*member)
229 this->Base::Bind(name, [member](Instance& instance) {
230 instance.Bind(static_cast<Result*>(instance.Result)->*member);
235 cmArgumentParser& Bind(cm::static_string_view name,
236 Continue (Result::*member)(cm::string_view),
237 ExpectAtLeast expect = { 1 })
239 this->Base::Bind(name, [member, expect](Instance& instance) {
240 Result* result = static_cast<Result*>(instance.Result);
242 [result, member](cm::string_view arg) -> Continue {
243 return (result->*member)(arg);
250 cmArgumentParser& Bind(cm::static_string_view name,
251 Continue (Result::*member)(cm::string_view,
253 ExpectAtLeast expect = { 1 })
255 this->Base::Bind(name, [member, expect](Instance& instance) {
256 Result* result = static_cast<Result*>(instance.Result);
257 cm::string_view keyword = instance.Keyword;
259 [result, member, keyword](cm::string_view arg) -> Continue {
260 return (result->*member)(keyword, arg);
267 cmArgumentParser& Bind(cm::static_string_view name,
268 std::function<Continue(Result&, cm::string_view)> f,
269 ExpectAtLeast expect = { 1 })
271 this->Base::Bind(name, [f, expect](Instance& instance) {
272 Result* result = static_cast<Result*>(instance.Result);
274 [result, &f](cm::string_view arg) -> Continue {
275 return f(*result, arg);
282 cmArgumentParser& Bind(
283 cm::static_string_view name,
284 std::function<Continue(Result&, cm::string_view, cm::string_view)> f,
285 ExpectAtLeast expect = { 1 })
287 this->Base::Bind(name, [f, expect](Instance& instance) {
288 Result* result = static_cast<Result*>(instance.Result);
289 cm::string_view keyword = instance.Keyword;
291 [result, keyword, &f](cm::string_view arg) -> Continue {
292 return f(*result, keyword, arg);
299 cmArgumentParser& Bind(std::size_t position,
300 cm::optional<std::string> Result::*member)
304 [member](Instance& instance, std::size_t, cm::string_view arg) {
305 Result* result = static_cast<Result*>(instance.Result);
306 result->*member = arg;
311 cmArgumentParser& BindParsedKeywords(
312 std::vector<cm::string_view> Result::*member)
314 this->Base::BindParsedKeyword(
315 [member](Instance& instance, cm::string_view arg) {
316 (static_cast<Result*>(instance.Result)->*member).emplace_back(arg);
321 template <typename Range>
322 bool Parse(Result& result, Range const& args,
323 std::vector<std::string>* unparsedArguments,
324 std::size_t pos = 0) const
326 using ArgumentParser::AsParseResultPtr;
327 ParseResult* parseResultPtr = AsParseResultPtr(result);
328 Instance instance(this->Bindings, parseResultPtr, unparsedArguments,
330 instance.Parse(args, pos);
331 return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true;
334 template <typename Range>
335 Result Parse(Range const& args, std::vector<std::string>* unparsedArguments,
336 std::size_t pos = 0) const
339 this->Parse(result, args, unparsedArguments, pos);
345 class cmArgumentParser<void> : private ArgumentParser::Base
348 template <typename T>
349 cmArgumentParser& Bind(cm::static_string_view name, T& ref)
351 this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); });
355 cmArgumentParser& Bind(cm::static_string_view name,
356 std::function<Continue(cm::string_view)> f,
357 ExpectAtLeast expect = { 1 })
359 this->Base::Bind(name, [f, expect](Instance& instance) {
360 instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); },
366 cmArgumentParser& Bind(
367 cm::static_string_view name,
368 std::function<Continue(cm::string_view, cm::string_view)> f,
369 ExpectAtLeast expect = { 1 })
371 this->Base::Bind(name, [f, expect](Instance& instance) {
372 cm::string_view keyword = instance.Keyword;
374 [keyword, &f](cm::string_view arg) -> Continue {
375 return f(keyword, arg);
382 cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref)
384 this->Base::Bind(position,
385 [&ref](Instance&, std::size_t, cm::string_view arg) {
386 ref = std::string(arg);
391 cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref)
393 this->Base::BindParsedKeyword(
394 [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
398 template <typename Range>
399 ParseResult Parse(Range const& args,
400 std::vector<std::string>* unparsedArguments,
401 std::size_t pos = 0) const
403 ParseResult parseResult;
404 Instance instance(this->Bindings, &parseResult, unparsedArguments);
405 instance.Parse(args, pos);
410 using Base::Instance;
411 using Base::BindKeywordMissingValue;
413 template <typename T>
414 bool Bind(cm::string_view name, T& ref)
416 return this->MaybeBind(name,
417 [&ref](Instance& instance) { instance.Bind(ref); });