Riyyi
2 years ago
8 changed files with 888 additions and 0 deletions
@ -0,0 +1,6 @@ |
|||||||
|
# -*- yaml -*- |
||||||
|
|
||||||
|
--- |
||||||
|
# FIXME: Figure out why NOLINTBEGIN/NOLINTEND doesnt work |
||||||
|
Checks: -misc-unused-using-decls |
||||||
|
... |
@ -0,0 +1,62 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <cstddef> // size_t |
||||||
|
#include <iomanip> // setprecision |
||||||
|
#include <ios> // fixed |
||||||
|
#include <limits> // numeric_limits |
||||||
|
#include <sstream> // stringstream |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "util/format/builder.h" |
||||||
|
|
||||||
|
namespace Util::Format { |
||||||
|
|
||||||
|
void Builder::putLiteral(std::string_view literal) |
||||||
|
{ |
||||||
|
for (size_t i = 0; i < literal.length(); ++i) { |
||||||
|
putCharacter(literal[i]); |
||||||
|
if (literal[i] == '{' || literal[i] == '}') { |
||||||
|
++i; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Builder::putF32(float number, size_t precision) const |
||||||
|
{ |
||||||
|
precision = (precision > std::numeric_limits<float>::digits10) ? m_precision : precision; |
||||||
|
|
||||||
|
std::stringstream stream; |
||||||
|
stream |
||||||
|
<< std::fixed |
||||||
|
<< std::setprecision(precision) |
||||||
|
<< number; |
||||||
|
std::string string = stream.str(); |
||||||
|
string = string.substr(0, string.find_first_of('0', string.find('.'))); |
||||||
|
m_builder << string; |
||||||
|
} |
||||||
|
|
||||||
|
void Builder::putF64(double number, size_t precision) const |
||||||
|
{ |
||||||
|
precision = (precision > std::numeric_limits<double>::digits10) ? m_precision : precision; |
||||||
|
|
||||||
|
std::stringstream stream; |
||||||
|
stream |
||||||
|
<< std::fixed |
||||||
|
<< std::setprecision(precision) |
||||||
|
<< number; |
||||||
|
std::string string = stream.str(); |
||||||
|
string = string.substr(0, string.find_first_of('0', string.find('.'))); |
||||||
|
m_builder << string; |
||||||
|
} |
||||||
|
|
||||||
|
void Builder::resetSpecifiers() |
||||||
|
{ |
||||||
|
setPrecision(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace Util::Format
|
@ -0,0 +1,50 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef UTIL_FORMAT_BUILDER_H |
||||||
|
#define UTIL_FORMAT_BUILDER_H |
||||||
|
|
||||||
|
#include <cstddef> // size_t |
||||||
|
#include <cstdint> // int32_t, uint32_t, int64_t |
||||||
|
#include <sstream> // stringstream |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
namespace Util::Format { |
||||||
|
|
||||||
|
class Builder { |
||||||
|
public: |
||||||
|
explicit Builder(std::stringstream& builder) |
||||||
|
: m_precision(6) |
||||||
|
, m_builder(builder) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void putLiteral(std::string_view literal); |
||||||
|
|
||||||
|
void putI32(int32_t number) const { m_builder << number; } // int
|
||||||
|
void putU32(uint32_t number) const { m_builder << number; } // unsigned int
|
||||||
|
void putI64(int64_t number) const { m_builder << number; } // long int
|
||||||
|
void putU64(size_t number) const { m_builder << number; } // long unsigned int
|
||||||
|
void putF32(float number, size_t precision = -1) const; |
||||||
|
void putF64(double number, size_t precision = -1) const; |
||||||
|
void putCharacter(char character) const { m_builder.write(&character, 1); } |
||||||
|
void putString(const std::string_view string) const { m_builder.write(string.data(), string.length()); } |
||||||
|
void putPointer(const void* pointer) const { m_builder << pointer; } |
||||||
|
|
||||||
|
void resetSpecifiers(); |
||||||
|
void setPrecision(size_t precision = 6) { m_precision = precision; }; |
||||||
|
|
||||||
|
const std::stringstream& builder() const { return m_builder; } |
||||||
|
std::stringstream& builder() { return m_builder; } |
||||||
|
|
||||||
|
private: |
||||||
|
size_t m_precision { 6 }; |
||||||
|
std::stringstream& m_builder; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace Util::Format
|
||||||
|
|
||||||
|
#endif // UTIL_FORMAT_BUILDER_H
|
@ -0,0 +1,216 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <sstream> // stringstream |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "util/format/builder.h" |
||||||
|
#include "util/format/format.h" |
||||||
|
#include "util/format/parser.h" |
||||||
|
|
||||||
|
namespace Util::Format { |
||||||
|
|
||||||
|
void variadicFormatImpl(Builder& builder, Parser& parser, TypeErasedParameters& parameters) |
||||||
|
{ |
||||||
|
const auto literal = parser.consumeLiteral(); |
||||||
|
builder.putLiteral(literal); |
||||||
|
|
||||||
|
if (!parameters.isEOF()) { |
||||||
|
std::string_view specifier; |
||||||
|
if (parser.consumeSpecifier(specifier)) { |
||||||
|
parser.applySpecifier(builder, specifier); |
||||||
|
} |
||||||
|
|
||||||
|
auto& parameter = parameters.parameter(parameters.tell()); |
||||||
|
parameter.format(builder, parameter.value); |
||||||
|
parameters.ignore(); |
||||||
|
|
||||||
|
builder.resetSpecifiers(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!parser.isEOF()) { |
||||||
|
variadicFormatImpl(builder, parser, parameters); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void variadicFormat(std::stringstream& stream, std::string_view format, TypeErasedParameters& parameters) |
||||||
|
{ |
||||||
|
Builder builder { stream }; |
||||||
|
Parser parser { format, parameters.size() }; |
||||||
|
|
||||||
|
variadicFormatImpl(builder, parser, parameters); |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void prettyVariadicFormat(Type type, bool bold, std::string_view format, TypeErasedParameters& parameters) |
||||||
|
{ |
||||||
|
std::stringstream stream; |
||||||
|
|
||||||
|
if (type != Type::None || bold) { |
||||||
|
stream << "\033["; |
||||||
|
switch (type) { |
||||||
|
case Type::Info: |
||||||
|
stream << "34"; |
||||||
|
break; |
||||||
|
case Type::Warn: |
||||||
|
stream << "33"; |
||||||
|
break; |
||||||
|
case Type::Danger: |
||||||
|
stream << "31"; |
||||||
|
break; |
||||||
|
case Type::Success: |
||||||
|
stream << "32"; |
||||||
|
break; |
||||||
|
case Type::Comment: |
||||||
|
stream << "37"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
}; |
||||||
|
|
||||||
|
if (bold) { |
||||||
|
stream << ";1"; |
||||||
|
} |
||||||
|
stream << "m"; |
||||||
|
} |
||||||
|
|
||||||
|
variadicFormat(stream, format, parameters); |
||||||
|
|
||||||
|
if (type != Type::None || bold) { |
||||||
|
stream << "\033[0m"; |
||||||
|
} |
||||||
|
|
||||||
|
printf("%s\n", stream.str().c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
Dbg::Dbg(Type type, bool bold) |
||||||
|
: m_type(type) |
||||||
|
, m_bold(bold) |
||||||
|
, m_stream() |
||||||
|
, m_builder(m_stream) |
||||||
|
{ |
||||||
|
if (type != Type::None || m_bold) { |
||||||
|
m_stream << "\033["; |
||||||
|
switch (type) { |
||||||
|
case Type::Info: |
||||||
|
m_stream << "34"; |
||||||
|
break; |
||||||
|
case Type::Warn: |
||||||
|
m_stream << "33"; |
||||||
|
break; |
||||||
|
case Type::Danger: |
||||||
|
m_stream << "31"; |
||||||
|
break; |
||||||
|
case Type::Success: |
||||||
|
m_stream << "32"; |
||||||
|
break; |
||||||
|
case Type::Comment: |
||||||
|
m_stream << "37"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
}; |
||||||
|
|
||||||
|
if (m_bold) { |
||||||
|
m_stream << ";1"; |
||||||
|
} |
||||||
|
m_stream << "m"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Dbg::~Dbg() |
||||||
|
{ |
||||||
|
if (m_type != Type::None || m_bold) { |
||||||
|
m_stream << "\033[0m"; |
||||||
|
} |
||||||
|
|
||||||
|
printf("%s", m_stream.str().c_str()); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg dbg() |
||||||
|
{ |
||||||
|
return Dbg(Type::None, false); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg dbgb() |
||||||
|
{ |
||||||
|
return Dbg(Type::None, true); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg info() |
||||||
|
{ |
||||||
|
return Dbg(Type::Info, false); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg infob() |
||||||
|
{ |
||||||
|
return Dbg(Type::Info, true); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg warn() |
||||||
|
{ |
||||||
|
return Dbg(Type::Warn, false); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg warnb() |
||||||
|
{ |
||||||
|
return Dbg(Type::Warn, true); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg danger() |
||||||
|
{ |
||||||
|
return Dbg(Type::Danger, false); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg dangerb() |
||||||
|
{ |
||||||
|
return Dbg(Type::Danger, true); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg success() |
||||||
|
{ |
||||||
|
return Dbg(Type::Success, false); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg successb() |
||||||
|
{ |
||||||
|
return Dbg(Type::Success, true); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg comment() |
||||||
|
{ |
||||||
|
return Dbg(Type::Comment, false); |
||||||
|
} |
||||||
|
|
||||||
|
Dbg commentb() |
||||||
|
{ |
||||||
|
return Dbg(Type::Comment, true); |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
Str::Str(std::string& fill) |
||||||
|
: m_fill(fill) |
||||||
|
, m_stream() |
||||||
|
, m_builder(m_stream) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Str::~Str() |
||||||
|
{ |
||||||
|
m_fill = m_stream.str(); |
||||||
|
} |
||||||
|
|
||||||
|
Str str(std::string& fill) |
||||||
|
{ |
||||||
|
return Str(fill); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace Util::Format
|
@ -0,0 +1,198 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef UTIL_FORMAT_FORMAT_H |
||||||
|
#define UTIL_FORMAT_FORMAT_H |
||||||
|
|
||||||
|
#include <cstddef> // size_t |
||||||
|
#include <sstream> // stringstream |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "util/format/builder.h" |
||||||
|
#include "util/format/parser.h" |
||||||
|
#include "util/format/toformat.h" |
||||||
|
|
||||||
|
namespace Util::Format { |
||||||
|
|
||||||
|
struct Parameter { |
||||||
|
const void* value; |
||||||
|
void (*format)(Builder& builder, const void* value); |
||||||
|
}; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
void formatParameterValue(Builder& builder, const void* value) |
||||||
|
{ |
||||||
|
format(builder, *static_cast<const T*>(value)); |
||||||
|
} |
||||||
|
|
||||||
|
class TypeErasedParameters { |
||||||
|
public: |
||||||
|
const Parameter parameter(size_t index) { return m_parameters[index]; } |
||||||
|
|
||||||
|
size_t tell() const { return m_index; } |
||||||
|
size_t size() const { return m_parameters.size(); } |
||||||
|
bool isEOF() const { return m_index >= m_parameters.size(); } |
||||||
|
void ignore() { m_index++; } |
||||||
|
|
||||||
|
protected: |
||||||
|
size_t m_index { 0 }; |
||||||
|
std::vector<Parameter> m_parameters; |
||||||
|
}; |
||||||
|
|
||||||
|
template<typename... Parameters> |
||||||
|
class VariadicParameters final : public TypeErasedParameters { |
||||||
|
public: |
||||||
|
VariadicParameters(const Parameters&... parameters) |
||||||
|
{ |
||||||
|
m_parameters = { { ¶meters, formatParameterValue<Parameters> }... }; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void variadicFormatImpl(Builder& builder, Parser& parser, TypeErasedParameters& parameters); |
||||||
|
void variadicFormat(std::stringstream& stream, std::string_view format, TypeErasedParameters& parameters); |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
enum class Type { |
||||||
|
None, // Foreground
|
||||||
|
Info, // Blue
|
||||||
|
Warn, // Yellow
|
||||||
|
Danger, // Red
|
||||||
|
Success, // Green
|
||||||
|
Comment, // White
|
||||||
|
}; |
||||||
|
|
||||||
|
void prettyVariadicFormat(Type type, bool bold, std::string_view format, TypeErasedParameters& parameters); |
||||||
|
|
||||||
|
#define FORMAT_FUNCTION(name, type, bold) \ |
||||||
|
template<size_t N, typename... Parameters> \
|
||||||
|
void name(const char(&format)[N] = "", const Parameters&... parameters) \
|
||||||
|
{ \
|
||||||
|
VariadicParameters variadicParameters { parameters... }; \
|
||||||
|
prettyVariadicFormat(Type::type, bold, { format, N - 1 }, variadicParameters); \
|
||||||
|
} |
||||||
|
|
||||||
|
FORMAT_FUNCTION(dbgln, None, false); |
||||||
|
FORMAT_FUNCTION(dbgbln, None, true); |
||||||
|
FORMAT_FUNCTION(infoln, Info, false); |
||||||
|
FORMAT_FUNCTION(infobln, Info, true); |
||||||
|
FORMAT_FUNCTION(warnln, Warn, false); |
||||||
|
FORMAT_FUNCTION(warnbln, Warn, true); |
||||||
|
FORMAT_FUNCTION(dangerln, Danger, false); |
||||||
|
FORMAT_FUNCTION(dangerbln, Danger, true); |
||||||
|
FORMAT_FUNCTION(successln, Success, false); |
||||||
|
FORMAT_FUNCTION(successbln, Success, true); |
||||||
|
FORMAT_FUNCTION(commentln, Comment, false); |
||||||
|
FORMAT_FUNCTION(commentbln, Comment, true); |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
class Dbg { |
||||||
|
public: |
||||||
|
Dbg(Type type, bool bold); |
||||||
|
virtual ~Dbg(); |
||||||
|
|
||||||
|
Builder& builder() { return m_builder; } |
||||||
|
|
||||||
|
private: |
||||||
|
Type m_type; |
||||||
|
bool m_bold; |
||||||
|
|
||||||
|
std::stringstream m_stream; |
||||||
|
Builder m_builder; |
||||||
|
}; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
const Dbg& operator<<(const Dbg& debug, const T& value) |
||||||
|
{ |
||||||
|
format(const_cast<Dbg&>(debug).builder(), value); |
||||||
|
return debug; |
||||||
|
} |
||||||
|
|
||||||
|
Dbg dbg(); |
||||||
|
Dbg dbgb(); |
||||||
|
Dbg info(); |
||||||
|
Dbg infob(); |
||||||
|
Dbg warn(); |
||||||
|
Dbg warnb(); |
||||||
|
Dbg danger(); |
||||||
|
Dbg dangerb(); |
||||||
|
Dbg success(); |
||||||
|
Dbg successb(); |
||||||
|
Dbg comment(); |
||||||
|
Dbg commentb(); |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
template<typename... Parameters> |
||||||
|
void strln(std::string& fill, std::string_view format, const Parameters&... parameters) |
||||||
|
{ |
||||||
|
std::stringstream stream; |
||||||
|
VariadicParameters variadicParameters { parameters... }; |
||||||
|
variadicFormat(stream, format, variadicParameters); |
||||||
|
fill = stream.str(); |
||||||
|
} |
||||||
|
|
||||||
|
class Str { |
||||||
|
public: |
||||||
|
Str(std::string& fill); |
||||||
|
virtual ~Str(); |
||||||
|
|
||||||
|
Builder& builder() { return m_builder; } |
||||||
|
|
||||||
|
private: |
||||||
|
std::string& m_fill; |
||||||
|
std::stringstream m_stream; |
||||||
|
Builder m_builder; |
||||||
|
}; |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
const Str& operator<<(const Str& string, const T& value) |
||||||
|
{ |
||||||
|
format(const_cast<Str&>(string).builder(), value); |
||||||
|
return string; |
||||||
|
} |
||||||
|
|
||||||
|
Str str(std::string& fill); |
||||||
|
|
||||||
|
} // namespace Util::Format
|
||||||
|
|
||||||
|
using Util::Format::commentbln; |
||||||
|
using Util::Format::commentln; |
||||||
|
using Util::Format::dangerbln; |
||||||
|
using Util::Format::dangerln; |
||||||
|
using Util::Format::dbgbln; |
||||||
|
using Util::Format::dbgln; |
||||||
|
using Util::Format::infobln; |
||||||
|
using Util::Format::infoln; |
||||||
|
using Util::Format::successbln; |
||||||
|
using Util::Format::successln; |
||||||
|
using Util::Format::warnbln; |
||||||
|
using Util::Format::warnln; |
||||||
|
|
||||||
|
using Util::Format::comment; |
||||||
|
using Util::Format::commentb; |
||||||
|
using Util::Format::danger; |
||||||
|
using Util::Format::dangerb; |
||||||
|
using Util::Format::dbg; |
||||||
|
using Util::Format::dbgb; |
||||||
|
using Util::Format::info; |
||||||
|
using Util::Format::infob; |
||||||
|
using Util::Format::success; |
||||||
|
using Util::Format::successb; |
||||||
|
using Util::Format::warn; |
||||||
|
using Util::Format::warnb; |
||||||
|
|
||||||
|
using Util::Format::str; |
||||||
|
using Util::Format::strln; |
||||||
|
|
||||||
|
using FormatBuilder = Util::Format::Builder; |
||||||
|
|
||||||
|
#endif // UTIL_FORMAT_FORMAT_H
|
@ -0,0 +1,151 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <algorithm> // replace |
||||||
|
#include <cassert> // assert |
||||||
|
#include <cstddef> // size_t |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "util/format/builder.h" |
||||||
|
#include "util/format/parser.h" |
||||||
|
|
||||||
|
namespace Util::Format { |
||||||
|
|
||||||
|
Parser::Parser(std::string_view format, size_t parameterCount) |
||||||
|
: GenericLexer(format) |
||||||
|
, m_parameterCount(parameterCount) |
||||||
|
{ |
||||||
|
checkFormatParameterConsistency(); |
||||||
|
} |
||||||
|
|
||||||
|
Parser::~Parser() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void Parser::checkFormatParameterConsistency() |
||||||
|
{ |
||||||
|
size_t length = m_input.length(); |
||||||
|
|
||||||
|
// Format string does not reference all passed parameters
|
||||||
|
assert(length >= m_parameterCount * 2); |
||||||
|
|
||||||
|
size_t braceOpen = 0; |
||||||
|
size_t braceClose = 0; |
||||||
|
while (!isEOF()) { |
||||||
|
char peek0 = peek(); |
||||||
|
char peek1 = peek(1); |
||||||
|
|
||||||
|
if (peek0 == '{' && peek1 == '{') { |
||||||
|
ignore(2); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (peek0 == '}' && peek1 == '}') { |
||||||
|
ignore(2); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (peek0 == '{') { |
||||||
|
braceOpen++; |
||||||
|
|
||||||
|
// Only empty references {} or specified references {:} are valid
|
||||||
|
assert(peek1 == '}' || peek1 == ':'); |
||||||
|
} |
||||||
|
if (peek0 == '}') { |
||||||
|
braceClose++; |
||||||
|
} |
||||||
|
|
||||||
|
ignore(); |
||||||
|
} |
||||||
|
m_index = 0; |
||||||
|
|
||||||
|
// Extra open braces in format string
|
||||||
|
assert(!(braceOpen < braceClose)); |
||||||
|
|
||||||
|
// Extra closing braces in format string
|
||||||
|
assert(!(braceOpen > braceClose)); |
||||||
|
|
||||||
|
// Format string does not reference all passed parameters
|
||||||
|
assert(!(braceOpen < m_parameterCount)); |
||||||
|
|
||||||
|
// Format string references nonexistent parameter
|
||||||
|
assert(!(braceOpen > m_parameterCount)); |
||||||
|
} |
||||||
|
|
||||||
|
std::string_view Parser::consumeLiteral() |
||||||
|
{ |
||||||
|
const auto begin = tell(); |
||||||
|
|
||||||
|
while (!isEOF()) { |
||||||
|
char peek0 = peek(); |
||||||
|
char peek1 = peek(1); |
||||||
|
|
||||||
|
if (peek0 == '{' && peek1 == '{') { |
||||||
|
ignore(2); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (peek0 == '}' && peek1 == '}') { |
||||||
|
ignore(2); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Get literal before the specifier {}
|
||||||
|
if (peek0 == '{' || peek0 == '}') { |
||||||
|
return m_input.substr(begin, tell() - begin); |
||||||
|
} |
||||||
|
|
||||||
|
ignore(); |
||||||
|
} |
||||||
|
|
||||||
|
return m_input.substr(begin); |
||||||
|
} |
||||||
|
|
||||||
|
bool Parser::consumeSpecifier(std::string_view& specifier) |
||||||
|
{ |
||||||
|
if (!consumeSpecific('{')) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!consumeSpecific(':')) { |
||||||
|
assert(consumeSpecific('}')); |
||||||
|
specifier = ""; |
||||||
|
} |
||||||
|
else { |
||||||
|
const auto begin = tell(); |
||||||
|
|
||||||
|
// Go until AFTER the closing brace
|
||||||
|
while (peek(-1) != '}') { |
||||||
|
consume(); |
||||||
|
} |
||||||
|
|
||||||
|
specifier = m_input.substr(begin, tell() - begin - 1); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void Parser::applySpecifier(Builder& builder, std::string_view specifier) |
||||||
|
{ |
||||||
|
if (specifier[0] == '.') { |
||||||
|
size_t value = 0; |
||||||
|
|
||||||
|
for (size_t i = 1; i < specifier.length(); ++i) { |
||||||
|
if (specifier[i] < '0' || specifier[i] > '9') { |
||||||
|
return; |
||||||
|
} |
||||||
|
value *= 10; |
||||||
|
value += specifier[i] - '0'; // Subtract ASCII 48 to get the number
|
||||||
|
} |
||||||
|
|
||||||
|
builder.setPrecision(value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace Util::Format
|
@ -0,0 +1,37 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef UTIL_FORMAT_PARSER_H |
||||||
|
#define UTIL_FORMAT_PARSER_H |
||||||
|
|
||||||
|
#include <cstddef> // size_t |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "util/genericlexer.h" |
||||||
|
|
||||||
|
namespace Util::Format { |
||||||
|
|
||||||
|
class Builder; |
||||||
|
|
||||||
|
class Parser final : public GenericLexer { |
||||||
|
public: |
||||||
|
Parser(std::string_view format, size_t parameterCount); |
||||||
|
virtual ~Parser(); |
||||||
|
|
||||||
|
void checkFormatParameterConsistency(); |
||||||
|
|
||||||
|
std::string_view consumeLiteral(); |
||||||
|
bool consumeSpecifier(std::string_view& specifier); |
||||||
|
|
||||||
|
void applySpecifier(Builder& builder, std::string_view specifier); |
||||||
|
|
||||||
|
private: |
||||||
|
size_t m_parameterCount { 0 }; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace Util::Format
|
||||||
|
|
||||||
|
#endif // UTIL_FORMAT_PARSER_H
|
@ -0,0 +1,168 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef UTIL_TO_FORMAT_H |
||||||
|
#define UTIL_TO_FORMAT_H |
||||||
|
|
||||||
|
#include <cstddef> // nullptr_t |
||||||
|
#include <cstdint> // int64_t |
||||||
|
#include <cstring> // strlen |
||||||
|
#include <iterator> // next |
||||||
|
#include <map> |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
#include <unordered_map> |
||||||
|
#include <utility> // forward |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "util/meta/odr.h" |
||||||
|
|
||||||
|
namespace Util::Format { |
||||||
|
|
||||||
|
namespace Detail { |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, std::nullptr_t) |
||||||
|
{ |
||||||
|
builder.putString("(nil)"); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder, typename T> |
||||||
|
void format(FormatBuilder& builder, T* pointer) |
||||||
|
{ |
||||||
|
builder.putPointer(static_cast<const void*>(pointer)); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, bool boolean) |
||||||
|
{ |
||||||
|
builder.putString(boolean ? "true" : "false"); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, int32_t number) // int
|
||||||
|
{ |
||||||
|
builder.putI32(number); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, uint32_t number) // unsigned int
|
||||||
|
{ |
||||||
|
builder.putU32(number); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, int64_t number) // long int
|
||||||
|
{ |
||||||
|
builder.putI64(number); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> // uint64_t
|
||||||
|
void format(FormatBuilder& builder, size_t number) // long unsigned int
|
||||||
|
{ |
||||||
|
builder.putU64(number); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, float number) |
||||||
|
{ |
||||||
|
builder.putF32(number); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, double number) |
||||||
|
{ |
||||||
|
builder.putF64(number); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, char character) |
||||||
|
{ |
||||||
|
builder.putCharacter(character); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, const char* string) |
||||||
|
{ |
||||||
|
builder.putString({ string, strlen(string) }); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, const std::string& string) |
||||||
|
{ |
||||||
|
builder.putString(string); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder> |
||||||
|
void format(FormatBuilder& builder, std::string_view string) |
||||||
|
{ |
||||||
|
builder.putString(string); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder, typename T> |
||||||
|
void format(FormatBuilder& builder, const std::vector<T>& array) |
||||||
|
{ |
||||||
|
builder.putString("{\n"); |
||||||
|
for (auto it = array.cbegin(); it != array.cend(); ++it) { |
||||||
|
builder.putString(" "); |
||||||
|
format(builder, *it); |
||||||
|
|
||||||
|
// Add comma, except after the last element
|
||||||
|
if (it != std::prev(array.end(), 1)) { |
||||||
|
builder.putCharacter(','); |
||||||
|
} |
||||||
|
builder.putCharacter('\n'); |
||||||
|
} |
||||||
|
builder.putCharacter('}'); |
||||||
|
} |
||||||
|
|
||||||
|
#define FORMAT_MAP \ |
||||||
|
builder.putString("{\n"); \
|
||||||
|
auto last = map.end(); \
|
||||||
|
for (auto it = map.begin(); it != last; ++it) { \
|
||||||
|
builder.putString(R"( ")"); \
|
||||||
|
format(builder, it->first); \
|
||||||
|
builder.putString(R"(": )"); \
|
||||||
|
format(builder, it->second); \
|
||||||
|
\
|
||||||
|
/* Add comma, except after the last element */ \
|
||||||
|
if (std::next(it) != last) { \
|
||||||
|
builder.putCharacter(','); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
builder.putCharacter('\n'); \
|
||||||
|
} \
|
||||||
|
builder.putCharacter('}'); |
||||||
|
|
||||||
|
template<typename FormatBuilder, typename K, typename V> |
||||||
|
void format(FormatBuilder& builder, const std::map<K, V>& map) |
||||||
|
{ |
||||||
|
FORMAT_MAP; |
||||||
|
} |
||||||
|
|
||||||
|
template<typename FormatBuilder, typename K, typename V> |
||||||
|
void format(FormatBuilder& builder, const std::unordered_map<K, V>& map) |
||||||
|
{ |
||||||
|
FORMAT_MAP; |
||||||
|
} |
||||||
|
|
||||||
|
struct formatFunction { |
||||||
|
template<typename FormatBuilder, typename T> |
||||||
|
auto operator()(FormatBuilder& builder, T&& value) const |
||||||
|
{ |
||||||
|
return format(builder, std::forward<T>(value)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace Detail
|
||||||
|
|
||||||
|
namespace { |
||||||
|
constexpr const auto& format = Util::Detail::staticConst<Detail::formatFunction>; // NOLINT(misc-definitions-in-headers,clang-diagnostic-unused-variable)
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
} // namespace Util::Format
|
||||||
|
|
||||||
|
#endif // UTIL_TO_FORMAT_H
|
Loading…
Reference in new issue