koutil
Loading...
Searching...
No Matches
parser.h
Go to the documentation of this file.
1#ifndef KOUTIL_ARGPARSER_PARSER_H
2#define KOUTIL_ARGPARSER_PARSER_H
3
6#include <cassert>
7#include <concepts>
8#include <cstddef>
9#include <span>
10#include <string_view>
11#include <utility>
12
13namespace koutil::argparser {
14
26
33template <typename T, typename Ctx>
34concept parser = requires(Ctx& ctx, const Arg& arg, std::string_view value, const SubcommandBase& cmd) {
35 { T::parse_argument(ctx, value) } -> std::same_as<ParseResult>;
36 { T::parse_option_flag(ctx, arg) } -> std::same_as<ParseResult>;
37 { T::parse_option_value(ctx, arg, value) } -> std::same_as<ParseResult>;
38 { T::parse_command(ctx, cmd) } -> std::same_as<ParseResult>;
39};
40
49template <typename Ctx, parser<Ctx> UParser, are_arguments Args, are_commands Cmds> class Parser {
50public:
51 constexpr Parser(Ctx& ctx, Args&& args, Cmds&& commands)
52 : m_args(std::move(args))
53 , m_subcommands(std::move(commands))
54 , m_ctx(ctx) { }
55
56 constexpr Parser(Ctx& ctx, const Args& args, const Cmds& commands)
57 : m_args(args)
58 , m_subcommands(commands)
59 , m_ctx(ctx) { }
60
68 ParseResult parse(std::size_t argc, const char** argv);
69
70private:
71 Args m_args;
73
76
77 Ctx& m_ctx;
78 std::span<const char*> m_values;
79 std::size_t m_offset = 0;
80
87 ParseResult parse_long_opt(std::string_view opt);
88
95 ParseResult parse_short_opt(std::string_view opt);
96
103 ParseResult parse_command_or_argument(std::string_view opt);
104};
105
118template <typename Ctx, parser<Ctx> UParser, are_arguments Args, are_commands Cmds>
119constexpr auto make_parser(Ctx& ctx, Args&& args, Cmds&& commands) {
121 ctx, std::forward<decltype(args)>(args), std::forward<decltype(commands)>(commands)
122 );
123}
124
139template <typename Ctx, parser<Ctx> UParser, are_arguments Args, are_commands Cmds>
140ParseResult process_args(std::size_t argc, const char** argv, Ctx& ctx, Args&& args, Cmds&& commands) {
142 ctx, std::forward<decltype(args)>(args), std::forward<decltype(commands)>(commands)
143 )
144 .parse(argc, argv);
145}
146
147template <typename Ctx, parser<Ctx> UParser, are_arguments Args, are_commands Cmds>
148ParseResult Parser<Ctx, UParser, Args, Cmds>::parse(std::size_t argc, const char** argv) {
149 m_values = std::span(argv + 1, argc - 1);
150
151 for (; m_offset < m_values.size(); ++m_offset) {
152 std::string_view arg(m_values[m_offset]);
153
154 ParseResult result;
155 if (arg.starts_with("--")) {
156 result = parse_long_opt(arg.substr(2));
157 } else if (arg.starts_with('-')) {
158 result = parse_short_opt(arg.substr(1));
159 } else {
160 result = parse_command_or_argument(arg);
161 }
162
163 if (result != ParseResult::OK) {
164 return result;
165 }
166 }
167
168 return ParseResult::OK;
169}
170
171template <typename Ctx, parser<Ctx> UParser, are_arguments Args, are_commands Cmds>
173
174 if (opt.empty()) {
176 }
177
178 while (!opt.empty()) {
179 const auto* arg_res = m_current_args->find_short(opt.front());
180 if (arg_res == nullptr) {
182 }
183
184 const auto& arg = *arg_res;
185 auto result = UParser::parse_option_flag(m_ctx, arg);
186 if (result != ParseResult::OK) {
187 return result;
188 }
189
190 opt = opt.substr(1);
191 }
192
193 return ParseResult::OK;
194}
195
196template <typename Ctx, parser<Ctx> UParser, are_arguments Args, are_commands Cmds>
198
199 if (opt.empty()) {
200 m_offset += 1;
201 for (; m_offset < m_values.size(); ++m_offset) {
202 auto result = UParser::parse_argument(m_ctx, m_values[m_offset]);
203 if (result != ParseResult::OK) {
204 return result;
205 }
206 }
207 return ParseResult::OK;
208 }
209
210 auto eql_it = opt.find('=', 1);
211 std::string_view option = opt.substr(0, eql_it);
212 std::string_view value;
213
214 const auto* arg_res = m_current_args->find_long(option);
215 if (arg_res == nullptr) {
217 }
218
219 const auto& arg = *arg_res;
220
221 ParseResult result;
222 switch (arg.type) {
223 case Arg::OPTION_FLAG:
224 result = UParser::parse_option_flag(m_ctx, arg);
225 break;
227 if (eql_it != std::string_view::npos) {
228 value = opt.substr(eql_it + 1);
229 } else if (m_offset + 1 >= m_values.size()) {
231 } else {
232 value = m_values[++m_offset];
233 }
234
235 result = UParser::parse_option_value(m_ctx, arg, value);
236 break;
237 default:
238 std::unreachable();
239 }
240
241 if (result != ParseResult::OK) {
242 return result;
243 }
244 return ParseResult::OK;
245}
246
247template <typename Ctx, parser<Ctx> UParser, are_arguments Args, are_commands Cmds>
249 if (opt.empty()) {
250 return ParseResult::OK;
251 } else if (m_current_cms->size() == 0) {
252 return UParser::parse_argument(m_ctx, opt);
253 }
254
255 const auto* cmd = m_current_cms->find(opt);
256 if (cmd == nullptr) {
258 }
259
260 m_current_args = &cmd->get_args();
261 m_current_cms = &cmd->get_cmds();
262
263 return UParser::parse_command(m_ctx, *cmd);
264}
265
266}
267
268#endif
Base class for commands.
Definition subcommand.h:67
Template class representing a parser.
Definition parser.h:49
std::span< const char * > m_values
Definition parser.h:78
ParseResult parse(std::size_t argc, const char **argv)
Parses the command-line arguments.
Definition parser.h:148
ParseResult parse_short_opt(std::string_view opt)
Parses a short option.
Definition parser.h:172
constexpr Parser(Ctx &ctx, Args &&args, Cmds &&commands)
Definition parser.h:51
ArgumentsBase const * m_current_args
Definition parser.h:74
Args m_args
Definition parser.h:71
ParseResult parse_long_opt(std::string_view opt)
Parses a long option.
Definition parser.h:197
CommandsBase const * m_current_cms
Definition parser.h:75
ParseResult parse_command_or_argument(std::string_view opt)
Parses a command or argument.
Definition parser.h:248
Ctx & m_ctx
Definition parser.h:77
Cmds m_subcommands
Definition parser.h:72
constexpr Parser(Ctx &ctx, const Args &args, const Cmds &commands)
Definition parser.h:56
std::size_t m_offset
Definition parser.h:79
Concept to check if a type is a parser.
Definition parser.h:34
Definition arg.h:13
constexpr auto make_parser(Ctx &ctx, Args &&args, Cmds &&commands)
Creates a Parser object.
Definition parser.h:119
ParseResult
Enum class representing possible parse results.
Definition parser.h:18
ParseResult process_args(std::size_t argc, const char **argv, Ctx &ctx, Args &&args, Cmds &&commands)
Processes the command-line arguments.
Definition parser.h:140
@ OPTION_FLAG
Definition arg.h:31
@ OPTION_VALUE
Definition arg.h:32