#ifndef LOGGER_PARSING_H #define LOGGER_PARSING_H #include namespace detail { /* * * Parse format string * */ enum class FormatType { s, c, b, B, d, o, x, X, a, A, e, E, f, F, g, G, p }; // TODO: Maybe remove the length from here template struct parse_result_t { bool is_valid = false; unsigned new_index = 0; unsigned length = 0; result_t result; }; struct fmt_string_result_t { bool has_zero_padding = false; int length = 6; int precision = 2; FormatType type = FormatType::s; }; // clang-format off /* * * fmtlib grammar: * replacement_field ::= "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}" * arg_id ::= integer | identifier * integer ::= digit+ * digit ::= "0"..."9" * identifier ::= id_start id_continue* * id_start ::= "a"..."z" | "A"..."Z" | "_" * id_continue ::= id_start | digit * * format_spec ::= [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type] * fill ::= * align ::= "<" | ">" | "^" * sign ::= "+" | "-" | " " * width ::= integer | "{" [arg_id] "}" * precision ::= integer | "{" [arg_id] "}" * type ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | * "o" | "p" | "s" | "x" | "X" * * grammar: * string ::= { braces | } * braces ::= "{" [":" fmt_string] "}" * fmt_string ::= ["0"][width]["." precision][type] * */ // clang-format on template constexpr bool is_digit(ConstString s, unsigned i) { return (s[i] > 47) && (s[i] < 58); } /* * * Functions to convert X to char array * and ConstString to X * */ template constexpr parse_result_t parse_number(unsigned i) { int number = 0; while ((i < s.size()) && is_digit(s, i)) { number = number * 10; number += (s[i] - 48); ++i; } return {true, i, 0, number}; } template constexpr parse_result_t parse_type(unsigned i) { if (s[i] == 's') { // string ++i; return {true, i, 1, FormatType::s}; } else if (s[i] == 'c') { // char ++i; return {true, i, 1, FormatType::c}; } else if (s[i] == 'b') { // int ++i; return {true, i, 1, FormatType::b}; } else if (s[i] == 'B') { ++i; return {true, i, 1, FormatType::B}; // } else if (s[i] == 'c') { // ++i; // return {true, i, 1, FormatType::c}; } else if (s[i] == 'd') { ++i; return {true, i, 1, FormatType::d}; } else if (s[i] == 'o') { ++i; return {true, i, 1, FormatType::o}; } else if (s[i] == 'x') { ++i; return {true, i, 1, FormatType::x}; } else if (s[i] == 'X') { ++i; return {true, i, 1, FormatType::X}; } else if (s[i] == 'a') { // float ++i; return {true, i, 1, FormatType::a}; } else if (s[i] == 'A') { ++i; return {true, i, 1, FormatType::A}; } else if (s[i] == 'e') { ++i; return {true, i, 1, FormatType::e}; } else if (s[i] == 'E') { ++i; return {true, i, 1, FormatType::E}; } else if (s[i] == 'f') { ++i; return {true, i, 1, FormatType::f}; } else if (s[i] == 'F') { ++i; return {true, i, 1, FormatType::F}; } else if (s[i] == 'g') { ++i; return {true, i, 1, FormatType::g}; } else if (s[i] == 'G') { ++i; return {true, i, 1, FormatType::G}; } else if (s[i] == 'p') { // pointer ++i; return {true, i, 1, FormatType::p}; } return {false, i, 0, FormatType::s}; } template constexpr parse_result_t parse_fmt_string(unsigned i) { fmt_string_result_t result; unsigned result_extra_len = 0; if (s[i] == '0') { ++i; result.has_zero_padding = true; } if (is_digit(s, i)) { auto [is_valid, new_i, len, number] = parse_number(i); if (!is_valid) return {false, i, 0, result}; i = new_i; result.length = number; } if (s[i] == '.') { ++i; auto [is_valid, new_i, len, number] = parse_number(i); if (!is_valid) return {false, i, 0, result}; i = new_i; result.precision = number; } if (s[i] != '}') { auto [is_valid, new_i, len, type] = parse_type(i); if (!is_valid) return {false, i, 0, result}; i = new_i; result.type = type; } return {true, i, result_extra_len, result}; } template constexpr unsigned count_braces(unsigned i) { unsigned result = 0; while (i < s.size()) { if (s[i] == '{') ++result; ++i; } return result; } template constexpr std::pair parse_braces(unsigned i) { int result_extra_len = 0; if (s[i] == '}') { ++i; return {i, result_extra_len}; } else if (s[i] == ':') { ++i; auto [is_valid, new_i, len, format_node] = parse_fmt_string(i); if (!is_valid) return {i, -1}; i = new_i; result_extra_len += len; if (s[i] == '}') { ++i; return {i, result_extra_len}; } } return {i, -1}; } template constexpr int get_output_len() { int result_extra_len = 0; for (unsigned i = 0; i < s.size(); ++i) { if (s[i] == '{') { ++i; auto [new_i, extra_len] = parse_braces(i); if (extra_len < 0) return -1; i = new_i; result_extra_len += extra_len; } else if (s[i] == '}') { return -1; } } return (result_extra_len + s.size()); } } // namespace detail template const std::array format(args_t...) { constexpr int len = detail::get_output_len(); static_assert(len > 0, "Syntax error in log string"); std::cout << "Computed Length: " << len << std::endl; return {0}; } #endif // LOGGER_PARSING_H