mirror of
https://git.lynn.is/Gwen/argparser.git
synced 2024-05-08 02:31:09 +02:00
173 lines
7.1 KiB
C++
173 lines
7.1 KiB
C++
#ifndef ARGPARSER_BUILTIN_PARSER_H
|
|
#define ARGPARSER_BUILTIN_PARSER_H
|
|
|
|
#include "defs.h"
|
|
#include "errors.h"
|
|
#include "parser_func.h"
|
|
#include <charconv>
|
|
#include <iomanip>
|
|
#include <string>
|
|
|
|
namespace argparser::internal {
|
|
|
|
template<typename T, parse_opt parse_opt>
|
|
class automatic_parser {
|
|
public:
|
|
static parser_func<T> make_parser(const std::string &name) {
|
|
return [name](const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited) {
|
|
T val;
|
|
auto pos = begin;
|
|
while (pos < end && isspace(*pos)) pos++;
|
|
auto res = std::from_chars(pos, end, val);
|
|
if (res.ec == std::errc{}) {
|
|
parse_end = res.ptr;
|
|
return val;
|
|
} else {
|
|
throw errors::type_parsing_error(name, std::string(begin, end), pos - begin, "invalid number");
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
constexpr parse_opt string_parse_opt_with_default(parse_opt opt) {
|
|
if ((opt & parse_opt::AnyString) == parse_opt::None) {
|
|
return opt | parse_opt::AnyString;
|
|
} else {
|
|
return opt;
|
|
}
|
|
}
|
|
|
|
constexpr bool string_parser_enable_bare(parse_opt opt) {
|
|
return enum_flag_contains(string_parse_opt_with_default(opt), parse_opt::BareString);
|
|
}
|
|
|
|
constexpr bool string_parser_enable_single_quoted(parse_opt opt) {
|
|
return enum_flag_contains(string_parse_opt_with_default(opt), parse_opt::SingleQuotedString);
|
|
}
|
|
|
|
constexpr bool string_parser_enable_double_quoted(parse_opt opt) {
|
|
return enum_flag_contains(string_parse_opt_with_default(opt), parse_opt::DoubleQuotedString);
|
|
}
|
|
|
|
template<parse_opt parse_opt>
|
|
class automatic_parser<std::string, parse_opt> {
|
|
static std::string trim_string(const std::string &str) {
|
|
auto start = str.find_first_not_of(whitespace);
|
|
auto end = str.find_last_not_of(whitespace);
|
|
return str.substr(start == std::string::npos ? 0 : start, end == std::string::npos ? 0 : end);
|
|
}
|
|
|
|
public:
|
|
static parser_func<std::string> make_parser(const std::string &name) {
|
|
|
|
return [name](const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited allow_undelimited) {
|
|
auto str = std::string_view(begin, end);
|
|
if (str.length() == 0) {
|
|
if (parse_opt == parse_opt::None || enum_flag_contains(parse_opt, parse_opt::BareString)) {
|
|
parse_end = begin;
|
|
return std::string("");
|
|
} else {
|
|
throw errors::type_parsing_error(name, std::string(""), 0, "unexpected empty input");
|
|
}
|
|
}
|
|
if (str[0] == '\'' && string_parser_enable_single_quoted(parse_opt)) {
|
|
auto ss = std::stringstream(std::string(str));
|
|
std::string val;
|
|
ss >> std::quoted(val, '\'', '\\');
|
|
auto len = ss.tellg();
|
|
parse_end = begin + len;
|
|
if (enum_flag_contains(parse_opt, parse_opt::TrimString)) {
|
|
return trim_string(val);
|
|
} else {
|
|
return val;
|
|
}
|
|
}
|
|
if (str[0] == '"' && string_parser_enable_double_quoted(parse_opt)) {
|
|
auto ss = std::stringstream(std::string(str));
|
|
std::string val;
|
|
ss >> std::quoted(val, '"', '\\');
|
|
auto len = ss.tellg();
|
|
parse_end = begin + len;
|
|
if (enum_flag_contains(parse_opt, parse_opt::TrimString)) {
|
|
return trim_string(val);
|
|
} else {
|
|
return val;
|
|
}
|
|
}
|
|
if (string_parser_enable_bare(parse_opt)) {
|
|
std::string illegal_characters = "\"'";
|
|
if (!internal::enum_flag_contains(allow_undelimited, internal::parser_allow_undelimited::Comma)) {
|
|
illegal_characters += ",";
|
|
}
|
|
if (!internal::enum_flag_contains(allow_undelimited, internal::parser_allow_undelimited::Parenthesis)) {
|
|
illegal_characters += "()";
|
|
}
|
|
if (!internal::enum_flag_contains(allow_undelimited, internal::parser_allow_undelimited::Brackets)) {
|
|
illegal_characters += "[]";
|
|
}
|
|
auto pos = str.find_first_of(illegal_characters);
|
|
if (pos == std::string_view::npos) {
|
|
parse_end = end;
|
|
} else {
|
|
parse_end = begin + pos;
|
|
}
|
|
std::string val{begin, parse_end};
|
|
if (enum_flag_contains(parse_opt, parse_opt::TrimString) || enum_flag_contains(parse_opt, parse_opt::TrimBareString)) {
|
|
return trim_string(val);
|
|
} else {
|
|
return val;
|
|
}
|
|
}
|
|
throw errors::type_parsing_error(name, std::string(begin, end), 0, "failed to parse string");
|
|
};
|
|
}
|
|
};
|
|
|
|
|
|
template<typename T>
|
|
parser_func<T> make_enum_parser(const std::string &name, internal::string_map<T> values) {
|
|
if (values.size() == 0) {
|
|
throw errors::empty_enum_map_error(name);
|
|
}
|
|
return [name, values](const char *begin, const char *end, const char *&parse_end, internal::parser_allow_undelimited) {
|
|
auto input = std::basic_string_view(begin, end);
|
|
auto start = input.find_first_not_of(whitespace);
|
|
if (start == std::string::npos) {
|
|
input = "";
|
|
} else {
|
|
input = std::basic_string_view(begin + start, end);
|
|
}
|
|
T value;
|
|
size_t value_len = 0;
|
|
for (const auto &[str, val]: values) {
|
|
if (str.length() <= input.length()) {
|
|
if (input.starts_with(str)) {
|
|
if (str.length() > value_len) {
|
|
value = val;
|
|
value_len = str.length();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (value_len == 0) {
|
|
std::string expected_values = values.begin()->first;
|
|
for (auto it = std::next(values.begin()); it != values.end(); it++) {
|
|
if (std::next(it) != values.end()) {
|
|
expected_values += ", " + it->first;
|
|
} else {
|
|
expected_values += " or " + it->first;
|
|
}
|
|
}
|
|
throw errors::type_parsing_error(name, std::string(begin, end), 0, std::format("expected {}", expected_values));
|
|
} else {
|
|
parse_end = begin + value_len;
|
|
return value;
|
|
}
|
|
};
|
|
}
|
|
|
|
}// namespace argparser::internal
|
|
|
|
|
|
#endif//ARGPARSER_BUILTIN_PARSER_H
|