From 7fbd662e126572933462d2e0263e3289d3092eda Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 22 May 2025 08:17:34 +0200 Subject: [PATCH 1/4] fix(Parser): throw, when user wants a value of a non-specified, but non-required arg --- include/Parser.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 2c29de0..588996c 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -20,7 +20,10 @@ class ClapParser { requires Parseable std::optional get_one_as(const std::string& name) { Arg* arg = ok_or(ClapParser::find_arg(*this, "--" + name), [] { return std::nullopt; }); - return Parse::parse(arg->get__value().value()); + + return Parse::parse( + ok_or_throw_str(arg->get__value(), "value for option: " + quote(arg->get__name()) + " is missing") + ); } static void print_parser(std::ostream& os, const ClapParser& parser, int indent); From 998c11591bf4f957981103e5a55addbc9e53bb19 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 22 May 2025 08:33:58 +0200 Subject: [PATCH 2/4] feat(Parser): introduce much improved value parsing (--val=abc and alike) --- include/Parser.hpp | 4 +++- src/Parser.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 588996c..466fa24 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -40,8 +40,10 @@ class ClapParser { static std::optional find_arg(ClapParser& parser, const std::string& name); void apply_defaults(); - void parse_cli_args(const std::vector& args); + void parse_cli_args(std::vector& args); void check_env(); void parse_positional_args(const std::vector& args); static void parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std::vector& args); + static void parse_value_for_flag(Arg* arg, size_t& cli_index, const std::vector& args); + static void analyze_token(std::string& token, size_t& cli_index, std::vector& args); }; diff --git a/src/Parser.cpp b/src/Parser.cpp index cdfb31c..ff3eea9 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -37,21 +37,26 @@ void ClapParser::parse(const int& argc, char* argv[]) { void ClapParser::add_arg(const Arg& arg) { args_.emplace_back(arg); } -void ClapParser::parse_cli_args(const std::vector& args) { +void ClapParser::parse_cli_args(std::vector& args) { for (size_t i = 0; i < args.size(); ++i) { - const auto& token = args.at(i); + std::string token = args.at(i); + // TODO this could be better with string view contains? if (token == "--help" || token == "-h") { print_help(); exit(0); } - auto* arg = ok_or_throw_str(ClapParser::find_arg(*this, token), "unknown option: \'" + token); + // solve --opt="value" stuff + ClapParser::analyze_token(token, i, args); + + + auto* arg = ok_or_throw_str(ClapParser::find_arg(*this, token), "unknown option: " + quote(token)); if (!arg->get__is_flag()) { ClapParser::parse_value_for_non_flag(arg, i, args); } else { - arg->set__value("1"); + ClapParser::parse_value_for_flag(arg, i, args); } } } @@ -74,6 +79,42 @@ void ClapParser::parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std } } +void ClapParser::parse_value_for_flag(Arg* arg, size_t& cli_index, const std::vector& args) { + if (cli_index + 1 < args.size() && !is_option(args.at(cli_index + 1))) { + if (args.at(cli_index + 1) == "true" || args.at(cli_index + 1) == "1") { + arg->set__value("1"); + cli_index++; + } else if (args.at(cli_index + 1) == "false" || args.at(cli_index + 1) == "0") { + arg->set__value("0"); + cli_index++; + } else { + throw std::runtime_error("boolean option " + quote(arg->get__name()) + " strictly takes: true|false|1|0 (got: " + args.at(cli_index + 1) + ")"); + } + } else { + arg->set__value("1"); + } +} + +void ClapParser::analyze_token(std::string& token, size_t& cli_index, std::vector& args) { + if (token.contains('=')) { + std::cerr << "'=' found, separating token ( "; + const auto middle = token.find('='); + + std::string token_name = token.substr(0, middle); + std::string token_value = token.substr(middle + 1); + if (token_value.empty()) { + throw std::runtime_error("value not specified after '='"); + } + + args.at(cli_index) = token_value; + cli_index--; + + token = token_name; + } else { + std::cerr << "ending token analysis, left alone\n"; + } +} + void ClapParser::check_env() { for (auto& arg : args_) { if (arg.get__auto_env()) { From 969429e46ea4f1ca99034d19957ed8a949960dac Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 22 May 2025 08:35:20 +0200 Subject: [PATCH 3/4] fix(Parser): removed unused import --- include/Parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 466fa24..ad9f026 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include From c9cba4d508f74289aea1c22d951d394111d4a5d8 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 22 May 2025 08:39:47 +0200 Subject: [PATCH 4/4] misc(Parser): don't debug to std::cerr, proper logging soon... --- src/Parser.cpp | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Parser.cpp b/src/Parser.cpp index ff3eea9..8f3ee01 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -38,8 +38,16 @@ void ClapParser::parse(const int& argc, char* argv[]) { void ClapParser::add_arg(const Arg& arg) { args_.emplace_back(arg); } void ClapParser::parse_cli_args(std::vector& args) { + // std::cerr << "\nstart cli arg parsing\n"; + // std::cerr << "args vector: ["; + // for (const auto& i : args) { + // std::cerr << quote(i) << " "; + // } + // std::cerr << "]\n"; + for (size_t i = 0; i < args.size(); ++i) { std::string token = args.at(i); + // std::cerr << "\ngoing through args: size=" << args.size() << ", current token=" << quote(token) << ", index=" << i << "\n"; // TODO this could be better with string view contains? if (token == "--help" || token == "-h") { @@ -49,19 +57,29 @@ void ClapParser::parse_cli_args(std::vector& args) { // solve --opt="value" stuff ClapParser::analyze_token(token, i, args); + // std::cerr << "args vector AGAIN: ["; + // for (const auto& i : args) { + // std::cerr << quote(i) << " "; + // } + // std::cerr << "]\n"; auto* arg = ok_or_throw_str(ClapParser::find_arg(*this, token), "unknown option: " + quote(token)); if (!arg->get__is_flag()) { + // std::cerr << "\nparsing non-flag\n"; ClapParser::parse_value_for_non_flag(arg, i, args); } else { + // std::cerr << "\nparsing flag\n"; ClapParser::parse_value_for_flag(arg, i, args); } } + std::cerr << "cli arg parsing done\n"; + std::cerr << *this << "\n"; } void ClapParser::parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std::vector& args) { + // std::cerr << "\ncli_ind, and arg size: " << cli_index << " " << args.size() << "\n"; if (cli_index + 1 < args.size() && !is_option(args.at(cli_index + 1))) { if (arg->get__accepts_many()) { std::string value; @@ -69,8 +87,10 @@ void ClapParser::parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std value += args.at(cli_index + 1) + ' '; cli_index++; } + // std::cerr << "value " << value << "\n"; arg->set__value(value); } else { + // std::cerr << "value " << args.at(cli_index + 1) << "\n"; arg->set__value(args.at(cli_index + 1)); cli_index++; // Skip the value in the next iteration } @@ -80,38 +100,56 @@ void ClapParser::parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std } void ClapParser::parse_value_for_flag(Arg* arg, size_t& cli_index, const std::vector& args) { + // std::cerr << "\ncli_ind, and arg size: " << cli_index << " " << args.size() << "\n"; if (cli_index + 1 < args.size() && !is_option(args.at(cli_index + 1))) { + // std::cerr << "found flag with value in cli value: ("; if (args.at(cli_index + 1) == "true" || args.at(cli_index + 1) == "1") { + // std::cerr << args.at(cli_index + 1) << ")\n"; arg->set__value("1"); cli_index++; } else if (args.at(cli_index + 1) == "false" || args.at(cli_index + 1) == "0") { + // std::cerr << args.at(cli_index + 1) << ")\n"; arg->set__value("0"); cli_index++; } else { + // std::cerr << "WRONG VALUE, throwing\n"; throw std::runtime_error("boolean option " + quote(arg->get__name()) + " strictly takes: true|false|1|0 (got: " + args.at(cli_index + 1) + ")"); } } else { + // std::cerr << "no value for flag, fallback to 1\n"; arg->set__value("1"); } } void ClapParser::analyze_token(std::string& token, size_t& cli_index, std::vector& args) { + // std::cerr << "\nstarting token analysis for: " << quote(token) << "\n"; + // std::cerr << "[WARNING]: might mess up arg vector!\n"; if (token.contains('=')) { - std::cerr << "'=' found, separating token ( "; + // std::cerr << "'=' found, separating token ( "; const auto middle = token.find('='); std::string token_name = token.substr(0, middle); + // std::cerr << "name: " << quote(token_name) << ", "; std::string token_value = token.substr(middle + 1); if (token_value.empty()) { throw std::runtime_error("value not specified after '='"); } + // std::cerr << "value: " << quote(token_value) << " )\n"; args.at(cli_index) = token_value; + // std::cerr << "TEST: " << cli_index << "\n"; cli_index--; + // std::cerr << "args vector: ["; + // for (const auto& i : args) { + // std::cerr << quote(i) << " "; + // } + // std::cerr << "]\n"; + + // std::cerr << "ending token analysis\n"; token = token_name; } else { - std::cerr << "ending token analysis, left alone\n"; + // std::cerr << "ending token analysis, left alone\n"; } }