Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions include/Parser.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include <cstdlib>
#include <cstddef>
#include <iostream>
#include <optional>
#include <string>
Expand All @@ -20,7 +20,10 @@ class ClapParser {
requires Parseable<T>
std::optional<T> get_one_as(const std::string& name) {
Arg* arg = ok_or(ClapParser::find_arg(*this, "--" + name), [] { return std::nullopt; });
return Parse<T>::parse(arg->get__value().value());

return Parse<T>::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);
Expand All @@ -37,8 +40,10 @@ class ClapParser {
static std::optional<Arg*> find_arg(ClapParser& parser, const std::string& name);
void apply_defaults();

void parse_cli_args(const std::vector<std::string>& args);
void parse_cli_args(std::vector<std::string>& args);
void check_env();
void parse_positional_args(const std::vector<std::string>& args);
static void parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std::vector<std::string>& args);
static void parse_value_for_flag(Arg* arg, size_t& cli_index, const std::vector<std::string>& args);
static void analyze_token(std::string& token, size_t& cli_index, std::vector<std::string>& args);
};
87 changes: 83 additions & 4 deletions src/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,35 +37,60 @@ 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<std::string>& args) {
void ClapParser::parse_cli_args(std::vector<std::string>& 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) {
const auto& token = args.at(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") {
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);
// 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 {
arg->set__value("1");
// 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<std::string>& 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;
while (cli_index + 1 < args.size() && !is_option(args.at(cli_index + 1))) {
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
}
Expand All @@ -74,6 +99,60 @@ 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<std::string>& 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<std::string>& 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 ( ";
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";
}
}

void ClapParser::check_env() {
for (auto& arg : args_) {
if (arg.get__auto_env()) {
Expand Down