tokenizer, basic logging works

main
Brett 2025-03-03 01:52:20 -05:00
parent ff2d77a1cd
commit 0490f50e3c
6 changed files with 237 additions and 11 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake)
set(BLT_VERSION 5.0.0)
set(BLT_VERSION 5.1.0)
set(BLT_TARGET BLT)

View File

@ -0,0 +1,64 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_LOGGING_FMT_TOKENIZER_H
#define BLT_LOGGING_FMT_TOKENIZER_H
#include <optional>
#include <string_view>
#include <vector>
#include <blt/std/types.h>
namespace blt::logging
{
enum class fmt_token_type : u8
{
STRING,
NUMBER,
SPACE,
COLON,
DOT,
MINUS,
PLUS
};
struct fmt_token_t
{
fmt_token_type type;
std::string_view value;
};
class fmt_tokenizer_t
{
public:
explicit fmt_tokenizer_t(const std::string_view fmt): m_fmt(fmt)
{}
static fmt_token_type get_type(char c);
std::optional<fmt_token_t> next();
std::vector<fmt_token_t> tokenize();
private:
size_t pos = 0;
std::string_view m_fmt;
};
}
#endif //BLT_LOGGING_FMT_TOKENIZER_H

View File

@ -19,11 +19,9 @@
#ifndef BLT_LOGGING_LOGGING_H
#define BLT_LOGGING_LOGGING_H
#include <iostream>
#include <ostream>
#include <sstream>
#include <string>
#include <blt/std/utility.h>
#include <vector>
#include <blt/meta/meta.h>
namespace blt::logging
@ -33,30 +31,56 @@ namespace blt::logging
explicit logger_t() = default;
template <typename T>
void print_value(T&& t)
std::string print_value(T&& t)
{
static_assert(meta::is_streamable_v<T>, "T must be streamable in order to work with blt::logging!");
m_stream.str("");
m_stream.clear();
m_stream << std::forward<T>(t);
return m_stream.str();
}
template <typename... Args>
std::string log(std::string fmt, Args&&... args)
{
compile(std::move(fmt));
((consume_until_fmt(), print_value(std::forward<Args>(args))), ...);
m_args_to_str.clear();
m_args_to_str.resize(sizeof...(Args));
insert(std::make_integer_sequence<size_t, sizeof...(Args)>{}, std::forward<Args>(args)...);
finish();
return to_string();
}
std::string to_string();
private:
template<typename... Args, size_t... Indexes>
void insert(std::integer_sequence<size_t, Indexes...>, Args&&... args)
{
((handle_insert<Indexes>(std::forward<Args>(args))), ...);
}
template<size_t index, typename T>
void handle_insert(T&& t)
{
m_args_to_str[index] = print_value(std::forward<T>(t));
}
void handle_fmt(std::string_view fmt);
const std::string& get(size_t index);
void compile(std::string fmt);
void consume_until_fmt();
bool consume_until_fmt();
void finish();
std::string m_fmt;
std::stringstream m_stream;
std::vector<std::string> m_args_to_str;
size_t m_last_fmt_pos = 0;
size_t m_arg_pos = 0;
};
void print(const std::string& fmt);

View File

@ -0,0 +1,81 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/logging/fmt_tokenizer.h>
namespace blt::logging
{
fmt_token_type fmt_tokenizer_t::get_type(const char c)
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return fmt_token_type::NUMBER;
case '+':
return fmt_token_type::PLUS;
case '-':
return fmt_token_type::MINUS;
case '.':
return fmt_token_type::DOT;
case ':':
return fmt_token_type::COLON;
case ' ':
return fmt_token_type::SPACE;
default:
return fmt_token_type::STRING;
}
}
std::optional<fmt_token_t> fmt_tokenizer_t::next()
{
if (pos >= m_fmt.size())
return {};
switch (const auto base_type = get_type(m_fmt[pos]))
{
case fmt_token_type::SPACE:
case fmt_token_type::PLUS:
case fmt_token_type::MINUS:
case fmt_token_type::DOT:
case fmt_token_type::COLON:
return fmt_token_t{base_type, std::string_view{m_fmt.data() + pos++, 1}};
default:
{
const auto begin = pos;
for (; pos < m_fmt.size() && get_type(m_fmt[pos]) == base_type; ++pos)
{}
return fmt_token_t{base_type, std::string_view{m_fmt.data() + begin, pos - begin}};
}
}
}
std::vector<fmt_token_t> fmt_tokenizer_t::tokenize()
{
std::vector<fmt_token_t> tokens;
while (auto token = next())
tokens.push_back(*token);
return tokens;
}
}

View File

@ -15,6 +15,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <blt/iterator/enumerate.h>
#include <blt/logging/logging.h>
#include <blt/std/types.h>
@ -33,18 +35,67 @@ namespace blt::logging
return str;
}
void logger_t::handle_fmt(const std::string_view fmt)
{
std::cout << fmt << std::endl;
}
const std::string& logger_t::get(const size_t index)
{
if (index >= m_args_to_str.size())
{
std::cerr << "Insufficient number of arguments provided to format string '" << m_fmt << "' got ";
for (const auto& [i, arg] : enumerate(std::as_const(m_args_to_str)))
{
std::cerr << '\'' << arg << "'";
if (i != m_args_to_str.size() - 1)
std::cerr << " ";
}
std::exit(EXIT_FAILURE);
}
return m_args_to_str[index];
}
void logger_t::compile(std::string fmt)
{
m_fmt = std::move(fmt);
m_last_fmt_pos = 0;
m_arg_pos = 0;
}
void logger_t::consume_until_fmt()
bool logger_t::consume_until_fmt()
{
const auto begin = m_fmt.find('{', m_last_fmt_pos);
if (begin == std::string::npos)
return false;
const auto end = m_fmt.find('}', begin);
m_stream << std::string_view(m_fmt.data() + static_cast<ptrdiff_t>(m_last_fmt_pos), begin - m_last_fmt_pos);
m_last_fmt_pos = end;
if (end == std::string::npos)
{
std::cerr << "Invalid format string, missing closing '}' near " << m_fmt.substr(std::min(static_cast<i64>(begin) - 5, 0l)) << std::endl;
std::exit(EXIT_FAILURE);
}
m_stream << std::string_view(m_fmt.data() + static_cast<ptrdiff_t>(m_last_fmt_pos), begin - m_last_fmt_pos);\
if (end - begin > 1)
handle_fmt(std::string_view(m_fmt.data() + static_cast<ptrdiff_t>(begin + 1), end - begin - 1));
else
{
// no arguments, must consume from args
m_stream << get(m_arg_pos++);
}
m_last_fmt_pos = end + 1;
return true;
}
void logger_t::finish()
{
m_stream.str("");
m_stream.clear();
while (consume_until_fmt())
{}
m_stream << std::string_view(m_fmt.data() + static_cast<ptrdiff_t>(m_last_fmt_pos), m_fmt.size() - m_last_fmt_pos);
m_last_fmt_pos = m_fmt.size();
}
logger_t& get_global_logger()

View File

@ -16,10 +16,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/logging/logging.h>
#include <blt/std/utility.h>
int main()
{
using endl_t = decltype(static_cast<std::ostream& (*)(std::ostream&)>(std::endl));
// blt::logging::println("{} | {} | {} | {}", blt::type_string<endl_t>());
blt::logging::println("This is a println!");
blt::logging::println("This is a println with args '{}'", 42);
blt::logging::println("This is a println with multiple args '{}' '{}' '{}'", 42, 32.34231233, "Hello World!");
blt::logging::println("This is a '{1}' fmt string with positionals '{0}'", "I am a string!", "Well so am I except cooler :3");
// blt::logging::println("This is println {}\twith a std::endl in the middle of it");
}