From d1ad559461b560071e8163c71c35adfe76a10ad6 Mon Sep 17 00:00:00 2001 From: q66 Date: Tue, 16 May 2017 19:25:20 +0200 Subject: [PATCH] argparse parsing cleanup and nicer api --- build.cc | 14 ++--- ostd/argparse.hh | 156 +++++++++++++++++++++-------------------------- 2 files changed, 75 insertions(+), 95 deletions(-) diff --git a/build.cc b/build.cc index 323db5a..b498fe2 100644 --- a/build.cc +++ b/build.cc @@ -148,31 +148,31 @@ int main(int argc, char **argv) { auto &help = ap.add_help("print this message and exit"); - ap.add_optional(0, "no-examples", ostd::arg_value::NONE) + ap.add_optional("--no-examples", ostd::arg_value::NONE) .help("do not build examples") .action(ostd::arg_store_false(build_examples)); - ap.add_optional(0, "no-test-suite", ostd::arg_value::NONE) + ap.add_optional("--no-test-suite", ostd::arg_value::NONE) .help("do not build test suite") .action(ostd::arg_store_false(build_testsuite)); - ap.add_optional(0, "no-static", ostd::arg_value::NONE) + ap.add_optional("--no-static", ostd::arg_value::NONE) .help("do not build static libostd") .action(ostd::arg_store_false(build_static)); - ap.add_optional(0, "shared", ostd::arg_value::NONE) + ap.add_optional("--shared", ostd::arg_value::NONE) .help("build shared libostd") .action(ostd::arg_store_true(build_shared)); - ap.add_optional(0, "config", ostd::arg_value::REQUIRED) + ap.add_optional("--config", ostd::arg_value::REQUIRED) .help("build configuration (debug/release)") .action(ostd::arg_store_str(build_cfg)); - ap.add_optional('v', "verbose", ostd::arg_value::NONE) + ap.add_optional("-v", "--verbose", ostd::arg_value::NONE) .help("print entire commands") .action(ostd::arg_store_true(verbose)); - ap.add_optional(0, "clean", ostd::arg_value::NONE) + ap.add_optional("--clean", ostd::arg_value::NONE) .help("remove generated files and exit") .action(ostd::arg_store_true(clean)); diff --git a/ostd/argparse.hh b/ostd/argparse.hh index 5b00584..43e1366 100644 --- a/ostd/argparse.hh +++ b/ostd/argparse.hh @@ -16,6 +16,7 @@ #ifndef OSTD_ARGPARSE_HH #define OSTD_ARGPARSE_HH +#include #include #include #include @@ -101,11 +102,10 @@ struct arg_optional: arg_argument { } bool is_arg(string_range name) const { - if (starts_with(name, "--")) { - return name.slice(2) == ostd::citer(p_lname); - } - if (name[0] == '-') { - return name[1] == p_sname; + for (auto const &nm: p_names) { + if (name == iter(nm)) { + return true; + } } return false; } @@ -134,17 +134,39 @@ struct arg_optional: arg_argument { return *this; } + arg_optional &add_name(string_range name) { + p_names.emplace_back(name); + return *this; + } + protected: arg_optional() = delete; + + arg_optional(string_range name, arg_value req, int nargs = 1): + arg_argument(req, nargs) + { + p_names.emplace_back(name); + } + arg_optional(string_range name, int nargs): + arg_argument(nargs) + { + p_names.emplace_back(name); + } + arg_optional( - char short_name, string_range long_name, - arg_value req, int nargs = 1 + string_range name1, string_range name2, arg_value req, int nargs = 1 ): - arg_argument(req, nargs), p_lname(long_name), p_sname(short_name) - {} - arg_optional(char short_name, string_range long_name, int nargs): - arg_argument(nargs), p_lname(long_name), p_sname(short_name) - {} + arg_argument(req, nargs) + { + p_names.emplace_back(name1); + p_names.emplace_back(name2); + } + arg_optional(string_range name1, string_range name2, int nargs): + arg_argument(nargs) + { + p_names.emplace_back(name1); + p_names.emplace_back(name2); + } void set_values(iterator_range vals) { if (p_action) { @@ -156,8 +178,7 @@ protected: private: std::function)> p_action; - std::string p_lname; - char p_sname; + std::vector p_names; bool p_used = false; }; @@ -300,7 +321,7 @@ struct arg_parser: arg_description_container { template arg_optional &add_help(OutputRange out, string_range msg) { - auto &opt = add_optional('h', "help", arg_value::NONE); + auto &opt = add_optional("-h", "--help", arg_value::NONE); opt.help(msg); opt.action([this, out = std::move(out)](auto) mutable { this->print_help(out); @@ -351,15 +372,10 @@ private: case arg_type::OPTIONAL: { auto &opt = static_cast(*p); std::size_t nl = 0; - if (opt.p_sname) { - nl += 2; - } - if (!opt.p_lname.empty()) { - if (opt.p_sname) { - nl += 2; - } - nl += opt.p_lname.size() + 2; + for (auto &s: opt.p_names) { + nl += s.size(); } + nl += 2 * (opt.p_names.size() - 1); opt_namel = std::max(opt_namel, nl); break; } @@ -405,17 +421,13 @@ private: auto &parg = static_cast(*p.get()); format(out, " "); std::size_t nd = 0; - if (parg.p_sname) { - nd += 2; - format(out, "-%c", parg.p_sname); - if (!parg.p_lname.empty()) { + for (auto &s: parg.p_names) { + if (nd) { nd += 2; format(out, ", "); } - } - if (!parg.p_lname.empty()) { - nd += parg.p_lname.size() + 2; - format(out, "--%s", parg.p_lname); + nd += s.size(); + format(out, "%s", s); } if (parg.p_helpstr.empty()) { out.put('\n'); @@ -432,83 +444,51 @@ private: template void parse_long(string_range arg, R &args) { - string_range val = find(arg, '='); - bool has_val = !val.empty(), arg_val = false; - if (has_val) { - arg = arg.slice(0, arg.size() - val.size()); - val.pop_front(); - } - - auto &desc = find_arg(arg); - - args.pop_front(); - auto needs = desc.needs_value(); - if (needs == arg_value::NONE) { - if (has_val) { - throw arg_error{format( - appender(), "argument '--%s' takes no value", - desc.p_lname - ).get()}; - } - desc.set_values(nullptr); - return; - } - if (!has_val) { - if (args.empty()) { - if (needs == arg_value::REQUIRED) { - throw arg_error{format( - appender(), "argument '--%s' needs a value", - desc.p_lname - ).get()}; - } - desc.set_values(nullptr); - return; - } - string_range tval = args.front(); - if ((needs != arg_value::OPTIONAL) || !find_arg_ptr(tval)) { - val = tval; - has_val = arg_val = true; - } - } - if (has_val) { - desc.set_values(iter({ val })); - if (arg_val) { - args.pop_front(); - } - } else { - desc.set_values(nullptr); + std::optional val; + if (auto sv = find(arg, '='); !sv.empty()) { + arg = arg.slice(0, arg.size() - sv.size()); + sv.pop_front(); + val = sv; } + parse_arg(arg, std::move(val), args); } template void parse_short(string_range arg, R &args) { - string_range val; - bool has_val = (arg.size() > 2), arg_val = false; - if (has_val) { + std::optional val; + if (arg.size() > 2) { val = arg.slice(2); arg = arg.slice(0, 2); } + parse_arg(arg, std::move(val), args); + } + template + void parse_arg( + string_range arg, std::optional val, R &args + ) { + bool arg_val = false; + std::string argname{arg}; auto &desc = find_arg(arg); args.pop_front(); auto needs = desc.needs_value(); if (needs == arg_value::NONE) { - if (has_val) { + if (val) { throw arg_error{format( - appender(), "argument '-%c' takes no value", - desc.p_sname + appender(), "argument '%s' takes no value", + argname ).get()}; } desc.set_values(nullptr); return; } - if (!has_val) { + if (!val) { if (args.empty()) { if (needs == arg_value::REQUIRED) { throw arg_error{format( - appender(), "argument '-%c' needs a value", - desc.p_sname + appender(), "argument '%s' needs a value", + argname ).get()}; } desc.set_values(nullptr); @@ -517,11 +497,11 @@ private: string_range tval = args.front(); if ((needs != arg_value::OPTIONAL) || !find_arg_ptr(tval)) { val = tval; - has_val = arg_val = true; + arg_val = true; } } - if (has_val) { - desc.set_values(iter({ val })); + if (val) { + desc.set_values(iter({ *val })); if (arg_val) { args.pop_front(); }