Unravel Engine C++ Reference
Loading...
Searching...
No Matches
parser.cpp
Go to the documentation of this file.
1#include "parser.h"
2
3namespace cmd_line
4{
5
6auto detail::get_internal_id() -> uint64_t
7{
8 static std::uint64_t id = 0;
9 return id++;
10}
11
12parser::cmd_base::cmd_base(const std::string& name,
13 const std::string& alternative,
14 const std::string& description,
15 bool required,
16 bool dominant,
17 bool is_variadic)
18 : name(name)
19 , command(!name.empty() ? "-" + name : "")
20 , alternative(!alternative.empty() ? "--" + alternative : "")
21 , description(description)
22 , required(required)
23 , handled(false)
24 , arguments({})
25 , dominant(dominant)
26 , variadic(is_variadic)
27
28{
29}
30
31parser::cmd_base::~cmd_base() = default;
32
33auto parser::cmd_base::is(const std::string& given) const -> bool
34{
35 return given == command || given == alternative;
36}
37
38auto parser::parse(const std::vector<std::string>& elements, const int&, int numberBase) -> int
39{
40 if(elements.size() != 1)
41 {
42 throw std::bad_cast();
43 }
44
45 return std::stoi(elements[0], nullptr, numberBase);
46}
47
48auto parser::parse(const std::vector<std::string>& elements, const bool& defval) -> bool
49{
50 if(!elements.empty())
51 {
52 throw std::runtime_error("A boolean command line parameter cannot have any arguments.");
53 }
54
55 return !defval;
56}
57
58auto parser::parse(const std::vector<std::string>& elements, const double&) -> double
59{
60 if(elements.size() != 1)
61 {
62 throw std::bad_cast();
63 }
64
65 return std::stod(elements[0]);
66}
67
68auto parser::parse(const std::vector<std::string>& elements, const float&) -> float
69{
70 if(elements.size() != 1)
71 {
72 throw std::bad_cast();
73 }
74
75 return std::stof(elements[0]);
76}
77
78auto parser::parse(const std::vector<std::string>& elements, const long double&) -> long double
79{
80 if(elements.size() != 1)
81 {
82 throw std::bad_cast();
83 }
84
85 return std::stold(elements[0]);
86}
87
88auto parser::parse(const std::vector<std::string>& elements, const unsigned int&, int numberBase) -> unsigned int
89{
90 if(elements.size() != 1)
91 {
92 throw std::bad_cast();
93 }
94
95 return static_cast<unsigned int>(std::stoul(elements[0], nullptr, numberBase));
96}
97
98auto parser::parse(const std::vector<std::string>& elements, const unsigned long&, int numberBase) -> unsigned long
99{
100 if(elements.size() != 1)
101 {
102 throw std::bad_cast();
103 }
104
105 return std::stoul(elements[0], nullptr, numberBase);
106}
107
108auto parser::parse(const std::vector<std::string>& elements, const long&) -> long
109{
110 if(elements.size() != 1)
111 {
112 throw std::bad_cast();
113 }
114
115 return std::stol(elements[0]);
116}
117
118auto parser::parse(const std::vector<std::string>& elements, const std::string&) -> std::string
119{
120 if(elements.size() != 1)
121 {
122 throw std::bad_cast();
123 }
124
125 return elements[0];
126}
127
128auto parser::stringify(const std::string& str) -> std::string
129{
130 return str;
131}
132
133parser::parser(int argc, const char** argv) : appname_(argv[0])
134{
135 for(int i = 1; i < argc; ++i)
136 {
137 arguments_.push_back(argv[i]);
138 }
139 enable_help();
140}
141
142parser::parser(int argc, char** argv) : appname_(argv[0])
143{
144 for(int i = 1; i < argc; ++i)
145 {
146 arguments_.push_back(argv[i]);
147 }
148 enable_help();
149}
150
151auto parser::has_help() const -> bool
152{
153 for(const auto& command_pair : commands_)
154 {
155 const auto& command = command_pair.second;
156 if(command->name == "h" && command->alternative == "--help")
157 {
158 return true;
159 }
160 }
161
162 return false;
163}
164
166{
167 set_callback("h",
168 "help",
169 std::function<bool(callback_args&)>(
170 [this](callback_args& args)
171 {
172 args.output << this->usage();
173
174 return false;
175 }),
176 "",
177 true);
178}
179
181{
182 for(auto command = commands_.begin(); command != commands_.end(); ++command)
183 {
184 if((*command).second->name == "h" && (*command).second->alternative == "--help")
185 {
186 commands_.erase(command);
187 break;
188 }
189 }
190}
191
193{
194 commands_.clear();
195 commands_.shrink_to_fit();
196}
197
199{
200 if(!run())
201 {
202 exit(1);
203 }
204}
205
206auto parser::run() -> bool
207{
208 return run(std::cout, std::cerr);
209}
210
211auto parser::run(std::ostream& output) -> bool
212{
213 return run(output, std::cerr);
214}
215
216auto parser::run(std::ostream& output, std::ostream& error) -> bool
217{
218 if(!arguments_.empty())
219 {
220 auto current = find_default();
221
222 for(const auto& arg : arguments_)
223 {
224 auto isarg = !arg.empty() && arg[0] == '-';
225 auto associated = isarg ? find(arg) : nullptr;
226
227 if(associated != nullptr)
228 {
229 current = associated;
230 associated->handled = true;
231 }
232 else if(current == nullptr)
233 {
234 //error << no_default();
235 //return false;
236 continue;
237 }
238 else
239 {
240 current->arguments.push_back(arg);
241 current->handled = true;
242 if(!current->variadic)
243 {
244 // If the current command is not variadic, then no more arguments
245 // should be added to it. In this case, switch back to the default
246 // command.
247 current = find_default();
248 }
249 }
250 }
251 }
252
253 // First, parse dominant arguments since they succeed even if required
254 // arguments are missing.
255 for(auto& command_pair : commands_)
256 {
257 auto& command = command_pair.second;
258 if(command->handled && command->dominant && !command->parse(output, error))
259 {
260 error << howto_use(command);
261 return false;
262 }
263 }
264
265 // Next, check for any missing arguments.
266 for(const auto& command_pair : commands_)
267 {
268 const auto& command = command_pair.second;
269 if(command->required && !command->handled)
270 {
271 error << howto_required(command);
272 return false;
273 }
274 }
275
276 // Finally, parse all remaining arguments.
277 for(auto& command_pair : commands_)
278 {
279 auto& command = command_pair.second;
280 if(command->handled && !command->dominant && !command->parse(output, error))
281 {
282 error << howto_use(command);
283 return false;
284 }
285 }
286
287 return true;
288}
289
290auto parser::requirements() const -> int
291{
292 int count = 0;
293
294 for(const auto& command_pair : commands_)
295 {
296 const auto& command = command_pair.second;
297 if(command->required)
298 {
299 ++count;
300 }
301 }
302
303 return count;
304}
305
306auto parser::commands() const -> int
307{
308 return static_cast<int>(commands_.size());
309}
310
311auto parser::app_name() const -> const std::string&
312{
313 return appname_;
314}
315
316auto parser::usage() const -> std::string
317{
318 std::stringstream ss{};
319 ss << "Available parameters:\n\n";
320
321 for(const auto& command_pair : commands_)
322 {
323 const auto& command = command_pair.second;
324
325 ss << " " << command->command << "\t" << command->alternative;
326
327 if(command->required)
328 {
329 ss << "\t(required)";
330 }
331
332 ss << "\n " << command->description;
333
334 if(!command->required)
335 {
336 ss << "\n "
337 << "This parameter is optional. The default value is '" + command->print_value() << "'.";
338 }
339
340 ss << "\n\n";
341 }
342
343 return ss.str();
344}
345
346void parser::print_help(std::stringstream& ss) const
347{
348 if(has_help())
349 {
350 ss << "For more help use --help or -h.\n";
351 }
352}
353
354auto parser::howto_required(const std::unique_ptr<cmd_base>& command) const -> std::string
355{
356 std::stringstream ss{};
357 ss << "The parameter " << command->name << " is required.\n";
358 ss << command->description << '\n';
359 print_help(ss);
360 return ss.str();
361}
362
363auto parser::howto_use(const std::unique_ptr<cmd_base>& command) const -> std::string
364{
365 std::stringstream ss{};
366 ss << "The parameter " << command->name << " has invalid arguments.\n";
367 ss << command->description << '\n';
368 print_help(ss);
369 return ss.str();
370}
371
372auto parser::no_default() const -> std::string
373{
374 std::stringstream ss{};
375 ss << "No default parameter has been specified.\n";
376 ss << "The given argument must be used with a parameter.\n";
377 print_help(ss);
378 return ss.str();
379}
380
381auto parser::find_default() -> parser::cmd_base*
382{
383 for(auto& command_pair : commands_)
384 {
385 auto& command = command_pair.second;
386 if(command->name.empty())
387 {
388 return command.get();
389 }
390 }
391
392 return nullptr;
393}
394
395auto parser::find(const std::string& name) -> parser::cmd_base*
396{
397 for(auto& command_pair : commands_)
398 {
399 auto& command = command_pair.second;
400 if(command->is(name))
401 {
402 return command.get();
403 }
404 }
405
406 return nullptr;
407}
408
409} // namespace cmd_line
auto howto_required(const std::unique_ptr< cmd_base > &command) const -> std::string
Definition parser.cpp:354
auto has_help() const -> bool
Definition parser.cpp:151
void disable_help()
Definition parser.cpp:180
auto app_name() const -> const std::string &
Definition parser.cpp:311
void enable_help()
Definition parser.cpp:165
auto find_default() -> cmd_base *
Definition parser.cpp:381
auto usage() const -> std::string
Definition parser.cpp:316
auto run() -> bool
Definition parser.cpp:206
void run_and_exit_if_error()
Definition parser.cpp:198
void print_help(std::stringstream &ss) const
Definition parser.cpp:346
void set_callback(const std::string &name, const std::string &alternative, std::function< T(callback_args &)> callback, const std::string &description="", bool dominant=false)
Definition parser.h:315
auto no_default() const -> std::string
Definition parser.cpp:372
auto howto_use(const std::unique_ptr< cmd_base > &command) const -> std::string
Definition parser.cpp:363
parser(int argc, const char **argv)
Definition parser.cpp:133
auto requirements() const -> int
Definition parser.cpp:290
auto commands() const -> int
Definition parser.cpp:306
auto find(const std::string &name) -> cmd_base *
Definition parser.cpp:395
std::string name
Definition hub.cpp:27
auto get_internal_id() -> std::uint64_t
Definition parser.cpp:6
std::ostream & output
Definition parser.h:57