To: vim_dev@googlegroups.com Subject: Patch 7.4.1394 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.1394 Problem: Can't sort inside a sort function. Solution: Use a struct to store the sort parameters. (Jacob Niehus) Files: src/eval.c, src/testdir/test_sort.vim *** ../vim-7.4.1393/src/eval.c 2016-02-22 21:48:26.813500402 +0100 --- src/eval.c 2016-02-22 22:48:37.471519676 +0100 *************** *** 18790,18805 **** int idx; } sortItem_T; ! static int item_compare_ic; ! static int item_compare_numeric; ! static int item_compare_numbers; #ifdef FEAT_FLOAT ! static int item_compare_float; #endif ! static char_u *item_compare_func; ! static dict_T *item_compare_selfdict; ! static int item_compare_func_err; ! static int item_compare_keep_zero; static void do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort); #define ITEM_COMPARE_FAIL 999 --- 18790,18810 ---- 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; ! dict_T *item_compare_selfdict; ! int item_compare_func_err; ! int item_compare_keep_zero; ! } sortinfo_T; ! static sortinfo_T *sortinfo = NULL; static void do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort); #define ITEM_COMPARE_FAIL 999 *************** *** 18825,18831 **** tv1 = &si1->item->li_tv; tv2 = &si2->item->li_tv; ! if (item_compare_numbers) { long v1 = get_tv_number(tv1); long v2 = get_tv_number(tv2); --- 18830,18836 ---- tv1 = &si1->item->li_tv; tv2 = &si2->item->li_tv; ! if (sortinfo->item_compare_numbers) { long v1 = get_tv_number(tv1); long v2 = get_tv_number(tv2); *************** *** 18834,18840 **** } #ifdef FEAT_FLOAT ! if (item_compare_float) { float_T v1 = get_tv_float(tv1); float_T v2 = get_tv_float(tv2); --- 18839,18845 ---- } #ifdef FEAT_FLOAT ! if (sortinfo->item_compare_float) { float_T v1 = get_tv_float(tv1); float_T v2 = get_tv_float(tv2); *************** *** 18848,18854 **** * non-string to do what the docs promise. */ if (tv1->v_type == VAR_STRING) { ! if (tv2->v_type != VAR_STRING || item_compare_numeric) p1 = (char_u *)"'"; else p1 = tv1->vval.v_string; --- 18853,18859 ---- * 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; *************** *** 18857,18863 **** p1 = tv2string(tv1, &tofree1, numbuf1, 0); if (tv2->v_type == VAR_STRING) { ! if (tv1->v_type != VAR_STRING || item_compare_numeric) p2 = (char_u *)"'"; else p2 = tv2->vval.v_string; --- 18862,18868 ---- 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; *************** *** 18868,18876 **** p1 = (char_u *)""; if (p2 == NULL) p2 = (char_u *)""; ! if (!item_compare_numeric) { ! if (item_compare_ic) res = STRICMP(p1, p2); else res = STRCMP(p1, p2); --- 18873,18881 ---- 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); *************** *** 18885,18891 **** /* When the result would be zero, compare the item indexes. Makes the * sort stable. */ ! if (res == 0 && !item_compare_keep_zero) res = si1->idx > si2->idx ? 1 : -1; vim_free(tofree1); --- 18890,18896 ---- /* 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); *************** *** 18906,18912 **** int dummy; /* shortcut after failure in previous call; compare all items equal */ ! if (item_compare_func_err) return 0; si1 = (sortItem_T *)s1; --- 18911,18917 ---- int dummy; /* shortcut after failure in previous call; compare all items equal */ ! if (sortinfo->item_compare_func_err) return 0; si1 = (sortItem_T *)s1; *************** *** 18918,18940 **** copy_tv(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ ! res = call_func(item_compare_func, (int)STRLEN(item_compare_func), &rettv, 2, argv, 0L, 0L, &dummy, TRUE, ! item_compare_selfdict); clear_tv(&argv[0]); clear_tv(&argv[1]); if (res == FAIL) res = ITEM_COMPARE_FAIL; else ! res = get_tv_number_chk(&rettv, &item_compare_func_err); ! if (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 && !item_compare_keep_zero) res = si1->idx > si2->idx ? 1 : -1; return res; --- 18923,18946 ---- copy_tv(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ ! res = call_func(sortinfo->item_compare_func, ! (int)STRLEN(sortinfo->item_compare_func), &rettv, 2, argv, 0L, 0L, &dummy, TRUE, ! sortinfo->item_compare_selfdict); clear_tv(&argv[0]); clear_tv(&argv[1]); if (res == FAIL) res = ITEM_COMPARE_FAIL; else ! res = get_tv_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; *************** *** 18949,18957 **** --- 18955,18970 ---- 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) EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); else *************** *** 18960,19021 **** if (l == NULL || tv_check_lock(l->lv_lock, (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), TRUE)) ! return; rettv->vval.v_list = l; rettv->v_type = VAR_LIST; ++l->lv_refcount; len = list_len(l); if (len <= 1) ! return; /* short list sorts pretty quickly */ ! item_compare_ic = FALSE; ! item_compare_numeric = FALSE; ! item_compare_numbers = FALSE; #ifdef FEAT_FLOAT ! item_compare_float = FALSE; #endif ! item_compare_func = NULL; ! item_compare_selfdict = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { /* optional second argument: {func} */ if (argvars[1].v_type == VAR_FUNC) ! item_compare_func = argvars[1].vval.v_string; else { int error = FALSE; i = get_tv_number_chk(&argvars[1], &error); if (error) ! return; /* type error; errmsg already given */ if (i == 1) ! item_compare_ic = TRUE; else ! item_compare_func = get_tv_string(&argvars[1]); ! if (item_compare_func != NULL) { ! if (STRCMP(item_compare_func, "n") == 0) { ! item_compare_func = NULL; ! item_compare_numeric = TRUE; } ! else if (STRCMP(item_compare_func, "N") == 0) { ! item_compare_func = NULL; ! item_compare_numbers = TRUE; } #ifdef FEAT_FLOAT ! else if (STRCMP(item_compare_func, "f") == 0) { ! item_compare_func = NULL; ! item_compare_float = TRUE; } #endif ! else if (STRCMP(item_compare_func, "i") == 0) { ! item_compare_func = NULL; ! item_compare_ic = TRUE; } } } --- 18973,19034 ---- if (l == NULL || tv_check_lock(l->lv_lock, (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), TRUE)) ! goto theend; rettv->vval.v_list = l; rettv->v_type = VAR_LIST; ++l->lv_refcount; 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_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 { int error = FALSE; i = get_tv_number_chk(&argvars[1], &error); if (error) ! goto theend; /* type error; errmsg already given */ if (i == 1) ! info.item_compare_ic = TRUE; else ! info.item_compare_func = get_tv_string(&argvars[1]); ! if (info.item_compare_func != NULL) { ! 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; } } } *************** *** 19026,19041 **** if (argvars[2].v_type != VAR_DICT) { EMSG(_(e_dictreq)); ! return; } ! item_compare_selfdict = argvars[2].vval.v_dict; } } /* Make an array with each entry pointing to an item in the List. */ ptrs = (sortItem_T *)alloc((int)(len * sizeof(sortItem_T))); if (ptrs == NULL) ! return; i = 0; if (sort) --- 19039,19054 ---- 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 = (sortItem_T *)alloc((int)(len * sizeof(sortItem_T))); if (ptrs == NULL) ! goto theend; i = 0; if (sort) *************** *** 19048,19057 **** ++i; } ! item_compare_func_err = FALSE; ! item_compare_keep_zero = FALSE; /* test the compare function */ ! if (item_compare_func != NULL && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) == ITEM_COMPARE_FAIL) EMSG(_("E702: Sort compare function failed")); --- 19061,19070 ---- ++i; } ! info.item_compare_func_err = FALSE; ! info.item_compare_keep_zero = FALSE; /* test the compare function */ ! if (info.item_compare_func != NULL && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) == ITEM_COMPARE_FAIL) EMSG(_("E702: Sort compare function failed")); *************** *** 19059,19067 **** { /* Sort the array with item pointers. */ qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), ! item_compare_func == NULL ? item_compare : item_compare2); ! if (!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; --- 19072,19081 ---- { /* Sort the array with item pointers. */ qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), ! info.item_compare_func == 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; *************** *** 19076,19084 **** int (*item_compare_func_ptr)(const void *, const void *); /* f_uniq(): ptrs will be a stack of items to remove */ ! item_compare_func_err = FALSE; ! item_compare_keep_zero = TRUE; ! item_compare_func_ptr = item_compare_func ? item_compare2 : item_compare; for (li = l->lv_first; li != NULL && li->li_next != NULL; --- 19090,19098 ---- 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 ? item_compare2 : item_compare; for (li = l->lv_first; li != NULL && li->li_next != NULL; *************** *** 19087,19100 **** if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) == 0) ptrs[i++].item = li; ! if (item_compare_func_err) { EMSG(_("E882: Uniq compare function failed")); break; } } ! if (!item_compare_func_err) { while (--i >= 0) { --- 19101,19114 ---- 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) { *************** *** 19113,19118 **** --- 19127,19134 ---- vim_free(ptrs); } + theend: + sortinfo = old_sortinfo; } /* *** ../vim-7.4.1393/src/testdir/test_sort.vim 2016-01-19 23:35:43.567530384 +0100 --- src/testdir/test_sort.vim 2016-02-22 22:41:26.036069267 +0100 *************** *** 1,5 **** --- 1,14 ---- " Test sort() + :func Compare1(a, b) abort + call sort(range(3), 'Compare2') + return a:a ># a:b + :endfunc + + :func Compare2(a, b) abort + return a:a <# a:b + :endfunc + func Test_sort_strings() " numbers compared as strings call assert_equal([1, 2, 3], sort([3, 2, 1])) *************** *** 21,23 **** --- 30,37 ---- func Test_sort_float() call assert_equal([0.28, 3, 13.5], sort([13.5, 0.28, 3], 'f')) endfunc + + func Test_sort_nested() + " test ability to call sort() from a compare function + call assert_equal([1, 3, 5], sort([3, 1, 5], 'Compare1')) + endfunc *** ../vim-7.4.1393/src/version.c 2016-02-22 22:19:18.838058941 +0100 --- src/version.c 2016-02-22 22:42:55.179130994 +0100 *************** *** 750,751 **** --- 750,753 ---- { /* Add new patch number below this line */ + /**/ + 1394, /**/ -- Trees moving back and forth is what makes the wind blow. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///