py/mpprint: Correctly format leading zeros with separators.

Correctly format integers when there are leading zeros with a grouping
character, such as "{:04,d}".format(0x100) -> "0,256".

The new padding patterns for commas-and-zeroes and underscores-and-zeroes
are smooshed together into the existing pad_zeroes to save space.

Only the two combinations of (decimal + commas) and (other bases +
underscores) are properly supported.

Also add a test for it.

Fixes issue #18082.

Signed-off-by: Jeff Epler <jepler@unpythonic.net>
This commit is contained in:
Jeff Epler
2025-09-17 10:29:54 -05:00
committed by Damien George
parent 01a11ea45e
commit 5552fbe172
2 changed files with 43 additions and 5 deletions

View File

@@ -40,8 +40,20 @@
#include "py/formatfloat.h"
#endif
static const char pad_spaces[] = " ";
static const char pad_zeroes[] = "0000000000000000";
static const char pad_spaces[16] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
#define pad_spaces_size (sizeof(pad_spaces))
static const char pad_common[23] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '_', '0', '0', '0', ',', '0', '0'};
// The contents of pad_common is arranged to provide the following padding
// strings with minimal flash size:
// 0000000000000000 <- pad_zeroes
// 0000_000 <- pad_zeroes_underscore (offset: 12, size 5)
// 000,00 <- pad_zeroes_comma (offset: 17, size 4)
#define pad_zeroes (pad_common + 0)
#define pad_zeroes_size (16)
#define pad_zeroes_underscore (pad_common + 12)
#define pad_zeroes_underscore_size (5)
#define pad_zeroes_comma (pad_common + 17)
#define pad_zeroes_comma_size (4)
static void plat_print_strn(void *env, const char *str, size_t len) {
(void)env;
@@ -65,13 +77,30 @@ int mp_print_strn(const mp_print_t *print, const char *str, size_t len, unsigned
int pad_size;
int total_chars_printed = 0;
const char *pad_chars;
char grouping = flags >> PF_FLAG_SEP_POS;
if (!fill || fill == ' ') {
pad_chars = pad_spaces;
pad_size = sizeof(pad_spaces) - 1;
} else if (fill == '0') {
pad_size = pad_spaces_size;
} else if (fill == '0' && !grouping) {
pad_chars = pad_zeroes;
pad_size = sizeof(pad_zeroes) - 1;
pad_size = pad_zeroes_size;
} else if (fill == '0') {
if (grouping == '_') {
pad_chars = pad_zeroes_underscore;
pad_size = pad_zeroes_underscore_size;
} else {
pad_chars = pad_zeroes_comma;
pad_size = pad_zeroes_comma_size;
}
// The result will never start with a grouping character. An extra leading zero is added.
// width is dead after this so we can use it in calculation
if (width % pad_size == 0) {
pad++;
width++;
}
// position the grouping character correctly within the pad repetition
pad_chars += pad_size - 1 - width % pad_size;
} else {
// Other pad characters are fairly unusual, so we'll take the hit
// and output them 1 at a time.

View File

@@ -0,0 +1,9 @@
try:
"%d" % 1
except TypeError:
print("SKIP")
raise SystemExit
for v in (0, 0x10, 0x1000, -0x10, -0x1000):
for sz in range(1, 12): print(("{:0%d,d}" % sz).format(v))
for sz in range(1, 12): print(("{:0%d_x}" % sz).format(v))