mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 10:47:35 +02:00
Parse alignment.
This commit is contained in:
76
format.cc
76
format.cc
@ -52,6 +52,11 @@ namespace {
|
|||||||
// Flags.
|
// Flags.
|
||||||
enum { PLUS_FLAG = 1, ZERO_FLAG = 2, HEX_PREFIX_FLAG = 4 };
|
enum { PLUS_FLAG = 1, ZERO_FLAG = 2, HEX_PREFIX_FLAG = 4 };
|
||||||
|
|
||||||
|
// Alignment.
|
||||||
|
enum Alignment {
|
||||||
|
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
|
||||||
|
};
|
||||||
|
|
||||||
void ReportUnknownType(char code, const char *type) {
|
void ReportUnknownType(char code, const char *type) {
|
||||||
if (std::isprint(static_cast<unsigned char>(code))) {
|
if (std::isprint(static_cast<unsigned char>(code))) {
|
||||||
throw fmt::FormatError(
|
throw fmt::FormatError(
|
||||||
@ -104,7 +109,7 @@ void Formatter::ReportError(const char *s, const std::string &message) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Formatter::FormatInt(T value, const FormatSpec &spec) {
|
void Formatter::FormatInt(T value, FormatSpec spec) {
|
||||||
unsigned size = 0;
|
unsigned size = 0;
|
||||||
char sign = 0;
|
char sign = 0;
|
||||||
typedef typename IntTraits<T>::UnsignedType UnsignedType;
|
typedef typename IntTraits<T>::UnsignedType UnsignedType;
|
||||||
@ -117,7 +122,7 @@ void Formatter::FormatInt(T value, const FormatSpec &spec) {
|
|||||||
sign = '+';
|
sign = '+';
|
||||||
++size;
|
++size;
|
||||||
}
|
}
|
||||||
size_t start = buffer_.size();
|
size_t offset = buffer_.size();
|
||||||
char *p = 0;
|
char *p = 0;
|
||||||
switch (spec.type) {
|
switch (spec.type) {
|
||||||
case 0: case 'd': {
|
case 0: case 'd': {
|
||||||
@ -173,11 +178,13 @@ void Formatter::FormatInt(T value, const FormatSpec &spec) {
|
|||||||
}
|
}
|
||||||
if (sign) {
|
if (sign) {
|
||||||
if ((spec.flags & ZERO_FLAG) != 0)
|
if ((spec.flags & ZERO_FLAG) != 0)
|
||||||
buffer_[start++] = sign;
|
buffer_[offset++] = sign;
|
||||||
else
|
else
|
||||||
*p-- = sign;
|
*p-- = sign;
|
||||||
}
|
}
|
||||||
std::fill(&buffer_[start], p + 1, spec.fill);
|
char *start = &buffer_[offset];
|
||||||
|
if (start != p)
|
||||||
|
std::fill(start, p + 1, spec.fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -238,8 +245,10 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) {
|
|||||||
snprintf(start, size, format, width, precision, value);
|
snprintf(start, size, format, width, precision, value);
|
||||||
}
|
}
|
||||||
if (n >= 0 && offset + n < buffer_.capacity()) {
|
if (n >= 0 && offset + n < buffer_.capacity()) {
|
||||||
while (*start == ' ')
|
if (spec.fill != ' ') {
|
||||||
*start++ = spec.fill;
|
while (*start == ' ')
|
||||||
|
*start++ = spec.fill;
|
||||||
|
}
|
||||||
GrowBuffer(n);
|
GrowBuffer(n);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -289,21 +298,41 @@ void Formatter::DoFormat() {
|
|||||||
|
|
||||||
const Arg &arg = ParseArgIndex(s);
|
const Arg &arg = ParseArgIndex(s);
|
||||||
|
|
||||||
FormatSpec spec = {};
|
FormatSpec spec;
|
||||||
spec.fill = ' ';
|
|
||||||
int precision = -1;
|
int precision = -1;
|
||||||
if (*s == ':') {
|
if (*s == ':') {
|
||||||
++s;
|
++s;
|
||||||
|
|
||||||
|
// Parse fill and alignment.
|
||||||
if (char c = *s) {
|
if (char c = *s) {
|
||||||
switch (s[1]) {
|
const char *p = s + 1;
|
||||||
case '<': case '>': case '=': case '^':
|
Alignment align = ALIGN_DEFAULT;
|
||||||
if (c != '{' && c != '}') {
|
do {
|
||||||
s += 2;
|
switch (*p) {
|
||||||
spec.fill = c;
|
case '<':
|
||||||
|
align = ALIGN_LEFT;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
align = ALIGN_RIGHT;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
align = ALIGN_NUMERIC;
|
||||||
|
break;
|
||||||
|
case '^':
|
||||||
|
align = ALIGN_CENTER;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
if (align != ALIGN_DEFAULT) {
|
||||||
}
|
if (p != s && c != '{' && c != '}') {
|
||||||
|
s += 2;
|
||||||
|
spec.fill = c;
|
||||||
|
} else ++s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (--p >= s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse sign.
|
||||||
if (*s == '+') {
|
if (*s == '+') {
|
||||||
++s;
|
++s;
|
||||||
if (arg.type > LAST_NUMERIC_TYPE)
|
if (arg.type > LAST_NUMERIC_TYPE)
|
||||||
@ -314,16 +343,17 @@ void Formatter::DoFormat() {
|
|||||||
}
|
}
|
||||||
spec.flags |= PLUS_FLAG;
|
spec.flags |= PLUS_FLAG;
|
||||||
}
|
}
|
||||||
if (*s == '0') {
|
|
||||||
++s;
|
|
||||||
if (arg.type > LAST_NUMERIC_TYPE)
|
|
||||||
ReportError(s, "format specifier '0' requires numeric argument");
|
|
||||||
spec.flags |= ZERO_FLAG;
|
|
||||||
spec.fill = '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse width.
|
// Parse width and zero flag.
|
||||||
if ('0' <= *s && *s <= '9') {
|
if ('0' <= *s && *s <= '9') {
|
||||||
|
if (*s == '0') {
|
||||||
|
if (arg.type > LAST_NUMERIC_TYPE)
|
||||||
|
ReportError(s, "format specifier '0' requires numeric argument");
|
||||||
|
spec.flags |= ZERO_FLAG;
|
||||||
|
spec.fill = '0';
|
||||||
|
}
|
||||||
|
// Zero may be parsed again as a part of the width, but it is simpler
|
||||||
|
// and more efficient than checking if the next char is a digit.
|
||||||
unsigned value = ParseUInt(s);
|
unsigned value = ParseUInt(s);
|
||||||
if (value > INT_MAX)
|
if (value > INT_MAX)
|
||||||
ReportError(s, "number is too big in format");
|
ReportError(s, "number is too big in format");
|
||||||
|
4
format.h
4
format.h
@ -128,6 +128,8 @@ struct FormatSpec {
|
|||||||
unsigned width;
|
unsigned width;
|
||||||
char type;
|
char type;
|
||||||
char fill;
|
char fill;
|
||||||
|
|
||||||
|
FormatSpec() : flags(0), width(0), type(0), fill(' ') {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Formatter provides string formatting functionality similar to Python's
|
// Formatter provides string formatting functionality similar to Python's
|
||||||
@ -266,7 +268,7 @@ class Formatter {
|
|||||||
|
|
||||||
// Formats an integer.
|
// Formats an integer.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void FormatInt(T value, const FormatSpec &spec);
|
void FormatInt(T value, FormatSpec spec);
|
||||||
|
|
||||||
// Formats a floating point number (double or long double).
|
// Formats a floating point number (double or long double).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -257,6 +257,22 @@ TEST(FormatterTest, EmptySpecs) {
|
|||||||
EXPECT_EQ("42", str(Format("{0:}") << 42));
|
EXPECT_EQ("42", str(Format("{0:}") << 42));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FormatterTest, Align) {
|
||||||
|
// TODO
|
||||||
|
EXPECT_EQ(" 42", str(Format("{0:>4}") << 42));
|
||||||
|
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42));
|
||||||
|
EXPECT_EQ(" 42", str(Format("{0:>5}") << 42u));
|
||||||
|
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42l));
|
||||||
|
EXPECT_EQ(" 42", str(Format("{0:>5}") << 42ul));
|
||||||
|
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42.0));
|
||||||
|
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42.0l));
|
||||||
|
EXPECT_EQ("c ", str(Format("{0:<5}") << 'c'));
|
||||||
|
EXPECT_EQ("abc ", str(Format("{0:<5}") << "abc"));
|
||||||
|
EXPECT_EQ(" 0xface",
|
||||||
|
str(Format("{0:>8}") << reinterpret_cast<void*>(0xface)));
|
||||||
|
EXPECT_EQ("def ", str(Format("{0:<5}") << TestString("def")));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, Fill) {
|
TEST(FormatterTest, Fill) {
|
||||||
EXPECT_EQ("**42", str(Format("{0:*>4}") << 42));
|
EXPECT_EQ("**42", str(Format("{0:*>4}") << 42));
|
||||||
EXPECT_EQ("**-42", str(Format("{0:*>5}") << -42));
|
EXPECT_EQ("**-42", str(Format("{0:*>5}") << -42));
|
||||||
@ -644,8 +660,7 @@ TEST(FormatterTest, FormatString) {
|
|||||||
|
|
||||||
TEST(FormatterTest, Write) {
|
TEST(FormatterTest, Write) {
|
||||||
Formatter format;
|
Formatter format;
|
||||||
fmt::FormatSpec spec = {};
|
fmt::FormatSpec spec;
|
||||||
spec.fill = ' ';
|
|
||||||
spec.width = 2;
|
spec.width = 2;
|
||||||
format.Write("12", spec);
|
format.Write("12", spec);
|
||||||
EXPECT_EQ("12", format.str());
|
EXPECT_EQ("12", format.str());
|
||||||
@ -660,8 +675,7 @@ TEST(FormatterTest, Write) {
|
|||||||
TEST(ArgFormatterTest, Write) {
|
TEST(ArgFormatterTest, Write) {
|
||||||
Formatter formatter;
|
Formatter formatter;
|
||||||
fmt::ArgFormatter format(formatter);
|
fmt::ArgFormatter format(formatter);
|
||||||
fmt::FormatSpec spec = {};
|
fmt::FormatSpec spec;
|
||||||
spec.fill = ' ';
|
|
||||||
spec.width = 2;
|
spec.width = 2;
|
||||||
format.Write("12", spec);
|
format.Write("12", spec);
|
||||||
EXPECT_EQ("12", formatter.str());
|
EXPECT_EQ("12", formatter.str());
|
||||||
|
Reference in New Issue
Block a user