forked from aniani/vim
patch 8.2.3849: functions implementing reduce and map are too long
Problem: Functions implementing reduce and map are too long.
Solution: Use a function for each type of value. Add a few more test cases
and add to the help. (Yegappan Lakshmanan, closes #9370)
This commit is contained in:
committed by
Bram Moolenaar
parent
0ccb5842f5
commit
389b72196e
@@ -4893,7 +4893,8 @@ filter({expr1}, {expr2}) *filter()*
|
||||
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: >
|
||||
call filter(mylist, 'v:val !~ "OLD"')
|
||||
< Removes the items where "OLD" appears. >
|
||||
@@ -7588,7 +7589,8 @@ map({expr1}, {expr2}) *map()*
|
||||
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: >
|
||||
:call map(mylist, '"> " . v:val . " <"')
|
||||
< This puts "> " before and " <" after each item in "mylist".
|
||||
@@ -8959,9 +8961,9 @@ readfile({fname} [, {type} [, {max}]])
|
||||
|
||||
reduce({object}, {func} [, {initial}]) *reduce()* *E998*
|
||||
{func} is called for every item in {object}, which can be a
|
||||
|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.
|
||||
|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
|
||||
|
||||
458
src/list.c
458
src/list.c
@@ -2274,122 +2274,42 @@ theend:
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of map() and filter().
|
||||
* Implementation of map() and filter() for a Dict.
|
||||
*/
|
||||
static void
|
||||
filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
filter_map_dict(
|
||||
dict_T *d,
|
||||
filtermap_T filtermap,
|
||||
type_T *argtype,
|
||||
char *func_name,
|
||||
char_u *arg_errmsg,
|
||||
typval_T *expr,
|
||||
typval_T *rettv)
|
||||
{
|
||||
typval_T *expr;
|
||||
listitem_T *li, *nli;
|
||||
list_T *l = NULL;
|
||||
dictitem_T *di;
|
||||
int prev_lock;
|
||||
dict_T *d_ret = NULL;
|
||||
hashtab_T *ht;
|
||||
hashitem_T *hi;
|
||||
dict_T *d = NULL;
|
||||
blob_T *b = NULL;
|
||||
int rem;
|
||||
dictitem_T *di;
|
||||
int todo;
|
||||
char *func_name = filtermap == FILTERMAP_MAP ? "map()"
|
||||
: filtermap == FILTERMAP_MAPNEW ? "mapnew()"
|
||||
: "filter()";
|
||||
char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
|
||||
? N_("map() argument")
|
||||
: filtermap == FILTERMAP_MAPNEW
|
||||
? N_("mapnew() argument")
|
||||
: N_("filter() argument"));
|
||||
int save_did_emsg;
|
||||
int idx = 0;
|
||||
type_T *type = NULL;
|
||||
garray_T type_list;
|
||||
int rem;
|
||||
|
||||
// map() and filter() return the first argument, also on failure.
|
||||
if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
|
||||
copy_tv(&argvars[0], rettv);
|
||||
|
||||
if (in_vim9script()
|
||||
&& (check_for_list_or_dict_or_blob_or_string_arg(argvars, 0)
|
||||
== FAIL))
|
||||
return;
|
||||
|
||||
if (filtermap == FILTERMAP_MAP && in_vim9script())
|
||||
{
|
||||
// Check that map() does not change the type of the dict.
|
||||
ga_init2(&type_list, sizeof(type_T *), 10);
|
||||
type = typval2type(argvars, get_copyID(), &type_list, TRUE);
|
||||
}
|
||||
|
||||
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)
|
||||
goto theend;
|
||||
}
|
||||
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(l->lv_lock, arg_errmsg, TRUE)))
|
||||
goto theend;
|
||||
}
|
||||
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
|
||||
if (d == NULL
|
||||
|| (filtermap == FILTERMAP_FILTER
|
||||
&& value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
|
||||
goto theend;
|
||||
}
|
||||
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),
|
||||
func_name);
|
||||
goto theend;
|
||||
}
|
||||
return;
|
||||
|
||||
expr = &argvars[1];
|
||||
// On type errors, the preceding call has already displayed an error
|
||||
// message. Avoid a misleading error message for an empty string that
|
||||
// was not passed as argument.
|
||||
if (expr->v_type != VAR_UNKNOWN)
|
||||
{
|
||||
typval_T save_val;
|
||||
typval_T save_key;
|
||||
|
||||
prepare_vimvar(VV_VAL, &save_val);
|
||||
prepare_vimvar(VV_KEY, &save_key);
|
||||
|
||||
// We reset "did_emsg" to be able to detect whether an error
|
||||
// occurred during evaluation of the expression.
|
||||
save_did_emsg = did_emsg;
|
||||
did_emsg = FALSE;
|
||||
|
||||
if (argvars[0].v_type == VAR_DICT)
|
||||
{
|
||||
int prev_lock = d->dv_lock;
|
||||
dict_T *d_ret = NULL;
|
||||
prev_lock = d->dv_lock;
|
||||
|
||||
if (filtermap == FILTERMAP_MAPNEW)
|
||||
{
|
||||
if (rettv_dict_alloc(rettv) == FAIL)
|
||||
goto theend;
|
||||
return;
|
||||
d_ret = rettv->vval.v_dict;
|
||||
}
|
||||
|
||||
@@ -2425,8 +2345,8 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
}
|
||||
if (filtermap == FILTERMAP_MAP)
|
||||
{
|
||||
if (type != NULL && check_typval_arg_type(
|
||||
type->tt_member, &newtv,
|
||||
if (argtype != NULL && check_typval_arg_type(
|
||||
argtype->tt_member, &newtv,
|
||||
func_name, 0) == FAIL)
|
||||
{
|
||||
clear_tv(&newtv);
|
||||
@@ -2457,18 +2377,39 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
}
|
||||
hash_unlock(ht);
|
||||
d->dv_lock = prev_lock;
|
||||
}
|
||||
else if (argvars[0].v_type == VAR_BLOB)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
blob_T *b;
|
||||
int i;
|
||||
typval_T tv;
|
||||
varnumber_T val;
|
||||
blob_T *b_ret = b;
|
||||
blob_T *b_ret;
|
||||
int idx = 0;
|
||||
int rem;
|
||||
|
||||
if (filtermap == FILTERMAP_MAPNEW)
|
||||
{
|
||||
rettv->v_type = VAR_BLOB;
|
||||
rettv->vval.v_blob = NULL;
|
||||
}
|
||||
if ((b = blob_arg) == NULL)
|
||||
return;
|
||||
|
||||
b_ret = b;
|
||||
if (filtermap == FILTERMAP_MAPNEW)
|
||||
{
|
||||
if (blob_copy(b, rettv) == FAIL)
|
||||
goto theend;
|
||||
return;
|
||||
b_ret = rettv->vval.v_blob;
|
||||
}
|
||||
|
||||
@@ -2499,7 +2440,7 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
}
|
||||
else if (rem)
|
||||
{
|
||||
char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data;
|
||||
char_u *p = (char_u *)blob_arg->bv_ga.ga_data;
|
||||
|
||||
mch_memmove(p + i, p + i + 1,
|
||||
(size_t)b->bv_ga.ga_len - i - 1);
|
||||
@@ -2508,25 +2449,39 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
else if (argvars[0].v_type == VAR_STRING)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of map() and filter() for a String.
|
||||
*/
|
||||
static void
|
||||
filter_map_string(
|
||||
char_u *str,
|
||||
filtermap_T filtermap,
|
||||
typval_T *expr,
|
||||
typval_T *rettv)
|
||||
{
|
||||
char_u *p;
|
||||
typval_T tv;
|
||||
garray_T ga;
|
||||
int len;
|
||||
int len = 0;
|
||||
int idx = 0;
|
||||
int rem;
|
||||
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
|
||||
// set_vim_var_nr() doesn't set the type
|
||||
set_vim_var_type(VV_KEY, VAR_NUMBER);
|
||||
|
||||
ga_init2(&ga, (int)sizeof(char), 80);
|
||||
for (p = tv_get_string(&argvars[0]); *p != NUL; p += len)
|
||||
for (p = str; *p != NUL; p += len)
|
||||
{
|
||||
typval_T newtv;
|
||||
|
||||
if (tv_get_first_char(p, &tv) == FAIL)
|
||||
break;
|
||||
len = STRLEN(tv.vval.v_string);
|
||||
len = (int)STRLEN(tv.vval.v_string);
|
||||
|
||||
set_vim_var_nr(VV_KEY, idx);
|
||||
if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL
|
||||
@@ -2560,16 +2515,43 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
rettv->vval.v_string = ga.ga_data;
|
||||
}
|
||||
else // argvars[0].v_type == VAR_LIST
|
||||
{
|
||||
int prev_lock = l->lv_lock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of map() and filter() for a List.
|
||||
*/
|
||||
static void
|
||||
filter_map_list(
|
||||
list_T *l,
|
||||
filtermap_T filtermap,
|
||||
type_T *argtype,
|
||||
char *func_name,
|
||||
char_u *arg_errmsg,
|
||||
typval_T *expr,
|
||||
typval_T *rettv)
|
||||
{
|
||||
int prev_lock;
|
||||
list_T *l_ret = NULL;
|
||||
int idx = 0;
|
||||
int rem;
|
||||
listitem_T *li, *nli;
|
||||
|
||||
if (filtermap == FILTERMAP_MAPNEW)
|
||||
{
|
||||
rettv->v_type = VAR_LIST;
|
||||
rettv->vval.v_list = NULL;
|
||||
}
|
||||
if (l == NULL
|
||||
|| (filtermap == FILTERMAP_FILTER
|
||||
&& value_check_lock(l->lv_lock, arg_errmsg, TRUE)))
|
||||
return;
|
||||
|
||||
prev_lock = l->lv_lock;
|
||||
|
||||
if (filtermap == FILTERMAP_MAPNEW)
|
||||
{
|
||||
if (rettv_list_alloc(rettv) == FAIL)
|
||||
goto theend;
|
||||
return;
|
||||
l_ret = rettv->vval.v_list;
|
||||
}
|
||||
// set_vim_var_nr() doesn't set the type
|
||||
@@ -2612,9 +2594,9 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
}
|
||||
if (filtermap != FILTERMAP_FILTER)
|
||||
{
|
||||
if (filtermap == FILTERMAP_MAP && type != NULL
|
||||
if (filtermap == FILTERMAP_MAP && argtype != NULL
|
||||
&& check_typval_arg_type(
|
||||
type->tt_member, &newtv,
|
||||
argtype->tt_member, &newtv,
|
||||
func_name, 0) == FAIL)
|
||||
{
|
||||
clear_tv(&newtv);
|
||||
@@ -2658,8 +2640,8 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
}
|
||||
if (filtermap == FILTERMAP_MAP)
|
||||
{
|
||||
if (type != NULL && check_typval_arg_type(
|
||||
type->tt_member, &newtv, func_name, 0) == FAIL)
|
||||
if (argtype != NULL && check_typval_arg_type(
|
||||
argtype->tt_member, &newtv, func_name, 0) == FAIL)
|
||||
{
|
||||
clear_tv(&newtv);
|
||||
break;
|
||||
@@ -2682,8 +2664,82 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
}
|
||||
|
||||
l->lv_lock = prev_lock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of map() and filter().
|
||||
*/
|
||||
static void
|
||||
filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
|
||||
{
|
||||
typval_T *expr;
|
||||
char *func_name = filtermap == FILTERMAP_MAP ? "map()"
|
||||
: filtermap == FILTERMAP_MAPNEW ? "mapnew()"
|
||||
: "filter()";
|
||||
char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP
|
||||
? N_("map() argument")
|
||||
: filtermap == FILTERMAP_MAPNEW
|
||||
? N_("mapnew() argument")
|
||||
: N_("filter() argument"));
|
||||
int save_did_emsg;
|
||||
type_T *type = NULL;
|
||||
garray_T type_list;
|
||||
|
||||
// map() and filter() return the first argument, also on failure.
|
||||
if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING)
|
||||
copy_tv(&argvars[0], rettv);
|
||||
|
||||
if (in_vim9script()
|
||||
&& (check_for_list_or_dict_or_blob_or_string_arg(argvars, 0)
|
||||
== FAIL))
|
||||
return;
|
||||
|
||||
if (filtermap == FILTERMAP_MAP && in_vim9script())
|
||||
{
|
||||
// Check that map() does not change the type of the dict.
|
||||
ga_init2(&type_list, sizeof(type_T *), 10);
|
||||
type = typval2type(argvars, get_copyID(), &type_list, TRUE);
|
||||
}
|
||||
|
||||
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);
|
||||
goto theend;
|
||||
}
|
||||
|
||||
expr = &argvars[1];
|
||||
// On type errors, the preceding call has already displayed an error
|
||||
// message. Avoid a misleading error message for an empty string that
|
||||
// was not passed as argument.
|
||||
if (expr->v_type != VAR_UNKNOWN)
|
||||
{
|
||||
typval_T save_val;
|
||||
typval_T save_key;
|
||||
|
||||
prepare_vimvar(VV_VAL, &save_val);
|
||||
prepare_vimvar(VV_KEY, &save_key);
|
||||
|
||||
// We reset "did_emsg" to be able to detect whether an error
|
||||
// occurred during evaluation of the expression.
|
||||
save_did_emsg = did_emsg;
|
||||
did_emsg = FALSE;
|
||||
|
||||
if (argvars[0].v_type == VAR_DICT)
|
||||
filter_map_dict(argvars[0].vval.v_dict, filtermap, type, func_name,
|
||||
arg_errmsg, expr, rettv);
|
||||
else if (argvars[0].v_type == VAR_BLOB)
|
||||
filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, rettv);
|
||||
else if (argvars[0].v_type == VAR_STRING)
|
||||
filter_map_string(tv_get_string(&argvars[0]), filtermap, expr,
|
||||
rettv);
|
||||
else // argvars[0].v_type == VAR_LIST
|
||||
filter_map_list(argvars[0].vval.v_list, filtermap, type, func_name,
|
||||
arg_errmsg, expr, rettv);
|
||||
|
||||
restore_vimvar(VV_KEY, &save_key);
|
||||
restore_vimvar(VV_VAL, &save_val);
|
||||
|
||||
@@ -3252,51 +3308,22 @@ f_reverse(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
|
||||
/*
|
||||
* "reduce(list, { accumulator, element -> value } [, initial])" function
|
||||
* reduce() on a List
|
||||
*/
|
||||
void
|
||||
f_reduce(typval_T *argvars, typval_T *rettv)
|
||||
static void
|
||||
reduce_list(
|
||||
typval_T *argvars,
|
||||
char_u *func_name,
|
||||
funcexe_T *funcexe,
|
||||
typval_T *rettv)
|
||||
{
|
||||
list_T *l = argvars[0].vval.v_list;
|
||||
listitem_T *li = NULL;
|
||||
typval_T initial;
|
||||
char_u *func_name;
|
||||
partial_T *partial = NULL;
|
||||
funcexe_T funcexe;
|
||||
typval_T argv[3];
|
||||
int r;
|
||||
int called_emsg_start = called_emsg;
|
||||
|
||||
if (in_vim9script()
|
||||
&& check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
|
||||
if (argvars[0].v_type != VAR_STRING
|
||||
&& argvars[0].v_type != VAR_LIST
|
||||
&& argvars[0].v_type != VAR_BLOB)
|
||||
semsg(_(e_string_list_or_blob_required), "reduce()");
|
||||
|
||||
if (argvars[1].v_type == VAR_FUNC)
|
||||
func_name = argvars[1].vval.v_string;
|
||||
else if (argvars[1].v_type == VAR_PARTIAL)
|
||||
{
|
||||
partial = argvars[1].vval.v_partial;
|
||||
func_name = partial_name(partial);
|
||||
}
|
||||
else
|
||||
func_name = tv_get_string(&argvars[1]);
|
||||
if (func_name == NULL || *func_name == NUL)
|
||||
{
|
||||
emsg(_(e_missing_function_argument));
|
||||
return;
|
||||
}
|
||||
|
||||
CLEAR_FIELD(funcexe);
|
||||
funcexe.fe_evaluate = TRUE;
|
||||
funcexe.fe_partial = partial;
|
||||
|
||||
if (argvars[0].v_type == VAR_LIST)
|
||||
{
|
||||
list_T *l = argvars[0].vval.v_list;
|
||||
listitem_T *li = NULL;
|
||||
int prev_locked;
|
||||
|
||||
if (l != NULL)
|
||||
CHECK_LIST_MATERIALIZE(l);
|
||||
@@ -3318,9 +3345,10 @@ f_reduce(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
copy_tv(&initial, rettv);
|
||||
|
||||
if (l != NULL)
|
||||
{
|
||||
int prev_locked = l->lv_lock;
|
||||
if (l == NULL)
|
||||
return;
|
||||
|
||||
prev_locked = l->lv_lock;
|
||||
|
||||
l->lv_lock = VAR_FIXED; // disallow the list changing here
|
||||
for ( ; li != NULL; li = li->li_next)
|
||||
@@ -3328,18 +3356,29 @@ f_reduce(typval_T *argvars, typval_T *rettv)
|
||||
argv[0] = *rettv;
|
||||
argv[1] = li->li_tv;
|
||||
rettv->v_type = VAR_UNKNOWN;
|
||||
r = call_func(func_name, -1, rettv, 2, argv, &funcexe);
|
||||
r = call_func(func_name, -1, rettv, 2, argv, funcexe);
|
||||
clear_tv(&argv[0]);
|
||||
if (r == FAIL || called_emsg != called_emsg_start)
|
||||
break;
|
||||
}
|
||||
l->lv_lock = prev_locked;
|
||||
}
|
||||
}
|
||||
else if (argvars[0].v_type == VAR_STRING)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* reduce() on a String
|
||||
*/
|
||||
static void
|
||||
reduce_string(
|
||||
typval_T *argvars,
|
||||
char_u *func_name,
|
||||
funcexe_T *funcexe,
|
||||
typval_T *rettv)
|
||||
{
|
||||
char_u *p = tv_get_string(&argvars[0]);
|
||||
int len;
|
||||
typval_T argv[3];
|
||||
int r;
|
||||
int called_emsg_start = called_emsg;
|
||||
|
||||
if (argvars[2].v_type == VAR_UNKNOWN)
|
||||
{
|
||||
@@ -3365,17 +3404,30 @@ f_reduce(typval_T *argvars, typval_T *rettv)
|
||||
argv[0] = *rettv;
|
||||
if (tv_get_first_char(p, &argv[1]) == FAIL)
|
||||
break;
|
||||
len = STRLEN(argv[1].vval.v_string);
|
||||
r = call_func(func_name, -1, rettv, 2, argv, &funcexe);
|
||||
len = (int)STRLEN(argv[1].vval.v_string);
|
||||
r = call_func(func_name, -1, rettv, 2, argv, funcexe);
|
||||
clear_tv(&argv[0]);
|
||||
clear_tv(&argv[1]);
|
||||
if (r == FAIL || called_emsg != called_emsg_start)
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* reduce() on a Blob
|
||||
*/
|
||||
static void
|
||||
reduce_blob(
|
||||
typval_T *argvars,
|
||||
char_u *func_name,
|
||||
funcexe_T *funcexe,
|
||||
typval_T *rettv)
|
||||
{
|
||||
blob_T *b = argvars[0].vval.v_blob;
|
||||
int called_emsg_start = called_emsg;
|
||||
int r;
|
||||
typval_T initial;
|
||||
typval_T argv[3];
|
||||
int i;
|
||||
|
||||
if (argvars[2].v_type == VAR_UNKNOWN)
|
||||
@@ -3401,18 +3453,68 @@ f_reduce(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
|
||||
copy_tv(&initial, rettv);
|
||||
if (b != NULL)
|
||||
{
|
||||
if (b == NULL)
|
||||
return;
|
||||
|
||||
for ( ; i < b->bv_ga.ga_len; i++)
|
||||
{
|
||||
argv[0] = *rettv;
|
||||
argv[1].v_type = VAR_NUMBER;
|
||||
argv[1].vval.v_number = blob_get(b, i);
|
||||
if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL)
|
||||
r = call_func(func_name, -1, rettv, 2, argv, funcexe);
|
||||
clear_tv(&argv[0]);
|
||||
if (r == FAIL || called_emsg != called_emsg_start)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "reduce(list, { accumulator, element -> value } [, initial])" function
|
||||
*/
|
||||
void
|
||||
f_reduce(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u *func_name;
|
||||
partial_T *partial = NULL;
|
||||
funcexe_T funcexe;
|
||||
|
||||
if (in_vim9script()
|
||||
&& check_for_string_or_list_or_blob_arg(argvars, 0) == FAIL)
|
||||
return;
|
||||
|
||||
if (argvars[0].v_type != VAR_STRING
|
||||
&& argvars[0].v_type != VAR_LIST
|
||||
&& argvars[0].v_type != VAR_BLOB)
|
||||
{
|
||||
semsg(_(e_string_list_or_blob_required), "reduce()");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argvars[1].v_type == VAR_FUNC)
|
||||
func_name = argvars[1].vval.v_string;
|
||||
else if (argvars[1].v_type == VAR_PARTIAL)
|
||||
{
|
||||
partial = argvars[1].vval.v_partial;
|
||||
func_name = partial_name(partial);
|
||||
}
|
||||
else
|
||||
func_name = tv_get_string(&argvars[1]);
|
||||
if (func_name == NULL || *func_name == NUL)
|
||||
{
|
||||
emsg(_(e_missing_function_argument));
|
||||
return;
|
||||
}
|
||||
|
||||
CLEAR_FIELD(funcexe);
|
||||
funcexe.fe_evaluate = TRUE;
|
||||
funcexe.fe_partial = partial;
|
||||
|
||||
if (argvars[0].v_type == VAR_LIST)
|
||||
reduce_list(argvars, func_name, &funcexe, rettv);
|
||||
else if (argvars[0].v_type == VAR_STRING)
|
||||
reduce_string(argvars, func_name, &funcexe, rettv);
|
||||
else
|
||||
reduce_blob(argvars, func_name, &funcexe, rettv);
|
||||
}
|
||||
|
||||
#endif // defined(FEAT_EVAL)
|
||||
|
||||
@@ -994,6 +994,9 @@ func Test_reduce()
|
||||
call assert_fails("call reduce('', { acc, val -> acc + val }, {})", 'E1253:')
|
||||
call assert_fails("call reduce('', { acc, val -> acc + val }, 0.1)", 'E1253:')
|
||||
call assert_fails("call reduce('', { acc, val -> acc + val }, function('tr'))", 'E1253:')
|
||||
call assert_fails("call reduce('abc', { a, v -> a10}, '')", 'E121:')
|
||||
call assert_fails("call reduce(0z01, { a, v -> a10}, 1)", 'E121:')
|
||||
call assert_fails("call reduce([1], { a, v -> a10}, '')", 'E121:')
|
||||
|
||||
let g:lut = [1, 2, 3, 4]
|
||||
func EvilRemove()
|
||||
|
||||
@@ -749,6 +749,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
3849,
|
||||
/**/
|
||||
3848,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user