diff --git a/CMakeLists.txt b/CMakeLists.txt index d876b96..e89680c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) include(cmake/color.cmake) -set(BLT_VERSION 5.1.3) +set(BLT_VERSION 5.1.4) set(BLT_TARGET BLT) diff --git a/include/blt/logging/fmt_tokenizer.h b/include/blt/logging/fmt_tokenizer.h index ce436aa..b34724b 100644 --- a/include/blt/logging/fmt_tokenizer.h +++ b/include/blt/logging/fmt_tokenizer.h @@ -34,7 +34,8 @@ namespace blt::logging COLON, DOT, MINUS, - PLUS + PLUS, + POUND }; enum class fmt_sign_t : u8 @@ -66,6 +67,7 @@ namespace blt::logging fmt_sign_t sign = fmt_sign_t::MINUS; bool leading_zeros = false; bool uppercase = false; + bool alternate_form = false; }; struct fmt_token_t @@ -121,10 +123,15 @@ namespace blt::logging private: void parse_fmt_field(); void parse_arg_id(); - void parse_fmt_spec_stage_1(); - void parse_fmt_spec_stage_2(); - void parse_fmt_spec_stage_3(); + + void parse_fmt_spec(); + void parse_fmt_spec_sign(); + void parse_fmt_spec_form(); + void parse_fmt_spec_width(); + void parse_fmt_spec_precision(); + void parse_sign(); + void parse_form(); void parse_width(); void parse_precision(); void parse_type(); diff --git a/include/blt/logging/logging.h b/include/blt/logging/logging.h index c3cf7b2..c419895 100644 --- a/include/blt/logging/logging.h +++ b/include/blt/logging/logging.h @@ -68,7 +68,7 @@ namespace blt::logging case fmt_sign_t::SPACE: if constexpr (std::is_arithmetic_v) { - if (t >= 0) + if (type.type != fmt_type_t::BINARY && t >= 0) stream << ' '; } break; @@ -90,7 +90,8 @@ namespace blt::logging { char buffer[sizeof(T)]; std::memcpy(buffer, &t, sizeof(T)); - stream << '0' << (type.uppercase ? 'B' : 'b'); + if (type.alternate_form) + stream << '0' << (type.uppercase ? 'B' : 'b'); for (size_t i = 0; i < sizeof(T); ++i) { for (size_t j = 0; j < 8; ++j) @@ -127,7 +128,7 @@ namespace blt::logging } break; default: - handle_type(stream, type.type); + handle_type(stream, type); stream << t; } }; @@ -135,7 +136,7 @@ namespace blt::logging void setup_stream(const fmt_spec_t& spec); void process_strings(); - static void handle_type(std::ostream& stream, fmt_type_t type); + static void handle_type(std::ostream& stream, const fmt_spec_t& spec); static void exponential(std::ostream& stream); static void fixed(std::ostream& stream); diff --git a/src/blt/logging/fmt_tokenizer.cpp b/src/blt/logging/fmt_tokenizer.cpp index bfb6426..a57f553 100644 --- a/src/blt/logging/fmt_tokenizer.cpp +++ b/src/blt/logging/fmt_tokenizer.cpp @@ -46,6 +46,8 @@ namespace blt::logging return fmt_token_type::COLON; case ' ': return fmt_token_type::SPACE; + case '#': + return fmt_token_type::POUND; default: return fmt_token_type::STRING; } @@ -62,6 +64,7 @@ namespace blt::logging case fmt_token_type::MINUS: case fmt_token_type::DOT: case fmt_token_type::COLON: + case fmt_token_type::POUND: return fmt_token_t{base_type, std::string_view{m_fmt.data() + m_pos++, 1}}; default: { @@ -100,30 +103,32 @@ namespace blt::logging if (!has_next()) throw std::runtime_error("Expected token when parsing format field"); const auto [type, value] = peek(); - if (type == fmt_token_type::COLON) - { - consume(); - parse_fmt_spec_stage_1(); - } - else if (type == fmt_token_type::NUMBER) + switch (type) { + case fmt_token_type::NUMBER: parse_arg_id(); if (has_next()) { if (peek().type == fmt_token_type::COLON) - { - consume(); - parse_fmt_spec_stage_1(); - } + parse_fmt_spec(); else throw std::runtime_error("Expected ':' when parsing format field after arg id!"); } - } - else - { - std::stringstream ss; - ss << "Expected unknown token '" << static_cast(type) << "' value '" << value << "' when parsing format field"; - throw std::runtime_error(ss.str()); + break; + case fmt_token_type::COLON: + parse_fmt_spec(); + break; + case fmt_token_type::STRING: + case fmt_token_type::SPACE: + case fmt_token_type::DOT: + case fmt_token_type::MINUS: + case fmt_token_type::PLUS: + case fmt_token_type::POUND: + { + std::stringstream ss; + ss << "Expected unknown token '" << static_cast(type) << "' value '" << value << "' when parsing format field"; + throw std::runtime_error(ss.str()); + } } if (has_next()) parse_type(); @@ -131,8 +136,6 @@ namespace blt::logging void fmt_parser_t::parse_arg_id() { - if (!has_next()) - throw std::runtime_error("Missing token when parsing arg id"); const auto [type, value] = next(); if (type != fmt_token_type::NUMBER) { @@ -143,11 +146,10 @@ namespace blt::logging m_spec.arg_id = std::stoll(std::string(value)); } - // handle start of fmt, with sign - void fmt_parser_t::parse_fmt_spec_stage_1() + void fmt_parser_t::parse_fmt_spec() { - if (!has_next()) - return; + // consume : + consume(); auto [type, value] = peek(); switch (type) { @@ -155,29 +157,89 @@ namespace blt::logging case fmt_token_type::COLON: { std::stringstream ss; - ss << "(Stage 1) Invalid token type " << static_cast(type) << " value " << value; + ss << "(Stage (Begin)) Invalid token type " << static_cast(type) << " value " << value; throw std::runtime_error(ss.str()); } case fmt_token_type::NUMBER: - parse_width(); - parse_fmt_spec_stage_3(); + parse_fmt_spec_width(); break; case fmt_token_type::DOT: - consume(); - parse_precision(); + parse_fmt_spec_precision(); break; case fmt_token_type::SPACE: case fmt_token_type::MINUS: case fmt_token_type::PLUS: - parse_sign(); - parse_fmt_spec_stage_2(); + parse_fmt_spec_sign(); + break; + case fmt_token_type::POUND: + parse_fmt_spec_form(); + break; + } + } + + // handle start of fmt, with sign + void fmt_parser_t::parse_fmt_spec_sign() + { + parse_sign(); + if (!has_next()) + return; + auto [type, value] = peek(); + switch (type) + { + case fmt_token_type::SPACE: + case fmt_token_type::MINUS: + case fmt_token_type::PLUS: + case fmt_token_type::STRING: + case fmt_token_type::COLON: + { + std::stringstream ss; + ss << "(Stage (Sign)) Invalid token type " << static_cast(type) << " value " << value; + throw std::runtime_error(ss.str()); + } + case fmt_token_type::NUMBER: + parse_fmt_spec_width(); + break; + case fmt_token_type::DOT: + parse_fmt_spec_precision(); + break; + case fmt_token_type::POUND: + parse_fmt_spec_form(); + break; + } + } + + void fmt_parser_t::parse_fmt_spec_form() + { + parse_form(); + if (!has_next()) + return; + auto [type, value] = peek(); + switch (type) + { + case fmt_token_type::STRING: + case fmt_token_type::SPACE: + case fmt_token_type::COLON: + case fmt_token_type::MINUS: + case fmt_token_type::PLUS: + case fmt_token_type::POUND: + { + std::stringstream ss; + ss << "(Stage (Form)) Invalid token type " << static_cast(type) << " value " << value; + throw std::runtime_error(ss.str()); + } + case fmt_token_type::NUMBER: + parse_fmt_spec_width(); + break; + case fmt_token_type::DOT: + parse_fmt_spec_precision(); break; } } // handle width parsing - void fmt_parser_t::parse_fmt_spec_stage_2() + void fmt_parser_t::parse_fmt_spec_width() { + parse_width(); if (!has_next()) return; auto [type, value] = peek(); @@ -188,45 +250,24 @@ namespace blt::logging case fmt_token_type::MINUS: case fmt_token_type::PLUS: case fmt_token_type::SPACE: + case fmt_token_type::POUND: + case fmt_token_type::NUMBER: { std::stringstream ss; - ss << "(Stage 2) Invalid token type " << static_cast(type) << " value " << value; + ss << "(Stage (Width)) Invalid token type " << static_cast(type) << " value " << value; throw std::runtime_error(ss.str()); } - case fmt_token_type::NUMBER: - parse_width(); - parse_fmt_spec_stage_3(); - break; case fmt_token_type::DOT: - consume(); - parse_precision(); + parse_fmt_spec_precision(); break; } } - void fmt_parser_t::parse_fmt_spec_stage_3() + void fmt_parser_t::parse_fmt_spec_precision() { - if (!has_next()) - return; - auto [type, value] = peek(); - switch (type) - { - case fmt_token_type::STRING: - case fmt_token_type::COLON: - case fmt_token_type::MINUS: - case fmt_token_type::PLUS: - case fmt_token_type::SPACE: - case fmt_token_type::NUMBER: - { - std::stringstream ss; - ss << "(Stage 3) Invalid token type " << static_cast(type) << " value " << value; - throw std::runtime_error(ss.str()); - } - case fmt_token_type::DOT: - consume(); - parse_precision(); - break; - } + // consume . + consume(); + parse_precision(); } void fmt_parser_t::parse_sign() @@ -258,6 +299,12 @@ namespace blt::logging } } + void fmt_parser_t::parse_form() + { + consume(); + m_spec.alternate_form = true; + } + void fmt_parser_t::parse_width() { auto [_, value] = next(); @@ -276,8 +323,6 @@ namespace blt::logging void fmt_parser_t::parse_type() { - if (!has_next()) - throw std::runtime_error("Missing token when parsing type"); auto [_, value] = next(); if (value.size() != 1) { diff --git a/src/blt/logging/logging.cpp b/src/blt/logging/logging.cpp index 9a8bb1b..94314c2 100644 --- a/src/blt/logging/logging.cpp +++ b/src/blt/logging/logging.cpp @@ -73,20 +73,26 @@ namespace blt::logging m_stream << *str_it; } - void logger_t::handle_type(std::ostream& stream, const fmt_type_t type) + void logger_t::handle_type(std::ostream& stream, const fmt_spec_t& spec) { - switch (type) + switch (spec.type) { case fmt_type_t::DECIMAL: stream << std::dec; break; case fmt_type_t::OCTAL: + if (spec.alternate_form) + stream << "0"; stream << std::oct; break; case fmt_type_t::HEX: + if (spec.alternate_form) + stream << (spec.uppercase ? "0X" : "0x"); stream << std::hex; break; case fmt_type_t::HEX_FLOAT: + if (spec.alternate_form) + stream << (spec.uppercase ? "0X" : "0x"); stream << std::hexfloat; break; case fmt_type_t::EXPONENT: diff --git a/tests/logger_tests.cpp b/tests/logger_tests.cpp index a7f4f62..d28a4e9 100644 --- a/tests/logger_tests.cpp +++ b/tests/logger_tests.cpp @@ -36,5 +36,7 @@ int main() blt::logging::println("This is a println with a with {:10}", 4120); blt::logging::println("This is a println with a with leading zeros {:010}", 4120); blt::logging::println("This is a println with a precision {:.10f}", 42.232342349); + blt::logging::println("This is a println with hex {:.10x}", 4250); + blt::logging::println("This is a println with hex with leading {:#.10x}", 4250); // blt::logging::println("This is println {}\twith a std::endl in the middle of it"); }