@@ -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 ) {
0 commit comments