diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-08-17 16:06:29 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-17 16:06:29 +0800 |
commit | 200dafb8a5839826bc157bdc9153f1171ce639e1 (patch) | |
tree | 0f2ba228c173892c16b082236f0eb124ad02fab8 /src | |
parent | 7f51829cf75e0d3ca546cab0e70a4c089eac40ec (diff) | |
parent | b193674b4a1dce1b348489fa13dd42254b9a3ebb (diff) | |
download | rneovim-200dafb8a5839826bc157bdc9153f1171ce639e1.tar.gz rneovim-200dafb8a5839826bc157bdc9153f1171ce639e1.tar.bz2 rneovim-200dafb8a5839826bc157bdc9153f1171ce639e1.zip |
Merge pull request #24749 from zeertzjq/vim-8.2.3848
vim-patch:8.2.{3848,partial:3849}: reduce() on a string
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 484 | ||||
-rw-r--r-- | src/nvim/eval.lua | 14 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 215 |
3 files changed, 413 insertions, 300 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index dab0896d75..589bee6f84 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -108,7 +108,7 @@ static const char e_dot_can_only_be_used_on_dictionary_str[] = N_("E1203: Dot can only be used on a dictionary: %s"); static const char e_empty_function_name[] = N_("E1192: Empty function name"); -static char e_argument_of_str_must_be_list_string_dictionary_or_blob[] +static const char e_argument_of_str_must_be_list_string_dictionary_or_blob[] = N_("E1250: Argument of %s must be a List, String, Dictionary or Blob"); static char * const namespace_char = "abglstvw"; @@ -5026,15 +5026,253 @@ void assert_error(garray_T *gap) tv_list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, (ptrdiff_t)gap->ga_len); } +/// Implementation of map() and filter() for a Dict. +static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_name, + const char *arg_errmsg, typval_T *expr, typval_T *rettv) +{ + if (filtermap == FILTERMAP_MAPNEW) { + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = NULL; + } + if (d == NULL + || (filtermap == FILTERMAP_FILTER + && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { + return; + } + + const VarLockStatus prev_lock = d->dv_lock; + dict_T *d_ret = NULL; + + if (filtermap == FILTERMAP_MAPNEW) { + tv_dict_alloc_ret(rettv); + d_ret = rettv->vval.v_dict; + } + + vimvars[VV_KEY].vv_type = VAR_STRING; + + if (filtermap != FILTERMAP_FILTER && d->dv_lock == VAR_UNLOCKED) { + d->dv_lock = VAR_LOCKED; + } + hash_lock(&d->dv_hashtab); + TV_DICT_ITER(d, di, { + if (filtermap == FILTERMAP_MAP + && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { + break; + } + + vimvars[VV_KEY].vv_str = xstrdup(di->di_key); + typval_T newtv; + bool rem; + int r = filter_map_one(&di->di_tv, expr, filtermap, &newtv, &rem); + tv_clear(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) { + tv_clear(&newtv); + break; + } + if (filtermap == FILTERMAP_MAP) { + // map(): replace the dict item value + tv_clear(&di->di_tv); + newtv.v_lock = VAR_UNLOCKED; + di->di_tv = newtv; + } else if (filtermap == FILTERMAP_MAPNEW) { + // mapnew(): add the item value to the new dict + r = tv_dict_add_tv(d_ret, di->di_key, strlen(di->di_key), &newtv); + tv_clear(&newtv); + if (r == FAIL) { + break; + } + } else if (filtermap == FILTERMAP_FILTER && rem) { + // filter(false): remove the item from the dict + if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { + break; + } + tv_dict_item_remove(d, di); + } + }); + hash_unlock(&d->dv_hashtab); + d->dv_lock = prev_lock; +} + +/// Implementation of map() and filter() for a Blob. +static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, + typval_T *rettv) +{ + if (filtermap == FILTERMAP_MAPNEW) { + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; + } + blob_T *b; + if ((b = blob_arg) == NULL) { + return; + } + + blob_T *b_ret = b; + + if (filtermap == FILTERMAP_MAPNEW) { + tv_blob_copy(b, rettv); + b_ret = rettv->vval.v_blob; + } + + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + for (int i = 0, idx = 0; i < b->bv_ga.ga_len; i++) { + const varnumber_T val = tv_blob_get(b, i); + typval_T tv = { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval.v_number = val, + }; + vimvars[VV_KEY].vv_nr = idx; + typval_T newtv; + bool rem; + if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL + || did_emsg) { + break; + } + if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) { + tv_clear(&newtv); + emsg(_(e_invalblob)); + break; + } + if (filtermap != FILTERMAP_FILTER) { + if (newtv.vval.v_number != val) { + tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number); + } + } else if (rem) { + char *const p = (char *)blob_arg->bv_ga.ga_data; + memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1)); + b->bv_ga.ga_len--; + i--; + } + idx++; + } +} + +/// Implementation of map() and filter() for a String. +static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *expr, + typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + int len = 0; + int idx = 0; + for (const char *p = str; *p != NUL; p += len) { + len = utfc_ptr2len(p); + typval_T tv = { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = xstrnsave(p, (size_t)len), + }; + + vimvars[VV_KEY].vv_nr = idx; + typval_T newtv; + bool rem; + if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL + || did_emsg) { + tv_clear(&newtv); + tv_clear(&tv); + break; + } else if (filtermap != FILTERMAP_FILTER) { + if (newtv.v_type != VAR_STRING) { + tv_clear(&newtv); + tv_clear(&tv); + emsg(_(e_stringreq)); + break; + } else { + ga_concat(&ga, newtv.vval.v_string); + } + } else if (!rem) { + ga_concat(&ga, tv.vval.v_string); + } + + tv_clear(&newtv); + tv_clear(&tv); + + idx++; + } + ga_append(&ga, NUL); + rettv->vval.v_string = ga.ga_data; +} + +/// Implementation of map() and filter() for a List. +static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_name, + const char *arg_errmsg, typval_T *expr, typval_T *rettv) +{ + if (filtermap == FILTERMAP_MAPNEW) { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + } + if (l == NULL + || (filtermap == FILTERMAP_FILTER + && value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { + return; + } + + const VarLockStatus prev_lock = tv_list_locked(l); + list_T *l_ret = NULL; + + if (filtermap == FILTERMAP_MAPNEW) { + tv_list_alloc_ret(rettv, kListLenUnknown); + l_ret = rettv->vval.v_list; + } + + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + if (filtermap != FILTERMAP_FILTER && tv_list_locked(l) == VAR_UNLOCKED) { + tv_list_set_lock(l, VAR_LOCKED); + } + + int idx = 0; + for (listitem_T *li = tv_list_first(l); li != NULL;) { + if (filtermap == FILTERMAP_MAP + && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, TV_TRANSLATE)) { + break; + } + vimvars[VV_KEY].vv_nr = idx; + typval_T newtv; + bool rem; + if (filter_map_one(TV_LIST_ITEM_TV(li), expr, filtermap, &newtv, &rem) == FAIL) { + break; + } + if (did_emsg) { + tv_clear(&newtv); + break; + } + if (filtermap == FILTERMAP_MAP) { + // map(): replace the list item value + tv_clear(TV_LIST_ITEM_TV(li)); + newtv.v_lock = VAR_UNLOCKED; + *TV_LIST_ITEM_TV(li) = newtv; + } else if (filtermap == FILTERMAP_MAPNEW) { + // mapnew(): append the list item value + tv_list_append_owned_tv(l_ret, newtv); + } + if (filtermap == FILTERMAP_FILTER && rem) { + li = tv_list_item_remove(l, li); + } else { + li = TV_LIST_ITEM_NEXT(l, li); + } + idx++; + } + + tv_list_set_lock(l, prev_lock); +} + /// Implementation of map() and filter(). static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) { - list_T *l = NULL; - dict_T *d = NULL; - blob_T *b = NULL; - int rem = false; - const char *const ermsg = (filtermap == FILTERMAP_MAP ? "map()" - : filtermap == FILTERMAP_MAPNEW ? "mapnew()" : "filter()"); + const char *const func_name = (filtermap == FILTERMAP_MAP + ? "map()" + : (filtermap == FILTERMAP_MAPNEW + ? "mapnew()" + : "filter()")); const char *const arg_errmsg = (filtermap == FILTERMAP_MAP ? N_("map() argument") : (filtermap == FILTERMAP_MAPNEW @@ -5046,39 +5284,11 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap tv_copy(&argvars[0], rettv); } - if (argvars[0].v_type == VAR_BLOB) { - if (filtermap == FILTERMAP_MAPNEW) { - rettv->v_type = VAR_BLOB; - rettv->vval.v_blob = NULL; - } - if ((b = argvars[0].vval.v_blob) == NULL) { - return; - } - } else if (argvars[0].v_type == VAR_LIST) { - if (filtermap == FILTERMAP_MAPNEW) { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - } - if ((l = argvars[0].vval.v_list) == NULL - || (filtermap == FILTERMAP_FILTER - && value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { - return; - } - } else if (argvars[0].v_type == VAR_DICT) { - if (filtermap == FILTERMAP_MAPNEW) { - rettv->v_type = VAR_DICT; - rettv->vval.v_dict = NULL; - } - if ((d = argvars[0].vval.v_dict) == NULL - || (filtermap == FILTERMAP_FILTER - && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { - return; - } - } else if (argvars[0].v_type == VAR_STRING) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - } else { - semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob), ermsg); + if (argvars[0].v_type != VAR_BLOB + && argvars[0].v_type != VAR_LIST + && argvars[0].v_type != VAR_DICT + && argvars[0].v_type != VAR_STRING) { + semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob), func_name); return; } @@ -5087,7 +5297,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap // message. Avoid a misleading error message for an empty string that // was not passed as argument. if (expr->v_type != VAR_UNKNOWN) { - int idx = 0; typval_T save_val; prepare_vimvar(VV_VAL, &save_val); @@ -5099,195 +5308,16 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap typval_T save_key; prepare_vimvar(VV_KEY, &save_key); if (argvars[0].v_type == VAR_DICT) { - const VarLockStatus prev_lock = d->dv_lock; - dict_T *d_ret = NULL; - - if (filtermap == FILTERMAP_MAPNEW) { - tv_dict_alloc_ret(rettv); - d_ret = rettv->vval.v_dict; - } - - vimvars[VV_KEY].vv_type = VAR_STRING; - - if (filtermap != FILTERMAP_FILTER && d->dv_lock == VAR_UNLOCKED) { - d->dv_lock = VAR_LOCKED; - } - hashtab_T *ht = &d->dv_hashtab; - hash_lock(ht); - int todo = (int)ht->ht_used; - for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - - dictitem_T *di = TV_DICT_HI2DI(hi); - if (filtermap == FILTERMAP_MAP - && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) - || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { - break; - } - - vimvars[VV_KEY].vv_str = xstrdup(di->di_key); - typval_T newtv; - int r = filter_map_one(&di->di_tv, expr, filtermap, &newtv, &rem); - tv_clear(&vimvars[VV_KEY].vv_tv); - if (r == FAIL || did_emsg) { - tv_clear(&newtv); - break; - } - if (filtermap == FILTERMAP_MAP) { - // map(): replace the dict item value - tv_clear(&di->di_tv); - newtv.v_lock = VAR_UNLOCKED; - di->di_tv = newtv; - } else if (filtermap == FILTERMAP_MAPNEW) { - // mapnew(): add the item value to the new dict - r = tv_dict_add_tv(d_ret, di->di_key, strlen(di->di_key), &newtv); - tv_clear(&newtv); - if (r == FAIL) { - break; - } - } else if (filtermap == FILTERMAP_FILTER && rem) { - // filter(false): remove the item from the dict - if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) - || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { - break; - } - tv_dict_item_remove(d, di); - } - } - } - hash_unlock(ht); - d->dv_lock = prev_lock; + filter_map_dict(argvars[0].vval.v_dict, filtermap, func_name, + arg_errmsg, expr, rettv); } else if (argvars[0].v_type == VAR_BLOB) { - blob_T *b_ret = b; - - if (filtermap == FILTERMAP_MAPNEW) { - tv_blob_copy(b, rettv); - b_ret = rettv->vval.v_blob; - } - - vimvars[VV_KEY].vv_type = VAR_NUMBER; - - for (int i = 0; i < b->bv_ga.ga_len; i++) { - const varnumber_T val = tv_blob_get(b, i); - typval_T tv = { - .v_type = VAR_NUMBER, - .v_lock = VAR_UNLOCKED, - .vval.v_number = val, - }; - vimvars[VV_KEY].vv_nr = idx; - typval_T newtv; - if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL - || did_emsg) { - break; - } - if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) { - tv_clear(&newtv); - emsg(_(e_invalblob)); - break; - } - if (filtermap != FILTERMAP_FILTER) { - if (newtv.vval.v_number != val) { - tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number); - } - } else if (rem) { - char *const p = argvars[0].vval.v_blob->bv_ga.ga_data; - memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1)); - b->bv_ga.ga_len--; - i--; - } - idx++; - } + filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, rettv); } else if (argvars[0].v_type == VAR_STRING) { - vimvars[VV_KEY].vv_type = VAR_NUMBER; - - garray_T ga; - ga_init(&ga, (int)sizeof(char), 80); - int len; - for (const char *p = tv_get_string(&argvars[0]); *p != NUL; p += len) { - len = utfc_ptr2len(p); - - typval_T tv = { - .v_type = VAR_STRING, - .v_lock = VAR_UNLOCKED, - .vval.v_string = xstrnsave(p, (size_t)len), - }; - - vimvars[VV_KEY].vv_nr = idx; - typval_T newtv; - if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL - || did_emsg) { - tv_clear(&newtv); - tv_clear(&tv); - break; - } else if (filtermap != FILTERMAP_FILTER) { - if (newtv.v_type != VAR_STRING) { - tv_clear(&newtv); - tv_clear(&tv); - emsg(_(e_stringreq)); - break; - } else { - ga_concat(&ga, newtv.vval.v_string); - } - } else if (!rem) { - ga_concat(&ga, tv.vval.v_string); - } - - tv_clear(&newtv); - tv_clear(&tv); - - idx++; - } - ga_append(&ga, NUL); - rettv->vval.v_string = ga.ga_data; + filter_map_string(tv_get_string(&argvars[0]), filtermap, expr, rettv); } else { assert(argvars[0].v_type == VAR_LIST); - - const VarLockStatus prev_lock = tv_list_locked(l); - list_T *l_ret = NULL; - - if (filtermap == FILTERMAP_MAPNEW) { - tv_list_alloc_ret(rettv, kListLenUnknown); - l_ret = rettv->vval.v_list; - } - - vimvars[VV_KEY].vv_type = VAR_NUMBER; - - if (filtermap != FILTERMAP_FILTER && tv_list_locked(l) == VAR_UNLOCKED) { - tv_list_set_lock(l, VAR_LOCKED); - } - for (listitem_T *li = tv_list_first(l); li != NULL;) { - if (filtermap == FILTERMAP_MAP - && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, - TV_TRANSLATE)) { - break; - } - vimvars[VV_KEY].vv_nr = idx; - typval_T newtv; - if (filter_map_one(TV_LIST_ITEM_TV(li), expr, filtermap, &newtv, &rem) == FAIL) { - break; - } - if (did_emsg) { - tv_clear(&newtv); - break; - } - if (filtermap == FILTERMAP_MAP) { - // map(): replace the list item value - tv_clear(TV_LIST_ITEM_TV(li)); - newtv.v_lock = VAR_UNLOCKED; - *TV_LIST_ITEM_TV(li) = newtv; - } else if (filtermap == FILTERMAP_MAPNEW) { - // mapnew(): append the list item value - tv_list_append_owned_tv(l_ret, newtv); - } - if (filtermap == FILTERMAP_FILTER && rem) { - li = tv_list_item_remove(l, li); - } else { - li = TV_LIST_ITEM_NEXT(l, li); - } - idx++; - } - tv_list_set_lock(l, prev_lock); + filter_map_list(argvars[0].vval.v_list, filtermap, func_name, + arg_errmsg, expr, rettv); } restore_vimvar(VV_KEY, &save_key); @@ -5305,7 +5335,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap /// @param newtv for map() an mapnew(): new value /// @param remp for filter(): remove flag static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filtermap, - typval_T *newtv, int *remp) + typval_T *newtv, bool *remp) FUNC_ATTR_NONNULL_ALL { typval_T argv[3]; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 4dbd63d131..f8ba16365a 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -2551,8 +2551,8 @@ M.funcs = { of the current item. For a |Dictionary| |v:key| has the key of the current item and for a |List| |v:key| has the index of the current item. For a |Blob| |v:key| has the index of the - current byte. - + current byte. For a |String| |v:key| has the index of the + current character. Examples: >vim call filter(mylist, 'v:val !~ "OLD"') <Removes the items where "OLD" appears. >vim @@ -6068,7 +6068,8 @@ M.funcs = { of the current item. For a |Dictionary| |v:key| has the key of the current item and for a |List| |v:key| has the index of the current item. For a |Blob| |v:key| has the index of the - current byte. + current byte. For a |String| |v:key| has the index of the + current character. Example: >vim call map(mylist, '"> " .. v:val .. " <"') <This puts "> " before and " <" after each item in "mylist". @@ -7876,9 +7877,9 @@ M.funcs = { tags = { 'E998' }, desc = [=[ {func} is called for every item in {object}, which can be a - |List| or a |Blob|. {func} is called with two arguments: the - result so far and current item. After processing all items - the result is returned. + |String|, |List| or a |Blob|. {func} is called with two + arguments: the result so far and current item. After + processing all items the result is returned. {initial} is the initial result. When omitted, the first item in {object} is used and {func} is first called for the second @@ -7889,6 +7890,7 @@ M.funcs = { echo reduce([1, 3, 5], { acc, val -> acc + val }) echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a') echo reduce(0z1122, { acc, val -> 2 * acc + val }) + echo reduce('xyz', { acc, val -> acc .. ',' .. val }) < ]=], name = 'reduce', diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 0506c08b07..d5d9726397 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -150,8 +150,12 @@ static const char *e_invalwindow = N_("E957: Invalid window number"); static const char e_invalid_submatch_number_nr[] = N_("E935: Invalid submatch number: %d"); static const char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value"); +static const char e_string_list_or_blob_required[] + = N_("E1098: String, List or Blob required"); static const char e_missing_function_argument[] = N_("E1132: Missing function argument"); +static const char e_string_expected_for_argument_nr[] + = N_("E1253: String expected for argument %d"); /// Dummy va_list for passing to vim_snprintf /// @@ -6167,11 +6171,149 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } +/// reduce() on a List +static void reduce_list(typval_T *argvars, const char *func_name, funcexe_T *funcexe, + typval_T *rettv) +{ + list_T *const l = argvars[0].vval.v_list; + const int called_emsg_start = called_emsg; + + typval_T initial; + const listitem_T *li = NULL; + if (argvars[2].v_type == VAR_UNKNOWN) { + if (tv_list_len(l) == 0) { + semsg(_(e_reduceempty), "List"); + return; + } + const listitem_T *const first = tv_list_first(l); + initial = *TV_LIST_ITEM_TV(first); + li = TV_LIST_ITEM_NEXT(l, first); + } else { + initial = argvars[2]; + li = tv_list_first(l); + } + + tv_copy(&initial, rettv); + + if (l == NULL) { + return; + } + + const VarLockStatus prev_locked = tv_list_locked(l); + + tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here + for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + typval_T argv[3]; + argv[0] = *rettv; + argv[1] = *TV_LIST_ITEM_TV(li); + rettv->v_type = VAR_UNKNOWN; + const int r = call_func(func_name, -1, rettv, 2, argv, funcexe); + tv_clear(&argv[0]); + if (r == FAIL || called_emsg != called_emsg_start) { + break; + } + } + tv_list_set_lock(l, prev_locked); +} + +/// reduce() on a String +static void reduce_string(typval_T *argvars, const char *func_name, funcexe_T *funcexe, + typval_T *rettv) +{ + const char *p = tv_get_string(&argvars[0]); + int len; + const int called_emsg_start = called_emsg; + + if (argvars[2].v_type == VAR_UNKNOWN) { + if (*p == NUL) { + semsg(_(e_reduceempty), "String"); + return; + } + len = utfc_ptr2len(p); + *rettv = (typval_T){ + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = xstrnsave(p, (size_t)len), + }; + p += len; + } else if (argvars[2].v_type != VAR_STRING) { + semsg(_(e_string_expected_for_argument_nr), 3); + return; + } else { + tv_copy(&argvars[2], rettv); + } + + for (; *p != NUL; p += len) { + typval_T argv[3]; + argv[0] = *rettv; + len = utfc_ptr2len(p); + argv[1] = (typval_T){ + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = xstrnsave(p, (size_t)len), + }; + const int r = call_func(func_name, -1, rettv, 2, argv, funcexe); + tv_clear(&argv[0]); + tv_clear(&argv[1]); + if (r == FAIL || called_emsg != called_emsg_start) { + break; + } + } +} + +/// reduce() on a Blob +static void reduce_blob(typval_T *argvars, const char *func_name, funcexe_T *funcexe, + typval_T *rettv) +{ + const blob_T *const b = argvars[0].vval.v_blob; + const int called_emsg_start = called_emsg; + + typval_T initial; + int i; + if (argvars[2].v_type == VAR_UNKNOWN) { + if (tv_blob_len(b) == 0) { + semsg(_(e_reduceempty), "Blob"); + return; + } + initial = (typval_T){ + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval.v_number = tv_blob_get(b, 0), + }; + i = 1; + } else if (argvars[2].v_type != VAR_NUMBER) { + emsg(_(e_number_exp)); + return; + } else { + initial = argvars[2]; + i = 0; + } + + tv_copy(&initial, rettv); + for (; i < tv_blob_len(b); i++) { + typval_T argv[3]; + argv[0] = *rettv; + argv[1] = (typval_T){ + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval.v_number = tv_blob_get(b, i), + }; + const int r = call_func(func_name, -1, rettv, 2, argv, funcexe); + if (r == FAIL || called_emsg != called_emsg_start) { + return; + } + } +} + /// "reduce(list, { accumulator, element -> value } [, initial])" function +/// "reduce(blob, { accumulator, element -> value } [, initial])" function +/// "reduce(string, { accumulator, element -> value } [, initial])" function static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { - emsg(_(e_listblobreq)); + if (argvars[0].v_type != VAR_STRING + && argvars[0].v_type != VAR_LIST + && argvars[0].v_type != VAR_BLOB) { + emsg(_(e_string_list_or_blob_required)); return; } @@ -6194,73 +6336,12 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) funcexe.fe_evaluate = true; funcexe.fe_partial = partial; - typval_T initial; - typval_T argv[3]; if (argvars[0].v_type == VAR_LIST) { - list_T *const l = argvars[0].vval.v_list; - const listitem_T *li; - - if (argvars[2].v_type == VAR_UNKNOWN) { - if (tv_list_len(l) == 0) { - semsg(_(e_reduceempty), "List"); - return; - } - const listitem_T *const first = tv_list_first(l); - initial = *TV_LIST_ITEM_TV(first); - li = TV_LIST_ITEM_NEXT(l, first); - } else { - initial = argvars[2]; - li = tv_list_first(l); - } - - tv_copy(&initial, rettv); - - if (l != NULL) { - const VarLockStatus prev_locked = tv_list_locked(l); - const int called_emsg_start = called_emsg; - - tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here - for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { - argv[0] = *rettv; - argv[1] = *TV_LIST_ITEM_TV(li); - rettv->v_type = VAR_UNKNOWN; - const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe); - tv_clear(&argv[0]); - if (r == FAIL || called_emsg != called_emsg_start) { - break; - } - } - tv_list_set_lock(l, prev_locked); - } + reduce_list(argvars, func_name, &funcexe, rettv); + } else if (argvars[0].v_type == VAR_STRING) { + reduce_string(argvars, func_name, &funcexe, rettv); } else { - const blob_T *const b = argvars[0].vval.v_blob; - int i; - - if (argvars[2].v_type == VAR_UNKNOWN) { - if (tv_blob_len(b) == 0) { - semsg(_(e_reduceempty), "Blob"); - return; - } - initial.v_type = VAR_NUMBER; - initial.vval.v_number = tv_blob_get(b, 0); - i = 1; - } else if (argvars[2].v_type != VAR_NUMBER) { - emsg(_(e_number_exp)); - return; - } else { - initial = argvars[2]; - i = 0; - } - - tv_copy(&initial, rettv); - for (; i < tv_blob_len(b); i++) { - argv[0] = *rettv; - argv[1].v_type = VAR_NUMBER; - argv[1].vval.v_number = tv_blob_get(b, i); - if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL) { - return; - } - } + reduce_blob(argvars, func_name, &funcexe, rettv); } } |