#ifndef ARGPARSER_PARSER_H #define ARGPARSER_PARSER_H #include "argument.h" #include "basic-type.h" #include "builtin_parser.h" #include "defs.h" #include "distinct-types.h" #include "errors.h" #include "list-type.h" #include "option.h" #include "optional-arg.h" #include "parse-result.h" #include "repeat-arg.h" #include "repeat-flag.h" #include "repeat-opt.h" #include "single-arg.h" #include "single-flag.h" #include "single-opt.h" #include "tuple-type.h" #include "type.h" #include "union-type.h" #include #include #include #include #include #include #include #include #include #include // TODO: help output namespace argparser { class parser { void validate_opt_or_flag_name(const std::string &name) { if (!name.starts_with("--")) { if (!name.starts_with("-") || name.length() != 2) { throw errors::invalid_option_name_error(name); } } if (options.find(name) != options.end()) { throw errors::duplicate_option_name(name); } } bool allow_remaining_args_{}; public: internal::string_map options; std::vector arguments; template [[nodiscard]] option_handle_impl option(const std::string &name, const type_handle_impl &type) { validate_opt_or_flag_name(name); auto o = std::make_shared>(name, type); options[name] = o; return o; } template [[nodiscard]] option_handle_impl option(const std::string &name, const std::string &alt_name, const type_handle_impl &type) { validate_opt_or_flag_name(name); validate_opt_or_flag_name(alt_name); auto o = std::make_shared>(name, type); options[name] = o; options[alt_name] = o; return o; } template [[nodiscard]] repeatable_option_handle_impl repeatable_option(const std::string &name, const type_handle_impl &type) { validate_opt_or_flag_name(name); auto o = std::make_shared>(name, type); options[name] = o; return o; } template [[nodiscard]] repeatable_option_handle_impl repeatable_option(const std::string &name, const std::string &alt_name, const type_handle_impl &type) { validate_opt_or_flag_name(name); validate_opt_or_flag_name(alt_name); auto o = std::make_shared>(name, type); options[name] = o; options[alt_name] = o; return o; } [[nodiscard]] flag_handle_impl flag(const std::string &name) { validate_opt_or_flag_name(name); auto f = std::make_shared(name); options[name] = f; return f; } [[nodiscard]] flag_handle_impl flag(const std::string &name, const std::string &alt_name) { validate_opt_or_flag_name(name); validate_opt_or_flag_name(alt_name); auto f = std::make_shared(name); options[name] = f; options[alt_name] = f; return f; } [[nodiscard]] repeatable_flag_handle_impl repeatable_flag(const std::string &name) { validate_opt_or_flag_name(name); auto f = std::make_shared(name); options[name] = f; return f; } [[nodiscard]] repeatable_flag_handle_impl repeatable_flag(const std::string &name, const std::string &alt_name) { validate_opt_or_flag_name(name); validate_opt_or_flag_name(alt_name); auto f = std::make_shared(name); options[name] = f; options[alt_name] = f; return f; } template [[nodiscard]] single_arg_handle arg(const std::string &name, type_handle_impl type) { auto a = std::make_shared>(name, type); arguments.push_back(a); return a; } template [[nodiscard]] optional_arg_handle optional_arg(const std::string name, type_handle_impl type) { auto a = std::make_shared>(name, type); arguments.push_back(a); return a; } template [[nodiscard]] repeatable_arg_handle repeatable_arg(const std::string name, type_handle_impl type) { auto a = std::make_shared>(name, type); arguments.push_back(a); return a; } [[nodiscard]] type_handle_impl enum_type(const std::string &name, const std::vector &values) { internal::string_map actual_values{}; for (const auto &item: values) { actual_values[item] = item; } auto parser_fn = internal::make_enum_parser(name, actual_values); auto t = std::make_shared>(name, parser_fn); known_types[name] = t; return t; } template [[nodiscard]] type_handle_impl enum_type(const std::string &name, const internal::string_map &values) { auto parser_fn = internal::make_enum_parser(name, values); auto t = std::make_shared>(name, parser_fn); known_types[name] = t; return t; } template [[nodiscard]] type_handle_impl> tuple_type(const std::string &name, const type_handle_impl &...types) { auto t = std::make_shared>(name, std::forward_as_tuple(types...)); known_types[name] = t; return t; } template [[nodiscard]] type_handle_impl tuple_type(const std::string &name, const type_handle_impl &...types) { auto t = std::make_shared>(name, std::forward_as_tuple(types...)); known_types[name] = t; return t; } template [[nodiscard]] type_handle_impl tuple_type(const std::string &name, typename std::type_identity>::type constructor, const type_handle_impl &...types) { auto t = std::make_shared>(name, std::forward_as_tuple(types...), constructor); known_types[name] = t; return t; } template [[nodiscard]] type_handle_impl tuple_type(const std::string &name, const std::tuple...> &types, typename std::type_identity>::type constructor) { auto t = std::make_shared>(name, std::move(types), constructor); known_types[name] = t; return t; } template [[nodiscard]] internal::if_not_single_type>, Ts...> union_type(const std::string &name, const type_handle_impl &...types) { auto t = std::make_shared>(name, std::forward_as_tuple(types...)); known_types[name] = t; return t; } template [[nodiscard]] type_handle_impl> union_type(const std::string &name, const type_handle_impl &...types) { auto t = std::make_shared>(name, std::forward_as_tuple(types...)); known_types[name] = t; return t; } template [[nodiscard]] type_handle_impl> list_type(const std::string &name, const type_handle_impl &type) { auto t = std::make_shared>(name, type); known_types[name] = t; return t; } internal::string_map known_types; template [[nodiscard]] type_handle_impl basic_type(const std::string &name) { auto parse_fn = internal::automatic_parser::make_parser(name); auto t = std::make_shared>(name, parse_fn); known_types[name] = t; return t; } void enable_remaining_args() { allow_remaining_args_ = true; } [[nodiscard]] parse_result parse(const std::vector ¶ms) { std::deque params_queue(params.size()); std::copy(params.begin(), params.end(), params_queue.begin()); auto pr = parse_result{}; auto arg_iter = arguments.begin(); while (!params_queue.empty()) { auto current = params_queue.front(); bool looks_like_option = false; option_handle opt{}; std::string opt_name; if (allow_remaining_args_ && current == "--") { params_queue.pop_front(); pr.set_remaining(std::vector(std::make_move_iterator(params_queue.begin()), std::make_move_iterator(params_queue.end()))); params_queue.clear(); break; } if (current.starts_with("--")) { looks_like_option = true; if (current.contains('=')) { opt_name = current.substr(0, current.find_first_of('=')); auto optit = options.find(opt_name); if (optit != options.end()) { opt = optit->second; if (!opt->consumes_value()) { throw errors::unexpected_option_value_error(opt_name); } auto remain = current.substr(opt_name.length() + 1); params_queue.pop_front(); params_queue.push_front(remain); } } else { opt_name = current; auto optit = options.find(opt_name); if (optit != options.end()) { opt = optit->second; params_queue.pop_front(); } } } else if (current.starts_with("-") && current.length() >= 2) { looks_like_option = true; if (current.length() == 2) { opt_name = current; auto optit = options.find(opt_name); if (optit != options.end()) { opt = optit->second; params_queue.pop_front(); } } else { opt_name = current.substr(0, 2); auto optit = options.find(opt_name); if (optit != options.end()) { opt = optit->second; auto remain = current.substr(2); params_queue.pop_front(); if (opt->consumes_value()) { params_queue.push_front(remain); } else { params_queue.push_front("-" + remain); } } } } if (opt == nullptr) { if (arg_iter == arguments.end()) { if (allow_remaining_args_) { pr.set_remaining(std::vector(std::make_move_iterator(params_queue.begin()), std::make_move_iterator(params_queue.end()))); params_queue.clear(); } else { if (looks_like_option) { throw errors::unknown_option_error(opt_name); } else { throw errors::too_many_arguments_error(); } } } else { auto arg = arg_iter->get(); try { arg->parse(current, pr); params_queue.pop_front(); } catch (errors::runtime_error &) { if (!arg->has_parsed_enough(pr)) { if (looks_like_option) { throw errors::unknown_option_error(opt_name); } else { throw; } } if (std::next(arg_iter) == arguments.end()) { throw; } arg_iter = std::next(arg_iter); continue;// retry parsing the value with the next arg } if (!arg->can_parse_more(pr)) { arg_iter = std::next(arg_iter); } } } else { std::optional option_val = std::nullopt; auto consumes_value = opt->consumes_value(); if (consumes_value) { if (params_queue.empty()) { throw errors::missing_option_value_error(opt_name); } option_val = params_queue.front(); params_queue.pop_front(); } std::any val = {}; auto canonical_name = opt->get_name(); if (pr.has_opt(canonical_name)) { val = pr.get_opt(canonical_name); } try { opt->parse(option_val, val); } catch (errors::type_parsing_error &) { if (option_val.has_value() && options.find(option_val.value()) != options.end()) { throw errors::missing_option_value_error(opt_name); } else { throw; } } pr.set_opt(canonical_name, val); } } if (arg_iter != arguments.end() && !arg_iter->get()->has_parsed_enough(pr)) { throw errors::missing_argument_error(arg_iter->get()->get_name()); } for (const auto &[name, option]: options) { option->validate(pr); } return pr; } }; }// namespace argparser #endif//ARGPARSER_PARSER_H