Added basic sprintf functionality (e.g. "fish %d %s" % [12, Vector2(1, 2)])

This commit is contained in:
Bil Bas (Spooner) 2015-01-10 20:44:20 +00:00
parent d6d85a23c9
commit 7a41f8c604
4 changed files with 193 additions and 2 deletions

View file

@ -487,7 +487,7 @@ struct test_27_data {
bool test_27() {
OS::get_singleton()->print("\n\nTest 26: begins_with\n");
OS::get_singleton()->print("\n\nTest 27: begins_with\n");
test_27_data tc[] = {
{"res://foobar", "res://", true},
{"res", "res://", false},
@ -504,11 +504,87 @@ bool test_27() {
}
if (!state) {
OS::get_singleton()->print("\n\t Failure on:\n\t\tstring: ", tc[i].data, "\n\t\tbegin: ", tc[i].begin, "\n\t\texpected: ", tc[i].expected ? "true" : "false", "\n");
break;
}
};
return state;
};
bool test_28() {
OS::get_singleton()->print("\n\nTest 28: sprintf\n");
bool success, state = true;
char output_format[] = "\tTest:\t%ls => %ls (%s)\n";
String format, output;
Array args;
// %%
format = "fish %% frog";
args.clear();
output = format.sprintf(args);
success = (format.sprintf(args) == String("fish % frog"));
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
if (!success) state = false;
// Int
format = "fish %d frog";
args.clear();
args.push_back(5);
output = format.sprintf(args);
success = (format.sprintf(args) == String("fish 5 frog"));
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
if (!success) state = false;
// Hex (lower)
format = "fish %x frog";
args.clear();
args.push_back(45);
output = format.sprintf(args);
success = (format.sprintf(args) == String("fish 2d frog"));
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
if (!success) state = false;
// Hex (upper)
format = "fish %X frog";
args.clear();
args.push_back(45);
output = format.sprintf(args);
success = (format.sprintf(args) == String("fish 2D frog"));
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
if (!success) state = false;
// Octal
format = "fish %o frog";
args.clear();
args.push_back(99);
output = format.sprintf(args);
success = (format.sprintf(args) == String("fish 143 frog"));
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
if (!success) state = false;
// 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"));
OS::get_singleton()->print(output_format, format.c_str(), output.c_str(), success ? "OK" : "FAIL");
if (!success) state = false;
// 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");
if (!success) state = false;
return state;
}
typedef bool (*TestFunc)(void);
TestFunc test_funcs[] = {
@ -540,6 +616,7 @@ TestFunc test_funcs[] = {
test_25,
test_26,
test_27,
test_28,
0
};

View file

@ -34,6 +34,8 @@
#include "io/md5.h"
#include "ucaps.h"
#include "color.h"
#include "variant.h"
#include <stdio.h>
#define MAX_DIGITS 6
#define UPPERCASE(m_c) (((m_c)>='a' && (m_c)<='z')?((m_c)-('a'-'A')):(m_c))
#define LOWERCASE(m_c) (((m_c)>='A' && (m_c)<='Z')?((m_c)+('a'-'A')):(m_c))
@ -3518,4 +3520,100 @@ String rtoss(double p_val) {
return String::num_scientific(p_val);
}
// sprintf is implemented in GDScript via:
// "fish %s pie" % "frog"
// "fish %s %d pie" % ["frog", 12]
const int FORMAT_BUFFER_SIZE = 1024;
const int OUTPUT_BUFFER_SIZE = 1024 * 100;
String String::sprintf(const Array& values) const {
String formatted;
CharType* self = (CharType*)c_str();
bool in_format = false;
int value_index = 0;
char format_format[FORMAT_BUFFER_SIZE] = "%d";
for (; *self; self++) {
const CharType c = *self;
if (in_format) { // We have % - lets see what else we get.
switch (c) {
case '%': // Manage %% as %
formatted += chr(c);
in_format = false;
break;
case 'd': // Integer (signed)
case 'o': // Octal
case 'x': // Hexadecimal (lowercase)
case 'X': // Hexadecimal (uppercase)
if (values[value_index].is_num()) {
char buffer[OUTPUT_BUFFER_SIZE];
int value = values[value_index];
format_format[1] = c;
format_format[2] = 0;
::sprintf(buffer, format_format, value);
formatted += String(buffer);
++value_index;
in_format = false;
} else {
// TODO: Error?
}
break;
case 'f': // Float
if (values[value_index].is_num()) {
char buffer[OUTPUT_BUFFER_SIZE];
double value = values[value_index];
::sprintf(buffer, "%f", value);
formatted += String(buffer);
++value_index;
in_format = false;
} else {
// TODO: Error?
}
break;
case 's': // String
String value = values[value_index];
formatted += value;
++value_index;
in_format = false;
break;
// case '-': // Left justify
// break;
// case '+': // Show + if positive.
// break;
// case '0': case '1': case '2': case '3': case '4':
// case '5': case '6': case '7': case '8': case '9':
// break;
// case '.': // Float separtor.
// break;
// case '*': // Dyanmic width, based on value.
// break;
//default:
// TODO: error?
}
} else { // Not in format string.
switch (c) {
case '%':
in_format = true;
break;
default:
formatted += chr(c);
}
}
}
return formatted;
}

View file

@ -31,6 +31,7 @@
#include "typedefs.h"
#include "vector.h"
#include "array.h"
/**
@author red <red@killy>
@ -127,6 +128,7 @@ public:
String insert(int p_at_pos,String p_string) const;
String pad_decimals(int p_digits) const;
String pad_zeros(int p_digits) const;
String sprintf(const Array& values) const;
static String num(double p_num,int p_decimals=-1);
static String num_scientific(double p_num);
static String num_real(double p_num);
@ -203,7 +205,7 @@ public:
String xml_unescape() const;
String c_escape() const;
String c_unescape() const;
String percent_encode() const;
String percent_decode() const;

View file

@ -736,6 +736,20 @@ void Variant::evaluate(const Operator& p_op, const Variant& p_a, const Variant&
}
#endif
_RETURN( p_a._data._int % p_b._data._int );
} else if (p_a.type==STRING) {
const String *str=reinterpret_cast<const String*>(p_a._data._mem);
if (p_b.type==ARRAY) {
// e.g. "frog %s %d" % ["fish", 12]
const Array *arr=reinterpret_cast<const Array*>(p_b._data._mem);
_RETURN(str->sprintf(*arr));
} else {
// e.g. "frog %d" % 12
Array arr;
arr.push_back(p_b);
_RETURN(str->sprintf(arr));
}
}
r_valid=false;