Skip to content

Commit 9032491

Browse files
jeplerdpgeorge
authored andcommitted
py/objstr: Add support for the :_b/o/x specifier in str.format.
This groups non-decimal values by fours, such as bbb_bbbb_bbbb. It also supports `{:_d}` to use underscore for decimal numbers (grouped in threes). Use of incorrect ":,b" is not diagnosed. Thanks to @dpgeorge for the suggestion to reduce code size. Signed-off-by: Jeff Epler <[email protected]>
1 parent f47e214 commit 9032491

File tree

7 files changed

+32
-21
lines changed

7 files changed

+32
-21
lines changed

py/mpprint.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ int mp_print_str(const mp_print_t *print, const char *str) {
5858
return len;
5959
}
6060

61-
int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width) {
61+
int mp_print_strn(const mp_print_t *print, const char *str, size_t len, unsigned int flags, char fill, int width) {
6262
int left_pad = 0;
6363
int right_pad = 0;
6464
int pad = width - len;
@@ -201,7 +201,7 @@ static int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base,
201201
return len;
202202
}
203203

204-
int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) {
204+
int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, unsigned int base, int base_char, int flags, char fill, int width, int prec) {
205205
// These are the only values for "base" that are required to be supported by this
206206
// function, since Python only allows the user to format integers in these bases.
207207
// If needed this function could be generalised to handle other values.
@@ -248,10 +248,7 @@ int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char
248248
int prefix_len = prefix - prefix_buf;
249249
prefix = prefix_buf;
250250

251-
char comma = '\0';
252-
if (flags & PF_FLAG_SHOW_COMMA) {
253-
comma = ',';
254-
}
251+
char comma = flags >> PF_FLAG_SEP_POS;
255252

256253
// The size of this buffer is rather arbitrary. If it's not large
257254
// enough, a dynamic one will be allocated.
@@ -340,7 +337,7 @@ int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char
340337
}
341338

342339
#if MICROPY_PY_BUILTINS_FLOAT
343-
int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec) {
340+
int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, unsigned int flags, char fill, int width, int prec) {
344341
char buf[32];
345342
char sign = '\0';
346343
int chrs = 0;

py/mpprint.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@
3333
#define PF_FLAG_SPACE_SIGN (0x004)
3434
#define PF_FLAG_NO_TRAILZ (0x008)
3535
#define PF_FLAG_SHOW_PREFIX (0x010)
36-
#define PF_FLAG_SHOW_COMMA (0x020)
37-
#define PF_FLAG_PAD_AFTER_SIGN (0x040)
38-
#define PF_FLAG_CENTER_ADJUST (0x080)
39-
#define PF_FLAG_ADD_PERCENT (0x100)
40-
#define PF_FLAG_SHOW_OCTAL_LETTER (0x200)
36+
#define PF_FLAG_PAD_AFTER_SIGN (0x020)
37+
#define PF_FLAG_CENTER_ADJUST (0x040)
38+
#define PF_FLAG_ADD_PERCENT (0x080)
39+
#define PF_FLAG_SHOW_OCTAL_LETTER (0x100)
40+
#define PF_FLAG_SEP_POS (9) // must be above all the above PF_FLAGs
4141

4242
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
4343
#define MP_PYTHON_PRINTER &mp_sys_stdout_print
@@ -69,9 +69,9 @@ extern const mp_print_t mp_sys_stdout_print;
6969
#endif
7070

7171
int mp_print_str(const mp_print_t *print, const char *str);
72-
int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width);
72+
int mp_print_strn(const mp_print_t *print, const char *str, size_t len, unsigned int flags, char fill, int width);
7373
#if MICROPY_PY_BUILTINS_FLOAT
74-
int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec);
74+
int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, unsigned int flags, char fill, int width, int prec);
7575
#endif
7676

7777
int mp_printf(const mp_print_t *print, const char *fmt, ...);

py/mpz.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,8 @@ size_t mpz_as_str_inpl(const mpz_t *i, unsigned int base, const char *prefix, ch
16721672

16731673
size_t ilen = i->len;
16741674

1675+
int n_comma = (base == 10) ? 3 : 4;
1676+
16751677
char *s = str;
16761678
if (ilen == 0) {
16771679
if (prefix) {
@@ -1717,7 +1719,7 @@ size_t mpz_as_str_inpl(const mpz_t *i, unsigned int base, const char *prefix, ch
17171719
break;
17181720
}
17191721
}
1720-
if (!done && comma && (s - last_comma) == 3) {
1722+
if (!done && comma && (s - last_comma) == n_comma) {
17211723
*s++ = comma;
17221724
last_comma = s;
17231725
}

py/objint.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ static const uint8_t log_base2_floor[] = {
209209
size_t mp_int_format_size(size_t num_bits, int base, const char *prefix, char comma) {
210210
assert(2 <= base && base <= 16);
211211
size_t num_digits = num_bits / log_base2_floor[base - 1] + 1;
212-
size_t num_commas = comma ? num_digits / 3 : 0;
212+
size_t num_commas = comma ? (base == 10 ? num_digits / 3 : num_digits / 4): 0;
213213
size_t prefix_len = prefix ? strlen(prefix) : 0;
214214
return num_digits + num_commas + prefix_len + 2; // +1 for sign, +1 for null byte
215215
}
@@ -251,6 +251,7 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co
251251
sign = '-';
252252
}
253253

254+
int n_comma = (base == 10) ? 3 : 4;
254255
size_t needed_size = mp_int_format_size(sizeof(fmt_int_t) * 8, base, prefix, comma);
255256
if (needed_size > *buf_size) {
256257
*buf = m_new(char, needed_size);
@@ -275,7 +276,7 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co
275276
c += '0';
276277
}
277278
*(--b) = c;
278-
if (comma && num != 0 && b > str && (last_comma - b) == 3) {
279+
if (comma && num != 0 && b > str && (last_comma - b) == n_comma) {
279280
*(--b) = comma;
280281
last_comma = b;
281282
}

py/objstr.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,7 +1184,7 @@ static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar
11841184
int width = -1;
11851185
int precision = -1;
11861186
char type = '\0';
1187-
int flags = 0;
1187+
unsigned int flags = 0;
11881188

11891189
if (format_spec) {
11901190
// The format specifier (from http://docs.python.org/2/library/string.html#formatspec)
@@ -1229,8 +1229,9 @@ static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar
12291229
}
12301230
}
12311231
s = str_to_int(s, stop, &width);
1232-
if (*s == ',') {
1233-
flags |= PF_FLAG_SHOW_COMMA;
1232+
if (*s == ',' || *s == '_') {
1233+
MP_STATIC_ASSERT((unsigned)'_' << PF_FLAG_SEP_POS >> PF_FLAG_SEP_POS == '_');
1234+
flags |= (unsigned)*s << PF_FLAG_SEP_POS;
12341235
s++;
12351236
}
12361237
if (*s == '.') {

py/runtime.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ void mp_event_wait_indefinite(void);
128128
void mp_event_wait_ms(mp_uint_t timeout_ms);
129129

130130
// extra printing method specifically for mp_obj_t's which are integral type
131-
int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec);
131+
int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, unsigned base, int base_char, int flags, char fill, int width, int prec);
132132

133133
void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig);
134134
static inline void mp_arg_check_num(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw) {

tests/basics/string_format.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,17 @@ def test(fmt, *args):
2222
test("{:4x}", 123)
2323
test("{:4X}", 123)
2424

25+
test("{:4,d}", 1)
26+
test("{:4_d}", 1)
27+
test("{:4_o}", 1)
28+
test("{:4_b}", 1)
29+
test("{:4_x}", 1)
30+
2531
test("{:4,d}", 12345678)
32+
test("{:4_d}", 12345678)
33+
test("{:4_o}", 12345678)
34+
test("{:4_b}", 12345678)
35+
test("{:4_x}", 12345678)
2636

2737
test("{:#4b}", 10)
2838
test("{:#4o}", 123)

0 commit comments

Comments
 (0)