vim-patch:partial: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 vim/vim#9370)

389b72196e

Partial port as this doesn't include handling for non-materialized List.

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
zeertzjq
2023-08-17 14:12:24 +08:00
parent c5576fcc80
commit b193674b4a
6 changed files with 418 additions and 346 deletions

View File

@@ -6171,17 +6171,150 @@ 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)
{
const int called_emsg_start = called_emsg;
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;
}
const char *func_name;
@@ -6203,110 +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);
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) {
const char *p = tv_get_string(&argvars[0]);
int len;
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) {
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_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);
}
}