argparser/src/builtin_parser.h
2023-05-17 10:17:56 +02:00

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