Browse Source

Completed more complex formatting.

Bil Bas (Spooner) 10 years ago
parent
commit
af7c8bdf23
2 changed files with 423 additions and 65 deletions
  1. 269 8
      bin/tests/test_string.cpp
  2. 154 57
      core/ustring.cpp

+ 269 - 8
bin/tests/test_string.cpp

@@ -524,16 +524,63 @@ bool test_28() {
 	format = "fish %% frog";
 	args.clear();
 	output = format.sprintf(args);
-	success = (format.sprintf(args) == String("fish % frog"));
+	success = (output == String("fish % frog"));
 	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
 	if (!success) state = false;
 
+	//////// INTS
+
 	// Int
 	format = "fish %d frog";
 	args.clear();
 	args.push_back(5);
 	output = format.sprintf(args);
-	success = (format.sprintf(args) == String("fish 5 frog"));
+	success = (output == String("fish 5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int left padded with zeroes.
+	format = "fish %05d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish 00005 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int left padded with spaces.
+	format = "fish %5d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish     5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int right padded with spaces.
+	format = "fish %-5d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish 5     frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int with sign (positive).
+	format = "fish %+d frog";
+	args.clear();
+	args.push_back(5);
+	output = format.sprintf(args);
+	success = (output == String("fish +5 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Negative int.
+	format = "fish %d frog";
+	args.clear();
+	args.push_back(-5);
+	output = format.sprintf(args);
+	success = (output == String("fish -5 frog"));
 	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
 	if (!success) state = false;
 
@@ -542,7 +589,7 @@ bool test_28() {
 	args.clear();
 	args.push_back(45);
 	output = format.sprintf(args);
-	success = (format.sprintf(args) == String("fish 2d frog"));
+	success = (output == String("fish 2d frog"));
 	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
 	if (!success) state = false;
 
@@ -551,7 +598,7 @@ bool test_28() {
 	args.clear();
 	args.push_back(45);
 	output = format.sprintf(args);
-	success = (format.sprintf(args) == String("fish 2D frog"));
+	success = (output == String("fish 2D frog"));
 	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
 	if (!success) state = false;
 
@@ -560,26 +607,240 @@ bool test_28() {
 	args.clear();
 	args.push_back(99);
 	output = format.sprintf(args);
-	success = (format.sprintf(args) == String("fish 143 frog"));
+	success = (output == String("fish 143 frog"));
 	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
 	if (!success) state = false;
 
+	////// REALS
+
 	// Real
 	format = "fish %f frog";
 	args.clear();
 	args.push_back(99.99);
 	output = format.sprintf(args);
-	success = (format.sprintf(args) == String("fish 99.990000 frog"));
+	success = (output == String("fish 99.990000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real left-padded
+	format = "fish %11f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish   99.990000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real right-padded
+	format = "fish %-11f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.990000   frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real given int.
+	format = "fish %f frog";
+	args.clear();
+	args.push_back(99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.000000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with sign (positive).
+	format = "fish %+f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish +99.990000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with 1 decimals.
+	format = "fish %.1f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 100.0 frog"));
 	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
 	if (!success) state = false;
 
+	// Real with 12 decimals.
+	format = "fish %.12f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 99.990000000000 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Real with no decimals.
+	format = "fish %.f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish 100 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	/////// Strings.
+
 	// String
 	format = "fish %s frog";
 	args.clear();
 	args.push_back("cheese");
 	output = format.sprintf(args);
-	success = (format.sprintf(args) == String("fish cheese frog"));
-	OS::get_singleton()->print(output_format , format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	success = (output == String("fish cheese frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// String left-padded
+	format = "fish %10s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish     cheese frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// String right-padded
+	format = "fish %-10s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish cheese     frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	///// Characters
+
+	// Character as string.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back("A");
+	output = format.sprintf(args);
+	success = (output == String("fish A frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Character as int.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back(65);
+	output = format.sprintf(args);
+	success = (output == String("fish A frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	///// Dynamic width
+
+	// String dynamic width
+	format = "fish %*s frog";
+	args.clear();
+	args.push_back(10);
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == String("fish     cheese frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Int dynamic width
+	format = "fish %*d frog";
+	args.clear();
+	args.push_back(10);
+	args.push_back(99);
+	output = format.sprintf(args);
+	success = (output == String("fish         99 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Float dynamic width
+	format = "fish %*.*f frog";
+	args.clear();
+	args.push_back(10);
+	args.push_back(3);
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == String("fish     99.990 frog"));
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	///// Errors
+
+	// More formats than arguments.
+	format = "fish %s %s frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// More arguments than formats.
+	format = "fish %s frog";
+	args.clear();
+	args.push_back("hello");
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Incomplete format.
+	format = "fish %10";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Bad character in format string
+	format = "fish %&f frog";
+	args.clear();
+	args.push_back("cheese");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Too many decimals.
+	format = "fish %2.2.2f frog";
+	args.clear();
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// * not a number
+	format = "fish %*f frog";
+	args.clear();
+	args.push_back("cheese");
+	args.push_back(99.99);
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Character too long.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back("sc");
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
+	if (!success) state = false;
+
+	// Character bad type.
+	format = "fish %c frog";
+	args.clear();
+	args.push_back(Array());
+	output = format.sprintf(args);
+	success = (output == "");
+	OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
 	if (!success) state = false;
 
 	return state;

+ 154 - 57
core/ustring.cpp

@@ -3554,15 +3554,17 @@ String String::sprintf(const Array& values) const {
 
 	String formatted;
 	CharType* self = (CharType*)c_str();
+	int num_items = values.size();
 	bool in_format = false;
 	int value_index = 0;
 	int min_chars;
-	int num_decimals;
+	int min_decimals;
 	bool in_decimals;
 	bool pad_with_zeroes;
 	bool left_justified;
 	bool show_sign;
 
+
 	for (; *self; self++) {
 		const CharType c = *self;
 
@@ -3577,71 +3579,88 @@ String String::sprintf(const Array& values) const {
 				case 'o': // Octal
 				case 'x': // Hexadecimal (lowercase)
 				case 'X': { // Hexadecimal (uppercase)
-					if (values[value_index].is_num()) {
-						int64_t value = values[value_index];
-						int base;
-						bool capitalize = false;
-						switch (c) {
-							case 'd': base = 10; break;
-							case 'o': base = 8; break;
-							case 'x': base = 16; break;
-							case 'X': base = 16; capitalize = true; break;
-						}
-						// Get basic number.
-						String str = String::num_int64(value, base, capitalize);
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
 
-						// Sign.
-						if (show_sign && value >= 0) {
-							str = str.insert(0, "+");
-						}
+					if (!values[value_index].is_num()) {
+						ERR_EXPLAIN("a number is required");
+						ERR_FAIL_V("");
+					}
+					
+					int64_t value = values[value_index];
+					int base;
+					bool capitalize = false;
+					switch (c) {
+						case 'd': base = 10; break;
+						case 'o': base = 8; break;
+						case 'x': base = 16; break;
+						case 'X': base = 16; capitalize = true; break;
+					}
+					// Get basic number.
+					String str = String::num_int64(value, base, capitalize);
 
-						// Padding.
-						String pad_char = pad_with_zeroes ? String("0") : String(" ");
-						if (left_justified) {
-							str = str.rpad(min_chars, pad_char);
-						} else {
-							str = str.lpad(min_chars, pad_char);
-						}
+					// Sign.
+					if (show_sign && value >= 0) {
+						str = str.insert(0, "+");
+					}
 
-						formatted += str;
-						++value_index;
-						in_format = false;
+					// Padding.
+					String pad_char = pad_with_zeroes ? String("0") : String(" ");
+					if (left_justified) {
+						str = str.rpad(min_chars, pad_char);
 					} else {
-						// TODO: Error?
+						str = str.lpad(min_chars, pad_char);
 					}
-					
+
+					formatted += str;
+					++value_index;
+					in_format = false;
+
 					break;
 				}
 				case 'f': { // Float
-					if (values[value_index].is_num()) {
-						double value = values[value_index];
-						String str = String::num(value, num_decimals);
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
 
-						// Pad decimals out.
-						str = str.pad_decimals(num_decimals);
+					if (!values[value_index].is_num()) {
+						ERR_EXPLAIN("a number is required");
+						ERR_FAIL_V("");
+					}
 
-						// Show sign
-						if (show_sign && value >= 0) {
-							str = str.insert(0, "+");
-						}
+					double value = values[value_index];
+					String str = String::num(value, min_decimals);
 
-						// Padding
-						if (left_justified) {
-							str = str.rpad(min_chars);
-						} else {
-							str = str.lpad(min_chars);
-						}
+					// Pad decimals out.
+					str = str.pad_decimals(min_decimals);
+
+					// Show sign
+					if (show_sign && value >= 0) {
+						str = str.insert(0, "+");
+					}
 
-						formatted += str;
-						++value_index;
-						in_format = false;
+					// Padding
+					if (left_justified) {
+						str = str.rpad(min_chars);
 					} else {
-						// TODO: Error?
+						str = str.lpad(min_chars);
 					}
+
+					formatted += str;
+					++value_index;
+					in_format = false;
 					
 					break;
 				}
 				case 's': { // String
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
 					String str = values[value_index];
 					// Padding.
 					if (left_justified) {
@@ -3655,6 +3674,47 @@ String String::sprintf(const Array& values) const {
 					in_format = false;
 					break;
 				}
+				case 'c': {
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					// Convert to character.
+					String str;
+					if (values[value_index].is_num()) {
+						int value = values[value_index];
+						if (value < 0) {
+							ERR_EXPLAIN("unsigned byte integer is lower than maximum")
+							ERR_FAIL_V("");
+						} else if (value > 255) {
+							ERR_EXPLAIN("unsigned byte integer is greater than maximum")
+							ERR_FAIL_V("");
+						}
+						str = chr(values[value_index]);
+					} else if (values[value_index].get_type() == Variant::STRING) {
+						str = values[value_index];
+						if (str.length() != 1) {
+							ERR_EXPLAIN("%c requires number or single-character string");
+							ERR_FAIL_V("");
+						}
+					} else {
+						ERR_EXPLAIN("%c requires number or single-character string");
+						ERR_FAIL_V("");
+					}
+
+					// Padding.
+					if (left_justified) {
+						str = str.rpad(min_chars);
+					} else {
+						str = str.lpad(min_chars);
+					}
+
+					formatted += str;
+					++value_index;
+					in_format = false;
+					break;
+				}
 				case '-': { // Left justify
 					left_justified = true;
 					break;
@@ -3667,8 +3727,8 @@ String String::sprintf(const Array& values) const {
 				case '5': case '6': case '7': case '8': case '9': {
 					int n = c - '0';
 					if (in_decimals) {
-						num_decimals *= 10;
-						num_decimals += n;
+						min_decimals *= 10;
+						min_decimals += n;
 					} else {
 						if (c == '0' && min_chars == 0) {
 							pad_with_zeroes = true;
@@ -3679,16 +3739,43 @@ String String::sprintf(const Array& values) const {
 					}
 					break;
 				}
-				case '.': // Float separtor.
+				case '.': { // Float separtor.
+					if (in_decimals) {
+						ERR_EXPLAIN("too many decimal points in format");
+						ERR_FAIL_V("");
+					}
 					in_decimals = true;
-					num_decimals = 0; // We want to add the value manually.
+					min_decimals = 0; // We want to add the value manually.
 					break;
+				}
+
+				case '*': { // Dyanmic width, based on value.
+					if (value_index >= values.size()) {
+						ERR_EXPLAIN("not enough arguments for format string");
+						ERR_FAIL_V("");
+					}
+
+					if (!values[value_index].is_num()) {
+						ERR_EXPLAIN("* wants number");
+						ERR_FAIL_V("");
+					}
+
+					int size = values[value_index];
+
+					if (in_decimals) {
+						min_decimals = size;
+					} else {
+						min_chars = size;
+					}
 
-				// case '*': // Dyanmic width, based on value.
-				// 	break;
+					++value_index;
+					break;
+				}
 
-				//default:
-					// TODO: error?
+				default: {
+					ERR_EXPLAIN("unsupported format character");
+  					ERR_FAIL_V("");
+  				}
 			}
 		} else { // Not in format string.
 			switch (c) {
@@ -3696,7 +3783,7 @@ String String::sprintf(const Array& values) const {
 					in_format = true;
 					// Back to defaults:
 					min_chars = 0;
-					num_decimals = 6;
+					min_decimals = 6;
 					pad_with_zeroes = false;
 					left_justified = false;
 					show_sign = false;
@@ -3708,5 +3795,15 @@ String String::sprintf(const Array& values) const {
 		}
 	}
 
+	if (in_format) {
+		ERR_EXPLAIN("incomplete format");
+  		ERR_FAIL_V("");
+	}
+
+	if (value_index != values.size()) {
+		ERR_EXPLAIN("not all arguments converted during string formatting");
+  		ERR_FAIL_V("");
+	}
+
 	return formatted;
 }