diff options
Diffstat (limited to 'py/objstr.c')
-rw-r--r-- | py/objstr.c | 111 |
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) { |