Commit 64236894 authored by Victor Zverovich's avatar Victor Zverovich

Parse alignment.

parent a0d685c7
...@@ -52,6 +52,11 @@ namespace { ...@@ -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 { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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()) {
if (spec.fill != ' ') {
while (*start == ' ') while (*start == ' ')
*start++ = spec.fill; *start++ = spec.fill;
}
GrowBuffer(n); GrowBuffer(n);
return; return;
} }
...@@ -289,21 +298,41 @@ void Formatter::DoFormat() { ...@@ -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 {
switch (*p) {
case '<':
align = ALIGN_LEFT;
break;
case '>':
align = ALIGN_RIGHT;
break;
case '=':
align = ALIGN_NUMERIC;
break;
case '^':
align = ALIGN_CENTER;
break;
}
if (align != ALIGN_DEFAULT) {
if (p != s && c != '{' && c != '}') {
s += 2; s += 2;
spec.fill = c; spec.fill = c;
} } else ++s;
break; 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() { ...@@ -314,16 +343,17 @@ void Formatter::DoFormat() {
} }
spec.flags |= PLUS_FLAG; spec.flags |= PLUS_FLAG;
} }
// Parse width and zero flag.
if ('0' <= *s && *s <= '9') {
if (*s == '0') { if (*s == '0') {
++s;
if (arg.type > LAST_NUMERIC_TYPE) if (arg.type > LAST_NUMERIC_TYPE)
ReportError(s, "format specifier '0' requires numeric argument"); ReportError(s, "format specifier '0' requires numeric argument");
spec.flags |= ZERO_FLAG; spec.flags |= ZERO_FLAG;
spec.fill = '0'; spec.fill = '0';
} }
// Zero may be parsed again as a part of the width, but it is simpler
// Parse width. // and more efficient than checking if the next char is a digit.
if ('0' <= *s && *s <= '9') {
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");
......
...@@ -128,6 +128,8 @@ struct FormatSpec { ...@@ -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 { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment