Euphoria
argparse.h
Go to the documentation of this file.
1 #pragma once
2 
3 
4 #include <map>
5 
6 
7 #include <memory>
8 #include <optional>
9 #include <functional>
10 #include <type_traits>
11 
12 #include "base/default_parse.h"
13 #include "base/stringmerger.h"
14 #include "base/result.h"
15 
16 
17 namespace eu::core::argparse
18 {
19  struct ParserBase;
20 
22  enum class State
23  {
25  };
26 
28  struct FileOutput
29  {
30  std::string file;
31  bool single;
32  int index = 0;
33 
34  explicit FileOutput(const std::string& o);
35 
36  std::string get_next_file(bool print = true);
37  void create_dir_if_missing() const;
38  };
39 
40 
41  struct ParseResult
42  {
43  enum class Type { error, ok, quit, custom };
44 
47 
48  constexpr explicit ParseResult(Type t)
49  : internal_type(t)
51  {
52  }
53 
54  constexpr explicit ParseResult(int rv)
55  : internal_type(ParseResult::Type::custom)
56  , return_value(rv)
57  {
58  }
59 
60  static constexpr int get_default_return_value(Type pr)
61  {
62  switch (pr)
63  {
64  case Type::ok: return 0;
65  case Type::error: return -1;
66  case Type::quit: return 0;
67  default: return -1;
68  }
69  }
70  };
71 
74 
77 
80 
81  std::string to_string(const ParseResult& pr);
82 }
83 
85 
86 namespace eu::core::argparse
87 {
88  bool
89  operator==(const ParseResult& lhs, const ParseResult& rhs);
90 
91 
92  bool
93  operator!=(const ParseResult& lhs, const ParseResult& rhs);
94 
95 
98  {
99  std::string name;
100  std::vector<std::string> arguments;
101 
102  NameAndArguments(const std::string& n, const std::vector<std::string>& a);
103 
104  static
106  extract(int argc, char* argv[]);
107  };
108  std::string to_string(const NameAndArguments& arguments);
109 }
110 
112 
113 namespace eu::core::argparse
114 {
117  {
120 
121  explicit ArgumentReader(const NameAndArguments& a);
122 
123  [[nodiscard]] std::string read();
124  void undo_read();
125 
126  [[nodiscard]] bool has_more() const;
127  [[nodiscard]] std::string peek() const;
128  };
129 
131  struct Printer
132  {
133  Printer() = default;
134  virtual ~Printer() = default;
135 
136  Printer(const Printer&) = delete;
137  Printer(Printer&&) = delete;
138  void operator=(const Printer&) = delete;
139  void operator=(Printer&&) = delete;
140 
141  virtual void
142  print_error(const std::string& line) = 0;
143 
144  virtual void
145  print_info(const std::string& line) = 0;
146  };
147 
149  struct ConsolePrinter : public Printer
150  {
151  void
152  print_error(const std::string& line) override;
153 
154  void
155  print_info(const std::string& line) override;
156  };
157 
159  struct Runner
160  {
162  std::shared_ptr<argparse::Printer> printer;
163  };
164 
165  void
167  (
168  Runner* runner,
169  ParserBase* base,
170  const std::string& error_message
171  );
172 
173 
181  struct Name
182  {
183  std::vector<std::string> names;
184 
185  Name(const char* nn);
186 
187  // return empty string if valid, return error message if not
188  [[nodiscard]]
189  std::string
190  validate() const;
191 
192  [[nodiscard]] bool
193  is_optional() const;
194  };
195 
196 
198  struct Argument
199  {
200  std::string help;
201  std::string nargs;
203 
204  // automatically filled
205  std::string default_value;
206 
207  Argument() = default;
208  virtual ~Argument() = default;
209 
210  Argument(const Argument&) = delete;
211  Argument(Argument&&) = delete;
212  void operator=(const Argument&) = delete;
213  void operator=(Argument&&) = delete;
214 
215  Argument&
216  set_nargs(const std::string& na);
217 
218  Argument&
219  set_help(const std::string& h);
220 
221  Argument&
223 
224  virtual
225  std::optional<std::string>
227 
228  virtual
229  bool
230  have_nargs() = 0;
231 
232  virtual
235  (
236  Runner* runner,
237  const std::string& name,
238  ParserBase* caller
239  ) = 0;
240  };
241 
244  {
246  std::shared_ptr<argparse::Argument> argument;
247 
248  ArgumentAndName(const argparse::Name& n, std::shared_ptr<argparse::Argument> a);
249  };
250 
253  struct ArgumentNoValue : public Argument
254  {
255  using CallbackFunction = std::function<ParseResult (Runner*)>;
256 
258 
259  explicit ArgumentNoValue(CallbackFunction cb);
260 
261  bool have_nargs() override;
262 
263  std::optional<std::string> get_second_line() override;
264 
266  (
267  Runner* runner,
268  const std::string& name,
269  ParserBase* caller
270  ) override;
271  };
272 
274  struct SingleArgument : public Argument
275  {
276  using CallbackFunction = std::function
277  <
279  (
280  Runner* runner,
281  const std::string& name,
282  ParserBase* caller,
283  const std::string& value
284  )
285  >;
286  using DescribeFunction = std::function<std::optional<std::string> ()>;
287 
290 
292 
293  bool have_nargs() override;
294  std::optional<std::string> get_second_line() override;
295 
297  (
298  Runner* runner,
299  const std::string& name,
300  ParserBase* caller
301  ) override;
302  };
303 
305  struct MultiArgument : public Argument
306  {
307  using CallbackFunction = std::function
308  <
310  (
311  Runner* runner,
312  const std::string& name,
313  ParserBase* caller,
314  const std::string& value
315  )
316  >;
317  using DescribeFunction = std::function<std::optional<std::string> ()>;
318 
321 
323 
324  bool have_nargs() override;
325 
326  std::optional<std::string> get_second_line() override;
327 
329  (
330  Runner* runner,
331  const std::string& name,
332  ParserBase* caller
333  ) override;
334  };
335 
337  template<typename T>
338  using ParseFunction = std::function<Result<T> (const std::string& value)>;
339 
340  struct SubParser;
341 
342  using SubParserCallback = std::function<ParseResult (SubParser*)>;
343  using CompleteFunction = std::function<ParseResult ()>;
344 
348  {
349  std::vector<std::string> names;
350 
351  SubParserNames(const char* str);
352  };
353 
356  {
358  std::string help;
360 
362  (
363  const SubParserNames& n,
364  const std::string& h,
366  );
367  };
368 
371  {
372  std::string title;
374  std::vector<std::shared_ptr<SubParserContainer>> parsers;
375 
377  (
378  const std::string& t,
379  ParserBase* o
380  );
381 
382  void add
383  (
384  const SubParserNames& names,
385  const std::string& desc,
387  );
388 
389  void add
390  (
391  const SubParserNames& names,
393  );
394  };
395 
398  enum class SubParserStyle
399  {
401  greedy,
402 
404  fallback,
405 
407  inherit
408  };
409 
411  struct ParserBase
412  {
413  std::string description;
414  std::vector<ArgumentAndName> positional_argument_list;
415  std::map<std::string, std::shared_ptr<Argument>> optional_arguments;
416  std::vector<ArgumentAndName> optional_argument_list;
418  std::vector<std::shared_ptr<SubParserGroup>> subparser_groups;
419  std::optional<CompleteFunction> on_complete_function;
422 
423  explicit ParserBase(const std::string& d);
424  virtual ~ParserBase() = default;
425 
426  ParserBase(const ParserBase&) = delete;
427  ParserBase(ParserBase&&) = delete;
428  void operator=(const ParserBase&) = delete;
429  void operator=(ParserBase&&) = delete;
430 
431  std::string generate_usage_string(const NameAndArguments& args);
432  void print_help(std::shared_ptr<Printer> printer, const NameAndArguments& args);
433  void on_complete(CompleteFunction com);
434 
435  Argument& add_argument(const Name& name, std::shared_ptr<Argument> argument);
436  Argument& add_void_function(const Name& name, std::function<void()> void_function);
437  Argument& set_true(const Name& name, bool* target);
438  Argument& set_false(const Name& name, bool* target);
439  std::shared_ptr<SubParserGroup> add_sub_parsers(const std::string& name = "commands");
440  [[nodiscard]] ParseResult parse_args(Runner* runner);
441 
442  template<typename T>
443  Argument& set_const(const Name& name, T* target, T t)
444  {
445  return add_void_function(name, [target, t]()
446  {
447  *target = t;
448  });
449  }
450 
451  template<typename T>
453  (
454  const Name& name,
455  T* target,
456  ParseFunction<T> parse_function = default_parse_function<T>
457  )
458  {
459  auto arg = std::make_shared<SingleArgument>(
460  [target, parse_function]
461  (
462  Runner* runner,
463  const std::string& argument_name,
464  ParserBase* caller,
465  const std::string& value
466  )
467  {
468  auto parsed = parse_function(value);
469  if(parsed)
470  {
471  *target = *parsed;
472  return argparse::ok;
473  }
474  else
475  {
476  const std::string base = fmt::format("'{}' is not accepted for '{}'", value, argument_name);
477  const auto parsed_error = parsed.get_error();
478  const std::string message = parsed_error.empty()
479  ? base
480  : fmt::format("{}, {}", base, parsed_error)
481  ;
482  print_parse_error(runner, caller, message);
483 
484  return argparse::error;
485  }
486  }, [](){
487  const std::optional<std::string> str = default_describe<T>();
488  return str;
489  });
491  return add_argument(name, arg);
492  }
493 
495  // todo(Gustav): add option for non-greedy and error if empty
496  template<typename T>
498  (
499  const Name& name,
500  std::vector<T>* target,
501  ParseFunction<T> parse_function = default_parse_function<T>
502  )
503  {
504  auto arg = std::make_shared<MultiArgument>(
505  [target, parse_function]
506  (
507  Runner* runner,
508  const std::string& argument_name,
509  ParserBase* caller,
510  const std::string& value
511  )
512  {
513  auto parsed = parse_function(value);
514  if(parsed)
515  {
516  target->emplace_back(*parsed);
517  return argparse::ok;
518  }
519  else
520  {
521  const std::string base = fmt::format("\'{}' is not accepted for '{}'", value, argument_name);
522  const auto parsed_error = parsed.get_error();
523  const std::string message = parsed_error.empty()
524  ? base
525  : fmt::format("{}, {}", base, parsed_error)
526  ;
527  print_parse_error(runner, caller, message);
528 
529  return argparse::error;
530  }
531  }, [](){
532  const std::optional<std::string> str = default_describe<T>();
533  return str;
534  });
535  std::vector<std::string> values;
536  for(const auto& t: *target)
537  {
538  values.emplace_back(from_default_value_to_string(t));
539  }
541  return add_argument(name, arg);
542  }
543 
544 
545  [[nodiscard]] std::shared_ptr<Argument> find_argument(const std::string& name) const;
546 
547  virtual std::string get_calling_name(const NameAndArguments& args) = 0;
549  };
550 
553  struct SubParser : public ParserBase
554  {
557  std::string calling_name;
558 
559  SubParser
560  (
561  const std::string& d,
562  ParserBase* p,
563  argparse::Runner* r,
564  const std::string& cn
565  );
566 
567  [[nodiscard]] ParseResult on_complete(CompleteFunction com);
568 
569  ParserBase* get_parent_or_null() override;
570  std::string get_calling_name(const NameAndArguments& args) override;
571  };
572 
574  struct Parser : public ParserBase
575  {
576  std::shared_ptr<argparse::Printer> printer;
577 
578  explicit Parser(const std::string& d = "");
579 
580  ParseResult parse(const NameAndArguments& args);
581 
584  std::optional<int> parse(int argc, char* argv[]);
585 
586  ParserBase* get_parent_or_null() override;
587  std::string get_calling_name(const NameAndArguments& args) override;
588  };
589 
591  int parse_from_main(Parser* parser, int argc, char* argv[]);
592 }
argparse::Runner * runner
Definition: argparse.cc:888
ParserBase * base
Definition: argparse.cc:887
ADD_DEFAULT_FORMATTER(eu::core::argparse::ParseResult, std::string, eu::core::argparse::to_string)
constexpr ParseResult error
no error occurred
Definition: argparse.h:73
constexpr ParseResult ok
all ok
Definition: argparse.h:76
std::function< ParseResult()> CompleteFunction
Definition: argparse.h:343
bool operator==(const ParseResult &lhs, const ParseResult &rhs)
Definition: argparse.cc:109
int parse_from_main(Parser *parser, int argc, char *argv[])
helper function for parsing directly from main
Definition: argparse.cc:1335
State
enum to catch adding arguments during parsing in a callback
Definition: argparse.h:23
std::function< Result< T >(const std::string &value)> ParseFunction
generic parse function
Definition: argparse.h:338
std::string to_string(const ParseResult &pr)
Definition: argparse.cc:105
void print_parse_error(Runner *runner, ParserBase *base, const std::string &error_message)
Definition: argparse.cc:216
constexpr ParseResult quit
all ok, but quit requested
Definition: argparse.h:79
bool operator!=(const ParseResult &lhs, const ParseResult &rhs)
Definition: argparse.cc:116
SubParserStyle
how the subparsing is handled, non-greedy are useful for 'scripting' with subparsers
Definition: argparse.h:399
@ fallback
if argument is invalid, go back one step and try there
std::function< ParseResult(SubParser *)> SubParserCallback
Definition: argparse.h:342
constexpr StringMerger array
Definition: stringmerger.h:91
std::string from_default_value_to_string(const T &t)
int line
Definition: nlp_sentence.cc:91
std::string merge(const std::vector< std::string > &strings) const
Definition: stringmerger.cc:11
named tuple class for argument and name
Definition: argparse.h:244
std::shared_ptr< argparse::Argument > argument
Definition: argparse.h:246
ArgumentAndName(const argparse::Name &n, std::shared_ptr< argparse::Argument > a)
Definition: argparse.cc:325
a argument with no value, probably either a –set-true or a void function like –help
Definition: argparse.h:254
std::function< ParseResult(Runner *)> CallbackFunction
Definition: argparse.h:255
CallbackFunction callback_function
Definition: argparse.h:257
std::optional< std::string > get_second_line() override
Definition: argparse.cc:346
ParseResult parse_arguments(Runner *runner, const std::string &name, ParserBase *caller) override
Definition: argparse.cc:355
ArgumentNoValue(CallbackFunction cb)
Definition: argparse.cc:332
"file" like api for reading arguments
Definition: argparse.h:117
ArgumentReader(const NameAndArguments &a)
Definition: argparse.cc:149
base class for argument
Definition: argparse.h:199
Argument & set_help(const std::string &h)
Definition: argparse.cc:310
void operator=(Argument &&)=delete
virtual ~Argument()=default
Argument & set_allow_before_positionals()
Definition: argparse.cc:318
Argument(const Argument &)=delete
virtual ParseResult parse_arguments(Runner *runner, const std::string &name, ParserBase *caller)=0
Argument & set_nargs(const std::string &na)
Definition: argparse.cc:302
virtual std::optional< std::string > get_second_line()=0
virtual bool have_nargs()=0
void operator=(const Argument &)=delete
Argument(Argument &&)=delete
void print_error(const std::string &line) override
Definition: argparse.cc:201
void print_info(const std::string &line) override
Definition: argparse.cc:208
option where the output can either be a single file or "infinite"
Definition: argparse.h:29
FileOutput(const std::string &o)
Definition: argparse.cc:74
std::string get_next_file(bool print=true)
Definition: argparse.cc:79
void create_dir_if_missing() const
Definition: argparse.cc:98
a single argument, probably either a –count 3 or positional input
Definition: argparse.h:306
ParseResult parse_arguments(Runner *runner, const std::string &name, ParserBase *caller) override
Definition: argparse.cc:388
std::function< std::optional< std::string >()> DescribeFunction
Definition: argparse.h:317
MultiArgument(CallbackFunction cb, DescribeFunction d)
Definition: argparse.cc:365
DescribeFunction describe_function
Definition: argparse.h:320
std::optional< std::string > get_second_line() override
Definition: argparse.cc:380
CallbackFunction callback_function
Definition: argparse.h:319
std::function< ParseResult(Runner *runner, const std::string &name, ParserBase *caller, const std::string &value) > CallbackFunction
Definition: argparse.h:316
container for arguments passed to main
Definition: argparse.h:98
NameAndArguments(const std::string &n, const std::vector< std::string > &a)
Definition: argparse.cc:122
static NameAndArguments extract(int argc, char *argv[])
Definition: argparse.cc:130
std::vector< std::string > arguments
Definition: argparse.h:100
represents a command line argument optional:
Definition: argparse.h:182
Name(const char *nn)
std::string validate() const
std::vector< std::string > names
Definition: argparse.h:183
static constexpr int get_default_return_value(Type pr)
Definition: argparse.h:60
constexpr ParseResult(int rv)
Definition: argparse.h:54
constexpr ParseResult(Type t)
Definition: argparse.h:48
base for the parser, start with Parser and add one or more subparsers
Definition: argparse.h:412
void operator=(ParserBase &&)=delete
void operator=(const ParserBase &)=delete
std::vector< std::shared_ptr< SubParserGroup > > subparser_groups
Definition: argparse.h:418
ParserBase(ParserBase &&)=delete
virtual std::string get_calling_name(const NameAndArguments &args)=0
std::map< std::string, std::shared_ptr< Argument > > optional_arguments
Definition: argparse.h:415
Argument & add_vector(const Name &name, std::vector< T > *target, ParseFunction< T > parse_function=default_parse_function< T >)
add greedy argument, currently also accepts zero
Definition: argparse.h:498
std::vector< ArgumentAndName > positional_argument_list
Definition: argparse.h:414
void on_complete(CompleteFunction com)
Definition: argparse.cc:852
std::shared_ptr< Argument > find_argument(const std::string &name) const
Definition: argparse.cc:869
Argument & set_const(const Name &name, T *target, T t)
Definition: argparse.h:443
SubParserStyle parser_style
Definition: argparse.h:421
virtual ParserBase * get_parent_or_null()=0
std::vector< ArgumentAndName > optional_argument_list
Definition: argparse.h:416
void print_help(std::shared_ptr< Printer > printer, const NameAndArguments &args)
Definition: argparse.cc:613
Argument & set_true(const Name &name, bool *target)
Definition: argparse.cc:836
ParserBase(const ParserBase &)=delete
Argument & set_false(const Name &name, bool *target)
Definition: argparse.cc:844
std::optional< CompleteFunction > on_complete_function
Definition: argparse.h:419
Argument & add_void_function(const Name &name, std::function< void()> void_function)
Definition: argparse.cc:829
virtual ~ParserBase()=default
std::shared_ptr< SubParserGroup > add_sub_parsers(const std::string &name="commands")
Definition: argparse.cc:859
ParserBase(const std::string &d)
Definition: argparse.cc:524
ParseResult parse_args(Runner *runner)
Definition: argparse.cc:1207
Argument & add(const Name &name, T *target, ParseFunction< T > parse_function=default_parse_function< T >)
Definition: argparse.h:453
Argument & add_argument(const Name &name, std::shared_ptr< Argument > argument)
Definition: argparse.cc:802
std::string generate_usage_string(const NameAndArguments &args)
Definition: argparse.cc:550
EnumToStringImplementation< std::shared_ptr< SubParserContainer > > subparsers
Definition: argparse.h:417
root parser. start argumentparsing with this one
Definition: argparse.h:575
ParseResult parse(const NameAndArguments &args)
Definition: argparse.cc:1303
std::shared_ptr< argparse::Printer > printer
Definition: argparse.h:576
ParserBase * get_parent_or_null() override
Definition: argparse.cc:1296
Parser(const std::string &d="")
Definition: argparse.cc:1288
std::string get_calling_name(const NameAndArguments &args) override
Definition: argparse.cc:1328
generic output class
Definition: argparse.h:132
void operator=(Printer &&)=delete
virtual void print_error(const std::string &line)=0
virtual void print_info(const std::string &line)=0
Printer(const Printer &)=delete
void operator=(const Printer &)=delete
Printer(Printer &&)=delete
virtual ~Printer()=default
shared data between parsing functions
Definition: argparse.h:160
ArgumentReader * arguments
Definition: argparse.h:161
std::shared_ptr< argparse::Printer > printer
Definition: argparse.h:162
a single argument, probably either a –count 3 or positional input
Definition: argparse.h:275
DescribeFunction describe_function
Definition: argparse.h:289
ParseResult parse_arguments(Runner *runner, const std::string &name, ParserBase *caller) override
Definition: argparse.cc:436
CallbackFunction callback_function
Definition: argparse.h:288
std::function< std::optional< std::string >()> DescribeFunction
Definition: argparse.h:286
SingleArgument(CallbackFunction cb, DescribeFunction d)
Definition: argparse.cc:413
std::optional< std::string > get_second_line() override
Definition: argparse.cc:428
std::function< ParseResult(Runner *runner, const std::string &name, ParserBase *caller, const std::string &value) > CallbackFunction
Definition: argparse.h:285
SubParserContainer(const SubParserNames &n, const std::string &h, SubParserCallback cb)
Definition: argparse.cc:473
subparsers can be grouped and this structs represents that group
Definition: argparse.h:371
void add(const SubParserNames &names, const std::string &desc, SubParserCallback sub)
Definition: argparse.cc:498
std::vector< std::shared_ptr< SubParserContainer > > parsers
Definition: argparse.h:374
SubParserGroup(const std::string &t, ParserBase *o)
Definition: argparse.cc:486
valid names for a single subparser example: [checkout, co] or [pull]
Definition: argparse.h:348
std::vector< std::string > names
Definition: argparse.h:349
subparser, don't create manually but add to a existing parser AddSubParsers()->Add(....
Definition: argparse.h:554
ParserBase * get_parent_or_null() override
Definition: argparse.cc:1267
argparse::Runner * runner
Definition: argparse.h:556
std::string get_calling_name(const NameAndArguments &args) override
Definition: argparse.cc:1274
SubParser(const std::string &d, ParserBase *p, argparse::Runner *r, const std::string &cn)
Definition: argparse.cc:1252
ParseResult on_complete(CompleteFunction com)
Definition: argparse.cc:1281