Compare commits

...
10 Commits
6 changed files with 194 additions and 66 deletions
+39 -3
View File
@@ -62,7 +62,16 @@ static constexpr std::string numberToString(size_t value, uint8_t base, bool upp
return result; return result;
} }
void Builder::putU64(size_t value, uint8_t base, bool uppercase, char fill, Align align, Sign sign, bool zeroPadding, size_t width, bool isNegative) const void Builder::putU64(size_t value,
uint8_t base,
bool uppercase,
char fill,
Align align,
Sign sign,
bool alternativeForm,
bool zeroPadding,
size_t width,
bool isNegative) const
{ {
std::string string = numberToString(value, base, uppercase); std::string string = numberToString(value, base, uppercase);
@@ -88,6 +97,25 @@ void Builder::putU64(size_t value, uint8_t base, bool uppercase, char fill, Alig
string.insert(0, signCharacter); string.insert(0, signCharacter);
} }
// Alternative form
if (alternativeForm) {
switch (base) {
case 2:
string.insert(0, (uppercase) ? "0B" : "0b");
break;
case 8:
break;
string.insert(0, 1, '0');
case 10:
break;
case 16:
string.insert(0, (uppercase) ? "0X" : "0x");
break;
default:
VERIFY_NOT_REACHED();
}
}
// Zero padding // Zero padding
if (zeroPadding) { if (zeroPadding) {
fill = '0'; fill = '0';
@@ -130,11 +158,19 @@ void Builder::putU64(size_t value, uint8_t base, bool uppercase, char fill, Alig
}; };
} }
void Builder::putI64(int64_t value, uint8_t base, bool uppercase, char fill, Align align, Sign sign, bool zeroPadding, size_t width) const void Builder::putI64(int64_t value,
uint8_t base,
bool uppercase,
char fill,
Align align,
Sign sign,
bool alternativeForm,
bool zeroPadding,
size_t width) const
{ {
bool isNegative = value < 0; bool isNegative = value < 0;
value = isNegative ? -value : value; value = isNegative ? -value : value;
putU64(static_cast<uint64_t>(value), base, uppercase, fill, align, sign, zeroPadding, width, isNegative); putU64(static_cast<uint64_t>(value), base, uppercase, fill, align, sign, alternativeForm, zeroPadding, width, isNegative);
} }
void Builder::putF64(double number, uint8_t precision) const void Builder::putF64(double number, uint8_t precision) const
+21 -3
View File
@@ -36,12 +36,30 @@ public:
void putLiteral(std::string_view literal); void putLiteral(std::string_view literal);
void putU64(size_t value, uint8_t base = 10, bool uppercase = false, char fill = ' ', Align align = Align::Right, Sign sign = Sign::Negative, bool zeroPadding = false, size_t width = 0, bool isNegative = false) const; void putU64(size_t value,
void putI64(int64_t value, uint8_t base = 10, bool uppercase = false, char fill = ' ', Align align = Align::Right, Sign sign = Sign::Negative, bool zeroPadding = false, size_t width = 0) const; uint8_t base = 10,
bool uppercase = false,
char fill = ' ',
Align align = Align::Right,
Sign sign = Sign::Negative,
bool alternativeForm = false,
bool zeroPadding = false,
size_t width = 0,
bool isNegative = false) const;
void putI64(int64_t value,
uint8_t base = 10,
bool uppercase = false,
char fill = ' ',
Align align = Align::Right,
Sign sign = Sign::Negative,
bool alternativeForm = false,
bool zeroPadding = false,
size_t width = 0) const;
void putF64(double number, uint8_t precision = 6) const; void putF64(double number, uint8_t precision = 6) const;
void putCharacter(char character) const { m_builder.write(&character, 1); } void putCharacter(char character) const { m_builder.write(&character, 1); }
void putString(std::string_view string, char fill = ' ', Align align = Align::Left, size_t width = 0) const; void putString(std::string_view string, char fill = ' ', Align align = Align::Left, size_t width = 0) const;
void putPointer(const void* pointer) const { m_builder << pointer; }
const std::stringstream& builder() const { return m_builder; } const std::stringstream& builder() const { return m_builder; }
std::stringstream& builder() { return m_builder; } std::stringstream& builder() { return m_builder; }
+31 -5
View File
@@ -21,13 +21,38 @@ namespace Util::Format {
template<> template<>
void Formatter<char>::format(Builder& builder, char value) const void Formatter<char>::format(Builder& builder, char value) const
{ {
builder.putCharacter(value); if (specifier.type != PresentationType::None && specifier.type != PresentationType::Character) {
// "Type char is a distinct type that has an implementation-defined
// choice of “signed char” or “unsigned char” as its underlying type."
// http://eel.is/c++draft/basic#fundamental
Formatter<signed char> formatter { .specifier = specifier };
return formatter.format(builder, static_cast<signed char>(value));
}
Formatter<std::string_view> formatter { .specifier = specifier };
return formatter.format(builder, { &value, 1 });
} }
template<> template<>
void Formatter<bool>::format(Builder& builder, bool value) const void Formatter<bool>::format(Builder& builder, bool value) const
{ {
builder.putString(value ? "true" : "false"); switch (specifier.type) {
case PresentationType::Binary:
case PresentationType::BinaryUppercase:
case PresentationType::Character:
case PresentationType::Decimal:
case PresentationType::Octal:
case PresentationType::Hex:
case PresentationType::HexUppercase: {
Formatter<uint8_t> formatter { .specifier = specifier };
return formatter.format(builder, static_cast<uint8_t>(value));
}
default:
break;
};
Formatter<std::string_view> formatter { .specifier = specifier };
formatter.format(builder, value ? "true" : "false");
} }
// String // String
@@ -46,9 +71,10 @@ void Formatter<const char*>::parse(Parser& parser)
void Formatter<const char*>::format(Builder& builder, const char* value) const void Formatter<const char*>::format(Builder& builder, const char* value) const
{ {
if (specifier.type == PresentationType::Pointer) { if (specifier.type == PresentationType::Pointer) {
Formatter<const void*> formatter { specifier }; Formatter<uintptr_t> formatter { .specifier = specifier };
formatter.format(builder, static_cast<const void*>(value)); formatter.specifier.alternativeForm = true;
return; formatter.specifier.type = PresentationType::Hex;
return formatter.format(builder, reinterpret_cast<uintptr_t>(value));
} }
Formatter<std::string_view>::format( Formatter<std::string_view>::format(
+19 -10
View File
@@ -8,7 +8,7 @@
#include <cassert> #include <cassert>
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // int8_t, int32_t, int64_t, uint8_t, uint32_t #include <cstdint> // int8_t, int32_t, int64_t, uint8_t, uint32_t, uintptr_t
#include <map> #include <map>
#include <string> #include <string>
#include <string_view> #include <string_view>
@@ -68,7 +68,10 @@ struct Formatter {
constexpr void parse(Parser& parser) constexpr void parse(Parser& parser)
{ {
if (std::is_same_v<T, char>) { if constexpr (std::is_same_v<T, char>) {
parser.parseSpecifier(specifier, Parser::ParameterType::Char);
}
else if (std::is_same_v<T, bool>) {
parser.parseSpecifier(specifier, Parser::ParameterType::Char); parser.parseSpecifier(specifier, Parser::ParameterType::Char);
} }
else if (std::is_same_v<T, std::string_view>) { else if (std::is_same_v<T, std::string_view>) {
@@ -92,6 +95,14 @@ struct Formatter<T> {
void format(Builder& builder, T value) const void format(Builder& builder, T value) const
{ {
if (specifier.type == PresentationType::Character) {
assert(value >= 0 && value <= 127);
Formatter<std::string_view> formatter { .specifier = specifier };
formatter.specifier.type = PresentationType::String;
return formatter.format(builder, { reinterpret_cast<const char*>(&value), 1 });
}
uint8_t base = 0; uint8_t base = 0;
bool uppercase = false; bool uppercase = false;
switch (specifier.type) { switch (specifier.type) {
@@ -119,12 +130,12 @@ struct Formatter<T> {
if constexpr (std::is_unsigned_v<T>) { if constexpr (std::is_unsigned_v<T>) {
builder.putU64( builder.putU64(
value, base, uppercase, specifier.fill, specifier.align, specifier.sign, value, base, uppercase, specifier.fill, specifier.align, specifier.sign,
specifier.zeroPadding, specifier.width); specifier.alternativeForm, specifier.zeroPadding, specifier.width);
} }
else { else {
builder.putI64( builder.putI64(
value, base, uppercase, specifier.fill, specifier.align, specifier.sign, value, base, uppercase, specifier.fill, specifier.align, specifier.sign,
specifier.zeroPadding, specifier.width); specifier.alternativeForm, specifier.zeroPadding, specifier.width);
} }
} }
}; };
@@ -185,19 +196,17 @@ struct Formatter<char[N]> : Formatter<const char*> {
// Pointer // Pointer
template<typename T> template<typename T>
struct Formatter<T*> { struct Formatter<T*> : Formatter<uintptr_t> {
Specifier specifier;
constexpr void parse(Parser& parser) constexpr void parse(Parser& parser)
{ {
parser.parseSpecifier(specifier, Parser::ParameterType::Pointer); parser.parseSpecifier(specifier, Parser::ParameterType::Pointer);
specifier.alternativeForm = true;
specifier.type = PresentationType::Hex;
} }
void format(Builder& builder, T* value) const void format(Builder& builder, T* value) const
{ {
value == nullptr Formatter<uintptr_t>::format(builder, reinterpret_cast<uintptr_t>(value));
? builder.putString("nullptr")
: builder.putPointer(static_cast<const void*>(value));
} }
}; };
+73 -39
View File
@@ -33,10 +33,6 @@ Parser::~Parser()
void Parser::checkFormatParameterConsistency() void Parser::checkFormatParameterConsistency()
{ {
size_t length = m_input.length();
VERIFY(length >= m_parameterCount * 2, "format string does not reference all parameters");
size_t braceOpen = 0; size_t braceOpen = 0;
size_t braceClose = 0; size_t braceClose = 0;
while (!isEOF()) { while (!isEOF()) {
@@ -68,13 +64,13 @@ void Parser::checkFormatParameterConsistency()
} }
m_index = 0; m_index = 0;
// VERIFY(!(braceOpen < braceClose), "extra open braces in format string"); VERIFY(!(braceOpen < braceClose), "extra open braces in format string");
VERIFY(!(braceOpen > braceClose), "extra closing braces in format string");
// VERIFY(!(braceOpen > braceClose), "extra closing braces in format string"); if (m_mode == ArgumentIndexingMode::Automatic) {
VERIFY(!(braceOpen < m_parameterCount), "format string does not reference all passed parameters");
// VERIFY(!(braceOpen < m_parameterCount), "format string does not reference all passed parameters"); VERIFY(!(braceOpen > m_parameterCount), "format string references nonexistent parameter");
}
// VERIFY(!(braceOpen > m_parameterCount), "format string references nonexistent parameter");
} }
size_t Parser::stringToNumber(std::string_view value) size_t Parser::stringToNumber(std::string_view value)
@@ -204,8 +200,6 @@ void Parser::parseSpecifier(Specifier& specifier, ParameterType type)
// Sign is only valid for numeric types // Sign is only valid for numeric types
if (peek0 == '+' || peek0 == '-' || peek0 == ' ') { if (peek0 == '+' || peek0 == '-' || peek0 == ' ') {
VERIFY(state < State::AfterSign, "unexpected '%c' at this position", peek0); VERIFY(state < State::AfterSign, "unexpected '%c' at this position", peek0);
VERIFY(type == ParameterType::Integral || type == ParameterType::FloatingPoint,
"sign option is only valid for numeric types");
state = State::AfterSign; state = State::AfterSign;
specifier.sign = static_cast<Builder::Sign>(peek0); specifier.sign = static_cast<Builder::Sign>(peek0);
} }
@@ -213,8 +207,6 @@ void Parser::parseSpecifier(Specifier& specifier, ParameterType type)
// Alternative form is only valid for numeric types // Alternative form is only valid for numeric types
if (peek0 == '#') { if (peek0 == '#') {
VERIFY(state < State::AfterAlternativeForm, "unexpected '#' at this position"); VERIFY(state < State::AfterAlternativeForm, "unexpected '#' at this position");
VERIFY(type == ParameterType::Integral || type == ParameterType::FloatingPoint,
"'#' option is only valid for numeric types");
state = State::AfterAlternativeForm; state = State::AfterAlternativeForm;
specifier.alternativeForm = true; specifier.alternativeForm = true;
} }
@@ -223,8 +215,6 @@ void Parser::parseSpecifier(Specifier& specifier, ParameterType type)
if (peek0 == '0') { if (peek0 == '0') {
if (state < State::AfterWidth) { if (state < State::AfterWidth) {
VERIFY(state < State::AfterZeroPadding, "unexpected '0' at this position"); VERIFY(state < State::AfterZeroPadding, "unexpected '0' at this position");
VERIFY(type == ParameterType::Integral || type == ParameterType::FloatingPoint,
"zero padding option is only valid for numeric types");
state = State::AfterZeroPadding; state = State::AfterZeroPadding;
specifier.zeroPadding = true; specifier.zeroPadding = true;
} }
@@ -294,24 +284,22 @@ constexpr void Parser::checkSpecifierIntegralType(const Specifier& specifier)
case PresentationType::None: case PresentationType::None:
case PresentationType::Binary: case PresentationType::Binary:
case PresentationType::BinaryUppercase: case PresentationType::BinaryUppercase:
case PresentationType::Character:
case PresentationType::Decimal: case PresentationType::Decimal:
case PresentationType::Octal: case PresentationType::Octal:
case PresentationType::Hex: case PresentationType::Hex:
case PresentationType::HexUppercase: case PresentationType::HexUppercase:
case PresentationType::Character:
break; break;
default: default:
VERIFY("invalid type spcifier"); VERIFY(false, "invalid type specifier");
}; };
// Invalid: precision
VERIFY(specifier.precision == -1, "invalid specifier option");
} }
constexpr void Parser::checkSpecifierType(const Specifier& specifier, ParameterType type) constexpr void Parser::checkSpecifierFloatingPointType(const Specifier& specifier)
{ {
switch (type) {
case ParameterType::Integral:
checkSpecifierIntegralType(specifier);
break;
case ParameterType::FloatingPoint:
switch (specifier.type) { switch (specifier.type) {
case PresentationType::Hexfloat: case PresentationType::Hexfloat:
case PresentationType::HexfloatUppercase: case PresentationType::HexfloatUppercase:
@@ -323,30 +311,76 @@ constexpr void Parser::checkSpecifierType(const Specifier& specifier, ParameterT
case PresentationType::GeneralUppercase: case PresentationType::GeneralUppercase:
break; break;
default: default:
VERIFY("invalid type spcifier"); VERIFY(false, "invalid type specifier");
} }
}
constexpr void Parser::checkSpecifierCharType(const Specifier& specifier)
{
checkSpecifierIntegralType(specifier);
// Valid: fill, align, width
// Invalid: sign, alternativeForm, zeroPadding, precision
if (specifier.type == PresentationType::None
|| specifier.type == PresentationType::Character) {
VERIFY(specifier.sign == Builder::Sign::None, "invalid specifier option");
VERIFY(specifier.alternativeForm == false, "invalid specifier option");
VERIFY(specifier.zeroPadding == false, "invalid specifier option");
}
// Precision checked in Integral
}
constexpr void Parser::checkSpecifierCStringType(const Specifier& specifier)
{
switch (specifier.type) {
case PresentationType::None:
case PresentationType::String:
case PresentationType::Pointer:
break;
default:
VERIFY(false, "invalid type specifier");
}
// Valid: fill, align, width
// Invalid: sign, alternativeForm, zeroPadding, precision
VERIFY(specifier.sign == Builder::Sign::None, "invalid specifier option");
VERIFY(specifier.alternativeForm == false, "invalid specifier option");
VERIFY(specifier.zeroPadding == false, "invalid specifier option");
VERIFY(specifier.precision == -1, "invalid specifier option");
}
constexpr void Parser::checkSpecifierStringType(const Specifier& specifier)
{
checkSpecifierCStringType(specifier);
VERIFY(specifier.type != PresentationType::Pointer, "invalid type specifier");
}
constexpr void Parser::checkSpecifierPointerType(const Specifier& specifier)
{
checkSpecifierCStringType(specifier);
VERIFY(specifier.type != PresentationType::String, "invalid type specifier");
}
constexpr void Parser::checkSpecifierType(const Specifier& specifier, ParameterType type)
{
switch (type) {
case ParameterType::Integral:
checkSpecifierIntegralType(specifier);
break;
case ParameterType::FloatingPoint:
checkSpecifierFloatingPointType(specifier);
break; break;
case ParameterType::Char: case ParameterType::Char:
if (specifier.type != PresentationType::None checkSpecifierCharType(specifier);
&& specifier.type != PresentationType::Character) {
checkSpecifierIntegralType(specifier);
}
break; break;
case ParameterType::CString: case ParameterType::CString:
VERIFY(specifier.type == PresentationType::None checkSpecifierCStringType(specifier);
|| specifier.type == PresentationType::String
|| specifier.type == PresentationType::Pointer,
"invalid type specifier");
break; break;
case ParameterType::String: case ParameterType::String:
VERIFY(specifier.type == PresentationType::None checkSpecifierStringType(specifier);
|| specifier.type == PresentationType::String,
"invalid type specifier");
break; break;
case ParameterType::Pointer: case ParameterType::Pointer:
VERIFY(specifier.type == PresentationType::None checkSpecifierPointerType(specifier);
|| specifier.type == PresentationType::Pointer,
"invalid type specifier");
break; break;
default: default:
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
+5
View File
@@ -44,6 +44,11 @@ public:
void parseSpecifier(Specifier& specifier, ParameterType type); void parseSpecifier(Specifier& specifier, ParameterType type);
constexpr void checkSpecifierIntegralType(const Specifier& specifier); constexpr void checkSpecifierIntegralType(const Specifier& specifier);
constexpr void checkSpecifierFloatingPointType(const Specifier& specifier);
constexpr void checkSpecifierCharType(const Specifier& specifier);
constexpr void checkSpecifierCStringType(const Specifier& specifier);
constexpr void checkSpecifierStringType(const Specifier& specifier);
constexpr void checkSpecifierPointerType(const Specifier& specifier);
constexpr void checkSpecifierType(const Specifier& specifier, ParameterType type); constexpr void checkSpecifierType(const Specifier& specifier, ParameterType type);
private: private: