summaryrefslogtreecommitdiffstatshomepage
path: root/py/objstr.c
diff options
context:
space:
mode:
Diffstat (limited to 'py/objstr.c')
-rw-r--r--py/objstr.c111
1 files changed, 65 insertions, 46 deletions
diff --git a/py/objstr.c b/py/objstr.c
index 012d6404f7..4e70b00812 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -40,7 +40,7 @@
#include "objstr.h"
#include "objlist.h"
-STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t *args);
+STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t *args, mp_obj_t dict);
const mp_obj_t mp_const_empty_bytes;
// use this macro to extract the string hash
@@ -307,14 +307,19 @@ STATIC mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
case MP_BINARY_OP_MODULO: {
mp_obj_t *args;
uint n_args;
+ mp_obj_t dict = MP_OBJ_NULL;
if (MP_OBJ_IS_TYPE(rhs_in, &mp_type_tuple)) {
// TODO: Support tuple subclasses?
mp_obj_tuple_get(rhs_in, &n_args, &args);
+ } else if (MP_OBJ_IS_TYPE(rhs_in, &mp_type_dict)) {
+ args = NULL;
+ n_args = 0;
+ dict = rhs_in;
} else {
args = &rhs_in;
n_args = 1;
}
- return str_modulo_format(lhs_in, n_args, args);
+ return str_modulo_format(lhs_in, n_args, args, dict);
}
//case MP_BINARY_OP_NOT_EQUAL: // This is never passed here
@@ -353,7 +358,7 @@ STATIC mp_obj_t str_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
mp_bound_slice_t slice;
if (!mp_seq_get_fast_slice_indexes(self_len, index, &slice)) {
nlr_raise(mp_obj_new_exception_msg(&mp_type_NotImplementedError,
- "Only slices with step=1 (aka None) are supported"));
+ "only slices with step=1 (aka None) are supported"));
}
return mp_obj_new_str_of_type(type, self_data + slice.start, slice.stop - slice.start);
}
@@ -776,7 +781,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
vstr_add_char(vstr, '}');
continue;
}
- nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Single '}' encountered in format string"));
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "single '}' encountered in format string"));
}
if (*str != '{') {
vstr_add_char(vstr, *str);
@@ -822,7 +827,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
// '{:d}'.format(True) returns '1'
// So we treat {:} as {} and this later gets treated to be {!s}
if (*str != '}') {
- format_spec = vstr_new();
+ format_spec = vstr_new();
while (str < top && *str != '}') {
vstr_add_char(format_spec, *str++);
}
@@ -840,7 +845,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
if (field_name) {
if (arg_i > 0) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "cannot switch from automatic field numbering to manual field specification"));
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't switch from automatic field numbering to manual field specification"));
}
int index = 0;
if (str_to_int(vstr_str(field_name), &index) != vstr_len(field_name) - 1) {
@@ -855,7 +860,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
field_name = NULL;
} else {
if (arg_i < 0) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "cannot switch from manual field specification to automatic field numbering"));
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "can't switch from manual field specification to automatic field numbering"));
}
if (arg_i >= n_args - 1) {
nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "tuple index out of range"));
@@ -873,7 +878,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
} else if (conversion == 'r') {
print_kind = PRINT_REPR;
} else {
- nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Unknown conversion specifier %c", conversion));
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "unknown conversion specifier %c", conversion));
}
vstr_t *arg_vstr = vstr_new();
mp_obj_print_helper((void (*)(void*, const char*, ...))vstr_printf, arg_vstr, arg, print_kind);
@@ -976,7 +981,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
if (arg_looks_integer(arg)) {
switch (type) {
case 'b':
- pfenv_print_mp_int(&pfenv_vstr, arg, 1, 2, 'a', flags, fill, width);
+ pfenv_print_mp_int(&pfenv_vstr, arg, 1, 2, 'a', flags, fill, width, 0);
continue;
case 'c':
@@ -989,7 +994,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
case '\0': // No explicit format type implies 'd'
case 'n': // I don't think we support locales in uPy so use 'd'
case 'd':
- pfenv_print_mp_int(&pfenv_vstr, arg, 1, 10, 'a', flags, fill, width);
+ pfenv_print_mp_int(&pfenv_vstr, arg, 1, 10, 'a', flags, fill, width, 0);
continue;
case 'o':
@@ -997,15 +1002,12 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
flags |= PF_FLAG_SHOW_OCTAL_LETTER;
}
- pfenv_print_mp_int(&pfenv_vstr, arg, 1, 8, 'a', flags, fill, width);
- continue;
-
- case 'x':
- pfenv_print_mp_int(&pfenv_vstr, arg, 1, 16, 'a', flags, fill, width);
+ pfenv_print_mp_int(&pfenv_vstr, arg, 1, 8, 'a', flags, fill, width, 0);
continue;
case 'X':
- pfenv_print_mp_int(&pfenv_vstr, arg, 1, 16, 'A', flags, fill, width);
+ case 'x':
+ pfenv_print_mp_int(&pfenv_vstr, arg, 1, 16, type - ('X' - 'A'), flags, fill, width, 0);
continue;
case 'e':
@@ -1021,7 +1023,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
default:
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
- "Unknown format code '%c' for object of type '%s'", type, mp_obj_get_type_str(arg)));
+ "unknown format code '%c' for object of type '%s'", type, mp_obj_get_type_str(arg)));
}
}
@@ -1033,7 +1035,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
// Even though the docs say that an unspecified type is the same
// as 'g', there is one subtle difference, when the exponent
// is one less than the precision.
- //
+ //
// '{:10.1}'.format(0.0) ==> '0e+00'
// '{:10.1g}'.format(0.0) ==> '0'
//
@@ -1072,7 +1074,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
case 'F':
case 'g':
case 'G':
- pfenv_print_float(&pfenv_vstr, mp_obj_get_float(arg), type, flags, fill, width, precision);
+ pfenv_print_float(&pfenv_vstr, mp_obj_get_float(arg), type, flags, fill, width, precision);
break;
case '%':
@@ -1083,7 +1085,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
default:
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
- "Unknown format code '%c' for object of type 'float'",
+ "unknown format code '%c' for object of type 'float'",
type, mp_obj_get_type_str(arg)));
}
} else {
@@ -1114,7 +1116,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
default:
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
- "Unknown format code '%c' for object of type 'str'",
+ "unknown format code '%c' for object of type 'str'",
type, mp_obj_get_type_str(arg)));
}
}
@@ -1125,7 +1127,7 @@ mp_obj_t mp_obj_str_format(uint n_args, const mp_obj_t *args) {
return s;
}
-STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t *args) {
+STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t *args, mp_obj_t dict) {
assert(MP_OBJ_IS_STR(pattern));
GET_STR_DATA_LEN(pattern, str, len);
@@ -1137,6 +1139,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
pfenv_vstr.print_strn = pfenv_vstr_add_strn;
for (const byte *top = str + len; str < top; str++) {
+ mp_obj_t arg = MP_OBJ_NULL;
if (*str != '%') {
vstr_add_char(vstr, *str);
continue;
@@ -1148,17 +1151,29 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
vstr_add_char(vstr, '%');
continue;
}
- if (arg_i >= n_args) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "not enough arguments for format string"));
+
+ // Dictionary value lookup
+ if (*str == '(') {
+ const byte *key = ++str;
+ while (*str != ')') {
+ if (str >= top) {
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "incomplete format key"));
+ }
+ ++str;
+ }
+ mp_obj_t k_obj = mp_obj_new_str((const char*)key, str - key, true);
+ arg = mp_obj_dict_get(dict, k_obj);
+ str++;
}
+
int flags = 0;
char fill = ' ';
- bool alt = false;
+ int alt = 0;
while (str < top) {
if (*str == '-') flags |= PF_FLAG_LEFT_ADJUST;
else if (*str == '+') flags |= PF_FLAG_SHOW_SIGN;
else if (*str == ' ') flags |= PF_FLAG_SPACE_SIGN;
- else if (*str == '#') alt = true;
+ else if (*str == '#') alt = PF_FLAG_SHOW_PREFIX;
else if (*str == '0') {
flags |= PF_FLAG_PAD_AFTER_SIGN;
fill = '0';
@@ -1166,9 +1181,12 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
str++;
}
// parse width, if it exists
- int width = 0;
+ int width = 0;
if (str < top) {
if (*str == '*') {
+ if (arg_i >= n_args) {
+ goto not_enough_args;
+ }
width = mp_obj_get_int(args[arg_i++]);
str++;
} else {
@@ -1181,6 +1199,9 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
if (str < top && *str == '.') {
if (++str < top) {
if (*str == '*') {
+ if (arg_i >= n_args) {
+ goto not_enough_args;
+ }
prec = mp_obj_get_int(args[arg_i++]);
str++;
} else {
@@ -1195,14 +1216,22 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
if (str >= top) {
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "incomplete format"));
}
- mp_obj_t arg = args[arg_i];
+
+ // Tuple value lookup
+ if (arg == MP_OBJ_NULL) {
+ if (arg_i >= n_args) {
+not_enough_args:
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "not enough arguments for format string"));
+ }
+ arg = args[arg_i++];
+ }
switch (*str) {
case 'c':
if (MP_OBJ_IS_STR(arg)) {
uint len;
const char *s = mp_obj_str_get_data(arg, &len);
if (len != 1) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "%c requires int or char"));
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "%%c requires int or char"));
break;
}
pfenv_print_strn(&pfenv_vstr, s, 1, flags, ' ', width);
@@ -1216,17 +1245,17 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
#if MICROPY_PY_BUILTINS_FLOAT
// This is what CPython reports, so we report the same.
if (MP_OBJ_IS_TYPE(arg, &mp_type_float)) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "integer argument expected, got float"));
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "integer argument expected, got float"));
}
#endif
- nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "an integer is required"));
- break;
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "an integer is required"));
+ break;
case 'd':
case 'i':
case 'u':
- pfenv_print_mp_int(&pfenv_vstr, arg_as_int(arg), 1, 10, 'a', flags, fill, width);
+ pfenv_print_mp_int(&pfenv_vstr, arg_as_int(arg), 1, 10, 'a', flags, fill, width, prec);
break;
#if MICROPY_PY_BUILTINS_FLOAT
@@ -1244,7 +1273,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
if (alt) {
flags |= (PF_FLAG_SHOW_PREFIX | PF_FLAG_SHOW_OCTAL_LETTER);
}
- pfenv_print_mp_int(&pfenv_vstr, arg_as_int(arg), 1, 8, 'a', flags, fill, width);
+ pfenv_print_mp_int(&pfenv_vstr, arg, 1, 8, 'a', flags, fill, width, prec);
break;
case 'r':
@@ -1265,18 +1294,9 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
break;
}
- case 'x':
- if (alt) {
- flags |= PF_FLAG_SHOW_PREFIX;
- }
- pfenv_print_mp_int(&pfenv_vstr, arg_as_int(arg), 1, 16, 'a', flags, fill, width);
- break;
-
case 'X':
- if (alt) {
- flags |= PF_FLAG_SHOW_PREFIX;
- }
- pfenv_print_mp_int(&pfenv_vstr, arg_as_int(arg), 1, 16, 'A', flags, fill, width);
+ case 'x':
+ pfenv_print_mp_int(&pfenv_vstr, arg, 1, 16, *str - ('X' - 'A'), flags | alt, fill, width, prec);
break;
default:
@@ -1284,7 +1304,6 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
"unsupported format character '%c' (0x%x) at index %d",
*str, *str, str - start_str));
}
- arg_i++;
}
if (arg_i != n_args) {