1
0
forked from aniani/vim

patch 8.1.1763: evalfunc.c is still too big

Problem:    Evalfunc.c is still too big.
Solution:   Move dict and list functions to a better place.
This commit is contained in:
Bram Moolenaar
2019-07-27 23:12:12 +02:00
parent c273405188
commit 9f9fe37f67
8 changed files with 704 additions and 673 deletions

View File

@@ -874,6 +874,40 @@ list_join(
return retval;
}
/*
* "join()" function
*/
void
f_join(typval_T *argvars, typval_T *rettv)
{
garray_T ga;
char_u *sep;
if (argvars[0].v_type != VAR_LIST)
{
emsg(_(e_listreq));
return;
}
if (argvars[0].vval.v_list == NULL)
return;
if (argvars[1].v_type == VAR_UNKNOWN)
sep = (char_u *)" ";
else
sep = tv_get_string_chk(&argvars[1]);
rettv->v_type = VAR_STRING;
if (sep != NULL)
{
ga_init2(&ga, (int)sizeof(char), 80);
list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0);
ga_append(&ga, NUL);
rettv->vval.v_string = (char_u *)ga.ga_data;
}
else
rettv->vval.v_string = NULL;
}
/*
* Allocate a variable for a List and fill it from "*arg".
* Return OK or FAIL.
@@ -1007,4 +1041,507 @@ init_static_list(staticList10_T *sl)
}
}
/*
* "list2str()" function
*/
void
f_list2str(typval_T *argvars, typval_T *rettv)
{
list_T *l;
listitem_T *li;
garray_T ga;
int utf8 = FALSE;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (argvars[0].v_type != VAR_LIST)
{
emsg(_(e_invarg));
return;
}
l = argvars[0].vval.v_list;
if (l == NULL)
return; // empty list results in empty string
if (argvars[1].v_type != VAR_UNKNOWN)
utf8 = (int)tv_get_number_chk(&argvars[1], NULL);
ga_init2(&ga, 1, 80);
if (has_mbyte || utf8)
{
char_u buf[MB_MAXBYTES + 1];
int (*char2bytes)(int, char_u *);
if (utf8 || enc_utf8)
char2bytes = utf_char2bytes;
else
char2bytes = mb_char2bytes;
for (li = l->lv_first; li != NULL; li = li->li_next)
{
buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL;
ga_concat(&ga, buf);
}
ga_append(&ga, NUL);
}
else if (ga_grow(&ga, list_len(l) + 1) == OK)
{
for (li = l->lv_first; li != NULL; li = li->li_next)
ga_append(&ga, tv_get_number(&li->li_tv));
ga_append(&ga, NUL);
}
rettv->v_type = VAR_STRING;
rettv->vval.v_string = ga.ga_data;
}
void
list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
{
list_T *l;
listitem_T *item, *item2;
listitem_T *li;
int error = FALSE;
int idx;
if ((l = argvars[0].vval.v_list) == NULL
|| var_check_lock(l->lv_lock, arg_errmsg, TRUE))
return;
idx = (long)tv_get_number_chk(&argvars[1], &error);
if (error)
; // type error: do nothing, errmsg already given
else if ((item = list_find(l, idx)) == NULL)
semsg(_(e_listidx), idx);
else
{
if (argvars[2].v_type == VAR_UNKNOWN)
{
/* Remove one item, return its value. */
vimlist_remove(l, item, item);
*rettv = item->li_tv;
vim_free(item);
}
else
{
// Remove range of items, return list with values.
int end = (long)tv_get_number_chk(&argvars[2], &error);
if (error)
; // type error: do nothing
else if ((item2 = list_find(l, end)) == NULL)
semsg(_(e_listidx), end);
else
{
int cnt = 0;
for (li = item; li != NULL; li = li->li_next)
{
++cnt;
if (li == item2)
break;
}
if (li == NULL) /* didn't find "item2" after "item" */
emsg(_(e_invrange));
else
{
vimlist_remove(l, item, item2);
if (rettv_list_alloc(rettv) == OK)
{
l = rettv->vval.v_list;
l->lv_first = item;
l->lv_last = item2;
item->li_prev = NULL;
item2->li_next = NULL;
l->lv_len = cnt;
}
}
}
}
}
}
static int item_compare(const void *s1, const void *s2);
static int item_compare2(const void *s1, const void *s2);
/* struct used in the array that's given to qsort() */
typedef struct
{
listitem_T *item;
int idx;
} sortItem_T;
/* struct storing information about current sort */
typedef struct
{
int item_compare_ic;
int item_compare_numeric;
int item_compare_numbers;
#ifdef FEAT_FLOAT
int item_compare_float;
#endif
char_u *item_compare_func;
partial_T *item_compare_partial;
dict_T *item_compare_selfdict;
int item_compare_func_err;
int item_compare_keep_zero;
} sortinfo_T;
static sortinfo_T *sortinfo = NULL;
#define ITEM_COMPARE_FAIL 999
/*
* Compare functions for f_sort() and f_uniq() below.
*/
static int
item_compare(const void *s1, const void *s2)
{
sortItem_T *si1, *si2;
typval_T *tv1, *tv2;
char_u *p1, *p2;
char_u *tofree1 = NULL, *tofree2 = NULL;
int res;
char_u numbuf1[NUMBUFLEN];
char_u numbuf2[NUMBUFLEN];
si1 = (sortItem_T *)s1;
si2 = (sortItem_T *)s2;
tv1 = &si1->item->li_tv;
tv2 = &si2->item->li_tv;
if (sortinfo->item_compare_numbers)
{
varnumber_T v1 = tv_get_number(tv1);
varnumber_T v2 = tv_get_number(tv2);
return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
}
#ifdef FEAT_FLOAT
if (sortinfo->item_compare_float)
{
float_T v1 = tv_get_float(tv1);
float_T v2 = tv_get_float(tv2);
return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;
}
#endif
/* tv2string() puts quotes around a string and allocates memory. Don't do
* that for string variables. Use a single quote when comparing with a
* non-string to do what the docs promise. */
if (tv1->v_type == VAR_STRING)
{
if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
p1 = (char_u *)"'";
else
p1 = tv1->vval.v_string;
}
else
p1 = tv2string(tv1, &tofree1, numbuf1, 0);
if (tv2->v_type == VAR_STRING)
{
if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
p2 = (char_u *)"'";
else
p2 = tv2->vval.v_string;
}
else
p2 = tv2string(tv2, &tofree2, numbuf2, 0);
if (p1 == NULL)
p1 = (char_u *)"";
if (p2 == NULL)
p2 = (char_u *)"";
if (!sortinfo->item_compare_numeric)
{
if (sortinfo->item_compare_ic)
res = STRICMP(p1, p2);
else
res = STRCMP(p1, p2);
}
else
{
double n1, n2;
n1 = strtod((char *)p1, (char **)&p1);
n2 = strtod((char *)p2, (char **)&p2);
res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1;
}
/* When the result would be zero, compare the item indexes. Makes the
* sort stable. */
if (res == 0 && !sortinfo->item_compare_keep_zero)
res = si1->idx > si2->idx ? 1 : -1;
vim_free(tofree1);
vim_free(tofree2);
return res;
}
static int
item_compare2(const void *s1, const void *s2)
{
sortItem_T *si1, *si2;
int res;
typval_T rettv;
typval_T argv[3];
int dummy;
char_u *func_name;
partial_T *partial = sortinfo->item_compare_partial;
/* shortcut after failure in previous call; compare all items equal */
if (sortinfo->item_compare_func_err)
return 0;
si1 = (sortItem_T *)s1;
si2 = (sortItem_T *)s2;
if (partial == NULL)
func_name = sortinfo->item_compare_func;
else
func_name = partial_name(partial);
/* Copy the values. This is needed to be able to set v_lock to VAR_FIXED
* in the copy without changing the original list items. */
copy_tv(&si1->item->li_tv, &argv[0]);
copy_tv(&si2->item->li_tv, &argv[1]);
rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
res = call_func(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
partial, sortinfo->item_compare_selfdict);
clear_tv(&argv[0]);
clear_tv(&argv[1]);
if (res == FAIL)
res = ITEM_COMPARE_FAIL;
else
res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err);
if (sortinfo->item_compare_func_err)
res = ITEM_COMPARE_FAIL; /* return value has wrong type */
clear_tv(&rettv);
/* When the result would be zero, compare the pointers themselves. Makes
* the sort stable. */
if (res == 0 && !sortinfo->item_compare_keep_zero)
res = si1->idx > si2->idx ? 1 : -1;
return res;
}
/*
* "sort()" or "uniq()" function
*/
static void
do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
{
list_T *l;
listitem_T *li;
sortItem_T *ptrs;
sortinfo_T *old_sortinfo;
sortinfo_T info;
long len;
long i;
/* Pointer to current info struct used in compare function. Save and
* restore the current one for nested calls. */
old_sortinfo = sortinfo;
sortinfo = &info;
if (argvars[0].v_type != VAR_LIST)
semsg(_(e_listarg), sort ? "sort()" : "uniq()");
else
{
l = argvars[0].vval.v_list;
if (l == NULL || var_check_lock(l->lv_lock,
(char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
TRUE))
goto theend;
rettv_list_set(rettv, l);
len = list_len(l);
if (len <= 1)
goto theend; /* short list sorts pretty quickly */
info.item_compare_ic = FALSE;
info.item_compare_numeric = FALSE;
info.item_compare_numbers = FALSE;
#ifdef FEAT_FLOAT
info.item_compare_float = FALSE;
#endif
info.item_compare_func = NULL;
info.item_compare_partial = NULL;
info.item_compare_selfdict = NULL;
if (argvars[1].v_type != VAR_UNKNOWN)
{
/* optional second argument: {func} */
if (argvars[1].v_type == VAR_FUNC)
info.item_compare_func = argvars[1].vval.v_string;
else if (argvars[1].v_type == VAR_PARTIAL)
info.item_compare_partial = argvars[1].vval.v_partial;
else
{
int error = FALSE;
i = (long)tv_get_number_chk(&argvars[1], &error);
if (error)
goto theend; /* type error; errmsg already given */
if (i == 1)
info.item_compare_ic = TRUE;
else if (argvars[1].v_type != VAR_NUMBER)
info.item_compare_func = tv_get_string(&argvars[1]);
else if (i != 0)
{
emsg(_(e_invarg));
goto theend;
}
if (info.item_compare_func != NULL)
{
if (*info.item_compare_func == NUL)
{
/* empty string means default sort */
info.item_compare_func = NULL;
}
else if (STRCMP(info.item_compare_func, "n") == 0)
{
info.item_compare_func = NULL;
info.item_compare_numeric = TRUE;
}
else if (STRCMP(info.item_compare_func, "N") == 0)
{
info.item_compare_func = NULL;
info.item_compare_numbers = TRUE;
}
#ifdef FEAT_FLOAT
else if (STRCMP(info.item_compare_func, "f") == 0)
{
info.item_compare_func = NULL;
info.item_compare_float = TRUE;
}
#endif
else if (STRCMP(info.item_compare_func, "i") == 0)
{
info.item_compare_func = NULL;
info.item_compare_ic = TRUE;
}
}
}
if (argvars[2].v_type != VAR_UNKNOWN)
{
/* optional third argument: {dict} */
if (argvars[2].v_type != VAR_DICT)
{
emsg(_(e_dictreq));
goto theend;
}
info.item_compare_selfdict = argvars[2].vval.v_dict;
}
}
/* Make an array with each entry pointing to an item in the List. */
ptrs = ALLOC_MULT(sortItem_T, len);
if (ptrs == NULL)
goto theend;
i = 0;
if (sort)
{
/* sort(): ptrs will be the list to sort */
for (li = l->lv_first; li != NULL; li = li->li_next)
{
ptrs[i].item = li;
ptrs[i].idx = i;
++i;
}
info.item_compare_func_err = FALSE;
info.item_compare_keep_zero = FALSE;
/* test the compare function */
if ((info.item_compare_func != NULL
|| info.item_compare_partial != NULL)
&& item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
== ITEM_COMPARE_FAIL)
emsg(_("E702: Sort compare function failed"));
else
{
/* Sort the array with item pointers. */
qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
info.item_compare_func == NULL
&& info.item_compare_partial == NULL
? item_compare : item_compare2);
if (!info.item_compare_func_err)
{
/* Clear the List and append the items in sorted order. */
l->lv_first = l->lv_last = l->lv_idx_item = NULL;
l->lv_len = 0;
for (i = 0; i < len; ++i)
list_append(l, ptrs[i].item);
}
}
}
else
{
int (*item_compare_func_ptr)(const void *, const void *);
/* f_uniq(): ptrs will be a stack of items to remove */
info.item_compare_func_err = FALSE;
info.item_compare_keep_zero = TRUE;
item_compare_func_ptr = info.item_compare_func != NULL
|| info.item_compare_partial != NULL
? item_compare2 : item_compare;
for (li = l->lv_first; li != NULL && li->li_next != NULL;
li = li->li_next)
{
if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
== 0)
ptrs[i++].item = li;
if (info.item_compare_func_err)
{
emsg(_("E882: Uniq compare function failed"));
break;
}
}
if (!info.item_compare_func_err)
{
while (--i >= 0)
{
li = ptrs[i].item->li_next;
ptrs[i].item->li_next = li->li_next;
if (li->li_next != NULL)
li->li_next->li_prev = ptrs[i].item;
else
l->lv_last = ptrs[i].item;
list_fix_watch(l, li);
listitem_free(li);
l->lv_len--;
}
}
}
vim_free(ptrs);
}
theend:
sortinfo = old_sortinfo;
}
/*
* "sort({list})" function
*/
void
f_sort(typval_T *argvars, typval_T *rettv)
{
do_sort_uniq(argvars, rettv, TRUE);
}
/*
* "uniq({list})" function
*/
void
f_uniq(typval_T *argvars, typval_T *rettv)
{
do_sort_uniq(argvars, rettv, FALSE);
}
#endif /* defined(FEAT_EVAL) */