Skip to content

Commit 6756a37

Browse files
committed
Implements most of str.modulo
The alternate form for floating point doesn't work yet. The %(name)s form doesn't work yet.
1 parent 5bf565e commit 6756a37

2 files changed

Lines changed: 168 additions & 16 deletions

File tree

py/objstr.c

Lines changed: 146 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -894,32 +894,162 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
894894
assert(MP_OBJ_IS_STR(pattern));
895895

896896
GET_STR_DATA_LEN(pattern, str, len);
897+
const byte *start_str = str;
897898
int arg_i = 0;
898899
vstr_t *vstr = vstr_new();
900+
pfenv_t pfenv_vstr;
901+
pfenv_vstr.data = vstr;
902+
pfenv_vstr.print_strn = pfenv_vstr_add_strn;
903+
899904
for (const byte *top = str + len; str < top; str++) {
905+
if (*str != '%') {
906+
vstr_add_char(vstr, *str);
907+
continue;
908+
}
909+
if (++str >= top) {
910+
break;
911+
}
900912
if (*str == '%') {
901-
if (++str >= top) {
902-
break;
903-
}
904-
if (*str == '%') {
905-
vstr_add_char(vstr, '%');
913+
vstr_add_char(vstr, '%');
914+
continue;
915+
}
916+
if (arg_i >= n_args) {
917+
nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "not enough arguments for format string"));
918+
}
919+
int flags = 0;
920+
char fill = ' ';
921+
bool alt = false;
922+
while (str < top) {
923+
if (*str == '-') flags |= PF_FLAG_LEFT_ADJUST;
924+
else if (*str == '+') flags |= PF_FLAG_SHOW_SIGN;
925+
else if (*str == ' ') flags |= PF_FLAG_SPACE_SIGN;
926+
else if (*str == '#') alt = true;
927+
else if (*str == '0') {
928+
flags |= PF_FLAG_PAD_AFTER_SIGN;
929+
fill = '0';
930+
} else break;
931+
str++;
932+
}
933+
// parse width, if it exists
934+
int width = 0;
935+
if (str < top) {
936+
if (*str == '*') {
937+
width = mp_obj_get_int(args[arg_i++]);
938+
str++;
906939
} else {
907-
if (arg_i >= n_args) {
908-
nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "not enough arguments for format string"));
940+
for (; str < top && '0' <= *str && *str <= '9'; str++) {
941+
width = width * 10 + *str - '0';
909942
}
910-
switch (*str) {
911-
case 's':
912-
mp_obj_print_helper((void (*)(void*, const char*, ...))vstr_printf, vstr, args[arg_i], PRINT_STR);
913-
break;
914-
case 'r':
915-
mp_obj_print_helper((void (*)(void*, const char*, ...))vstr_printf, vstr, args[arg_i], PRINT_REPR);
943+
}
944+
}
945+
int prec = -1;
946+
if (str < top && *str == '.') {
947+
if (++str < top) {
948+
if (*str == '*') {
949+
prec = mp_obj_get_int(args[arg_i++]);
950+
str++;
951+
} else {
952+
prec = 0;
953+
for (; str < top && '0' <= *str && *str <= '9'; str++) {
954+
prec = prec * 10 + *str - '0';
955+
}
956+
}
957+
}
958+
}
959+
960+
if (str >= top) {
961+
nlr_jump(mp_obj_new_exception_msg(&mp_type_ValueError, "incomplete format"));
962+
}
963+
mp_obj_t arg = args[arg_i];
964+
switch (*str) {
965+
case 'c':
966+
if (MP_OBJ_IS_STR(arg)) {
967+
uint len;
968+
const char *s = mp_obj_str_get_data(arg, &len);
969+
if (len != 1) {
970+
nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "%c requires int or char"));
916971
break;
972+
}
973+
pfenv_print_strn(&pfenv_vstr, s, 1, flags, ' ', width);
974+
break;
975+
}
976+
if (arg_looks_integer(arg)) {
977+
char ch = mp_obj_get_int(arg);
978+
pfenv_print_strn(&pfenv_vstr, &ch, 1, flags, ' ', width);
979+
break;
980+
}
981+
#if MICROPY_ENABLE_FLOAT
982+
// This is what CPython reports, so we report the same.
983+
if (MP_OBJ_IS_TYPE(arg, &mp_type_float)) {
984+
nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "integer argument expected, got float"));
985+
986+
}
987+
#endif
988+
nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "an integer is required"));
989+
break;
990+
991+
case 'd':
992+
case 'i':
993+
case 'u':
994+
pfenv_print_int(&pfenv_vstr, mp_obj_get_int(arg), 1, 10, 'a', flags, fill, width);
995+
break;
996+
997+
#if MICROPY_ENABLE_FLOAT
998+
case 'e':
999+
case 'E':
1000+
case 'f':
1001+
case 'F':
1002+
case 'g':
1003+
case 'G':
1004+
pfenv_print_float(&pfenv_vstr, mp_obj_get_float(arg), *str, flags, fill, width, prec);
1005+
break;
1006+
#endif
1007+
1008+
case 'o':
1009+
if (alt) {
1010+
flags |= PF_FLAG_SHOW_PREFIX;
1011+
}
1012+
pfenv_print_int(&pfenv_vstr, mp_obj_get_int(arg), 1, 8, 'a', flags, fill, width);
1013+
break;
1014+
1015+
case 'r':
1016+
case 's':
1017+
{
1018+
vstr_t *arg_vstr = vstr_new();
1019+
mp_obj_print_helper((void (*)(void*, const char*, ...))vstr_printf,
1020+
arg_vstr, arg, *str == 'r' ? PRINT_REPR : PRINT_STR);
1021+
uint len = vstr_len(arg_vstr);
1022+
if (prec < 0) {
1023+
prec = len;
1024+
}
1025+
if (len > prec) {
1026+
len = prec;
9171027
}
918-
arg_i++;
1028+
pfenv_print_strn(&pfenv_vstr, vstr_str(arg_vstr), len, flags, ' ', width);
1029+
vstr_free(arg_vstr);
1030+
break;
9191031
}
920-
} else {
921-
vstr_add_char(vstr, *str);
1032+
1033+
case 'x':
1034+
if (alt) {
1035+
flags |= PF_FLAG_SHOW_PREFIX;
1036+
}
1037+
pfenv_print_int(&pfenv_vstr, mp_obj_get_int(arg), 1, 16, 'a', flags, fill, width);
1038+
break;
1039+
1040+
case 'X':
1041+
if (alt) {
1042+
flags |= PF_FLAG_SHOW_PREFIX;
1043+
}
1044+
pfenv_print_int(&pfenv_vstr, mp_obj_get_int(arg), 1, 16, 'A', flags, fill, width);
1045+
break;
1046+
1047+
default:
1048+
nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
1049+
"unsupported format character '%c' (0x%x) at index %d",
1050+
*str, *str, str - start_str));
9221051
}
1052+
arg_i++;
9231053
}
9241054

9251055
if (arg_i != n_args) {

tests/basics/string-format-modulo.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,25 @@
2020
print("=%s=" % (1, 2))
2121
except TypeError:
2222
print("TypeError")
23+
24+
print("%c" % 48)
25+
print("%c" % 'a')
26+
print("%10s" % 'abc')
27+
print("%-10s" % 'abc')
28+
print("%d" % 10)
29+
print("%+d" % 10)
30+
print("% d" % 10)
31+
print("%d" % -10)
32+
print("%x" % 18)
33+
print("%o" % 18)
34+
print("%X" % 18)
35+
print("%#x" % 18)
36+
print("%#X" % 18)
37+
print("%#6x" % 18)
38+
print("%#06x" % 18)
39+
print("%e" % 1.23456)
40+
print("%E" % 1.23456)
41+
print("%f" % 1.23456)
42+
print("%F" % 1.23456)
43+
print("%g" % 1.23456)
44+
print("%G" % 1.23456)

0 commit comments

Comments
 (0)