diff options
author | pohmelie <multisosnooley@gmail.com> | 2016-01-29 12:09:10 +0300 |
---|---|---|
committer | Damien George <damien.p.george@gmail.com> | 2016-02-02 16:25:24 +0000 |
commit | e3a29de1dc7649656228460caf72e7841440a373 (patch) | |
tree | c3b4f936989c7888300b7e2198dcec05a01c7379 /py/objstr.c | |
parent | 2bd758fe96e2913f48c1a0fdccafddc3dfb31cf9 (diff) | |
download | micropython-e3a29de1dc7649656228460caf72e7841440a373.tar.gz micropython-e3a29de1dc7649656228460caf72e7841440a373.zip |
py/objstr: For str.format, add nested/computed fields support.
Eg: '{:{}}'.format(123, '>20')
@pohmelie was the original author of this patch, but @dpgeorge made
significant changes to reduce code size and improve efficiency.
Diffstat (limited to 'py/objstr.c')
-rw-r--r-- | py/objstr.c | 53 |
1 files changed, 34 insertions, 19 deletions
diff --git a/py/objstr.c b/py/objstr.c index 73de7b5da2..c0f8c23b94 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -34,6 +34,7 @@ #include "py/objlist.h" #include "py/runtime0.h" #include "py/runtime.h" +#include "py/stackctrl.h" STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, mp_uint_t n_args, const mp_obj_t *args, mp_obj_t dict); @@ -848,16 +849,12 @@ STATIC NORETURN void terse_str_format_value_error(void) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "bad format string")); } -mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - assert(MP_OBJ_IS_STR_OR_BYTES(args[0])); - - GET_STR_DATA_LEN(args[0], str, len); - int arg_i = 0; +vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *arg_i, mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { vstr_t vstr; mp_print_t print; vstr_init_print(&vstr, 16, &print); - for (const byte *top = str + len; str < top; str++) { + for (; str < top; str++) { if (*str == '}') { str++; if (str < top && *str == '}') { @@ -886,7 +883,7 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs vstr_t *field_name = NULL; char conversion = '\0'; - vstr_t *format_spec = NULL; + const char *format_spec = NULL; if (str < top && *str != '}' && *str != '!' && *str != ':') { field_name = vstr_new(); @@ -927,9 +924,16 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs // '{:d}'.format(True) returns '1' // So we treat {:} as {} and this later gets treated to be {!s} if (*str != '}') { - format_spec = vstr_new(); - while (str < top && *str != '}') { - vstr_add_byte(format_spec, *str++); + format_spec = str; + for (int nest = 1; str < top;) { + if (*str == '{') { + ++nest; + } else if (*str == '}') { + if (--nest == 0) { + break; + } + } + ++str; } } } @@ -957,7 +961,7 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs const char *field = vstr_null_terminated_str(field_name); const char *lookup = NULL; if (MP_LIKELY(unichar_isdigit(*field))) { - if (arg_i > 0) { + if (*arg_i > 0) { if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { terse_str_format_value_error(); } else { @@ -970,7 +974,7 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "tuple index out of range")); } arg = args[index + 1]; - arg_i = -1; + *arg_i = -1; } else { for (lookup = field; *lookup && *lookup != '.' && *lookup != '['; lookup++); mp_obj_t field_q = mp_obj_new_str(field, lookup - field, true/*?*/); @@ -986,7 +990,7 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs vstr_free(field_name); field_name = NULL; } else { - if (arg_i < 0) { + if (*arg_i < 0) { if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { terse_str_format_value_error(); } else { @@ -994,11 +998,11 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs "can't switch from manual field specification to automatic field numbering")); } } - if ((uint)arg_i >= n_args - 1) { + if ((uint)*arg_i >= n_args - 1) { nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "tuple index out of range")); } - arg = args[arg_i + 1]; - arg_i++; + arg = args[(*arg_i) + 1]; + (*arg_i)++; } if (!format_spec && !conversion) { conversion = 's'; @@ -1037,7 +1041,10 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs // precision ::= integer // type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%" - const char *s = vstr_null_terminated_str(format_spec); + // recursively call the formatter to format any nested specifiers + MP_STACK_CHECK(); + vstr_t format_spec_vstr = mp_obj_str_format_helper(format_spec, str, arg_i, n_args, args, kwargs); + const char *s = vstr_null_terminated_str(&format_spec_vstr); if (isalignment(*s)) { align = *s++; } else if (*s && isalignment(s[1])) { @@ -1084,8 +1091,7 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs "invalid format specifier")); } } - vstr_free(format_spec); - format_spec = NULL; + vstr_clear(&format_spec_vstr); } if (!align) { if (arg_looks_numeric(arg)) { @@ -1288,6 +1294,15 @@ mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs } } + return vstr; +} + +mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + assert(MP_OBJ_IS_STR_OR_BYTES(args[0])); + + GET_STR_DATA_LEN(args[0], str, len); + int arg_i = 0; + vstr_t vstr = mp_obj_str_format_helper((const char*)str, (const char*)str + len, &arg_i, n_args, args, kwargs); return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); } |