[String] Allow using Java style %[arg$] prefix in sprintf.
This commit is contained in:
parent
be587f3a2d
commit
f69edf810e
2 changed files with 76 additions and 30 deletions
|
|
@ -5176,10 +5176,13 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
static const String MINUS("-");
|
||||
static const String PLUS("+");
|
||||
|
||||
LocalVector<bool> used_args;
|
||||
used_args.resize_initialized(values.size());
|
||||
String formatted;
|
||||
char32_t *self = (char32_t *)get_data();
|
||||
bool in_format = false;
|
||||
uint64_t value_index = 0;
|
||||
int selected_index = -1;
|
||||
int min_chars = 0;
|
||||
int min_decimals = 0;
|
||||
bool in_decimals = false;
|
||||
|
|
@ -5206,15 +5209,16 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
case 'o': // Octal
|
||||
case 'x': // Hexadecimal (lowercase)
|
||||
case 'X': { // Hexadecimal (uppercase)
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
if (!values[value_index].is_num()) {
|
||||
if (!values[index].is_num()) {
|
||||
return "a number is required";
|
||||
}
|
||||
|
||||
int64_t value = values[value_index];
|
||||
int64_t value = values[index];
|
||||
int base = 16;
|
||||
bool capitalize = false;
|
||||
switch (c) {
|
||||
|
|
@ -5270,21 +5274,25 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case 'f': { // Float
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
if (!values[value_index].is_num()) {
|
||||
if (!values[index].is_num()) {
|
||||
return "a number is required";
|
||||
}
|
||||
|
||||
double value = values[value_index];
|
||||
double value = values[index];
|
||||
bool is_negative = std::signbit(value);
|
||||
String str = String::num(Math::abs(value), min_decimals);
|
||||
const bool is_finite = Math::is_finite(value);
|
||||
|
|
@ -5316,17 +5324,21 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
break;
|
||||
}
|
||||
case 'v': { // Vector2/3/4/2i/3i/4i
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
int count;
|
||||
switch (values[value_index].get_type()) {
|
||||
switch (values[index].get_type()) {
|
||||
case Variant::VECTOR2:
|
||||
case Variant::VECTOR2I: {
|
||||
count = 2;
|
||||
|
|
@ -5344,7 +5356,7 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
}
|
||||
|
||||
Vector4 vec = values[value_index];
|
||||
Vector4 vec = values[index];
|
||||
String str = "(";
|
||||
for (int i = 0; i < count; i++) {
|
||||
double val = vec[i];
|
||||
|
|
@ -5386,16 +5398,20 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
str += ")";
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
break;
|
||||
}
|
||||
case 's': { // String
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
String str = values[value_index];
|
||||
String str = values[index];
|
||||
// Padding.
|
||||
if (left_justified) {
|
||||
str = str.rpad(min_chars);
|
||||
|
|
@ -5404,19 +5420,23 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
// Convert to character.
|
||||
String str;
|
||||
if (values[value_index].is_num()) {
|
||||
int value = values[value_index];
|
||||
if (values[index].is_num()) {
|
||||
int value = values[index];
|
||||
if (value < 0) {
|
||||
return "unsigned integer is lower than minimum";
|
||||
} else if (value >= 0xd800 && value <= 0xdfff) {
|
||||
|
|
@ -5424,9 +5444,9 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
} else if (value > 0x10ffff) {
|
||||
return "unsigned integer is greater than maximum";
|
||||
}
|
||||
str = chr(values[value_index]);
|
||||
} else if (values[value_index].get_type() == Variant::STRING) {
|
||||
str = values[value_index];
|
||||
str = chr(values[index]);
|
||||
} else if (values[index].get_type() == Variant::STRING) {
|
||||
str = values[index];
|
||||
if (str.length() != 1) {
|
||||
return "%c requires number or single-character string";
|
||||
}
|
||||
|
|
@ -5442,7 +5462,10 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
formatted += str;
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
in_format = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -5486,6 +5509,14 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case '$': {
|
||||
if (min_chars > 0) {
|
||||
selected_index = min_chars - 1;
|
||||
}
|
||||
min_chars = 0;
|
||||
pad_with_zeros = false;
|
||||
break;
|
||||
}
|
||||
case '.': { // Float/Vector separator.
|
||||
if (in_decimals) {
|
||||
return "too many decimal points in format";
|
||||
|
|
@ -5496,27 +5527,30 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
}
|
||||
|
||||
case '*': { // Dynamic width, based on value.
|
||||
if (value_index >= values.size()) {
|
||||
uint64_t index = (selected_index >= 0 ? selected_index : value_index);
|
||||
if (index >= values.size()) {
|
||||
return "not enough arguments for format string";
|
||||
}
|
||||
|
||||
Variant::Type value_type = values[value_index].get_type();
|
||||
if (!values[value_index].is_num() &&
|
||||
Variant::Type value_type = values[index].get_type();
|
||||
if (!values[index].is_num() &&
|
||||
value_type != Variant::VECTOR2 && value_type != Variant::VECTOR2I &&
|
||||
value_type != Variant::VECTOR3 && value_type != Variant::VECTOR3I &&
|
||||
value_type != Variant::VECTOR4 && value_type != Variant::VECTOR4I) {
|
||||
return "* wants number or vector";
|
||||
}
|
||||
|
||||
int size = values[value_index];
|
||||
int size = values[index];
|
||||
|
||||
if (in_decimals) {
|
||||
min_decimals = size;
|
||||
} else {
|
||||
min_chars = size;
|
||||
}
|
||||
|
||||
++value_index;
|
||||
if (selected_index == -1) {
|
||||
++value_index;
|
||||
}
|
||||
used_args[index] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -5535,6 +5569,7 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
left_justified = false;
|
||||
show_sign = false;
|
||||
in_decimals = false;
|
||||
selected_index = -1;
|
||||
break;
|
||||
default:
|
||||
formatted += c;
|
||||
|
|
@ -5546,8 +5581,10 @@ String String::sprintf(const Span<Variant> &values, bool *error) const {
|
|||
return "incomplete format";
|
||||
}
|
||||
|
||||
if (value_index != values.size()) {
|
||||
return "not all arguments converted during string formatting";
|
||||
for (const bool &b : used_args) {
|
||||
if (!b) {
|
||||
return "not all arguments converted during string formatting";
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
|
|
|
|||
|
|
@ -1296,6 +1296,15 @@ TEST_CASE("[String] sprintf") {
|
|||
REQUIRE(error == false);
|
||||
CHECK(output == String("fish 99.990 frog"));
|
||||
|
||||
///// Argument indices.
|
||||
format = "fish %2$d frog %1$s xx";
|
||||
args.clear();
|
||||
args.push_back("test");
|
||||
args.push_back(5);
|
||||
output = format.sprintf(args, &error);
|
||||
REQUIRE(error == false);
|
||||
CHECK(output == String("fish 5 frog test xx"));
|
||||
|
||||
///// Errors
|
||||
|
||||
// More formats than arguments.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue