Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Source/cmArgumentParser.h
4998 views
1
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2
file LICENSE.rst or https://cmake.org/licensing for details. */
3
#pragma once
4
5
#include "cmConfigure.h" // IWYU pragma: keep
6
7
#include <cassert>
8
#include <cstddef>
9
#include <functional>
10
#include <map>
11
#include <set>
12
#include <string>
13
#include <utility>
14
#include <vector>
15
16
#include <cm/optional>
17
#include <cm/string_view>
18
#include <cm/type_traits>
19
#include <cmext/string_view>
20
#include <cmext/type_traits>
21
22
#include "cmArgumentParserTypes.h" // IWYU pragma: keep
23
24
template <typename Result>
25
class cmArgumentParser; // IWYU pragma: keep
26
27
class cmExecutionStatus;
28
class cmMakefile;
29
30
namespace ArgumentParser {
31
32
class ParseResult
33
{
34
using KeywordErrorList = std::set<std::string>;
35
using KeywordErrorMap = std::map<cm::string_view, KeywordErrorList>;
36
37
KeywordErrorMap KeywordErrors;
38
39
public:
40
explicit operator bool() const { return this->KeywordErrors.empty(); }
41
42
void AddKeywordError(cm::string_view key, cm::string_view text)
43
44
{
45
this->KeywordErrors[key].emplace(text);
46
}
47
48
KeywordErrorMap const& GetKeywordErrors() const
49
{
50
return this->KeywordErrors;
51
}
52
53
bool MaybeReportError(cmMakefile& mf) const;
54
55
/// Check if argument parsing succeeded. Return \c false and set an error if
56
/// any errors were encountered, or if \p unparsedArguments is non-empty.
57
bool Check(cm::string_view context,
58
std::vector<std::string> const* unparsedArguments,
59
cmExecutionStatus& status) const;
60
};
61
62
template <typename Result>
63
typename std::enable_if<std::is_base_of<ParseResult, Result>::value,
64
ParseResult*>::type
65
AsParseResultPtr(Result& result)
66
{
67
return &result;
68
}
69
70
template <typename Result>
71
typename std::enable_if<!std::is_base_of<ParseResult, Result>::value,
72
ParseResult*>::type
73
AsParseResultPtr(Result&)
74
{
75
return nullptr;
76
}
77
78
enum class Continue
79
{
80
No,
81
Yes,
82
};
83
84
struct ExpectAtLeast
85
{
86
std::size_t Count = 0;
87
88
ExpectAtLeast(std::size_t count)
89
: Count(count)
90
{
91
}
92
};
93
94
class Instance;
95
using KeywordAction = std::function<void(Instance&)>;
96
using KeywordNameAction = std::function<void(Instance&, cm::string_view)>;
97
using PositionAction =
98
std::function<void(Instance&, std::size_t, cm::string_view)>;
99
100
// using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>;
101
class KeywordActionMap
102
: public std::vector<std::pair<cm::string_view, KeywordAction>>
103
{
104
public:
105
std::pair<iterator, bool> Emplace(cm::string_view name,
106
KeywordAction action);
107
const_iterator Find(cm::string_view name) const;
108
};
109
110
// using PositionActionMap = cm::flat_map<cm::string_view, PositionAction>;
111
class PositionActionMap
112
: public std::vector<std::pair<std::size_t, PositionAction>>
113
{
114
public:
115
std::pair<iterator, bool> Emplace(std::size_t pos, PositionAction action);
116
const_iterator Find(std::size_t pos) const;
117
};
118
119
class ActionMap
120
{
121
public:
122
KeywordActionMap Keywords;
123
KeywordNameAction KeywordMissingValue;
124
KeywordNameAction ParsedKeyword;
125
PositionActionMap Positions;
126
KeywordAction TrailingArgs;
127
};
128
129
class Base
130
{
131
public:
132
using ExpectAtLeast = ArgumentParser::ExpectAtLeast;
133
using Continue = ArgumentParser::Continue;
134
using Instance = ArgumentParser::Instance;
135
using ParseResult = ArgumentParser::ParseResult;
136
137
ArgumentParser::ActionMap Bindings;
138
139
bool MaybeBind(cm::string_view name, KeywordAction action)
140
{
141
return this->Bindings.Keywords.Emplace(name, std::move(action)).second;
142
}
143
144
void Bind(cm::string_view name, KeywordAction action)
145
{
146
bool const inserted = this->MaybeBind(name, std::move(action));
147
assert(inserted);
148
static_cast<void>(inserted);
149
}
150
151
void BindParsedKeyword(KeywordNameAction action)
152
{
153
assert(!this->Bindings.ParsedKeyword);
154
this->Bindings.ParsedKeyword = std::move(action);
155
}
156
157
void BindKeywordMissingValue(KeywordNameAction action)
158
{
159
assert(!this->Bindings.KeywordMissingValue);
160
this->Bindings.KeywordMissingValue = std::move(action);
161
}
162
163
void BindTrailingArgs(KeywordAction action)
164
{
165
assert(!this->Bindings.TrailingArgs);
166
this->Bindings.TrailingArgs = std::move(action);
167
}
168
169
void Bind(std::size_t pos, PositionAction action)
170
{
171
bool const inserted =
172
this->Bindings.Positions.Emplace(pos, std::move(action)).second;
173
assert(inserted);
174
static_cast<void>(inserted);
175
}
176
};
177
178
class ParserState
179
{
180
public:
181
cm::string_view Keyword;
182
std::size_t Pos = 0;
183
std::size_t KeywordValuesSeen = 0;
184
std::size_t KeywordValuesExpected = 0;
185
std::function<Continue(cm::string_view)> KeywordValueFunc = nullptr;
186
187
ActionMap const& Bindings;
188
void* Result = nullptr;
189
bool DoneWithPositional = false;
190
191
ParserState(ActionMap const& bindings, void* result)
192
: Bindings(bindings)
193
, Result(result)
194
{
195
}
196
};
197
198
class Instance
199
{
200
public:
201
Instance(ActionMap const& bindings, ParseResult* parseResult,
202
std::vector<std::string>* unparsedArguments, void* result = nullptr)
203
: ParseResults(parseResult)
204
, UnparsedArguments(unparsedArguments)
205
{
206
PushState(bindings, result);
207
}
208
209
ParserState& GetState() { return this->Stack.back(); }
210
211
void PushState(ActionMap const& bindings, void* result)
212
{
213
this->Stack.emplace_back(bindings, result);
214
}
215
216
void PopState()
217
{
218
if (!this->Stack.empty()) {
219
this->Stack.pop_back();
220
}
221
}
222
223
void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect);
224
void Bind(bool& val);
225
void Bind(std::string& val);
226
void Bind(MaybeEmpty<std::string>& val);
227
void Bind(NonEmpty<std::string>& val);
228
void Bind(Maybe<std::string>& val);
229
void Bind(MaybeEmpty<std::vector<std::string>>& val);
230
void Bind(NonEmpty<std::vector<std::string>>& val);
231
void Bind(std::vector<std::vector<std::string>>& val);
232
233
template <typename U>
234
void Bind(NonEmpty<std::vector<std::pair<std::string, U>>>& val,
235
U const& context)
236
{
237
this->Bind(
238
[&val, &context](cm::string_view arg) -> Continue {
239
val.emplace_back(std::string(arg), context);
240
return Continue::Yes;
241
},
242
ExpectAtLeast{ 1 });
243
}
244
245
// cm::optional<> records the presence the keyword to which it binds.
246
template <typename T>
247
void Bind(cm::optional<T>& optVal)
248
{
249
if (!optVal) {
250
optVal.emplace();
251
}
252
this->Bind(*optVal);
253
}
254
255
template <typename T, typename U>
256
void Bind(cm::optional<T>& optVal, U const& context)
257
{
258
if (!optVal) {
259
optVal.emplace();
260
}
261
this->Bind(*optVal, context);
262
}
263
264
template <typename Range>
265
void Parse(Range& args, std::size_t const pos = 0)
266
{
267
GetState().Pos = pos;
268
for (cm::string_view arg : args) {
269
for (size_t j = 0; j < FindKeywordOwnerLevel(arg); ++j) {
270
this->PopState();
271
}
272
273
this->Consume(arg);
274
GetState().Pos++;
275
}
276
277
this->FinishKeyword();
278
279
while (this->Stack.size() > 1) {
280
this->FinishKeyword();
281
this->PopState();
282
}
283
}
284
285
std::size_t FindKeywordOwnerLevel(cm::string_view arg) const
286
{
287
for (std::size_t i = Stack.size(); i--;) {
288
if (this->Stack[i].Bindings.Keywords.Find(arg) !=
289
this->Stack[i].Bindings.Keywords.end()) {
290
return (this->Stack.size() - 1) - i;
291
}
292
}
293
return 0;
294
}
295
296
private:
297
std::vector<ParserState> Stack;
298
ParseResult* ParseResults = nullptr;
299
std::vector<std::string>* UnparsedArguments = nullptr;
300
301
void Consume(cm::string_view arg);
302
void FinishKeyword();
303
304
template <typename Result>
305
friend class ::cmArgumentParser;
306
};
307
308
} // namespace ArgumentParser
309
310
template <typename Result>
311
class cmArgumentParser : private ArgumentParser::Base
312
{
313
public:
314
using Base::Bindings;
315
316
// I *think* this function could be made `constexpr` when the code is
317
// compiled as C++20. This would allow building a parser at compile time.
318
template <typename T, typename cT = cm::member_pointer_class_t<T>,
319
typename mT = cm::remove_member_pointer_t<T>,
320
typename = cm::enable_if_t<std::is_base_of<cT, Result>::value>,
321
typename = cm::enable_if_t<!std::is_function<mT>::value>>
322
cmArgumentParser& Bind(cm::static_string_view name, T member)
323
{
324
this->Base::Bind(name, [member](Instance& instance) {
325
instance.Bind(static_cast<Result*>(instance.GetState().Result)->*member);
326
});
327
return *this;
328
}
329
330
template <typename T, typename U>
331
cmArgumentParser& BindWithContext(cm::static_string_view name,
332
T Result::*member, U Result::*context)
333
{
334
this->Base::Bind(name, [member, context](Instance& instance) {
335
auto* result = static_cast<Result*>(instance.GetState().Result);
336
instance.Bind(result->*member, result->*context);
337
});
338
return *this;
339
}
340
341
cmArgumentParser& Bind(cm::static_string_view name,
342
Continue (Result::*member)(cm::string_view),
343
ExpectAtLeast expect = { 1 })
344
{
345
this->Base::Bind(name, [member, expect](Instance& instance) {
346
Result* result = static_cast<Result*>(instance.GetState().Result);
347
instance.Bind(
348
[result, member](cm::string_view arg) -> Continue {
349
return (result->*member)(arg);
350
},
351
expect);
352
});
353
return *this;
354
}
355
356
cmArgumentParser& Bind(cm::static_string_view name,
357
Continue (Result::*member)(cm::string_view,
358
cm::string_view),
359
ExpectAtLeast expect = { 1 })
360
{
361
this->Base::Bind(name, [member, expect](Instance& instance) {
362
Result* result = static_cast<Result*>(instance.GetState().Result);
363
cm::string_view keyword = instance.GetState().Keyword;
364
instance.Bind(
365
[result, member, keyword](cm::string_view arg) -> Continue {
366
return (result->*member)(keyword, arg);
367
},
368
expect);
369
});
370
return *this;
371
}
372
373
cmArgumentParser& Bind(cm::static_string_view name,
374
std::function<Continue(Result&, cm::string_view)> f,
375
ExpectAtLeast expect = { 1 })
376
{
377
this->Base::Bind(name, [f, expect](Instance& instance) {
378
Result* result = static_cast<Result*>(instance.GetState().Result);
379
instance.Bind(
380
[result, &f](cm::string_view arg) -> Continue {
381
return f(*result, arg);
382
},
383
expect);
384
});
385
return *this;
386
}
387
388
cmArgumentParser& Bind(
389
cm::static_string_view name,
390
std::function<Continue(Result&, cm::string_view, cm::string_view)> f,
391
ExpectAtLeast expect = { 1 })
392
{
393
this->Base::Bind(name, [f, expect](Instance& instance) {
394
Result* result = static_cast<Result*>(instance.GetState().Result);
395
cm::string_view keyword = instance.GetState().Keyword;
396
instance.Bind(
397
[result, keyword, &f](cm::string_view arg) -> Continue {
398
return f(*result, keyword, arg);
399
},
400
expect);
401
});
402
return *this;
403
}
404
405
cmArgumentParser& Bind(std::size_t position,
406
cm::optional<std::string> Result::*member)
407
{
408
this->Base::Bind(
409
position,
410
[member](Instance& instance, std::size_t, cm::string_view arg) {
411
Result* result = static_cast<Result*>(instance.GetState().Result);
412
result->*member = arg;
413
});
414
return *this;
415
}
416
417
cmArgumentParser& BindParsedKeywords(
418
std::vector<cm::string_view> Result::*member)
419
{
420
this->Base::BindParsedKeyword(
421
[member](Instance& instance, cm::string_view arg) {
422
(static_cast<Result*>(instance.GetState().Result)->*member)
423
.emplace_back(arg);
424
});
425
return *this;
426
}
427
428
template <typename T, typename cT = cm::member_pointer_class_t<T>,
429
typename mT = cm::remove_member_pointer_t<T>,
430
typename = cm::enable_if_t<std::is_base_of<cT, Result>::value>,
431
typename = cm::enable_if_t<!std::is_function<mT>::value>>
432
cmArgumentParser& BindTrailingArgs(T member)
433
{
434
this->Base::BindTrailingArgs([member](Instance& instance) {
435
instance.Bind(static_cast<Result*>(instance.GetState().Result)->*member);
436
});
437
return *this;
438
}
439
440
template <typename T, typename U>
441
cmArgumentParser& BindSubParser(cm::static_string_view name,
442
cmArgumentParser<T>& parser,
443
cm::optional<T> U::*member)
444
{
445
446
this->Base::Bind(name, [name, parser, member](Instance& instance) {
447
auto* parentResult = static_cast<Result*>(instance.GetState().Result);
448
auto& childResult = parentResult->*member;
449
childResult.emplace(T());
450
instance.Bind(nullptr, ExpectAtLeast{ 0 });
451
instance.PushState(parser.Bindings, &(*childResult));
452
instance.Consume(name);
453
});
454
455
return *this;
456
}
457
458
template <typename T, typename U>
459
cmArgumentParser& BindSubParser(cm::static_string_view name,
460
cmArgumentParser<T>& parser, T U::*member)
461
{
462
this->Base::Bind(name, [name, parser, member](Instance& instance) {
463
auto* parentResult = static_cast<Result*>(instance.GetState().Result);
464
auto* childResult = &(parentResult->*member);
465
instance.Bind(nullptr, ExpectAtLeast{ 1 });
466
instance.PushState(parser.Bindings, childResult);
467
instance.Consume(name);
468
});
469
470
return *this;
471
}
472
473
template <typename Range>
474
bool Parse(Result& result, Range const& args,
475
std::vector<std::string>* unparsedArguments,
476
std::size_t pos = 0) const
477
{
478
using ArgumentParser::AsParseResultPtr;
479
ParseResult* parseResultPtr = AsParseResultPtr(result);
480
Instance instance(this->Bindings, parseResultPtr, unparsedArguments,
481
&result);
482
instance.Parse(args, pos);
483
return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true;
484
}
485
486
template <typename Range>
487
Result Parse(Range const& args, std::vector<std::string>* unparsedArguments,
488
std::size_t pos = 0) const
489
{
490
Result result;
491
this->Parse(result, args, unparsedArguments, pos);
492
return result;
493
}
494
495
bool HasKeyword(cm::string_view key) const
496
{
497
return this->Bindings.Keywords.Find(key) != this->Bindings.Keywords.end();
498
}
499
};
500
501
template <>
502
class cmArgumentParser<void> : private ArgumentParser::Base
503
{
504
public:
505
using Base::Bindings;
506
507
template <typename T>
508
cmArgumentParser& Bind(cm::static_string_view name, T& ref)
509
{
510
this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); });
511
return *this;
512
}
513
514
cmArgumentParser& Bind(cm::static_string_view name,
515
std::function<Continue(cm::string_view)> f,
516
ExpectAtLeast expect = { 1 })
517
{
518
this->Base::Bind(name, [f, expect](Instance& instance) {
519
instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); },
520
expect);
521
});
522
return *this;
523
}
524
525
cmArgumentParser& Bind(
526
cm::static_string_view name,
527
std::function<Continue(cm::string_view, cm::string_view)> f,
528
ExpectAtLeast expect = { 1 })
529
{
530
this->Base::Bind(name, [f, expect](Instance& instance) {
531
cm::string_view keyword = instance.GetState().Keyword;
532
instance.Bind(
533
[keyword, &f](cm::string_view arg) -> Continue {
534
return f(keyword, arg);
535
},
536
expect);
537
});
538
return *this;
539
}
540
541
cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref)
542
{
543
this->Base::Bind(position,
544
[&ref](Instance&, std::size_t, cm::string_view arg) {
545
ref = std::string(arg);
546
});
547
return *this;
548
}
549
550
cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref)
551
{
552
this->Base::BindParsedKeyword(
553
[&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
554
return *this;
555
}
556
557
template <typename T>
558
cmArgumentParser& BindTrailingArgs(T& ref)
559
{
560
this->Base::BindTrailingArgs(
561
[&ref](Instance& instance) { instance.Bind(ref); });
562
return *this;
563
}
564
565
template <typename T, typename U>
566
cmArgumentParser& BindSubParser(cm::static_string_view name,
567
cmArgumentParser<T>& parser,
568
cm::optional<U>& subResult)
569
{
570
this->Base::Bind(name, [name, parser, &subResult](Instance& instance) {
571
subResult.emplace(U());
572
instance.Bind(nullptr, ExpectAtLeast{ 0 });
573
instance.PushState(parser.Bindings, &(*subResult));
574
instance.Consume(name);
575
});
576
return *this;
577
}
578
579
template <typename T, typename U>
580
cmArgumentParser& BindSubParser(cm::static_string_view name,
581
cmArgumentParser<T>& parser, U& subResult)
582
{
583
this->Base::Bind(name, [name, parser, &subResult](Instance& instance) {
584
instance.Bind(nullptr, ExpectAtLeast{ 1 });
585
instance.PushState(parser.Bindings, &subResult);
586
instance.Consume(name);
587
});
588
return *this;
589
}
590
591
template <typename Range>
592
ParseResult Parse(Range const& args,
593
std::vector<std::string>* unparsedArguments,
594
std::size_t pos = 0) const
595
{
596
ParseResult parseResult;
597
Instance instance(this->Bindings, &parseResult, unparsedArguments);
598
instance.Parse(args, pos);
599
return parseResult;
600
}
601
602
bool HasKeyword(cm::string_view key) const
603
{
604
return this->Bindings.Keywords.Find(key) != this->Bindings.Keywords.end();
605
}
606
607
protected:
608
using Base::Instance;
609
using Base::BindKeywordMissingValue;
610
611
template <typename T>
612
bool Bind(cm::string_view name, T& ref)
613
{
614
return this->MaybeBind(name,
615
[&ref](Instance& instance) { instance.Bind(ref); });
616
}
617
};
618
619