use new arg parse + defaults now assigned

v1
Brett 2023-08-07 17:42:59 -04:00
parent b083245ab4
commit 2a656a687e
7 changed files with 586 additions and 1230 deletions

View File

@ -119,13 +119,6 @@ build CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o: CXX_COMPILER__BLT_unscann
OBJECT_DIR = CMakeFiles/BLT.dir OBJECT_DIR = CMakeFiles/BLT.dir
OBJECT_FILE_DIR = CMakeFiles/BLT.dir/src/blt/parse OBJECT_FILE_DIR = CMakeFiles/BLT.dir/src/blt/parse
build CMakeFiles/BLT.dir/src/blt/parse/argparse_2.cpp.o: CXX_COMPILER__BLT_unscanned_RelWithDebInfo /home/brett/Documents/code/c++/BLT/src/blt/parse/argparse_2.cpp || cmake_object_order_depends_target_BLT
DEP_FILE = CMakeFiles/BLT.dir/src/blt/parse/argparse_2.cpp.o.d
FLAGS = -O2 -g -DNDEBUG -std=gnu++20 -fdiagnostics-color=always -Wall -Wextra -Wpedantic
INCLUDES = -I/home/brett/Documents/code/c++/BLT/libraries/parallel-hashmap -I/home/brett/Documents/code/c++/BLT/include -I/home/brett/Documents/code/c++/BLT/cmake-build-reldebug-asan/config
OBJECT_DIR = CMakeFiles/BLT.dir
OBJECT_FILE_DIR = CMakeFiles/BLT.dir/src/blt/parse
# ============================================================================= # =============================================================================
# Link build statements for STATIC_LIBRARY target BLT # Link build statements for STATIC_LIBRARY target BLT
@ -134,7 +127,7 @@ build CMakeFiles/BLT.dir/src/blt/parse/argparse_2.cpp.o: CXX_COMPILER__BLT_unsca
############################################# #############################################
# Link the static library libBLT.a # Link the static library libBLT.a
build libBLT.a: CXX_STATIC_LIBRARY_LINKER__BLT_RelWithDebInfo CMakeFiles/BLT.dir/src/blt/std/filesystem.cpp.o CMakeFiles/BLT.dir/src/blt/std/format.cpp.o CMakeFiles/BLT.dir/src/blt/std/loader.cpp.o CMakeFiles/BLT.dir/src/blt/std/logging.cpp.o CMakeFiles/BLT.dir/src/blt/std/string.cpp.o CMakeFiles/BLT.dir/src/blt/std/system.cpp.o CMakeFiles/BLT.dir/src/blt/profiling/profiler.cpp.o CMakeFiles/BLT.dir/src/blt/nbt/nbt.cpp.o CMakeFiles/BLT.dir/src/blt/nbt/nbt_block.cpp.o CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o CMakeFiles/BLT.dir/src/blt/parse/argparse_2.cpp.o build libBLT.a: CXX_STATIC_LIBRARY_LINKER__BLT_RelWithDebInfo CMakeFiles/BLT.dir/src/blt/std/filesystem.cpp.o CMakeFiles/BLT.dir/src/blt/std/format.cpp.o CMakeFiles/BLT.dir/src/blt/std/loader.cpp.o CMakeFiles/BLT.dir/src/blt/std/logging.cpp.o CMakeFiles/BLT.dir/src/blt/std/string.cpp.o CMakeFiles/BLT.dir/src/blt/std/system.cpp.o CMakeFiles/BLT.dir/src/blt/profiling/profiler.cpp.o CMakeFiles/BLT.dir/src/blt/nbt/nbt.cpp.o CMakeFiles/BLT.dir/src/blt/nbt/nbt_block.cpp.o CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o
LANGUAGE_COMPILE_FLAGS = -O2 -g -DNDEBUG LANGUAGE_COMPILE_FLAGS = -O2 -g -DNDEBUG
OBJECT_DIR = CMakeFiles/BLT.dir OBJECT_DIR = CMakeFiles/BLT.dir
POST_BUILD = : POST_BUILD = :

View File

@ -1,5 +1,5 @@
/* /*
* Created by Brett on 28/07/23. * Created by Brett on 06/08/23.
* Licensed under GNU General Public License V3.0 * Licensed under GNU General Public License V3.0
* See LICENSE file for license detail * See LICENSE file for license detail
*/ */
@ -7,17 +7,22 @@
#ifndef BLT_TESTS_ARGPARSE_H #ifndef BLT_TESTS_ARGPARSE_H
#define BLT_TESTS_ARGPARSE_H #define BLT_TESTS_ARGPARSE_H
#include <utility>
#include <vector> #include <vector>
#include <string> #include <string>
#include <initializer_list> #include <initializer_list>
#include <optional> #include <optional>
#include <blt/std/hashmap.h> #include <blt/std/hashmap.h>
#include <variant> #include <variant>
#include <blt/std/logging.h>
namespace blt::parser { namespace blt
{
typedef std::variant<std::string, bool, int32_t> arg_data_internal_t;
typedef std::vector<arg_data_internal_t> arg_data_vec_t;
typedef std::variant<arg_data_internal_t, arg_data_vec_t> arg_data_t;
enum class arg_action_t { enum class arg_action_t
{
STORE, STORE,
STORE_CONST, STORE_CONST,
STORE_TRUE, STORE_TRUE,
@ -30,73 +35,50 @@ namespace blt::parser {
EXTEND EXTEND
}; };
enum class arg_result_t { class arg_vector_t
BOOL, {
VALUE,
VECTOR
};
class arg_vector_t {
private:
std::vector<std::string> names;
std::vector<std::string> flags;
bool isFlags = false;
void insertAndSort(const std::string& str);
public:
arg_vector_t() = default;
arg_vector_t(const std::vector<std::string>& args);
arg_vector_t(std::initializer_list<std::string> args);
arg_vector_t(const std::string& arg);
arg_vector_t(const char* arg);
arg_vector_t& operator=(const std::string& arg);
arg_vector_t& operator=(const char* arg);
arg_vector_t& operator=(std::initializer_list<std::string>& args);
arg_vector_t& operator=(std::vector<std::string>& args);
[[nodiscard]] inline std::vector<std::string>& getNames() {
return names;
}
[[nodiscard]] inline std::vector<std::string>& getFlags() {
return flags;
}
[[nodiscard]] inline const std::vector<std::string>& getNames() const {
return names;
}
[[nodiscard]] inline const std::vector<std::string>& getFlags() const {
return flags;
}
/**
* @return true if contains flags
*/
[[nodiscard]] inline bool isFlag() const {
return isFlags;
}
[[nodiscard]] inline bool contains(std::string_view string) const {
return std::any_of(flags.begin(), flags.end(), [&string](const auto& flag) -> bool {
return flag == string;
}) || std::any_of(names.begin(), names.end(), [&string](const auto& name) -> bool {
return name == string;
});
}
};
class arg_nargs_t {
private:
friend class arg_parse; friend class arg_parse;
private:
std::vector<std::string> flags;
std::string name;
void validateFlags();
public:
arg_vector_t(std::vector<std::string> flags): flags(std::move(flags))
{
validateFlags();
}
arg_vector_t(std::initializer_list<std::string> flags): flags(flags)
{
validateFlags();
}
arg_vector_t(const char* str);
arg_vector_t(const std::string& str);
[[nodiscard]] inline bool isFlag() const
{
return !flags.empty();
}
[[nodiscard]] inline bool contains(const std::string& str)
{
return std::any_of(
flags.begin(), flags.end(), [&str](const std::string& flag) -> bool {
return flag == str;
}
) || str == name;
}
};
class arg_nargs_t
{
friend class arg_parse;
private:
static constexpr int UNKNOWN = 0x1; static constexpr int UNKNOWN = 0x1;
static constexpr int ALL = 0x2; static constexpr int ALL = 0x2;
static constexpr int ALL_REQUIRED = 0x4; static constexpr int ALL_REQUIRED = 0x4;
@ -110,182 +92,239 @@ namespace blt::parser {
public: public:
arg_nargs_t() = default; arg_nargs_t() = default;
arg_nargs_t(int args): args(args) {} arg_nargs_t(int args): args(args)
{}
arg_nargs_t(char c); arg_nargs_t(char c);
arg_nargs_t(std::string s); arg_nargs_t(std::string s);
arg_nargs_t(const char* s); arg_nargs_t(const char* s);
arg_nargs_t& operator=(const std::string& s);
arg_nargs_t& operator=(const char* s);
arg_nargs_t& operator=(char c);
arg_nargs_t& operator=(int args);
}; };
struct arg_properties_t { struct arg_properties_t
{
private: private:
public: public:
arg_vector_t a_flags; arg_vector_t a_flags;
arg_action_t a_action = arg_action_t::STORE; arg_action_t a_action = arg_action_t::STORE;
arg_nargs_t a_nargs = 1; arg_nargs_t a_nargs = 1;
std::string a_const{}; std::string a_const{};
std::string a_default{}; arg_data_internal_t a_default{};
std::string a_dest{}; std::string a_dest{};
std::string a_help{}; std::string a_help{};
std::string a_version{}; std::string a_version{};
std::string a_metavar{}; std::string a_metavar{};
bool a_required = false; bool a_required = true;
}; };
class arg_builder { class arg_builder
{
arg_properties_t properties; arg_properties_t properties;
public: public:
arg_builder(const arg_vector_t& flags): properties(flags) {} arg_builder(const arg_vector_t& flags): properties(flags)
{}
inline arg_properties_t build() { inline arg_properties_t build()
{
return properties; return properties;
} }
inline arg_builder& setAction(arg_action_t action){ inline arg_builder& setAction(arg_action_t action)
{
properties.a_action = action; properties.a_action = action;
return *this; return *this;
} }
inline arg_builder& setNArgs(const arg_nargs_t& nargs){ inline arg_builder& setNArgs(const arg_nargs_t& nargs)
{
properties.a_nargs = nargs; properties.a_nargs = nargs;
return *this; return *this;
} }
inline arg_builder& setConst(const std::string& a_const){ inline arg_builder& setConst(const std::string& a_const)
{
properties.a_const = a_const; properties.a_const = a_const;
return *this; return *this;
} }
inline arg_builder& setDefault(const std::string& def){ inline arg_builder& setDefault(const arg_data_internal_t& def)
{
properties.a_default = def; properties.a_default = def;
return *this; return *this;
} }
inline arg_builder& setDest(const std::string& dest){ inline arg_builder& setDest(const std::string& dest)
{
properties.a_dest = dest; properties.a_dest = dest;
return *this; return *this;
} }
inline arg_builder& setHelp(const std::string& help){ inline arg_builder& setHelp(const std::string& help)
{
properties.a_help = help; properties.a_help = help;
return *this; return *this;
} }
inline arg_builder& setVersion(const std::string& version){ inline arg_builder& setVersion(const std::string& version)
{
properties.a_version = version; properties.a_version = version;
return *this; return *this;
} }
inline arg_builder& setMetavar(const std::string& metavar){ inline arg_builder& setMetavar(const std::string& metavar)
{
properties.a_metavar = metavar; properties.a_metavar = metavar;
return *this; return *this;
} }
inline arg_builder& setRequired(){ inline arg_builder& setRequired()
{
properties.a_required = true; properties.a_required = true;
return *this; return *this;
} }
};
class arg_tokenizer_t {
private:
static constexpr char FLAG = '-';
std::vector<std::string> args;
size_t nextIndex = 0;
inline const std::string& get(size_t i) {
return args[i];
}
inline bool hasNext(size_t i) {
return (size_t) i < args.size();
}
};
class arg_tokenizer
{
private:
std::vector<std::string> args;
size_t currentIndex = 0;
public: public:
arg_tokenizer_t() = default; arg_tokenizer(std::vector<std::string> args): args(std::move(args))
{}
arg_tokenizer_t(size_t argc, const char** argv); // returns the current arg
inline const std::string& get()
inline void forward() { {
nextIndex++; return args[currentIndex];
} }
inline const std::string& get() { // returns if we have next arg to process
return get(nextIndex); inline bool hasNext()
{
return currentIndex + 1 < args.size();
} }
inline const std::string& next() { inline bool hasCurrent()
return get(nextIndex++); {
return currentIndex < args.size();
} }
inline bool hasNext() { // returns true if the current arg is a flag
return hasNext(nextIndex); inline bool isFlag()
{
return args[currentIndex].starts_with('-');
} }
inline bool isFlag(size_t i) { // returns true if we have next and the next arg is a flag
return get(i).starts_with(FLAG); inline bool isNextFlag()
{
return hasNext() && args[currentIndex + 1].starts_with('-');
} }
inline bool isFlag() { // advances to the next flag
return isFlag(nextIndex); inline size_t advance()
{
return currentIndex++;
} }
}; };
class arg_parse { class arg_parse
public: {
typedef std::variant<std::string, bool, int32_t, std::vector<std::string>> arg_data_t;
private: private:
struct { struct
{
friend arg_parse; friend arg_parse;
private: private:
std::vector<arg_properties_t> arg_storage; std::vector<arg_properties_t*> arg_properties_storage;
public: public:
std::vector<std::pair<std::string, arg_properties_t*>> name_associations; std::vector<arg_properties_t*> name_associations;
HASHMAP<std::string, arg_properties_t*> flag_associations; HASHMAP<std::string, arg_properties_t*> flag_associations;
HASHSET<std::string> required_vars;
} user_args; } user_args;
struct arg_results { struct arg_results
{
friend arg_parse; friend arg_parse;
private: private:
HASHSET<std::string> found_required; // stores dest value not the flag/name!
HASHSET<std::string> found_args;
std::vector<std::string> unrecognized_args; std::vector<std::string> unrecognized_args;
public: public:
std::string program_name; std::string program_name;
HASHMAP <std::string, arg_data_t> positional_args; HASHMAP<std::string, arg_data_t> data;
HASHMAP <std::string, arg_data_t> flag_args;
inline arg_data_t& operator[](const std::string& key)
{
return data[key];
}
inline auto begin()
{
return data.begin();
}
inline auto end()
{
return data.end();
}
inline bool contains(const std::string& key)
{
return data.find(key) != data.end();
}
} loaded_args; } loaded_args;
private: private:
static std::string filename(const std::string& path); static std::string filename(const std::string& path);
static bool validateArgument(const arg_properties_t& args);
static bool consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector<std::string>& v_out); // expects that the current flag has already been consumed (advanced past), leaves tokenizer in a state where the next element is 'current'
void handlePositionalArgument(arg_tokenizer_t& arg_tokenizer, size_t& last_pos); static bool consumeArguments(arg_tokenizer& tokenizer, const arg_properties_t& properties, std::vector<arg_data_internal_t>& v_out);
void handleFlagArgument(arg_tokenizer_t& arg_tokenizer);
void processFlag(arg_tokenizer_t& arg_tokenizer, const std::string& flag); void handlePositionalArgument(arg_tokenizer& tokenizer, size_t& last_pos);
void handleFlagArgument(arg_tokenizer& tokenizer);
void processFlag(arg_tokenizer& tokenizer, const std::string& flag);
template<typename T>
static inline bool holds_alternative(const arg_data_t& v){
if constexpr (std::is_same_v<T, arg_data_vec_t>)
return std::holds_alternative<T>(v);
else
return std::holds_alternative<arg_data_internal_t>(v) && std::holds_alternative<T>(std::get<arg_data_internal_t>(v));
}
template<typename T>
static inline T& get(arg_data_t& v){
if constexpr (std::is_same_v<T, arg_data_vec_t>)
return std::get<arg_data_vec_t>(v);
else
return std::get<T>(std::get<arg_data_internal_t>(v));
}
public: public:
arg_parse() { arg_parse()
//addArgument(arg_builder({"--help", "-h"}).setAction(arg_action_t::HELP).setHelp("Show this help menu").build()); {
addArgument(arg_builder({"--help", "-h"}).setAction(arg_action_t::HELP).setHelp("Show this help menu").build());
}; };
void addArgument(const arg_properties_t& args); void addArgument(const arg_properties_t& args);
const arg_results& parse_args(int argc, const char** argv); arg_results parse_args(int argc, const char** argv);
arg_results parse_args(const std::vector<std::string>& args);
void printHelp(); void printHelp();
~arg_parse()
{
for (auto* p : user_args.arg_properties_storage)
delete p;
}
}; };
std::string to_string(const blt::parser::arg_parse::arg_data_t& v); std::string to_string(const blt::arg_data_t& v);
std::string to_string(const blt::arg_data_internal_t& v);
} }

View File

@ -1,337 +0,0 @@
/*
* Created by Brett on 06/08/23.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#ifndef BLT_TESTS_ARGPARSE_2_H
#define BLT_TESTS_ARGPARSE_2_H
#include <utility>
#include <vector>
#include <string>
#include <initializer_list>
#include <optional>
#include <blt/std/hashmap.h>
#include <variant>
namespace blt
{
typedef std::variant<std::string, bool, int32_t> arg_data_internal_t;
typedef std::vector<arg_data_internal_t> arg_data_vec_t;
typedef std::variant<arg_data_internal_t, arg_data_vec_t> arg_data_t;
enum class arg_action_t
{
STORE,
STORE_CONST,
STORE_TRUE,
STORE_FALSE,
APPEND,
APPEND_CONST,
COUNT,
HELP,
VERSION,
EXTEND
};
class arg_vector_t
{
friend class arg_parse;
private:
std::vector<std::string> flags;
std::string name;
void validateFlags();
public:
arg_vector_t(std::vector<std::string> flags): flags(std::move(flags))
{
validateFlags();
}
arg_vector_t(std::initializer_list<std::string> flags): flags(flags)
{
validateFlags();
}
arg_vector_t(const char* str);
arg_vector_t(const std::string& str);
[[nodiscard]] inline bool isFlag() const
{
return !flags.empty();
}
[[nodiscard]] inline bool contains(const std::string& str)
{
return std::any_of(
flags.begin(), flags.end(), [&str](const std::string& flag) -> bool {
return flag == str;
}
) || str == name;
}
};
class arg_nargs_t
{
friend class arg_parse;
private:
static constexpr int UNKNOWN = 0x1;
static constexpr int ALL = 0x2;
static constexpr int ALL_REQUIRED = 0x4;
// 0 means ignore
int args = 1;
// 0 indicates args is used
int flags = 0;
void decode(char c);
public:
arg_nargs_t() = default;
arg_nargs_t(int args): args(args)
{}
arg_nargs_t(char c);
arg_nargs_t(std::string s);
arg_nargs_t(const char* s);
};
struct arg_properties_t
{
private:
public:
arg_vector_t a_flags;
arg_action_t a_action = arg_action_t::STORE;
arg_nargs_t a_nargs = 1;
std::string a_const{};
arg_data_internal_t a_default{};
std::string a_dest{};
std::string a_help{};
std::string a_version{};
std::string a_metavar{};
bool a_required = true;
};
class arg_builder
{
arg_properties_t properties;
public:
arg_builder(const arg_vector_t& flags): properties(flags)
{}
inline arg_properties_t build()
{
return properties;
}
inline arg_builder& setAction(arg_action_t action)
{
properties.a_action = action;
return *this;
}
inline arg_builder& setNArgs(const arg_nargs_t& nargs)
{
properties.a_nargs = nargs;
return *this;
}
inline arg_builder& setConst(const std::string& a_const)
{
properties.a_const = a_const;
return *this;
}
inline arg_builder& setDefault(const arg_data_internal_t& def)
{
properties.a_default = def;
return *this;
}
inline arg_builder& setDest(const std::string& dest)
{
properties.a_dest = dest;
return *this;
}
inline arg_builder& setHelp(const std::string& help)
{
properties.a_help = help;
return *this;
}
inline arg_builder& setVersion(const std::string& version)
{
properties.a_version = version;
return *this;
}
inline arg_builder& setMetavar(const std::string& metavar)
{
properties.a_metavar = metavar;
return *this;
}
inline arg_builder& setRequired()
{
properties.a_required = true;
return *this;
}
};
class arg_tokenizer
{
private:
std::vector<std::string> args;
size_t currentIndex = 0;
public:
arg_tokenizer(std::vector<std::string> args): args(std::move(args))
{}
// returns the current arg
inline const std::string& get()
{
return args[currentIndex];
}
// returns if we have next arg to process
inline bool hasNext()
{
return currentIndex + 1 < args.size();
}
inline bool hasCurrent()
{
return currentIndex < args.size();
}
// returns true if the current arg is a flag
inline bool isFlag()
{
return args[currentIndex].starts_with('-');
}
// returns true if we have next and the next arg is a flag
inline bool isNextFlag()
{
return hasNext() && args[currentIndex + 1].starts_with('-');
}
// advances to the next flag
inline size_t advance()
{
return currentIndex++;
}
};
class arg_parse
{
private:
struct
{
friend arg_parse;
private:
std::vector<arg_properties_t*> arg_properties_storage;
public:
std::vector<arg_properties_t*> name_associations;
HASHMAP<std::string, arg_properties_t*> flag_associations;
} user_args;
struct arg_results
{
friend arg_parse;
private:
HASHSET<std::string> found_args;
std::vector<std::string> unrecognized_args;
public:
std::string program_name;
HASHMAP<std::string, arg_data_t> data;
inline arg_data_t& operator[](const std::string& key)
{
return data[key];
}
inline auto begin()
{
return data.begin();
}
inline auto end()
{
return data.end();
}
inline bool contains(const std::string& key)
{
return data.find(key) != data.end();
}
} loaded_args;
private:
static std::string filename(const std::string& path);
// expects that the current flag has already been consumed (advanced past), leaves tokenizer in a state where the next element is 'current'
static bool consumeArguments(arg_tokenizer& tokenizer, const arg_properties_t& properties, std::vector<arg_data_internal_t>& v_out);
void handlePositionalArgument(arg_tokenizer& tokenizer, size_t& last_pos);
void handleFlagArgument(arg_tokenizer& tokenizer);
void processFlag(arg_tokenizer& tokenizer, const std::string& flag);
template<typename T>
static inline bool holds_alternative(const arg_data_t& v){
if constexpr (std::is_same_v<T, arg_data_vec_t>)
return std::holds_alternative<T>(v);
else
{
if (std::holds_alternative<arg_data_internal_t>(v))
{
arg_data_internal_t data = std::get<arg_data_internal_t>(v);
return std::holds_alternative<T>(data);
} else
return false;
}
}
template<typename T>
static inline T& get(arg_data_t& v){
if constexpr (std::is_same_v<T, arg_data_vec_t>)
return std::get<arg_data_vec_t>(v);
else
return std::get<T>(std::get<arg_data_internal_t>(v));
}
public:
arg_parse()
{
addArgument(arg_builder({"--help", "-h"}).setAction(arg_action_t::HELP).setHelp("Show this help menu").build());
};
void addArgument(const arg_properties_t& args);
arg_results parse_args(int argc, const char** argv);
arg_results parse_args(const std::vector<std::string>& args);
void printHelp();
~arg_parse()
{
for (auto* p : user_args.arg_properties_storage)
delete p;
}
};
std::string to_string(const blt::arg_data_t& v);
std::string to_string(const blt::arg_data_internal_t& v);
}
#endif //BLT_TESTS_ARGPARSE_2_H

View File

@ -1,351 +1,404 @@
/* /*
* Created by Brett on 28/07/23. * Created by Brett on 06/08/23.
* Licensed under GNU General Public License V3.0 * Licensed under GNU General Public License V3.0
* See LICENSE file for license detail * See LICENSE file for license detail
*/ */
#include "blt/std/logging.h" #include <blt/parse/argparse.h>
#include <blt/std/logging.h>
#include <blt/std/string.h> #include <blt/std/string.h>
//namespace blt::parser { namespace blt
// {
// arg_vector_t::arg_vector_t(const std::vector<std::string>& args) {
// for (auto& arg : args) void arg_nargs_t::decode(char c)
// insertAndSort(arg); {
// } if (c == '?')
// flags = UNKNOWN;
// arg_vector_t::arg_vector_t(std::initializer_list<std::string> args) { else if (c == '+')
// for (auto& arg : args) flags = ALL_REQUIRED;
// insertAndSort(arg); else if (c == '*')
// } flags = ALL;
// else
// arg_vector_t::arg_vector_t(const std::string& arg) { flags = 0;
// insertAndSort(arg); }
// }
// arg_nargs_t::arg_nargs_t(char c)
// arg_vector_t::arg_vector_t(const char* arg) { {
// insertAndSort(arg); decode(c);
// } }
//
// arg_vector_t& arg_vector_t::operator=(const std::string& arg) { arg_nargs_t::arg_nargs_t(std::string s)
// insertAndSort(arg); {
// return *this; decode(s[0]);
// } }
//
// arg_vector_t& arg_vector_t::operator=(const char* arg) { arg_nargs_t::arg_nargs_t(const char* s)
// insertAndSort(arg); {
// return *this; decode(*s);
// } }
//
// arg_vector_t& arg_vector_t::operator=(std::initializer_list<std::string>& args) { class invalid_argument_exception : public std::runtime_error
// for (auto& arg : args) {
// insertAndSort(arg); public:
// return *this; invalid_argument_exception(const std::string& str): std::runtime_error(str)
// } {}
// };
// arg_vector_t& arg_vector_t::operator=(std::vector<std::string>& args) {
// for (auto& arg : args) void arg_vector_t::validateFlags()
// insertAndSort(arg); {
// return *this; for (const auto& flag : flags)
// } if (!flag.starts_with('-'))
// throw invalid_argument_exception("Flag '" + flag + "' must start with - or --");
// void arg_vector_t::insertAndSort(const std::string& str) { }
// if (str.starts_with('-')) {
// flags.push_back(str); arg_vector_t::arg_vector_t(const char* str) {
// isFlags = true; std::string as_string(str);
// } else { if (as_string.starts_with('-'))
// names.push_back(str); flags.emplace_back(as_string);
// isFlags = false; else
// } name = as_string;
// if (!flags.empty() && !names.empty()) }
// BLT_WARN("Flags provided alongside named positional args. This is not supported!"); arg_vector_t::arg_vector_t(const std::string& str) {
// } if (str.starts_with('-'))
// flags.emplace_back(str);
// arg_nargs_t::arg_nargs_t(char c) { else
// decode(c); name = str;
// } }
//
// arg_nargs_t::arg_nargs_t(std::string s) { std::string to_string(const arg_data_t& v)
// decode(s[0]); {
// } if (holds_alternative<arg_data_internal_t>(v))
// return to_string(std::get<arg_data_internal_t>(v));
// arg_nargs_t::arg_nargs_t(const char* s) { else if (std::holds_alternative<arg_data_vec_t>(v))
// decode(*s); {
// } const auto& vec = std::get<arg_data_vec_t>(v);
// if (vec.size() == 1)
// arg_nargs_t& arg_nargs_t::operator=(const std::string& s) { return to_string(vec[0]);
// decode(s[0]); if (vec.empty())
// return *this; return "Empty Vector";
// } std::string str;
// for (const auto& r : vec)
// arg_nargs_t& arg_nargs_t::operator=(char c) { {
// decode(c); str += to_string(r);
// return *this; str += ' ';
// } }
// return "Vector of contents: " + str;
// arg_nargs_t& arg_nargs_t::operator=(int a) { }
// args = a; return "Empty";
// return *this; }
// }
// std::string to_string(const arg_data_internal_t& v)
// arg_nargs_t& arg_nargs_t::operator=(const char* s) { {
// decode(*s); if (std::holds_alternative<std::string>(v)){
// return *this; return std::get<std::string>(v);
// } } else if (std::holds_alternative<bool>(v)) {
// return std::get<bool>(v) ? "True" : "False";
// void arg_nargs_t::decode(char c) { }
// if (c == '?') return std::to_string(std::get<int32_t>(v));
// flags = UNKNOWN; }
// else if (c == '+')
// flags = ALL_REQUIRED; std::string arg_parse::filename(const std::string& path)
// else if (c == '*') {
// flags = ALL; auto paths = blt::string::split(path, "/");
// else auto final = paths[paths.size() - 1];
// flags = 0; if (final == "/")
// } return paths[paths.size() - 2];
// return final;
// arg_tokenizer_t::arg_tokenizer_t(size_t argc, const char** argv) { }
// for (size_t i = 0; i < argc; i++)
// args.emplace_back(argv[i]); void arg_parse::addArgument(const arg_properties_t& args)
// } {
// auto properties = new arg_properties_t(args);
// bool arg_parse::validateArgument(const arg_properties_t& args) {
// return !(args.a_flags.getFlags().empty() ^ args.a_flags.getNames().empty()); // determine where to store the arg when parsing
// } if (properties->a_dest.empty())
// {
// void arg_parse::addArgument(const arg_properties_t& args) { if (properties->a_flags.isFlag())
// if (validateArgument(args)) { {
// BLT_WARN("Argument contains both flags and positional names, this is not allowed!"); // take first arg so a_dest exists, could be - or --
// return; properties->a_dest = properties->a_flags.flags[0];
// } // look for a -- arg (python's behaviour)
// // store one copy of the properties (arg properties could in theory be very large!) for (const auto& flag : properties->a_flags.flags)
// auto pos = user_args.arg_storage.size(); {
// user_args.arg_storage.push_back(args); if (flag.starts_with("--"))
// auto& arg = user_args.arg_storage[pos]; {
// if (arg.a_dest.empty()) { properties->a_dest = flag;
// // update dest with first full flag (same as python's behaviour) break;
// if (arg.a_flags.isFlag()) { }
// for (const auto& flag : arg.a_flags.getFlags()) { }
// if (flag.starts_with("--")) { } else
// arg.a_dest = flag; properties->a_dest = properties->a_flags.name;
// break; }
// }
// } if (properties->a_dest.starts_with("--"))
// } else { properties->a_dest = properties->a_dest.substr(2);
// for (const auto& name : arg.a_flags.getNames()) { else if (properties->a_dest.starts_with('-'))
// arg.a_dest = name; properties->a_dest = properties->a_dest.substr(1);
// break;
// } // associate flags with their properties
// } for (const auto& flag : properties->a_flags.flags)
// } user_args.flag_associations[flag] = properties;
// BLT_TRACE("Flag %s: %p", arg.a_dest.c_str(), &arg);
// // associate the arg properties per name and flag to allow for quick access when parsing // positional args uses index (vector) to store the properties
// auto& names = arg.a_flags.getNames(); if (!properties->a_flags.isFlag())
// for (const auto& name : names) { user_args.name_associations.push_back(properties);
// user_args.name_associations.emplace_back(name, &arg);
// user_args.required_vars.insert(name); user_args.arg_properties_storage.push_back(properties);
// } }
//
// auto& flags = arg.a_flags.getFlags(); bool arg_parse::consumeArguments(arg_tokenizer& tokenizer, const arg_properties_t& properties, std::vector<arg_data_internal_t>& v_out)
// for (const auto& flag : flags) {
// user_args.flag_associations[flag] = &arg; switch (properties.a_nargs.flags)
// {
// if (arg.a_required) case 0:
// user_args.required_vars.insert(arg.a_dest); for (int i = 0; i < properties.a_nargs.args; i++)
// {
// if (arg.a_nargs.flags == arg_nargs_t::UNKNOWN) { // if we don't have another arg to consume we have a problem!
// if (arg.a_flags.getNames().empty()) { if (!tokenizer.hasCurrent())
// for (const auto& flag : arg.a_flags.getFlags()) {
// loaded_args.flag_args[flag] = arg.a_default; BLT_WARN("Expected %d arguments got %d instead!", properties.a_nargs.args, i);
// } else { return false;
// for (const auto& name : arg.a_flags.getNames()) }
// loaded_args.positional_args[name] = arg.a_default; // if we do have one, but it is a flag then we also have a problem!
// } if (tokenizer.isFlag())
// } {
// BLT_WARN("Expected %d arguments, found flag instead!", properties.a_nargs.args);
// BLT_DEBUG("PRINTING: %d, %d", user_args.arg_storage[pos].a_flags.getFlags().size(), user_args.arg_storage[pos].a_flags.getNames().size()); return false;
// }
// } // get the value and advance
// v_out.emplace_back(tokenizer.get());
// const arg_parse::arg_results& arg_parse::parse_args(int argc, const char** argv) { tokenizer.advance();
// loaded_args = {}; }
// arg_tokenizer_t arg_tokenizer(argc, argv); return true;
// loaded_args.program_name = arg_tokenizer.next(); case arg_nargs_t::UNKNOWN:
// // no arg next
// size_t last_positional = 0; if (!tokenizer.hasCurrent() || tokenizer.isFlag())
// while (arg_tokenizer.hasNext()) { {
// // a token isn't a flag it must be a positional arg as flags will consume nargs // python's default is to store const if around otherwise store default
// if (!arg_tokenizer.isFlag()){ if (!properties.a_const.empty())
// handlePositionalArgument(arg_tokenizer, last_positional); v_out.emplace_back(properties.a_const);
// continue; else // this should no longer be required with the changes to how defaults are handled
// } v_out.emplace_back(properties.a_default);
// handleFlagArgument(arg_tokenizer); return true;
// } }
// v_out.emplace_back(tokenizer.get());
// if (loaded_args.unrecognized_args.empty()) tokenizer.advance();
// return loaded_args; return true;
// case arg_nargs_t::ALL:
// std::string unrec; while (tokenizer.hasCurrent() && !tokenizer.isFlag())
// for (const auto& r : loaded_args.unrecognized_args) { {
// unrec += '\''; v_out.emplace_back(tokenizer.get());
// unrec += r; tokenizer.advance();
// unrec += '\''; }
// unrec += ' '; return true;
// } case arg_nargs_t::ALL_REQUIRED:
// unrec = unrec.substr(0, unrec.size()-1); if (tokenizer.hasCurrent() && tokenizer.isFlag())
// BLT_WARN("Unrecognized args: %s", unrec.c_str()); {
// printHelp(); BLT_WARN("At least one argument is required!");
// return false;
// return loaded_args; }
// } while (tokenizer.hasCurrent() && !tokenizer.isFlag())
// {
// void arg_parse::printHelp() { v_out.emplace_back(tokenizer.get());
// tokenizer.advance();
// } }
// return true;
// void arg_parse::handlePositionalArgument(arg_tokenizer_t& arg_tokenizer, size_t& last_pos) { }
// // TODO: return false;
// auto index = last_pos++; }
// if (index >= user_args.name_associations.size())
// loaded_args.unrecognized_args.push_back(arg_tokenizer.next()); void arg_parse::handlePositionalArgument(arg_tokenizer& tokenizer, size_t& last_pos)
// else {
// loaded_args.positional_args[user_args.name_associations[index].first] = arg_tokenizer.next(); auto index = last_pos++;
// } if (index >= user_args.name_associations.size())
// loaded_args.unrecognized_args.push_back(tokenizer.get());
// void arg_parse::handleFlagArgument(arg_tokenizer_t& arg_tokenizer) { else
// // token is a flag, find out special information about it {
// auto flag = arg_tokenizer.next(); loaded_args.data[user_args.name_associations[index]->a_dest] = tokenizer.get();
// loaded_args.found_args.insert(user_args.name_associations[index]->a_dest);
// if (flag.starts_with("--")) }
// processFlag(arg_tokenizer, flag); tokenizer.advance();
// else { }
// // handle special args like -vvv
// if (!flag.starts_with('-')) void arg_parse::handleFlagArgument(arg_tokenizer& tokenizer)
// BLT_TRACE("Flag processed but does not start with '-'!"); {
// // size without - auto flag = tokenizer.get();
// auto len = flag.size()-1; tokenizer.advance();
// // get flag type
// std::string str = "- "; // token is a flag, figure out how to handle it
// str[1] = flag[1]; if (flag.starts_with("--"))
// for (size_t i = 0; i < len; i++) processFlag(tokenizer, flag);
// processFlag(arg_tokenizer, str); else
// } {
// } // handle special args like -vvv
// if (!flag.starts_with('-'))
// void arg_parse::processFlag(arg_tokenizer_t& arg_tokenizer, const std::string& flag) { BLT_ERROR("Flag processed but does not start with '-'");
// auto loc = user_args.flag_associations.find(flag); // make sure the flag only contains the same character
// if (loc == user_args.flag_associations.end()){ auto type = flag[1];
// BLT_WARN("Flag '%s' not an option!", flag.c_str()); for (char c : flag.substr(1))
// printHelp(); {
// return; if (c != type)
// } {
// BLT_ERROR("Processed flag '%s' expected %c found %c", flag.c_str(), type, c);
// auto* flag_properties = loc->second; return;
// if (flag_properties->a_dest.empty()) { }
// BLT_ERROR("Flag dest empty! %s : %d, %p", flag.c_str(), flag_properties->a_flags.getFlags().size(), flag_properties); }
// return; // size without -
// } auto len = flag.size() - 1;
// auto dest = flag_properties->a_dest; // get flag type
// std::string str = "- ";
// arg_data_t& data = loaded_args.flag_args[dest]; str[1] = flag[1];
// switch(flag_properties->a_action){ for (size_t i = 0; i < len; i++)
// case arg_action_t::HELP: processFlag(tokenizer, str);
// printHelp(); }
// break; }
// case arg_action_t::STORE: {
// std::vector<std::string> v; void arg_parse::processFlag(arg_tokenizer& tokenizer, const std::string& flag)
// if (!consumeArguments(arg_tokenizer, *flag_properties, v)) { {
// printHelp(); auto flag_itr = user_args.flag_associations.find(flag);
// return; if (flag_itr == user_args.flag_associations.end())
// } {
// if (v.size() == 1) loaded_args.unrecognized_args.push_back(flag);
// data = v[0]; return;
// else }
// data = v;
// break; const auto* const properties = user_args.flag_associations.at(flag);
// }
// case arg_action_t::STORE_CONST: if (properties->a_dest.empty())
// data = flag_properties->a_const; {
// break; loaded_args.unrecognized_args.push_back(flag);
// case arg_action_t::STORE_FALSE: return;
// data = false; }
// break; auto dest = properties->a_dest;
// case arg_action_t::STORE_TRUE:
// data = true; loaded_args.found_args.insert(dest);
// break;
// case arg_action_t::COUNT: { switch (properties->a_action)
// if (!std::holds_alternative<int32_t>(data)) {
// data = 0; case arg_action_t::HELP:
// data = std::get<int32_t>(data) + 1; printHelp();
// break; break;
// } case arg_action_t::STORE:
// case arg_action_t::EXTEND: { {
// arg_data_t& data = loaded_args.data[dest];
// break; arg_data_vec_t v;
// } if (!consumeArguments(tokenizer, *properties, v))
// case arg_action_t::VERSION: { {
// auto file = filename(loaded_args.program_name); printHelp();
// BLT_INFO("%s, %s", file.c_str(), flag_properties->a_version.c_str()); return;
// break; }
// } if (v.size() == 1)
// case arg_action_t::APPEND_CONST: { data = v[0];
// if (!holds_alternative<std::vector<std::string>>(data)) { else
// data = std::vector<std::string>(); data = v;
// } break;
// auto& l = get<std::vector<std::string>>(data); }
// l.emplace_back(flag_properties->a_const); case arg_action_t::STORE_CONST:
// break; loaded_args.data[dest] = properties->a_const;
// } break;
// case arg_action_t::APPEND: { case arg_action_t::STORE_FALSE:
// if (!holds_alternative<std::vector<std::string>>(data)) loaded_args.data[dest] = false;
// data = std::vector<std::string>(); break;
// auto& l = get<std::vector<std::string>>(data); case arg_action_t::STORE_TRUE:
// consumeArguments(arg_tokenizer, *flag_properties, l); loaded_args.data[dest] = true;
// break; break;
// } case arg_action_t::COUNT:
// } {
// } auto& data = loaded_args.data[dest];
// if (!holds_alternative<int32_t>(data))
// bool arg_parse::consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector<std::string>& v_out) { data = 0;
// switch (properties.a_nargs.flags) { data = get<int32_t>(data) + 1;
// case 0: break;
// for (int i = 0; i < properties.a_nargs.args; i++) { }
// if (!arg_tokenizer.hasNext()){ case arg_action_t::EXTEND:
// BLT_WARN("Expected %d arguments got %d instead!", properties.a_nargs.args, i); {
// return false;
// } break;
// if (arg_tokenizer.isFlag()) { }
// BLT_WARN("Expected %d arguments, found flag instead!", properties.a_nargs.args); case arg_action_t::VERSION:
// return false; {
// } auto file = filename(loaded_args.program_name);
// v_out.emplace_back(arg_tokenizer.next()); BLT_INFO("%s, %s", file.c_str(), properties->a_version.c_str());
// } break;
// return true; }
// case arg_nargs_t::UNKNOWN: case arg_action_t::APPEND_CONST:
// // no arg next {
// if (!arg_tokenizer.hasNext() || arg_tokenizer.isFlag()) { auto& data = loaded_args.data[dest];
// if (!properties.a_const.empty()) if (!std::holds_alternative<arg_data_vec_t>(data))
// v_out.emplace_back(properties.a_const); {
// else data = arg_data_vec_t();
// v_out.emplace_back(properties.a_default); }
// return true; auto& l = get<arg_data_vec_t>(data);
// } l.emplace_back(properties->a_const);
// v_out.emplace_back(arg_tokenizer.next()); break;
// return true; }
// case arg_nargs_t::ALL: case arg_action_t::APPEND:
// while (arg_tokenizer.hasNext() && !arg_tokenizer.isFlag()) {
// v_out.emplace_back(arg_tokenizer.next()); auto& data = loaded_args.data[dest];
// return true; if (!holds_alternative<arg_data_vec_t>(data))
// case arg_nargs_t::ALL_REQUIRED: data = arg_data_vec_t();
// if (arg_tokenizer.isFlag()) { auto& l = get<arg_data_vec_t>(data);
// BLT_WARN("At least one argument is required!"); consumeArguments(tokenizer, *properties, l);
// return false; break;
// } }
// while (arg_tokenizer.hasNext() && !arg_tokenizer.isFlag()) }
// v_out.emplace_back(arg_tokenizer.next()); }
// return true;
// } arg_parse::arg_results arg_parse::parse_args(int argc, const char** argv)
// return false; {
// } std::vector<std::string> args;
// args.reserve(argc);
//} for (int i = 0; i < argc; i++)
args.emplace_back(argv[i]);
return parse_args(args);
}
arg_parse::arg_results arg_parse::parse_args(const std::vector<std::string>& args)
{
arg_tokenizer tokenizer(args);
loaded_args.program_name = tokenizer.get();
tokenizer.advance();
size_t last_positional = 0;
while (tokenizer.hasCurrent())
{
if (tokenizer.isFlag())
handleFlagArgument(tokenizer);
else
handlePositionalArgument(tokenizer, last_positional);
}
// load defaults for args which were not found
for (const auto* arg : user_args.arg_properties_storage) {
if (!to_string(arg->a_default).empty() && !loaded_args.contains(arg->a_dest))
loaded_args.data[arg->a_dest] = arg->a_default;
}
// if there was no problems processing then return the loaded args
if (loaded_args.unrecognized_args.empty())
return loaded_args;
// otherwise construct a string detailing the unrecognized args
std::string unrec;
for (const auto& r : loaded_args.unrecognized_args)
{
unrec += '\'';
unrec += r;
unrec += '\'';
unrec += ' ';
}
// remove the last space caused by the for loop
unrec = unrec.substr(0, unrec.size() - 1);
// TODO: use exceptions?
BLT_WARN("Unrecognized args: %s", unrec.c_str());
printHelp();
return loaded_args;
}
void arg_parse::printHelp()
{
BLT_TRACE("I am helpful!");
std::exit(0);
}
}

View File

@ -1,393 +0,0 @@
/*
* Created by Brett on 06/08/23.
* Licensed under GNU General Public License V3.0
* See LICENSE file for license detail
*/
#include <blt/parse/argparse_2.h>
#include <blt/std/logging.h>
#include <blt/std/string.h>
namespace blt
{
void arg_nargs_t::decode(char c)
{
if (c == '?')
flags = UNKNOWN;
else if (c == '+')
flags = ALL_REQUIRED;
else if (c == '*')
flags = ALL;
else
flags = 0;
}
arg_nargs_t::arg_nargs_t(char c)
{
decode(c);
}
arg_nargs_t::arg_nargs_t(std::string s)
{
decode(s[0]);
}
arg_nargs_t::arg_nargs_t(const char* s)
{
decode(*s);
}
class invalid_argument_exception : public std::runtime_error
{
public:
invalid_argument_exception(const std::string& str): std::runtime_error(str)
{}
};
void arg_vector_t::validateFlags()
{
for (const auto& flag : flags)
if (!flag.starts_with('-'))
throw invalid_argument_exception("Flag '" + flag + "' must start with - or --");
}
arg_vector_t::arg_vector_t(const char* str) {
std::string as_string(str);
if (as_string.starts_with('-'))
flags.emplace_back(as_string);
else
name = as_string;
}
arg_vector_t::arg_vector_t(const std::string& str) {
if (str.starts_with('-'))
flags.emplace_back(str);
else
name = str;
}
std::string to_string(const arg_data_t& v)
{
if (holds_alternative<arg_data_internal_t>(v))
return to_string(std::get<arg_data_internal_t>(v));
else if (std::holds_alternative<arg_data_vec_t>(v))
{
const auto& vec = std::get<arg_data_vec_t>(v);
if (vec.size() == 1)
return to_string(vec[0]);
if (vec.empty())
return "Empty Vector";
std::string str;
for (const auto& r : vec)
{
str += to_string(r);
str += ' ';
}
return "Vector of contents: " + str;
}
return "Empty";
}
std::string to_string(const arg_data_internal_t& v)
{
if (std::holds_alternative<std::string>(v)){
return std::get<std::string>(v);
} else if (std::holds_alternative<bool>(v)) {
return std::get<bool>(v) ? "True" : "False";
}
return std::to_string(std::get<int32_t>(v));
}
std::string arg_parse::filename(const std::string& path)
{
auto paths = blt::string::split(path, "/");
auto final = paths[paths.size() - 1];
if (final == "/")
return paths[paths.size() - 2];
return final;
}
void arg_parse::addArgument(const arg_properties_t& args)
{
auto properties = new arg_properties_t(args);
// determine where to store the arg when parsing
if (properties->a_dest.empty())
{
if (properties->a_flags.isFlag())
{
// take first arg so a_dest exists, could be - or --
properties->a_dest = properties->a_flags.flags[0];
// look for a -- arg (python's behaviour)
for (const auto& flag : properties->a_flags.flags)
{
if (flag.starts_with("--"))
{
properties->a_dest = flag;
break;
}
}
} else
properties->a_dest = properties->a_flags.name;
}
// associate flags with their properties
for (const auto& flag : properties->a_flags.flags)
user_args.flag_associations[flag] = properties;
// positional args uses index (vector) to store the properties
if (!properties->a_flags.isFlag())
user_args.name_associations.push_back(properties);
user_args.arg_properties_storage.push_back(properties);
}
bool arg_parse::consumeArguments(arg_tokenizer& tokenizer, const arg_properties_t& properties, std::vector<arg_data_internal_t>& v_out)
{
switch (properties.a_nargs.flags)
{
case 0:
for (int i = 0; i < properties.a_nargs.args; i++)
{
// if we don't have another arg to consume we have a problem!
if (!tokenizer.hasCurrent())
{
BLT_WARN("Expected %d arguments got %d instead!", properties.a_nargs.args, i);
return false;
}
// if we do have one, but it is a flag then we also have a problem!
if (tokenizer.isFlag())
{
BLT_WARN("Expected %d arguments, found flag instead!", properties.a_nargs.args);
return false;
}
// get the value and advance
v_out.emplace_back(tokenizer.get());
tokenizer.advance();
}
return true;
case arg_nargs_t::UNKNOWN:
// no arg next
if (!tokenizer.hasCurrent() || tokenizer.isFlag())
{
// python's default is to store const if around otherwise store default
if (!properties.a_const.empty())
v_out.emplace_back(properties.a_const);
else // this should no longer be required with the changes to how defaults are handled
v_out.emplace_back(properties.a_default);
return true;
}
v_out.emplace_back(tokenizer.get());
tokenizer.advance();
return true;
case arg_nargs_t::ALL:
while (tokenizer.hasCurrent() && !tokenizer.isFlag())
{
v_out.emplace_back(tokenizer.get());
tokenizer.advance();
}
return true;
case arg_nargs_t::ALL_REQUIRED:
if (tokenizer.hasCurrent() && tokenizer.isFlag())
{
BLT_WARN("At least one argument is required!");
return false;
}
while (tokenizer.hasCurrent() && !tokenizer.isFlag())
{
v_out.emplace_back(tokenizer.get());
tokenizer.advance();
}
return true;
}
return false;
}
void arg_parse::handlePositionalArgument(arg_tokenizer& tokenizer, size_t& last_pos)
{
auto index = last_pos++;
if (index >= user_args.name_associations.size())
loaded_args.unrecognized_args.push_back(tokenizer.get());
else
loaded_args.data[user_args.name_associations[index]->a_dest] = tokenizer.get();
tokenizer.advance();
}
void arg_parse::handleFlagArgument(arg_tokenizer& tokenizer)
{
auto flag = tokenizer.get();
tokenizer.advance();
// token is a flag, figure out how to handle it
if (flag.starts_with("--"))
processFlag(tokenizer, flag);
else
{
// handle special args like -vvv
if (!flag.starts_with('-'))
BLT_ERROR("Flag processed but does not start with '-'");
// make sure the flag only contains the same character
auto type = flag[1];
for (char c : flag.substr(1))
{
if (c != type)
{
BLT_ERROR("Processed flag '%s' expected %c found %c", flag.c_str(), type, c);
return;
}
}
// size without -
auto len = flag.size() - 1;
// get flag type
std::string str = "- ";
str[1] = flag[1];
for (size_t i = 0; i < len; i++)
processFlag(tokenizer, str);
}
}
void arg_parse::processFlag(arg_tokenizer& tokenizer, const std::string& flag)
{
auto flag_itr = user_args.flag_associations.find(flag);
if (flag_itr == user_args.flag_associations.end())
{
loaded_args.unrecognized_args.push_back(flag);
return;
}
const auto* const properties = user_args.flag_associations.at(flag);
if (properties->a_dest.empty())
{
loaded_args.unrecognized_args.push_back(flag);
return;
}
auto dest = properties->a_dest;
if (dest.starts_with("--"))
dest = dest.substr(2);
else if (dest.starts_with('-'))
dest = dest.substr(1);
switch (properties->a_action)
{
case arg_action_t::HELP:
printHelp();
break;
case arg_action_t::STORE:
{
arg_data_t& data = loaded_args.data[dest];
arg_data_vec_t v;
if (!consumeArguments(tokenizer, *properties, v))
{
printHelp();
return;
}
if (v.size() == 1)
data = v[0];
else
data = v;
break;
}
case arg_action_t::STORE_CONST:
loaded_args.data[dest] = properties->a_const;
break;
case arg_action_t::STORE_FALSE:
loaded_args.data[dest] = false;
break;
case arg_action_t::STORE_TRUE:
loaded_args.data[dest] = true;
break;
case arg_action_t::COUNT:
{
auto& data = loaded_args.data[dest];
if (!holds_alternative<int32_t>(data))
data = 0;
data = get<int32_t>(data) + 1;
break;
}
case arg_action_t::EXTEND:
{
break;
}
case arg_action_t::VERSION:
{
auto file = filename(loaded_args.program_name);
BLT_INFO("%s, %s", file.c_str(), properties->a_version.c_str());
break;
}
case arg_action_t::APPEND_CONST:
{
auto& data = loaded_args.data[dest];
if (!std::holds_alternative<arg_data_vec_t>(data))
{
data = arg_data_vec_t();
}
auto& l = get<arg_data_vec_t>(data);
l.emplace_back(properties->a_const);
break;
}
case arg_action_t::APPEND:
{
auto& data = loaded_args.data[dest];
if (!holds_alternative<arg_data_vec_t>(data))
data = arg_data_vec_t();
auto& l = get<arg_data_vec_t>(data);
consumeArguments(tokenizer, *properties, l);
break;
}
}
}
arg_parse::arg_results arg_parse::parse_args(int argc, const char** argv)
{
std::vector<std::string> args;
args.reserve(argc);
for (int i = 0; i < argc; i++)
args.emplace_back(argv[i]);
return parse_args(args);
}
arg_parse::arg_results arg_parse::parse_args(const std::vector<std::string>& args)
{
arg_tokenizer tokenizer(args);
loaded_args.program_name = tokenizer.get();
tokenizer.advance();
size_t last_positional = 0;
while (tokenizer.hasCurrent())
{
if (tokenizer.isFlag())
handleFlagArgument(tokenizer);
else
handlePositionalArgument(tokenizer, last_positional);
}
// if there was no problems processing then return the loaded args
if (loaded_args.unrecognized_args.empty())
return loaded_args;
// otherwise construct a string detailing the unrecognized args
std::string unrec;
for (const auto& r : loaded_args.unrecognized_args)
{
unrec += '\'';
unrec += r;
unrec += '\'';
unrec += ' ';
}
// remove the last space caused by the for loop
unrec = unrec.substr(0, unrec.size() - 1);
// TODO: use exceptions?
BLT_WARN("Unrecognized args: %s", unrec.c_str());
printHelp();
return loaded_args;
}
void arg_parse::printHelp()
{
BLT_TRACE("I am helpful!");
std::exit(0);
}
}

View File

@ -4,7 +4,7 @@
//#include "logging.h" //#include "logging.h"
#include "profiling_tests.h" #include "profiling_tests.h"
#include "nbt_tests.h" #include "nbt_tests.h"
#include "blt/parse/argparse_2.h" #include "blt/parse/argparse.h"
//#include "queue_tests.h" //#include "queue_tests.h"
//#include "blt/math/vectors.h" //#include "blt/math/vectors.h"
//#include "blt/math/matrix.h" //#include "blt/math/matrix.h"
@ -79,7 +79,8 @@ int main(int argc, const char** argv) {
std::vector<std::string> superArgs { std::vector<std::string> superArgs {
"BLT_TESTS", "BLT_TESTS",
"Sexy", "Sexy",
"--poo", "I have poop", "-p", "I have poop",
"-f"
}; };
auto args2 = parser.parse_args(superArgs); auto args2 = parser.parse_args(superArgs);