From 5a384996f39c8155fb3026949098fd16db541071 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 18 Feb 2025 08:18:25 +0100 Subject: [PATCH 1/7] Change tokens to contain an indication whether a token was consumed --- include/argparse.h | 74 ++++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index ed5922b8..9dee586b 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -156,7 +156,15 @@ namespace argparse }; private: - using tokens = std::vector; + struct Token + { + std::string m_token; + bool m_consumed = false; + + bool operator==(Token const &) const = default; + }; + + using tokens = std::vector; using optstring = std::optional; class HelpRequested {}; @@ -340,6 +348,22 @@ namespace argparse return result; } + static auto join(tokens const & tokens, std::string const & separator) -> std::string + { + auto result = std::string(); + + for (auto it = tokens.begin(); it != tokens.end(); ++it) + { + if (it != tokens.begin()) + { + result += separator; + } + result += it->m_token; + } + + return result; + } + auto parse_optional_arguments(tokens args) -> tokens { for (auto const & arg : m_arguments @@ -364,7 +388,7 @@ namespace argparse static auto remove_pseudo_arguments(tokens args) -> tokens { - std::erase(args, "--"); + std::erase(args, Token{"--"}); return args; } @@ -754,9 +778,9 @@ namespace argparse auto consume_arg(tokens & args, std::any & value) const -> void { - if (!m_options.type_handler->from_string(args.front(), value)) + if (!m_options.type_handler->from_string(args.front().m_token, value)) { - throw parsing_error(std::format("argument {}: invalid value: '{}'", get_dest_name(), args.front())); + throw parsing_error(std::format("argument {}: invalid value: '{}'", get_dest_name(), args.front().m_token)); } if (!m_options.choices.empty()) { @@ -810,7 +834,7 @@ namespace argparse } else { - if (it == args.end() || it->starts_with("-")) + if (it == args.end() || it->m_token.starts_with("-")) { throw parsing_error(std::format("argument {}: expected one argument", join(get_names(), "/"))); } @@ -927,7 +951,7 @@ namespace argparse { case zero_or_one: { - if (it == args.end() || it->starts_with("-")) + if (it == args.end() || it->m_token.starts_with("-")) { m_value = m_options.const_; } @@ -965,7 +989,7 @@ namespace argparse auto find_pseudo_arg(tokens & args) const -> tokens::iterator { - return std::ranges::find(args, "--"); + return std::ranges::find(args, Token{"--"}); } auto find_arg(tokens::iterator begin, tokens::iterator end) const -> std::pair @@ -976,15 +1000,15 @@ namespace argparse { if (name[1] != '-') { - if (it->starts_with("-") && !it->starts_with("--") && it->find(name[1]) != std::string::npos) + if (it->m_token.starts_with("-") && !it->m_token.starts_with("--") && it->m_token.find(name[1]) != std::string::npos) { return {it, name}; } } else { - auto const [first_it, second_it] = std::ranges::mismatch(name, *it); - if (first_it == name.end() && (second_it == it->end() || *second_it == '=')) + auto const [first_it, second_it] = std::ranges::mismatch(name, it->m_token); + if (first_it == name.end() && (second_it == it->m_token.end() || *second_it == '=')) { return {it, name}; } @@ -997,12 +1021,12 @@ namespace argparse auto consume_name(tokens & args, tokens::iterator it, std::string const & name) const -> tokens::iterator { - if (auto const & arg = *it; arg.starts_with("--")) + if (auto const & arg = *it; arg.m_token.starts_with("--")) { - if (auto const pos = arg.find('='); pos != std::string::npos) + if (auto const pos = arg.m_token.find('='); pos != std::string::npos) { - auto const value = arg.substr(pos + 1); - *it = value; + auto const value = arg.m_token.substr(pos + 1); + *it = Token{value}; } else { @@ -1011,22 +1035,22 @@ namespace argparse } else { - if (it->size() != 2) + if (it->m_token.size() != 2) { - auto const pos = it->find(name[1]); - it->erase(pos, 1); + auto const pos = it->m_token.find(name[1]); + it->m_token.erase(pos, 1); if (m_options.action == store) { if (pos == 1) { - it->erase(0, 1); + it->m_token.erase(0, 1); } else { - auto const prefix = it->substr(0, pos); - auto const value = it->substr(pos); - *it = prefix; - it = args.insert(it, value); + auto const prefix = it->m_token.substr(0, pos); + auto const value = it->m_token.substr(pos); + *it = Token{prefix}; + it = args.insert(it, Token{value}); } } } @@ -1042,7 +1066,7 @@ namespace argparse auto count_args(tokens::const_iterator it, tokens::const_iterator end) const -> std::size_t { auto result = std::size_t(0); - while (it != end && !it->starts_with('-')) + while (it != end && !it->m_token.starts_with('-')) { ++result; ++it; @@ -1052,9 +1076,9 @@ namespace argparse auto consume_arg(tokens & args, tokens::iterator & arg_it, std::any & value) const -> void { - if (!m_options.type_handler->from_string(*arg_it, value)) + if (!m_options.type_handler->from_string(arg_it->m_token, value)) { - throw parsing_error(std::format("argument {}: invalid value: '{}'", join(get_names(), "/"), *arg_it)); + throw parsing_error(std::format("argument {}: invalid value: '{}'", join(get_names(), "/"), arg_it->m_token)); } if (!m_options.choices.empty()) { From fe69a1fa613090a5b38c237d074cbe08db4c049f Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Wed, 19 Feb 2025 22:28:36 +0100 Subject: [PATCH 2/7] Change consuming args --- include/argparse.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index 9dee586b..6d9071fa 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -685,7 +685,7 @@ namespace argparse { if (!args.empty()) { - consume_arg(args, m_value); + m_value = consume_arg(args); } } @@ -736,8 +736,7 @@ namespace argparse private: auto parse_arguments_number(tokens & args, std::size_t number) -> void { - auto values = std::vector(number); - consume_args(args, values); + auto values = consume_args(args, number); m_value = m_options.type_handler->transform(values); } @@ -750,7 +749,7 @@ namespace argparse { if (!args.empty()) { - consume_arg(args, m_value); + m_value = consume_arg(args); } else { @@ -765,8 +764,7 @@ namespace argparse } case one_or_more: { - auto values = std::vector(args.size()); - consume_args(args, values); + auto values = consume_args(args, args.size()); if (!values.empty()) { m_value = m_options.type_handler->transform(values); @@ -776,8 +774,9 @@ namespace argparse } } - auto consume_arg(tokens & args, std::any & value) const -> void + auto consume_arg(tokens & args) const -> std::any { + std::any value; if (!m_options.type_handler->from_string(args.front().m_token, value)) { throw parsing_error(std::format("argument {}: invalid value: '{}'", get_dest_name(), args.front().m_token)); @@ -787,14 +786,17 @@ namespace argparse check_choices(value); } args.erase(args.begin()); + return value; } - auto consume_args(tokens & args, std::vector & values) const -> void + auto consume_args(tokens & args, std::size_t number) const -> std::vector { - for (auto & value : values) + std::vector values; + for (std::size_t i = 0; i < number; ++i) { - consume_arg(args, value); + values.push_back(consume_arg(args)); } + return values; } private: From 52eeda7bdf21a4848c26f2c7fdef056deb82f20c Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Wed, 19 Feb 2025 22:51:02 +0100 Subject: [PATCH 3/7] Change consuming args --- include/argparse.h | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index 6d9071fa..3ccbaa3f 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -364,6 +364,22 @@ namespace argparse return result; } + static auto join(std::ranges::view auto strings, std::string const & separator) -> std::string + { + auto result = std::string(); + + for (auto it = strings.begin(); it != strings.end(); ++it) + { + if (it != strings.begin()) + { + result += separator; + } + result += *it; + } + + return result; + } + auto parse_optional_arguments(tokens args) -> tokens { for (auto const & arg : m_arguments @@ -395,9 +411,10 @@ namespace argparse auto ensure_no_unrecognised_arguments(tokens const & args) const -> void { - if (!args.empty()) + auto unconsumed = std::ranges::filter_view(args, [](auto const & token) { return !token.m_consumed; }); + if (!unconsumed.empty()) { - throw parsing_error(std::format("unrecognised arguments: {}", join(args, " "))); + throw parsing_error(std::format("unrecognised arguments: {}", join(unconsumed | std::views::transform([](auto const & token) { return token.m_token; }), " "))); } } @@ -670,6 +687,7 @@ namespace argparse auto parse_args(tokens args) -> tokens override { + auto consumable = std::ranges::drop_while_view(args, [](auto const & token) { return token.m_consumed; }); if (has_nargs()) { if (has_nargs_number()) @@ -683,9 +701,9 @@ namespace argparse } else { - if (!args.empty()) + if (!consumable.empty()) { - m_value = consume_arg(args); + m_value = consume_arg(consumable.front()); } } @@ -749,7 +767,7 @@ namespace argparse { if (!args.empty()) { - m_value = consume_arg(args); + m_value = consume_arg(args.front()); } else { @@ -774,18 +792,18 @@ namespace argparse } } - auto consume_arg(tokens & args) const -> std::any + auto consume_arg(Token & arg) const -> std::any { std::any value; - if (!m_options.type_handler->from_string(args.front().m_token, value)) + if (!m_options.type_handler->from_string(arg.m_token, value)) { - throw parsing_error(std::format("argument {}: invalid value: '{}'", get_dest_name(), args.front().m_token)); + throw parsing_error(std::format("argument {}: invalid value: '{}'", get_dest_name(), arg.m_token)); } if (!m_options.choices.empty()) { check_choices(value); } - args.erase(args.begin()); + arg.m_consumed = true; return value; } @@ -794,7 +812,7 @@ namespace argparse std::vector values; for (std::size_t i = 0; i < number; ++i) { - values.push_back(consume_arg(args)); + values.push_back(consume_arg(args[i])); } return values; } From 3e1c8af1f5cebfe8b537d7cfa3a28e3fb1b21cf6 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Thu, 20 Feb 2025 00:39:17 +0100 Subject: [PATCH 4/7] Change passing and consuming args --- include/argparse.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index 3ccbaa3f..207cf5b5 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -692,11 +692,11 @@ namespace argparse { if (has_nargs_number()) { - parse_arguments_number(args, std::min(get_nargs_number(), args.size())); + parse_arguments_number(consumable, get_nargs_number()); } else { - parse_arguments_option(args); + parse_arguments_option(consumable); } } else @@ -752,14 +752,14 @@ namespace argparse } private: - auto parse_arguments_number(tokens & args, std::size_t number) -> void + auto parse_arguments_number(std::ranges::view auto args, std::size_t number) -> void { - auto values = consume_args(args, number); + auto values = consume_args(args | std::views::take(number)); m_value = m_options.type_handler->transform(values); } - auto parse_arguments_option(tokens & args) -> void + auto parse_arguments_option(std::ranges::view auto args) -> void { switch (get_nargs_option()) { @@ -782,7 +782,7 @@ namespace argparse } case one_or_more: { - auto values = consume_args(args, args.size()); + auto values = consume_args(args); if (!values.empty()) { m_value = m_options.type_handler->transform(values); @@ -807,12 +807,12 @@ namespace argparse return value; } - auto consume_args(tokens & args, std::size_t number) const -> std::vector + auto consume_args(std::ranges::view auto args) const -> std::vector { std::vector values; - for (std::size_t i = 0; i < number; ++i) + for (auto & arg : args) { - values.push_back(consume_arg(args[i])); + values.push_back(consume_arg(arg)); } return values; } From db55fa4c2767ed165b5d12b0b844af08d216d9bf Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Thu, 20 Feb 2025 00:44:20 +0100 Subject: [PATCH 5/7] Refactor: change function signature --- include/argparse.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index 207cf5b5..85ce5d65 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -692,7 +692,7 @@ namespace argparse { if (has_nargs_number()) { - parse_arguments_number(consumable, get_nargs_number()); + parse_arguments_number(consumable | std::views::take(get_nargs_number())); } else { @@ -752,9 +752,9 @@ namespace argparse } private: - auto parse_arguments_number(std::ranges::view auto args, std::size_t number) -> void + auto parse_arguments_number(std::ranges::view auto args) -> void { - auto values = consume_args(args | std::views::take(number)); + auto values = consume_args(args); m_value = m_options.type_handler->transform(values); } @@ -777,7 +777,7 @@ namespace argparse } case zero_or_more: { - parse_arguments_number(args, args.size()); + parse_arguments_number(args); break; } case one_or_more: From c65113386d245a6c2b8d558d149f3a34b723a23c Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Thu, 20 Feb 2025 00:47:17 +0100 Subject: [PATCH 6/7] Cleanup: rename function --- include/argparse.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index 85ce5d65..e5283d3f 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -692,7 +692,7 @@ namespace argparse { if (has_nargs_number()) { - parse_arguments_number(consumable | std::views::take(get_nargs_number())); + parse_arguments(consumable | std::views::take(get_nargs_number())); } else { @@ -752,7 +752,7 @@ namespace argparse } private: - auto parse_arguments_number(std::ranges::view auto args) -> void + auto parse_arguments(std::ranges::view auto args) -> void { auto values = consume_args(args); @@ -777,7 +777,7 @@ namespace argparse } case zero_or_more: { - parse_arguments_number(args); + parse_arguments(args); break; } case one_or_more: From cdf6cf910ac83df24d9b99208deb6ed85e8d006a Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Thu, 20 Feb 2025 00:53:46 +0100 Subject: [PATCH 7/7] Refactor: replace raw loop with a function --- include/argparse.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index e5283d3f..b2976d9c 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -810,10 +810,7 @@ namespace argparse auto consume_args(std::ranges::view auto args) const -> std::vector { std::vector values; - for (auto & arg : args) - { - values.push_back(consume_arg(arg)); - } + std::ranges::copy(std::views::transform(args, [this](auto & arg) { return consume_arg(arg); }), std::back_inserter(values)); return values; }