use new arg parse + defaults now assigned
parent
b083245ab4
commit
2a656a687e
Binary file not shown.
|
@ -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 = :
|
||||||
|
|
|
@ -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 {
|
class arg_tokenizer
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
static constexpr char FLAG = '-';
|
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
size_t nextIndex = 0;
|
size_t currentIndex = 0;
|
||||||
|
|
||||||
inline const std::string& get(size_t i) {
|
|
||||||
return args[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool hasNext(size_t i) {
|
|
||||||
return (size_t) i < args.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue