diff --git a/cmake-build-reldebug-asan/.cmake/api/v1/reply/codemodel-v2-de55cce266dfc6d38dc8.json b/cmake-build-reldebug-asan/.cmake/api/v1/reply/codemodel-v2-de55cce266dfc6d38dc8.json deleted file mode 100644 index 3d36913..0000000 --- a/cmake-build-reldebug-asan/.cmake/api/v1/reply/codemodel-v2-de55cce266dfc6d38dc8.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "configurations" : - [ - { - "directories" : - [ - { - "build" : ".", - "jsonFile" : "directory-.-RelWithDebInfo-f5ebdc15457944623624.json", - "minimumCMakeVersion" : - { - "string" : "3.5" - }, - "projectIndex" : 0, - "source" : ".", - "targetIndexes" : - [ - 0, - 1 - ] - } - ], - "name" : "RelWithDebInfo", - "projects" : - [ - { - "directoryIndexes" : - [ - 0 - ], - "name" : "BLT_TESTS", - "targetIndexes" : - [ - 0, - 1 - ] - } - ], - "targets" : - [ - { - "directoryIndex" : 0, - "id" : "BLT::@6890427a1f51a3e7e1df", - "jsonFile" : "target-BLT-RelWithDebInfo-41bcd67f0942cdd9dd8f.json", - "name" : "BLT", - "projectIndex" : 0 - }, - { - "directoryIndex" : 0, - "id" : "BLT_TESTS::@6890427a1f51a3e7e1df", - "jsonFile" : "target-BLT_TESTS-RelWithDebInfo-cd26a691ed5f8a807cdc.json", - "name" : "BLT_TESTS", - "projectIndex" : 0 - } - ] - } - ], - "kind" : "codemodel", - "paths" : - { - "build" : "/home/brett/Documents/code/c++/BLT/cmake-build-reldebug-asan", - "source" : "/home/brett/Documents/code/c++/BLT" - }, - "version" : - { - "major" : 2, - "minor" : 5 - } -} diff --git a/cmake-build-reldebug-asan/.cmake/api/v1/reply/target-BLT-RelWithDebInfo-41bcd67f0942cdd9dd8f.json b/cmake-build-reldebug-asan/.cmake/api/v1/reply/target-BLT-RelWithDebInfo-41bcd67f0942cdd9dd8f.json deleted file mode 100644 index 30ec268..0000000 --- a/cmake-build-reldebug-asan/.cmake/api/v1/reply/target-BLT-RelWithDebInfo-41bcd67f0942cdd9dd8f.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "archive" : {}, - "artifacts" : - [ - { - "path" : "libBLT.a" - } - ], - "backtrace" : 1, - "backtraceGraph" : - { - "commands" : - [ - "add_library", - "target_compile_options", - "include_directories" - ], - "files" : - [ - "CMakeLists.txt" - ], - "nodes" : - [ - { - "file" : 0 - }, - { - "command" : 0, - "file" : 0, - "line" : 70, - "parent" : 0 - }, - { - "command" : 1, - "file" : 0, - "line" : 86, - "parent" : 0 - }, - { - "command" : 2, - "file" : 0, - "line" : 50, - "parent" : 0 - }, - { - "command" : 2, - "file" : 0, - "line" : 62, - "parent" : 0 - }, - { - "command" : 2, - "file" : 0, - "line" : 63, - "parent" : 0 - } - ] - }, - "compileGroups" : - [ - { - "compileCommandFragments" : - [ - { - "fragment" : "-O2 -g -DNDEBUG -std=gnu++20 -fdiagnostics-color=always" - }, - { - "backtrace" : 2, - "fragment" : "-Wall" - }, - { - "backtrace" : 2, - "fragment" : "-Wextra" - }, - { - "backtrace" : 2, - "fragment" : "-Wpedantic" - } - ], - "includes" : - [ - { - "backtrace" : 3, - "path" : "/home/brett/Documents/code/c++/BLT/libraries/parallel-hashmap" - }, - { - "backtrace" : 4, - "path" : "/home/brett/Documents/code/c++/BLT/include" - }, - { - "backtrace" : 5, - "path" : "/home/brett/Documents/code/c++/BLT/cmake-build-reldebug-asan/config" - } - ], - "language" : "CXX", - "languageStandard" : - { - "backtraces" : - [ - 1 - ], - "standard" : "20" - }, - "sourceIndexes" : - [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ] - } - ], - "id" : "BLT::@6890427a1f51a3e7e1df", - "name" : "BLT", - "nameOnDisk" : "libBLT.a", - "paths" : - { - "build" : ".", - "source" : "." - }, - "sourceGroups" : - [ - { - "name" : "Source Files", - "sourceIndexes" : - [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ] - } - ], - "sources" : - [ - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/std/filesystem.cpp", - "sourceGroupIndex" : 0 - }, - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/std/format.cpp", - "sourceGroupIndex" : 0 - }, - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/std/loader.cpp", - "sourceGroupIndex" : 0 - }, - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/std/logging.cpp", - "sourceGroupIndex" : 0 - }, - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/std/string.cpp", - "sourceGroupIndex" : 0 - }, - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/std/system.cpp", - "sourceGroupIndex" : 0 - }, - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/profiling/profiler.cpp", - "sourceGroupIndex" : 0 - }, - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/nbt/nbt.cpp", - "sourceGroupIndex" : 0 - }, - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/nbt/nbt_block.cpp", - "sourceGroupIndex" : 0 - }, - { - "backtrace" : 1, - "compileGroupIndex" : 0, - "path" : "src/blt/parse/argparse.cpp", - "sourceGroupIndex" : 0 - } - ], - "type" : "STATIC_LIBRARY" -} diff --git a/cmake-build-reldebug-asan/.ninja_deps b/cmake-build-reldebug-asan/.ninja_deps index e011817..14ff182 100644 Binary files a/cmake-build-reldebug-asan/.ninja_deps and b/cmake-build-reldebug-asan/.ninja_deps differ diff --git a/cmake-build-reldebug-asan/.ninja_log b/cmake-build-reldebug-asan/.ninja_log index c614260..05f6705 100644 --- a/cmake-build-reldebug-asan/.ninja_log +++ b/cmake-build-reldebug-asan/.ninja_log @@ -1,14 +1,14 @@ # ninja log v5 -3 16349 1691041792278637372 CMakeFiles/BLT_TESTS.dir/src/tests/main.cpp.o afa34da1fa35196 -3 5121 1691041781062294416 CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o ed3943164b764c38 +0 15364 1691286051810595408 CMakeFiles/BLT_TESTS.dir/src/tests/main.cpp.o afa34da1fa35196 +1 4605 1691285961179863097 CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o ed3943164b764c38 2 2769 1691041778718222743 CMakeFiles/BLT.dir/src/blt/nbt/nbt.cpp.o c511168a92f29b0a 2 1272 1691041777222177000 CMakeFiles/BLT.dir/src/blt/std/string.cpp.o 79ade50d7fbeec83 2 2414 1691041778362211859 CMakeFiles/BLT.dir/src/blt/std/loader.cpp.o 178ad4ad35296377 1 890 1691041776826164891 CMakeFiles/BLT.dir/src/blt/std/filesystem.cpp.o 61458b1f0ba1d345 1 1571 1691041777522186173 CMakeFiles/BLT.dir/src/blt/std/format.cpp.o cbf3f157224e1459 3 701 1691038916362697654 CMakeFiles/BLT.dir/src/blt/nbt/nbt_block.cpp.o 32c42e82379a6795 -5121 5246 1691041781182298086 libBLT.a b72df77f3797ead +4605 5124 1691285961687878411 libBLT.a b72df77f3797ead 1 1271 1691038916934715120 CMakeFiles/BLT.dir/src/blt/std/system.cpp.o 60fa2509dba0689f -16349 16468 1691041792414641529 BLT_TESTS 4d8b8ad3e98f9f8e +15364 15487 1691286051950599630 BLT_TESTS 4d8b8ad3e98f9f8e 3 3939 1691041779886258458 CMakeFiles/BLT.dir/src/blt/profiling/profiler.cpp.o 8af198e8c08c00c0 2 3789 1691041779734253811 CMakeFiles/BLT.dir/src/blt/std/logging.cpp.o a378c7caf6217fa7 diff --git a/cmake-build-reldebug-asan/BLT_TESTS b/cmake-build-reldebug-asan/BLT_TESTS index 121579d..38c37c1 100755 Binary files a/cmake-build-reldebug-asan/BLT_TESTS and b/cmake-build-reldebug-asan/BLT_TESTS differ diff --git a/cmake-build-reldebug-asan/CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o b/cmake-build-reldebug-asan/CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o index fff400e..68e3b62 100644 Binary files a/cmake-build-reldebug-asan/CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o and b/cmake-build-reldebug-asan/CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o differ diff --git a/cmake-build-reldebug-asan/CMakeFiles/BLT_TESTS.dir/src/tests/main.cpp.o b/cmake-build-reldebug-asan/CMakeFiles/BLT_TESTS.dir/src/tests/main.cpp.o index e2a4690..c4fecca 100644 Binary files a/cmake-build-reldebug-asan/CMakeFiles/BLT_TESTS.dir/src/tests/main.cpp.o and b/cmake-build-reldebug-asan/CMakeFiles/BLT_TESTS.dir/src/tests/main.cpp.o differ diff --git a/cmake-build-reldebug-asan/Testing/Temporary/LastTest.log b/cmake-build-reldebug-asan/Testing/Temporary/LastTest.log index 48eff27..d26a895 100644 --- a/cmake-build-reldebug-asan/Testing/Temporary/LastTest.log +++ b/cmake-build-reldebug-asan/Testing/Temporary/LastTest.log @@ -1,3 +1,3 @@ -Start testing: Aug 03 01:51 EDT +Start testing: Aug 05 21:40 EDT ---------------------------------------------------------- -End testing: Aug 03 01:51 EDT +End testing: Aug 05 21:40 EDT diff --git a/cmake-build-reldebug-asan/build.ninja b/cmake-build-reldebug-asan/build.ninja index a56fb89..bcc57b6 100644 --- a/cmake-build-reldebug-asan/build.ninja +++ b/cmake-build-reldebug-asan/build.ninja @@ -119,6 +119,13 @@ build CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o: CXX_COMPILER__BLT_unscann OBJECT_DIR = CMakeFiles/BLT.dir 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 @@ -127,7 +134,7 @@ build CMakeFiles/BLT.dir/src/blt/parse/argparse.cpp.o: CXX_COMPILER__BLT_unscann ############################################# # 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 +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 LANGUAGE_COMPILE_FLAGS = -O2 -g -DNDEBUG OBJECT_DIR = CMakeFiles/BLT.dir POST_BUILD = : diff --git a/cmake-build-reldebug-asan/libBLT.a b/cmake-build-reldebug-asan/libBLT.a index 18eb5cb..d7e0537 100644 Binary files a/cmake-build-reldebug-asan/libBLT.a and b/cmake-build-reldebug-asan/libBLT.a differ diff --git a/include/blt/parse/argparse.h b/include/blt/parse/argparse.h index a2cad3a..6b49797 100644 --- a/include/blt/parse/argparse.h +++ b/include/blt/parse/argparse.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace blt::parser { @@ -39,9 +40,9 @@ namespace blt::parser { private: std::vector names; std::vector flags; + bool isFlags = false; void insertAndSort(const std::string& str); - public: arg_vector_t() = default; @@ -77,52 +78,30 @@ namespace blt::parser { return flags; } - struct equals { - bool operator()(const arg_vector_t& a1, const arg_vector_t& a2) const { - // arg cannot have both - if (!a1.names.empty()) { - // match all pos arg - return std::ranges::all_of(a1.names, [&a2](const auto& n) -> bool { - if (std::find(a2.names.begin(), a2.names.end(), n) == a2.names.end()) - return false; - return true; - }); - } else { - // match any flag (--foo or -f) - return std::ranges::all_of(a1.flags, [&a2](const auto& f) -> bool { - if (std::find(a2.flags.begin(), a2.flags.end(), f) != a2.flags.end()) - return true; - return false; - }); - } - } - }; + /** + * @return true if contains flags + */ + [[nodiscard]] inline bool isFlag() const { + return isFlags; + } - struct hash { - size_t operator()(const arg_vector_t& a) const { - size_t v = 0; - std::hash hash; - for (const auto& n : a.names) { - v >>= 8; - v += hash(n); - } - for (const auto& f : a.flags) { - v >>= 8; - v += hash(f); - } - return v; - } - }; + [[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 argparse; + friend class arg_parse; static constexpr int UNKNOWN = 0x1; static constexpr int ALL = 0x2; static constexpr int ALL_REQUIRED = 0x4; - // 0 means default behaviour (consume 1 if presented otherwise ignore) - int args = 0; + // 0 means ignore + int args = 1; // 0 indicates args is used int flags = 0; @@ -153,7 +132,7 @@ namespace blt::parser { public: arg_vector_t a_flags; arg_action_t a_action = arg_action_t::STORE; - arg_nargs_t a_nargs = 0; + arg_nargs_t a_nargs = 1; std::string a_const{}; std::string a_default{}; std::string a_dest{}; @@ -163,6 +142,62 @@ namespace blt::parser { bool a_required = false; }; + 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 std::string& 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_t { private: static constexpr char FLAG = '-'; @@ -207,38 +242,41 @@ namespace blt::parser { } }; - class argparse { + class arg_parse { public: typedef std::variant> arg_data_t; private: struct { - friend argparse; + friend arg_parse; private: std::vector arg_storage; public: std::vector> name_associations; HASHMAP flag_associations; - HASHSET required_vars; + HASHSET required_vars; } user_args; struct arg_results { - friend argparse; + friend arg_parse; private: - HASHSET found_required; + HASHSET found_required; + std::vector unrecognized_args; public: std::string program_name; - HASHMAP positional_args; - HASHMAP flag_args; + HASHMAP positional_args; + HASHMAP flag_args; } loaded_args; private: 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& v); + static bool consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector& v_out); void handlePositionalArgument(arg_tokenizer_t& arg_tokenizer, size_t& last_pos); void handleFlagArgument(arg_tokenizer_t& arg_tokenizer); void processFlag(arg_tokenizer_t& arg_tokenizer, const std::string& flag); public: - argparse() = default; + 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); @@ -247,6 +285,8 @@ namespace blt::parser { void printHelp(); }; + std::string to_string(const blt::parser::arg_parse::arg_data_t& v); + } #endif //BLT_TESTS_ARGPARSE_H diff --git a/include/blt/parse/argparse_2.h b/include/blt/parse/argparse_2.h new file mode 100644 index 0000000..c469bb6 --- /dev/null +++ b/include/blt/parse/argparse_2.h @@ -0,0 +1,337 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +namespace blt +{ + typedef std::variant arg_data_internal_t; + typedef std::vector arg_data_vec_t; + typedef std::variant 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 flags; + std::string name; + + void validateFlags(); + + public: + arg_vector_t(std::vector flags): flags(std::move(flags)) + { + validateFlags(); + } + + arg_vector_t(std::initializer_list 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 args; + size_t currentIndex = 0; + public: + arg_tokenizer(std::vector 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_storage; + public: + std::vector name_associations; + HASHMAP flag_associations; + } user_args; + + struct arg_results + { + friend arg_parse; + private: + HASHSET found_args; + std::vector unrecognized_args; + public: + std::string program_name; + HASHMAP 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& 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 + static inline bool holds_alternative(const arg_data_t& v){ + if constexpr (std::is_same_v) + return std::holds_alternative(v); + else + { + if (std::holds_alternative(v)) + { + arg_data_internal_t data = std::get(v); + return std::holds_alternative(data); + } else + return false; + } + } + template + static inline T& get(arg_data_t& v){ + if constexpr (std::is_same_v) + return std::get(v); + else + return std::get(std::get(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& 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 diff --git a/src/blt/parse/argparse.cpp b/src/blt/parse/argparse.cpp index cf5a7d0..7407088 100644 --- a/src/blt/parse/argparse.cpp +++ b/src/blt/parse/argparse.cpp @@ -3,299 +3,349 @@ * Licensed under GNU General Public License V3.0 * See LICENSE file for license detail */ -#include #include "blt/std/logging.h" + #include -namespace blt::parser { - - arg_vector_t::arg_vector_t(const std::vector& args) { - for (auto& arg : args) - insertAndSort(arg); - } - - arg_vector_t::arg_vector_t(std::initializer_list args) { - for (auto& arg : args) - insertAndSort(arg); - } - - arg_vector_t::arg_vector_t(const std::string& arg) { - insertAndSort(arg); - } - - arg_vector_t::arg_vector_t(const char* arg) { - insertAndSort(arg); - } - - arg_vector_t& arg_vector_t::operator=(const std::string& arg) { - insertAndSort(arg); - return *this; - } - - arg_vector_t& arg_vector_t::operator=(const char* arg) { - insertAndSort(arg); - return *this; - } - - arg_vector_t& arg_vector_t::operator=(std::initializer_list& args) { - for (auto& arg : args) - insertAndSort(arg); - return *this; - } - - arg_vector_t& arg_vector_t::operator=(std::vector& args) { - for (auto& arg : args) - insertAndSort(arg); - return *this; - } - - void arg_vector_t::insertAndSort(const std::string& str) { - if (str.starts_with('-')) - flags.push_back(str); - else - names.push_back(str); - } - - 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); - } - - arg_nargs_t& arg_nargs_t::operator=(const std::string& s) { - decode(s[0]); - return *this; - } - - arg_nargs_t& arg_nargs_t::operator=(char c) { - decode(c); - return *this; - } - - arg_nargs_t& arg_nargs_t::operator=(int a) { - args = a; - return *this; - } - - arg_nargs_t& arg_nargs_t::operator=(const char* s) { - decode(*s); - return *this; - } - - 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_tokenizer_t::arg_tokenizer_t(size_t argc, const char** argv) { - for (size_t i = 0; i < argc; i++) - args.emplace_back(argv[i]); - } - - bool argparse::validateArgument(const arg_properties_t& args) { - return !args.a_flags.getFlags().empty() ^ !args.a_flags.getNames().empty(); - } - - void argparse::addArgument(const arg_properties_t& args) { - if (!validateArgument(args)) { - BLT_WARN("Argument contains both flags and positional names, this is not allowed!"); - BLT_WARN("(Discarding argument)"); - return; - } - // store one copy of the properties (arg properties could in theory be very large!) - auto pos = user_args.arg_storage.size(); - user_args.arg_storage.push_back(args); - auto& arg = user_args.arg_storage[pos]; - // associate the arg properties per name and flag to allow for quick access when parsing - auto& names = args.a_flags.getNames(); - for (const auto& name : names) - user_args.name_associations.emplace_back(name, &arg); - - auto& flags = args.a_flags.getFlags(); - for (const auto& flag : flags) - user_args.flag_associations[flag] = &arg; - - if (args.a_required) - user_args.required_vars.insert(args.a_flags); - - if (args.a_nargs.flags == arg_nargs_t::UNKNOWN) { - if (args.a_flags.getNames().empty()) - loaded_args.flag_args[args.a_flags] = args.a_default; - else - loaded_args.positional_args[args.a_flags] = args.a_default; - } - } - - const argparse::arg_results& argparse::parse_args(int argc, const char** argv) { - loaded_args = {}; - arg_tokenizer_t arg_tokenizer(argc, argv); - loaded_args.program_name = arg_tokenizer.next(); - BLT_TRACE("Loading args for %s", loaded_args.program_name.c_str()); - - size_t last_positional = 0; - while (arg_tokenizer.hasNext()) { - // a token isn't a flag it must be a positional arg as flags will consume nargs - if (!arg_tokenizer.isFlag()){ - handlePositionalArgument(arg_tokenizer, last_positional); - continue; - } - handleFlagArgument(arg_tokenizer); - } - - return loaded_args; - } - - void argparse::printHelp() { - - } - - void argparse::handlePositionalArgument(arg_tokenizer_t& arg_tokenizer, size_t& last_pos) { - // TODO: - loaded_args.positional_args[user_args.name_associations[last_pos++].first] = arg_tokenizer.next(); - } - - void argparse::handleFlagArgument(arg_tokenizer_t& arg_tokenizer) { - // token is a flag, find out special information about it - auto flag = arg_tokenizer.next(); - - if (flag.starts_with("--")) - processFlag(arg_tokenizer, flag); - else { - // handle special args like -vvv - if (!flag.starts_with('-')) - BLT_TRACE("Flag processed but does not start with '-'!"); - // 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(arg_tokenizer, str); - } - } - - void argparse::processFlag(arg_tokenizer_t& arg_tokenizer, const std::string& flag) { - auto loc = user_args.flag_associations.find(flag); - if (loc == user_args.flag_associations.end()){ - BLT_WARN("Flag '%s' not an option!", flag.c_str()); - printHelp(); - return; - } - - auto flag_properties = loc->second; - auto dest = flag_properties->a_dest.empty() ? flag_properties->a_flags : arg_vector_t{flag_properties->a_dest}; - - arg_data_t& data = loaded_args.flag_args[dest]; - switch(flag_properties->a_action){ - case arg_action_t::HELP: - printHelp(); - break; - case arg_action_t::STORE: { - std::vector v; - if (!consumeArguments(arg_tokenizer, *flag_properties, v)) - return; - if (v.size() == 1) - data = v[0]; - else - data = v; - break; - } - case arg_action_t::STORE_CONST: - data = flag_properties->a_const; - break; - case arg_action_t::STORE_FALSE: - data = false; - break; - case arg_action_t::STORE_TRUE: - data = true; - break; - case arg_action_t::COUNT: { - if (!std::holds_alternative(data)) - data = 0; - data = std::get(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(), flag_properties->a_version.c_str()); - break; - } - case arg_action_t::APPEND_CONST: { - if (!holds_alternative>(data)) { - data = std::vector(); - } - auto& l = get>(data); - l.emplace_back(flag_properties->a_const); - break; - } - case arg_action_t::APPEND: { - if (!holds_alternative>(data)) - data = std::vector(); - auto& l = get>(data); - consumeArguments(arg_tokenizer, *flag_properties, l); - break; - } - } - } - - bool argparse::consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector& v) { - switch (properties.a_nargs.flags) { - case 0: - for (int i = 0; i < properties.a_nargs.args; i++) { - if (arg_tokenizer.isFlag()) { - BLT_WARN("Expected %d arguments, found flag instead!", properties.a_nargs.args); - return false; - } - v.emplace_back(arg_tokenizer.next()); - } - return true; - case arg_nargs_t::UNKNOWN: - // no arg next - if (arg_tokenizer.isFlag()) { - if (!properties.a_const.empty()) - v.emplace_back(properties.a_const); - return true; - } - v.emplace_back(arg_tokenizer.next()); - return true; - case arg_nargs_t::ALL: - while (arg_tokenizer.hasNext() && !arg_tokenizer.isFlag()) - v.emplace_back(arg_tokenizer.next()); - return true; - case arg_nargs_t::ALL_REQUIRED: - if (arg_tokenizer.isFlag()) { - BLT_WARN("At least one argument is required!"); - return false; - } - while (arg_tokenizer.hasNext() && !arg_tokenizer.isFlag()) - v.emplace_back(arg_tokenizer.next()); - return true; - } - return false; - } - - std::string argparse::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; - } - - -} \ No newline at end of file +//namespace blt::parser { +// +// arg_vector_t::arg_vector_t(const std::vector& args) { +// for (auto& arg : args) +// insertAndSort(arg); +// } +// +// arg_vector_t::arg_vector_t(std::initializer_list args) { +// for (auto& arg : args) +// insertAndSort(arg); +// } +// +// arg_vector_t::arg_vector_t(const std::string& arg) { +// insertAndSort(arg); +// } +// +// arg_vector_t::arg_vector_t(const char* arg) { +// insertAndSort(arg); +// } +// +// arg_vector_t& arg_vector_t::operator=(const std::string& arg) { +// insertAndSort(arg); +// return *this; +// } +// +// arg_vector_t& arg_vector_t::operator=(const char* arg) { +// insertAndSort(arg); +// return *this; +// } +// +// arg_vector_t& arg_vector_t::operator=(std::initializer_list& args) { +// for (auto& arg : args) +// insertAndSort(arg); +// return *this; +// } +// +// arg_vector_t& arg_vector_t::operator=(std::vector& args) { +// for (auto& arg : args) +// insertAndSort(arg); +// return *this; +// } +// +// void arg_vector_t::insertAndSort(const std::string& str) { +// if (str.starts_with('-')) { +// flags.push_back(str); +// isFlags = true; +// } else { +// names.push_back(str); +// isFlags = false; +// } +// if (!flags.empty() && !names.empty()) +// BLT_WARN("Flags provided alongside named positional args. This is not supported!"); +// } +// +// 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); +// } +// +// arg_nargs_t& arg_nargs_t::operator=(const std::string& s) { +// decode(s[0]); +// return *this; +// } +// +// arg_nargs_t& arg_nargs_t::operator=(char c) { +// decode(c); +// return *this; +// } +// +// arg_nargs_t& arg_nargs_t::operator=(int a) { +// args = a; +// return *this; +// } +// +// arg_nargs_t& arg_nargs_t::operator=(const char* s) { +// decode(*s); +// return *this; +// } +// +// 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_tokenizer_t::arg_tokenizer_t(size_t argc, const char** argv) { +// for (size_t i = 0; i < argc; i++) +// args.emplace_back(argv[i]); +// } +// +// bool arg_parse::validateArgument(const arg_properties_t& args) { +// return !(args.a_flags.getFlags().empty() ^ args.a_flags.getNames().empty()); +// } +// +// void arg_parse::addArgument(const arg_properties_t& args) { +// if (validateArgument(args)) { +// BLT_WARN("Argument contains both flags and positional names, this is not allowed!"); +// return; +// } +// // store one copy of the properties (arg properties could in theory be very large!) +// auto pos = user_args.arg_storage.size(); +// user_args.arg_storage.push_back(args); +// auto& arg = user_args.arg_storage[pos]; +// if (arg.a_dest.empty()) { +// // update dest with first full flag (same as python's behaviour) +// if (arg.a_flags.isFlag()) { +// for (const auto& flag : arg.a_flags.getFlags()) { +// if (flag.starts_with("--")) { +// arg.a_dest = flag; +// break; +// } +// } +// } else { +// for (const auto& name : arg.a_flags.getNames()) { +// arg.a_dest = name; +// break; +// } +// } +// } +// 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 +// auto& names = arg.a_flags.getNames(); +// for (const auto& name : names) { +// user_args.name_associations.emplace_back(name, &arg); +// user_args.required_vars.insert(name); +// } +// +// auto& flags = arg.a_flags.getFlags(); +// for (const auto& flag : flags) +// user_args.flag_associations[flag] = &arg; +// +// if (arg.a_required) +// user_args.required_vars.insert(arg.a_dest); +// +// if (arg.a_nargs.flags == arg_nargs_t::UNKNOWN) { +// if (arg.a_flags.getNames().empty()) { +// for (const auto& flag : arg.a_flags.getFlags()) +// loaded_args.flag_args[flag] = arg.a_default; +// } else { +// for (const auto& name : arg.a_flags.getNames()) +// loaded_args.positional_args[name] = arg.a_default; +// } +// } +// +// BLT_DEBUG("PRINTING: %d, %d", user_args.arg_storage[pos].a_flags.getFlags().size(), user_args.arg_storage[pos].a_flags.getNames().size()); +// +// } +// +// const arg_parse::arg_results& arg_parse::parse_args(int argc, const char** argv) { +// loaded_args = {}; +// arg_tokenizer_t arg_tokenizer(argc, argv); +// loaded_args.program_name = arg_tokenizer.next(); +// +// size_t last_positional = 0; +// while (arg_tokenizer.hasNext()) { +// // a token isn't a flag it must be a positional arg as flags will consume nargs +// if (!arg_tokenizer.isFlag()){ +// handlePositionalArgument(arg_tokenizer, last_positional); +// continue; +// } +// handleFlagArgument(arg_tokenizer); +// } +// +// if (loaded_args.unrecognized_args.empty()) +// return loaded_args; +// +// std::string unrec; +// for (const auto& r : loaded_args.unrecognized_args) { +// unrec += '\''; +// unrec += r; +// unrec += '\''; +// unrec += ' '; +// } +// unrec = unrec.substr(0, unrec.size()-1); +// BLT_WARN("Unrecognized args: %s", unrec.c_str()); +// printHelp(); +// +// return loaded_args; +// } +// +// void arg_parse::printHelp() { +// +// } +// +// void arg_parse::handlePositionalArgument(arg_tokenizer_t& arg_tokenizer, size_t& last_pos) { +// // TODO: +// auto index = last_pos++; +// if (index >= user_args.name_associations.size()) +// loaded_args.unrecognized_args.push_back(arg_tokenizer.next()); +// else +// loaded_args.positional_args[user_args.name_associations[index].first] = arg_tokenizer.next(); +// } +// +// void arg_parse::handleFlagArgument(arg_tokenizer_t& arg_tokenizer) { +// // token is a flag, find out special information about it +// auto flag = arg_tokenizer.next(); +// +// if (flag.starts_with("--")) +// processFlag(arg_tokenizer, flag); +// else { +// // handle special args like -vvv +// if (!flag.starts_with('-')) +// BLT_TRACE("Flag processed but does not start with '-'!"); +// // 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(arg_tokenizer, str); +// } +// } +// +// void arg_parse::processFlag(arg_tokenizer_t& arg_tokenizer, const std::string& flag) { +// auto loc = user_args.flag_associations.find(flag); +// if (loc == user_args.flag_associations.end()){ +// BLT_WARN("Flag '%s' not an option!", flag.c_str()); +// printHelp(); +// return; +// } +// +// auto* flag_properties = loc->second; +// 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; +// } +// auto dest = flag_properties->a_dest; +// +// arg_data_t& data = loaded_args.flag_args[dest]; +// switch(flag_properties->a_action){ +// case arg_action_t::HELP: +// printHelp(); +// break; +// case arg_action_t::STORE: { +// std::vector v; +// if (!consumeArguments(arg_tokenizer, *flag_properties, v)) { +// printHelp(); +// return; +// } +// if (v.size() == 1) +// data = v[0]; +// else +// data = v; +// break; +// } +// case arg_action_t::STORE_CONST: +// data = flag_properties->a_const; +// break; +// case arg_action_t::STORE_FALSE: +// data = false; +// break; +// case arg_action_t::STORE_TRUE: +// data = true; +// break; +// case arg_action_t::COUNT: { +// if (!std::holds_alternative(data)) +// data = 0; +// data = std::get(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(), flag_properties->a_version.c_str()); +// break; +// } +// case arg_action_t::APPEND_CONST: { +// if (!holds_alternative>(data)) { +// data = std::vector(); +// } +// auto& l = get>(data); +// l.emplace_back(flag_properties->a_const); +// break; +// } +// case arg_action_t::APPEND: { +// if (!holds_alternative>(data)) +// data = std::vector(); +// auto& l = get>(data); +// consumeArguments(arg_tokenizer, *flag_properties, l); +// break; +// } +// } +// } +// +// bool arg_parse::consumeArguments(arg_tokenizer_t& arg_tokenizer, const arg_properties_t& properties, std::vector& v_out) { +// switch (properties.a_nargs.flags) { +// case 0: +// for (int i = 0; i < properties.a_nargs.args; i++) { +// if (!arg_tokenizer.hasNext()){ +// BLT_WARN("Expected %d arguments got %d instead!", properties.a_nargs.args, i); +// return false; +// } +// if (arg_tokenizer.isFlag()) { +// BLT_WARN("Expected %d arguments, found flag instead!", properties.a_nargs.args); +// return false; +// } +// v_out.emplace_back(arg_tokenizer.next()); +// } +// return true; +// case arg_nargs_t::UNKNOWN: +// // no arg next +// if (!arg_tokenizer.hasNext() || arg_tokenizer.isFlag()) { +// if (!properties.a_const.empty()) +// v_out.emplace_back(properties.a_const); +// else +// v_out.emplace_back(properties.a_default); +// return true; +// } +// v_out.emplace_back(arg_tokenizer.next()); +// return true; +// case arg_nargs_t::ALL: +// while (arg_tokenizer.hasNext() && !arg_tokenizer.isFlag()) +// v_out.emplace_back(arg_tokenizer.next()); +// return true; +// case arg_nargs_t::ALL_REQUIRED: +// if (arg_tokenizer.isFlag()) { +// BLT_WARN("At least one argument is required!"); +// return false; +// } +// while (arg_tokenizer.hasNext() && !arg_tokenizer.isFlag()) +// v_out.emplace_back(arg_tokenizer.next()); +// return true; +// } +// return false; +// } +// +//} \ No newline at end of file diff --git a/src/blt/parse/argparse_2.cpp b/src/blt/parse/argparse_2.cpp new file mode 100644 index 0000000..a3dc1bf --- /dev/null +++ b/src/blt/parse/argparse_2.cpp @@ -0,0 +1,393 @@ +/* + * Created by Brett on 06/08/23. + * Licensed under GNU General Public License V3.0 + * See LICENSE file for license detail + */ +#include +#include +#include + +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(v)) + return to_string(std::get(v)); + else if (std::holds_alternative(v)) + { + const auto& vec = std::get(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(v)){ + return std::get(v); + } else if (std::holds_alternative(v)) { + return std::get(v) ? "True" : "False"; + } + return std::to_string(std::get(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& 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(data)) + data = 0; + data = get(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(data)) + { + data = arg_data_vec_t(); + } + auto& l = get(data); + l.emplace_back(properties->a_const); + break; + } + case arg_action_t::APPEND: + { + auto& data = loaded_args.data[dest]; + if (!holds_alternative(data)) + data = arg_data_vec_t(); + auto& l = get(data); + consumeArguments(tokenizer, *properties, l); + break; + } + } + } + + arg_parse::arg_results arg_parse::parse_args(int argc, const char** argv) + { + std::vector 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& 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); + } +} \ No newline at end of file diff --git a/src/tests/main.cpp b/src/tests/main.cpp index d1aeebe..cc96081 100755 --- a/src/tests/main.cpp +++ b/src/tests/main.cpp @@ -4,7 +4,7 @@ //#include "logging.h" #include "profiling_tests.h" #include "nbt_tests.h" -#include "blt/parse/argparse.h" +#include "blt/parse/argparse_2.h" //#include "queue_tests.h" //#include "blt/math/vectors.h" //#include "blt/math/matrix.h" @@ -68,11 +68,25 @@ int (*func_func)(int) = [](int i) -> int { int (*func_func_in)(int) = &test_as_func; int main(int argc, const char** argv) { - blt::parser::argparse parser; - parser.addArgument({{"--foo", "-f"}}); + blt::arg_parse parser; + parser.addArgument(blt::arg_builder({"--poo", "-p"}).build()); + parser.addArgument(blt::arg_builder({"--foo", "-f"}).setAction(blt::arg_action_t::STORE_TRUE).setDefault(false).build()); + parser.addArgument(blt::arg_builder({"--goo", "-g"}).build()); + parser.addArgument(blt::arg_builder({"--oop", "-o"}).build()); + parser.addArgument(blt::arg_builder("Sexy_pos").build()); + auto args = parser.parse_args(argc, argv); - const char* r[2]{"BLT_TESTS", "--foo"}; - auto args2 = parser.parse_args(2, r); + std::vector superArgs { + "BLT_TESTS", + "Sexy", + "--poo", "I have poop", + }; + auto args2 = parser.parse_args(superArgs); + + for (const auto& a : args2.data){ + BLT_TRACE("['%s' = '%s']", a.first.c_str(), blt::to_string(a.second).c_str()); + } + BLT_TRACE(args2.program_name); // // if (argc > 1 && std::string(argv[1]) == "--no_color") { // for (int i = (int)blt::logging::log_level::NONE; i < (int)blt::logging::log_level::FATAL; i++) {