Commit 64236894 authored by Victor Zverovich's avatar Victor Zverovich

Parse alignment.

parent a0d685c7
......@@ -52,6 +52,11 @@ namespace {
// Flags.
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) {
if (std::isprint(static_cast<unsigned char>(code))) {
throw fmt::FormatError(
......@@ -104,7 +109,7 @@ void Formatter::ReportError(const char *s, const std::string &message) const {
}
template <typename T>
void Formatter::FormatInt(T value, const FormatSpec &spec) {
void Formatter::FormatInt(T value, FormatSpec spec) {
unsigned size = 0;
char sign = 0;
typedef typename IntTraits<T>::UnsignedType UnsignedType;
......@@ -117,7 +122,7 @@ void Formatter::FormatInt(T value, const FormatSpec &spec) {
sign = '+';
++size;
}
size_t start = buffer_.size();
size_t offset = buffer_.size();
char *p = 0;
switch (spec.type) {
case 0: case 'd': {
......@@ -173,11 +178,13 @@ void Formatter::FormatInt(T value, const FormatSpec &spec) {
}
if (sign) {
if ((spec.flags & ZERO_FLAG) != 0)
buffer_[start++] = sign;
buffer_[offset++] = sign;
else
*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>
......@@ -238,8 +245,10 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) {
snprintf(start, size, format, width, precision, value);
}
if (n >= 0 && offset + n < buffer_.capacity()) {
while (*start == ' ')
*start++ = spec.fill;
if (spec.fill != ' ') {
while (*start == ' ')
*start++ = spec.fill;
}
GrowBuffer(n);
return;
}
......@@ -289,21 +298,41 @@ void Formatter::DoFormat() {
const Arg &arg = ParseArgIndex(s);
FormatSpec spec = {};
spec.fill = ' ';
FormatSpec spec;
int precision = -1;
if (*s == ':') {
++s;
// Parse fill and alignment.
if (char c = *s) {
switch (s[1]) {
case '<': case '>': case '=': case '^':
if (c != '{' && c != '}') {
s += 2;
spec.fill = c;
const char *p = s + 1;
Alignment align = ALIGN_DEFAULT;
do {
switch (*p) {
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 == '+') {
++s;
if (arg.type > LAST_NUMERIC_TYPE)
......@@ -314,16 +343,17 @@ void Formatter::DoFormat() {
}
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 (*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);
if (value > INT_MAX)
ReportError(s, "number is too big in format");
......
......@@ -128,6 +128,8 @@ struct FormatSpec {
unsigned width;
char type;
char fill;
FormatSpec() : flags(0), width(0), type(0), fill(' ') {}
};
// Formatter provides string formatting functionality similar to Python's
......@@ -266,7 +268,7 @@ class Formatter {
// Formats an integer.
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).
template <typename T>
......
......@@ -257,6 +257,22 @@ TEST(FormatterTest, EmptySpecs) {
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) {
EXPECT_EQ("**42", str(Format("{0:*>4}") << 42));
EXPECT_EQ("**-42", str(Format("{0:*>5}") << -42));
......@@ -644,8 +660,7 @@ TEST(FormatterTest, FormatString) {
TEST(FormatterTest, Write) {
Formatter format;
fmt::FormatSpec spec = {};
spec.fill = ' ';
fmt::FormatSpec spec;
spec.width = 2;
format.Write("12", spec);
EXPECT_EQ("12", format.str());
......@@ -660,8 +675,7 @@ TEST(FormatterTest, Write) {
TEST(ArgFormatterTest, Write) {
Formatter formatter;
fmt::ArgFormatter format(formatter);
fmt::FormatSpec spec = {};
spec.fill = ' ';
fmt::FormatSpec spec;
spec.width = 2;
format.Write("12", spec);
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