@@ -249,10 +249,127 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) {
249249}
250250MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (re_split_obj , 2 , 3 , re_split );
251251
252+ #if MICROPY_PY_URE_SUB
253+
254+ STATIC mp_obj_t re_sub_helper (mp_obj_t self_in , size_t n_args , const mp_obj_t * args ) {
255+ mp_obj_re_t * self = MP_OBJ_TO_PTR (self_in );
256+ mp_obj_t replace = args [1 ];
257+ mp_obj_t where = args [2 ];
258+ mp_int_t count = 0 ;
259+ if (n_args > 3 ) {
260+ count = mp_obj_get_int (args [3 ]);
261+ // Note: flags are currently ignored
262+ }
263+
264+ size_t where_len ;
265+ const char * where_str = mp_obj_str_get_data (where , & where_len );
266+ Subject subj ;
267+ subj .begin = where_str ;
268+ subj .end = subj .begin + where_len ;
269+ int caps_num = (self -> re .sub + 1 ) * 2 ;
270+
271+ vstr_t vstr_return ;
272+ vstr_return .buf = NULL ; // We'll init the vstr after the first match
273+ mp_obj_match_t * match = mp_local_alloc (sizeof (mp_obj_match_t ) + caps_num * sizeof (char * ));
274+ match -> base .type = & match_type ;
275+ match -> num_matches = caps_num / 2 ; // caps_num counts start and end pointers
276+ match -> str = where ;
277+
278+ for (;;) {
279+ // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char
280+ memset ((char * )match -> caps , 0 , caps_num * sizeof (char * ));
281+ int res = re1_5_recursiveloopprog (& self -> re , & subj , match -> caps , caps_num , false);
282+
283+ // If we didn't have a match, or had an empty match, it's time to stop
284+ if (!res || match -> caps [0 ] == match -> caps [1 ]) {
285+ break ;
286+ }
287+
288+ // Initialise the vstr if it's not already
289+ if (vstr_return .buf == NULL ) {
290+ vstr_init (& vstr_return , match -> caps [0 ] - subj .begin );
291+ }
292+
293+ // Add pre-match string
294+ vstr_add_strn (& vstr_return , subj .begin , match -> caps [0 ] - subj .begin );
295+
296+ // Get replacement string
297+ const char * repl = mp_obj_str_get_str ((mp_obj_is_callable (replace ) ? mp_call_function_1 (replace , MP_OBJ_FROM_PTR (match )) : replace ));
298+
299+ // Append replacement string to result, substituting any regex groups
300+ while (* repl != '\0' ) {
301+ if (* repl == '\\' ) {
302+ ++ repl ;
303+ bool is_g_format = false;
304+ if (* repl == 'g' && repl [1 ] == '<' ) {
305+ // Group specified with syntax "\g<number>"
306+ repl += 2 ;
307+ is_g_format = true;
308+ }
309+
310+ if ('0' <= * repl && * repl <= '9' ) {
311+ // Group specified with syntax "\g<number>" or "\number"
312+ unsigned int match_no = 0 ;
313+ do {
314+ match_no = match_no * 10 + (* repl ++ - '0' );
315+ } while ('0' <= * repl && * repl <= '9' );
316+ if (is_g_format && * repl == '>' ) {
317+ ++ repl ;
318+ }
319+
320+ if (match_no >= (unsigned int )match -> num_matches ) {
321+ nlr_raise (mp_obj_new_exception_arg1 (& mp_type_IndexError , MP_OBJ_NEW_SMALL_INT (match_no )));
322+ }
323+
324+ const char * start_match = match -> caps [match_no * 2 ];
325+ if (start_match != NULL ) {
326+ // Add the substring matched by group
327+ const char * end_match = match -> caps [match_no * 2 + 1 ];
328+ vstr_add_strn (& vstr_return , start_match , end_match - start_match );
329+ }
330+ }
331+ } else {
332+ // Just add the current byte from the replacement string
333+ vstr_add_byte (& vstr_return , * repl ++ );
334+ }
335+ }
336+
337+ // Move start pointer to end of last match
338+ subj .begin = match -> caps [1 ];
339+
340+ // Stop substitutions if count was given and gets to 0
341+ if (count > 0 && -- count == 0 ) {
342+ break ;
343+ }
344+ }
345+
346+ mp_local_free (match );
347+
348+ if (vstr_return .buf == NULL ) {
349+ // Optimisation for case of no substitutions
350+ return where ;
351+ }
352+
353+ // Add post-match string
354+ vstr_add_strn (& vstr_return , subj .begin , subj .end - subj .begin );
355+
356+ return mp_obj_new_str_from_vstr (mp_obj_get_type (where ), & vstr_return );
357+ }
358+
359+ STATIC mp_obj_t re_sub (size_t n_args , const mp_obj_t * args ) {
360+ return re_sub_helper (args [0 ], n_args , args );
361+ }
362+ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (re_sub_obj , 3 , 5 , re_sub );
363+
364+ #endif
365+
252366STATIC const mp_rom_map_elem_t re_locals_dict_table [] = {
253367 { MP_ROM_QSTR (MP_QSTR_match ), MP_ROM_PTR (& re_match_obj ) },
254368 { MP_ROM_QSTR (MP_QSTR_search ), MP_ROM_PTR (& re_search_obj ) },
255369 { MP_ROM_QSTR (MP_QSTR_split ), MP_ROM_PTR (& re_split_obj ) },
370+ #if MICROPY_PY_URE_SUB
371+ { MP_ROM_QSTR (MP_QSTR_sub ), MP_ROM_PTR (& re_sub_obj ) },
372+ #endif
256373};
257374
258375STATIC MP_DEFINE_CONST_DICT (re_locals_dict , re_locals_dict_table );
@@ -307,11 +424,22 @@ STATIC mp_obj_t mod_re_search(size_t n_args, const mp_obj_t *args) {
307424}
308425MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (mod_re_search_obj , 2 , 4 , mod_re_search );
309426
427+ #if MICROPY_PY_URE_SUB
428+ STATIC mp_obj_t mod_re_sub (size_t n_args , const mp_obj_t * args ) {
429+ mp_obj_t self = mod_re_compile (1 , args );
430+ return re_sub_helper (self , n_args , args );
431+ }
432+ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (mod_re_sub_obj , 3 , 5 , mod_re_sub );
433+ #endif
434+
310435STATIC const mp_rom_map_elem_t mp_module_re_globals_table [] = {
311436 { MP_ROM_QSTR (MP_QSTR___name__ ), MP_ROM_QSTR (MP_QSTR_ure ) },
312437 { MP_ROM_QSTR (MP_QSTR_compile ), MP_ROM_PTR (& mod_re_compile_obj ) },
313438 { MP_ROM_QSTR (MP_QSTR_match ), MP_ROM_PTR (& mod_re_match_obj ) },
314439 { MP_ROM_QSTR (MP_QSTR_search ), MP_ROM_PTR (& mod_re_search_obj ) },
440+ #if MICROPY_PY_URE_SUB
441+ { MP_ROM_QSTR (MP_QSTR_sub ), MP_ROM_PTR (& mod_re_sub_obj ) },
442+ #endif
315443 { MP_ROM_QSTR (MP_QSTR_DEBUG ), MP_ROM_INT (FLAG_DEBUG ) },
316444};
317445
0 commit comments