feat(eval): partially port v8.2.0878

Problem:    No reduce() function.
Solution:   Add a reduce() function. (closes vim/vim#5481)
85629985b7

Needs CHECK_LIST_MATERIALIZE from v8.2.0751 (and range_list_materialize from
8.2.0149).

Move e_reduceempty to funcs.c, as it's only used there. Make it static.
Use tv_blob_len, tv_list_len == 0 for empty checks.
Replace vim_memset(&funcexe, 0, ...) with FUNCEXE_INIT.
Leave li initially undefined (tv_list_first returns NULL if list is NULL).

This patch has a memory leak fixed by v8.2.0882.
This commit is contained in:
Sean Dewar
2021-12-06 20:50:29 +00:00
committed by zeertzjq
parent 2870311a37
commit d746f5aa41
4 changed files with 138 additions and 0 deletions

View File

@@ -100,6 +100,7 @@ PRAGMA_DIAG_POP
static char *e_listarg = N_("E686: Argument of %s must be a List");
static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob");
static char *e_invalwindow = N_("E957: Invalid window number");
static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value");
/// Dummy va_list for passing to vim_snprintf
///
@@ -7855,6 +7856,90 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
/// "reduce(list, { accumlator, element -> value } [, initial])" function
static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) {
emsg(_(e_listblobreq));
return;
}
const char_u *func_name;
partial_T *partial = NULL;
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 = (const char_u *)tv_get_string(&argvars[1]);
}
if (*func_name == NUL) {
return; // type error or empty name
}
funcexe_T funcexe = FUNCEXE_INIT;
funcexe.evaluate = true;
funcexe.partial = partial;
typval_T accum;
typval_T argv[3];
if (argvars[0].v_type == VAR_LIST) {
const 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);
accum = *TV_LIST_ITEM_TV(first);
li = TV_LIST_ITEM_NEXT(l, first);
} else {
accum = argvars[2];
li = tv_list_first(l);
}
tv_copy(&accum, rettv);
for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
argv[0] = accum;
argv[1] = *TV_LIST_ITEM_TV(li);
if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL) {
return;
}
accum = *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;
}
accum.v_type = VAR_NUMBER;
accum.vval.v_number = tv_blob_get(b, 0);
i = 1;
} else {
accum = argvars[2];
i = 0;
}
tv_copy(&accum, rettv);
for (; i < tv_blob_len(b); i++) {
argv[0] = accum;
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;
}
accum = *rettv;
}
}
}
#define SP_NOMOVE 0x01 ///< don't move cursor
#define SP_REPEAT 0x02 ///< repeat to find outer pair
#define SP_RETCOUNT 0x04 ///< return matchcount