From bbd13a492b623c2a60e0a28733f1733e3698920d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 9 Dec 2012 14:13:23 -0800 Subject: [PATCH] Test precision. --- format.cc | 108 ++++++++++++++++++++++++++++++++++--------------- format.h | 17 ++++---- format_test.cc | 102 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 171 insertions(+), 56 deletions(-) diff --git a/format.cc b/format.cc index 4548c0d0..c57926bd 100644 --- a/format.cc +++ b/format.cc @@ -72,6 +72,7 @@ void fmt::Formatter::Format() { const char *s = start; while (*s) { if (*s++ != '{') continue; + // TODO: handle escape sequence buffer_.insert(buffer_.end(), start, s - 1); // Parse argument index. @@ -90,26 +91,31 @@ void fmt::Formatter::Format() { int width = -1; int precision = -1; char type = 0; + bool is_floating_point = false; if (*s == ':') { ++s; if (*s == '+') { if (arg.type > LAST_NUMERIC_TYPE) { Throw(s, - "format specifier '+' used with non-numeric type"); + "format specifier '+' requires numeric argument"); + } + if (arg.type == UINT || arg.type == ULONG) { + Throw(s, + "format specifier '+' requires signed argument"); } *arg_format_ptr++ = *s++; } if (*s == '0') { if (arg.type > LAST_NUMERIC_TYPE) { Throw(s, - "format specifier '0' used with non-numeric type"); + "format specifier '0' requires numeric argument"); } *arg_format_ptr++ = *s++; } // Parse width. if ('0' <= *s && *s <= '9') { - if (arg.type > LAST_NUMERIC_TYPE) + if (arg.type > LAST_NUMERIC_TYPE && arg.type != POINTER) *arg_format_ptr++ = '-'; *arg_format_ptr++ = '*'; unsigned value = ParseUInt(s); @@ -127,16 +133,36 @@ void fmt::Formatter::Format() { if ('0' <= *s && *s <= '9') { unsigned value = ParseUInt(s); if (value > INT_MAX) - Throw(s, "number is too big in format"); // TODO: test + Throw(s, "number is too big in format"); precision = value; } else { - // TODO: error + Throw(s, "missing precision in format"); + } + if (arg.type > LAST_NUMERIC_TYPE || + (*s == '}' && arg.type != DOUBLE && arg.type != LONG_DOUBLE)) { + Throw(s, + "precision specifier requires floating-point type"); } } // Parse type. - if (*s == 'f' || *s == 'g') - type = *s++; // TODO: check if the type matches + if (*s != '}' && *s) { + type = *s++; + if (arg.type <= LAST_NUMERIC_TYPE) { + switch (type) { + case 'd': case 'o': case 'x': case 'X': + // TODO: check that argument is integer + break; + case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': + is_floating_point = true; + break; + default: + // TODO: error + break; + } + } + // TODO: check non-numeric types (string, character) + } } if (*s++ != '}') @@ -145,36 +171,39 @@ void fmt::Formatter::Format() { // Format argument. switch (arg.type) { - case CHAR: - if (width == -1 && precision == -1) { - buffer_.push_back(arg.int_value); - break; - } - *arg_format_ptr++ = 'c'; - *arg_format_ptr = '\0'; - FormatBuiltinArg(arg_format, arg.int_value, width, precision); - break; case INT: - *arg_format_ptr++ = 'd'; + *arg_format_ptr++ = type ? type : 'd'; *arg_format_ptr = '\0'; - FormatBuiltinArg(arg_format, arg.int_value, width, precision); + if (is_floating_point) + FormatBuiltinArg(arg_format, arg.int_value, width, precision); + else + FormatBuiltinArg(arg_format, arg.int_value, width, precision); break; case UINT: - *arg_format_ptr++ = 'd'; + *arg_format_ptr++ = type ? type : 'u'; *arg_format_ptr = '\0'; - FormatBuiltinArg(arg_format, arg.uint_value, width, precision); + if (is_floating_point) + FormatBuiltinArg(arg_format, arg.uint_value, width, precision); + else + FormatBuiltinArg(arg_format, arg.uint_value, width, precision); break; case LONG: *arg_format_ptr++ = 'l'; - *arg_format_ptr++ = 'd'; + *arg_format_ptr++ = type ? type : 'd'; *arg_format_ptr = '\0'; - FormatBuiltinArg(arg_format, arg.long_value, width, precision); + if (is_floating_point) + FormatBuiltinArg(arg_format, arg.long_value, width, precision); + else + FormatBuiltinArg(arg_format, arg.long_value, width, precision); break; case ULONG: *arg_format_ptr++ = 'l'; - *arg_format_ptr++ = 'd'; + *arg_format_ptr++ = type ? type : 'u'; *arg_format_ptr = '\0'; - FormatBuiltinArg(arg_format, arg.ulong_value, width, precision); + if (is_floating_point) + FormatBuiltinArg(arg_format, arg.ulong_value, width, precision); + else + FormatBuiltinArg(arg_format, arg.ulong_value, width, precision); break; case DOUBLE: *arg_format_ptr++ = type ? type : 'g'; @@ -183,15 +212,31 @@ void fmt::Formatter::Format() { break; case LONG_DOUBLE: *arg_format_ptr++ = 'L'; - *arg_format_ptr++ = 'g'; + *arg_format_ptr++ = type ? type : 'g'; *arg_format_ptr = '\0'; FormatBuiltinArg(arg_format, arg.long_double_value, width, precision); break; + case POINTER: + // TODO: don't allow any type specifiers other than 'p' + *arg_format_ptr++ = 'p'; + *arg_format_ptr = '\0'; + FormatBuiltinArg(arg_format, arg.pointer_value, width, precision); + break; + case CHAR: + // TODO: check if type is 'c' or none + if (width <= 1) { + buffer_.push_back(arg.int_value); + break; + } + *arg_format_ptr++ = 'c'; + *arg_format_ptr = '\0'; + FormatBuiltinArg(arg_format, arg.int_value, width, precision); + break; case STRING: - // TODO: align string left by default - if (width == -1 && precision == -1) { + // TODO: check if type is 's' or none + if (width == -1 || width <= arg.size) { const char *str = arg.string_value; - std::size_t size = arg.size; + size_t size = arg.size; if (size == 0 && *str) size = std::strlen(str); buffer_.reserve(buffer_.size() + size + 1); @@ -203,17 +248,14 @@ void fmt::Formatter::Format() { FormatBuiltinArg(arg_format, arg.string_value, width, precision); break; case WSTRING: + // TODO: check if type is 's' or none *arg_format_ptr++ = 'l'; *arg_format_ptr++ = 's'; *arg_format_ptr = '\0'; FormatBuiltinArg(arg_format, arg.wstring_value, width, precision); break; - case POINTER: - *arg_format_ptr++ = 'p'; - *arg_format_ptr = '\0'; - FormatBuiltinArg(arg_format, arg.pointer_value, width, precision); - break; case CUSTOM: + // TODO: check if type is 's' or none (this->*arg.format)(arg.custom_value, width); break; default: diff --git a/format.h b/format.h index cfc21ea4..899ee7b7 100644 --- a/format.h +++ b/format.h @@ -33,8 +33,9 @@ class Formatter { enum Type { // Numeric types should go first. - INT, UINT, LONG, ULONG, DOUBLE, LONG_DOUBLE, POINTER, - LAST_NUMERIC_TYPE = POINTER, CHAR, STRING, WSTRING, CUSTOM + INT, UINT, LONG, ULONG, DOUBLE, LONG_DOUBLE, + LAST_NUMERIC_TYPE = LONG_DOUBLE, + CHAR, STRING, WSTRING, POINTER, CUSTOM }; typedef void (Formatter::*FormatFunc)(const void *arg, int width); @@ -63,7 +64,6 @@ class Formatter { }; }; - explicit Arg(char value) : type(CHAR), int_value(value) {} explicit Arg(int value) : type(INT), int_value(value) {} explicit Arg(unsigned value) : type(UINT), uint_value(value) {} explicit Arg(long value) : type(LONG), long_value(value) {} @@ -71,6 +71,7 @@ class Formatter { explicit Arg(double value) : type(DOUBLE), double_value(value) {} explicit Arg(long double value) : type(LONG_DOUBLE), long_double_value(value) {} + explicit Arg(char value) : type(CHAR), int_value(value) {} explicit Arg(const char *value, std::size_t size = 0) : type(STRING), string_value(value), size(size) {} explicit Arg(const wchar_t *value) : type(WSTRING), wstring_value(value) {} @@ -158,11 +159,6 @@ class ArgFormatter { return af.FinishFormatting()->c_str(); } - ArgFormatter &operator<<(char value) { - formatter_->Add(Formatter::Arg(value)); - return *this; - } - ArgFormatter &operator<<(int value) { formatter_->Add(Formatter::Arg(value)); return *this; @@ -193,6 +189,11 @@ class ArgFormatter { return *this; } + ArgFormatter &operator<<(char value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + ArgFormatter &operator<<(const char *value) { formatter_->Add(Formatter::Arg(value)); return *this; diff --git a/format_test.cc b/format_test.cc index 4652317c..1ca1ae86 100644 --- a/format_test.cc +++ b/format_test.cc @@ -117,23 +117,25 @@ TEST(FormatterTest, PlusFlag) { EXPECT_EQ("+42", str(Format("{0:+}") << 42)); EXPECT_EQ("-42", str(Format("{0:+}") << -42)); EXPECT_EQ("+42", str(Format("{0:+}") << 42)); - EXPECT_EQ("+42", str(Format("{0:+}") << 42u)); + EXPECT_THROW_MSG(Format("{0:+}") << 42u, + FormatError, "format specifier '+' requires signed argument"); EXPECT_EQ("+42", str(Format("{0:+}") << 42l)); - EXPECT_EQ("+42", str(Format("{0:+}") << 42ul)); + EXPECT_THROW_MSG(Format("{0:+}") << 42ul, + FormatError, "format specifier '+' requires signed argument"); EXPECT_EQ("+42", str(Format("{0:+}") << 42.0)); EXPECT_EQ("+42", str(Format("{0:+}") << 42.0l)); - EXPECT_EQ("+0x42", - str(Format("{0:+}") << reinterpret_cast(0x42))); EXPECT_THROW_MSG(Format("{0:+") << 'c', FormatError, "unmatched '{' in format"); EXPECT_THROW_MSG(Format("{0:+}") << 'c', - FormatError, "format specifier '+' used with non-numeric type"); + FormatError, "format specifier '+' requires numeric argument"); EXPECT_THROW_MSG(Format("{0:+}") << "abc", - FormatError, "format specifier '+' used with non-numeric type"); + FormatError, "format specifier '+' requires numeric argument"); EXPECT_THROW_MSG(Format("{0:+}") << L"abc", - FormatError, "format specifier '+' used with non-numeric type"); + FormatError, "format specifier '+' requires numeric argument"); + EXPECT_THROW_MSG(Format("{0:+}") << reinterpret_cast(0x42), + FormatError, "format specifier '+' requires numeric argument"); EXPECT_THROW_MSG(Format("{0:+}") << TestString(), - FormatError, "format specifier '+' used with non-numeric type"); + FormatError, "format specifier '+' requires numeric argument"); } TEST(FormatterTest, ZeroFlag) { @@ -144,18 +146,18 @@ TEST(FormatterTest, ZeroFlag) { EXPECT_EQ("00042", str(Format("{0:05}") << 42ul)); EXPECT_EQ("-0042", str(Format("{0:05}") << -42.0)); EXPECT_EQ("-0042", str(Format("{0:05}") << -42.0l)); - EXPECT_EQ("0x0042", - str(Format("{0:06}") << reinterpret_cast(0x42))); EXPECT_THROW_MSG(Format("{0:0") << 'c', FormatError, "unmatched '{' in format"); EXPECT_THROW_MSG(Format("{0:05}") << 'c', - FormatError, "format specifier '0' used with non-numeric type"); + FormatError, "format specifier '0' requires numeric argument"); EXPECT_THROW_MSG(Format("{0:05}") << "abc", - FormatError, "format specifier '0' used with non-numeric type"); + FormatError, "format specifier '0' requires numeric argument"); EXPECT_THROW_MSG(Format("{0:05}") << L"abc", - FormatError, "format specifier '0' used with non-numeric type"); + FormatError, "format specifier '0' requires numeric argument"); + EXPECT_THROW_MSG(Format("{0:05}") << reinterpret_cast(0x42), + FormatError, "format specifier '0' requires numeric argument"); EXPECT_THROW_MSG(Format("{0:05}") << TestString(), - FormatError, "format specifier '0' used with non-numeric type"); + FormatError, "format specifier '0' requires numeric argument"); } TEST(FormatterTest, Width) { @@ -181,7 +183,7 @@ TEST(FormatterTest, Width) { EXPECT_EQ(" -42", str(Format("{0:4}") << -42)); EXPECT_EQ(" 42", str(Format("{0:5}") << 42u)); EXPECT_EQ(" -42", str(Format("{0:6}") << -42l)); - EXPECT_EQ(" 42", str(Format("{0:7}") << 42lu)); + EXPECT_EQ(" 42", str(Format("{0:7}") << 42ul)); EXPECT_EQ(" -1.23", str(Format("{0:8}") << -1.23)); EXPECT_EQ(" -1.23", str(Format("{0:9}") << -1.23l)); EXPECT_EQ(" 0xcafe", @@ -192,6 +194,76 @@ TEST(FormatterTest, Width) { EXPECT_EQ("test ", str(Format("{0:14}") << TestString("test"))); } +TEST(FormatterTest, Precision) { + char format[256]; + if (ULONG_MAX > UINT_MAX) { + std::sprintf(format, "{0:.%lu", INT_MAX + 1l); + EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format"); + std::sprintf(format, "{0:.%lu}", UINT_MAX + 1l); + EXPECT_THROW_MSG(Format(format) << 0, + FormatError, "number is too big in format"); + } else { + std::sprintf(format, "{0:.%u0", UINT_MAX); + EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format"); + std::sprintf(format, "{0:.%u0}", UINT_MAX); + EXPECT_THROW_MSG(Format(format) << 0, + FormatError, "number is too big in format"); + } + + std::sprintf(format, "{0:.%u", INT_MAX + 1u); + EXPECT_THROW_MSG(Format(format), FormatError, "unmatched '{' in format"); + std::sprintf(format, "{0:.%u}", INT_MAX + 1u); + EXPECT_THROW_MSG(Format(format) << 0, + FormatError, "number is too big in format"); + + EXPECT_THROW_MSG(Format("{0:.") << 0, + FormatError, "unmatched '{' in format"); + EXPECT_THROW_MSG(Format("{0:.}") << 0, + FormatError, "missing precision in format"); + EXPECT_THROW_MSG(Format("{0:.2") << 0, + FormatError, "unmatched '{' in format"); + + EXPECT_THROW_MSG(Format("{0:.2}") << 42, + FormatError, "precision specifier requires floating-point type"); + EXPECT_EQ("42.00", str(Format("{0:.2f}") << 42)); + EXPECT_THROW_MSG(Format("{0:.2}") << 42u, + FormatError, "precision specifier requires floating-point type"); + EXPECT_EQ("42.00", str(Format("{0:.2f}") << 42u)); + EXPECT_THROW_MSG(Format("{0:.2}") << 42l, + FormatError, "precision specifier requires floating-point type"); + EXPECT_EQ("42.00", str(Format("{0:.2f}") << 42l)); + EXPECT_THROW_MSG(Format("{0:.2}") << 42ul, + FormatError, "precision specifier requires floating-point type"); + EXPECT_EQ("42.00", str(Format("{0:.2f}") << 42ul)); + EXPECT_EQ("1.2", str(Format("{0:.2}") << 1.2345)); + EXPECT_EQ("1.2", str(Format("{0:.2}") << 1.2345l)); + + EXPECT_THROW_MSG(Format("{0:.2}") << reinterpret_cast(0xcafe), + FormatError, "precision specifier requires floating-point type"); + EXPECT_THROW_MSG(Format("{0:.2f}") << reinterpret_cast(0xcafe), + FormatError, "precision specifier requires floating-point type"); + + EXPECT_THROW_MSG(Format("{0:.2}") << 'x', + FormatError, "precision specifier requires floating-point type"); + EXPECT_THROW_MSG(Format("{0:.2f}") << 'x', + FormatError, "precision specifier requires floating-point type"); + + EXPECT_THROW_MSG(Format("{0:.2}") << "str", + FormatError, "precision specifier requires floating-point type"); + EXPECT_THROW_MSG(Format("{0:.2f}") << "str", + FormatError, "precision specifier requires floating-point type"); + + EXPECT_THROW_MSG(Format("{0:.2}") << L"str", + FormatError, "precision specifier requires floating-point type"); + EXPECT_THROW_MSG(Format("{0:.2f}") << L"str", + FormatError, "precision specifier requires floating-point type"); + + EXPECT_THROW_MSG(Format("{0:.2}") << TestString(), + FormatError, "precision specifier requires floating-point type"); + EXPECT_THROW_MSG(Format("{0:.2f}") << TestString(), + FormatError, "precision specifier requires floating-point type"); +} + TEST(FormatterTest, FormatInt) { EXPECT_EQ("42", str(Format("{0}") << 42)); EXPECT_EQ("-1234", str(Format("{0}") << -1234));