diff options
author | Yegappan Lakshmanan <yegappan@yahoo.com> | 2021-12-22 18:19:26 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-12-22 18:19:26 +0000 |
commit | f973eeb4911de09258e84cb2248dc0f9392824b4 (patch) | |
tree | 81ac7737af13f9eff0d6b69306b7ed6691d23804 | |
parent | 1aeccdb464d81f0af047b399cbad160307a91f7c (diff) |
patch 8.2.3871: list.c contains code for dict and blobv8.2.3871
Problem: List.c contains code for dict and blob.
Solution: Refactor to put code where it belongs. (Yegappan Lakshmanan,
closes #9386)
-rw-r--r-- | src/blob.c | 324 | ||||
-rw-r--r-- | src/dict.c | 289 | ||||
-rw-r--r-- | src/list.c | 969 | ||||
-rw-r--r-- | src/proto/blob.pro | 5 | ||||
-rw-r--r-- | src/proto/dict.pro | 5 | ||||
-rw-r--r-- | src/proto/list.pro | 1 | ||||
-rw-r--r-- | src/proto/strings.pro | 3 | ||||
-rw-r--r-- | src/strings.c | 184 | ||||
-rw-r--r-- | src/structs.h | 8 | ||||
-rw-r--r-- | src/testdir/test_filter_map.vim | 1 | ||||
-rw-r--r-- | src/testdir/test_listdict.vim | 26 | ||||
-rw-r--r-- | src/testdir/test_sort.vim | 16 | ||||
-rw-r--r-- | src/version.c | 2 |
13 files changed, 968 insertions, 865 deletions
diff --git a/src/blob.c b/src/blob.c index 5658370527..1387a8e653 100644 --- a/src/blob.c +++ b/src/blob.c @@ -410,78 +410,306 @@ blob_set_range(blob_T *dest, long n1, long n2, typval_T *src) } /* - * "remove({blob})" function + * "add(blob, item)" function + */ + void +blob_add(typval_T *argvars, typval_T *rettv) +{ + blob_T *b = argvars[0].vval.v_blob; + int error = FALSE; + varnumber_T n; + + if (b == NULL) + { + if (in_vim9script()) + emsg(_(e_cannot_add_to_null_blob)); + return; + } + + if (value_check_lock(b->bv_lock, (char_u *)N_("add() argument"), TRUE)) + return; + + n = tv_get_number_chk(&argvars[1], &error); + if (error) + return; + + ga_append(&b->bv_ga, (int)n); + copy_tv(&argvars[0], rettv); +} + +/* + * "remove({blob}, {idx} [, {end}])" function */ void blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) { blob_T *b = argvars[0].vval.v_blob; + blob_T *newblob; int error = FALSE; long idx; long end; + int len; + char_u *p; if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TRUE)) return; idx = (long)tv_get_number_chk(&argvars[1], &error); - if (!error) + if (error) + return; + + len = blob_len(b); + + if (idx < 0) + // count from the end + idx = len + idx; + if (idx < 0 || idx >= len) + { + semsg(_(e_blobidx), idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) { - int len = blob_len(b); - char_u *p; + // Remove one item, return its value. + p = (char_u *)b->bv_ga.ga_data; + rettv->vval.v_number = (varnumber_T) *(p + idx); + mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); + --b->bv_ga.ga_len; + return; + } - if (idx < 0) - // count from the end - idx = len + idx; - if (idx < 0 || idx >= len) - { - semsg(_(e_blobidx), idx); + // Remove range of items, return blob with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) + return; + if (end < 0) + // count from the end + end = len + end; + if (end >= len || idx > end) + { + semsg(_(e_blobidx), end); + return; + } + newblob = blob_alloc(); + if (newblob == NULL) + return; + newblob->bv_ga.ga_len = end - idx + 1; + if (ga_grow(&newblob->bv_ga, end - idx + 1) == FAIL) + { + vim_free(newblob); + return; + } + p = (char_u *)b->bv_ga.ga_data; + mch_memmove((char_u *)newblob->bv_ga.ga_data, p + idx, + (size_t)(end - idx + 1)); + ++newblob->bv_refcount; + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = newblob; + + if (len - end - 1 > 0) + mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); + b->bv_ga.ga_len -= end - idx + 1; +} + +/* + * Implementation of map() and filter() for a Blob. Apply "expr" to every + * number in Blob "blob_arg" and return the result in "rettv". + */ + void +blob_filter_map( + 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; + 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) return; + b_ret = rettv->vval.v_blob; + } + + // set_vim_var_nr() doesn't set the type + set_vim_var_type(VV_KEY, VAR_NUMBER); + + for (i = 0; i < b->bv_ga.ga_len; i++) + { + typval_T newtv; + + tv.v_type = VAR_NUMBER; + val = blob_get(b, i); + tv.vval.v_number = val; + set_vim_var_nr(VV_KEY, idx); + if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL + || did_emsg) + break; + if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) + { + clear_tv(&newtv); + emsg(_(e_invalblob)); + break; } - if (argvars[2].v_type == VAR_UNKNOWN) + if (filtermap != FILTERMAP_FILTER) { - // Remove one item, return its value. - p = (char_u *)b->bv_ga.ga_data; - rettv->vval.v_number = (varnumber_T) *(p + idx); - mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); + if (newtv.vval.v_number != val) + blob_set(b_ret, i, newtv.vval.v_number); + } + else if (rem) + { + 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); --b->bv_ga.ga_len; + --i; } - else + ++idx; + } +} + +/* + * "insert(blob, {item} [, {idx}])" function + */ + void +blob_insert_func(typval_T *argvars, typval_T *rettv) +{ + blob_T *b = argvars[0].vval.v_blob; + long before = 0; + int error = FALSE; + int val, len; + char_u *p; + + if (b == NULL) + { + if (in_vim9script()) + emsg(_(e_cannot_add_to_null_blob)); + return; + } + + if (value_check_lock(b->bv_lock, (char_u *)N_("insert() argument"), TRUE)) + return; + + len = blob_len(b); + if (argvars[2].v_type != VAR_UNKNOWN) + { + before = (long)tv_get_number_chk(&argvars[2], &error); + if (error) + return; // type error; errmsg already given + if (before < 0 || before > len) { - blob_T *blob; - - // Remove range of items, return blob with values. - end = (long)tv_get_number_chk(&argvars[2], &error); - if (error) - return; - if (end < 0) - // count from the end - end = len + end; - if (end >= len || idx > end) - { - semsg(_(e_blobidx), end); - return; - } - blob = blob_alloc(); - if (blob == NULL) - return; - blob->bv_ga.ga_len = end - idx + 1; - if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL) - { - vim_free(blob); - return; - } - p = (char_u *)b->bv_ga.ga_data; - mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx, - (size_t)(end - idx + 1)); - ++blob->bv_refcount; - rettv->v_type = VAR_BLOB; - rettv->vval.v_blob = blob; + semsg(_(e_invarg2), tv_get_string(&argvars[2])); + return; + } + } + val = tv_get_number_chk(&argvars[1], &error); + if (error) + return; + if (val < 0 || val > 255) + { + semsg(_(e_invarg2), tv_get_string(&argvars[1])); + return; + } + + if (ga_grow(&b->bv_ga, 1) == FAIL) + return; + p = (char_u *)b->bv_ga.ga_data; + mch_memmove(p + before + 1, p + before, (size_t)len - before); + *(p + before) = val; + ++b->bv_ga.ga_len; + + copy_tv(&argvars[0], rettv); +} + +/* + * reduce() Blob argvars[0] using the function 'funcname' with arguments in + * 'funcexe' starting with the initial value argvars[2] and return the result + * in 'rettv'. + */ + void +blob_reduce( + 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 (len - end - 1 > 0) - mch_memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); - b->bv_ga.ga_len -= end - idx + 1; + if (argvars[2].v_type == VAR_UNKNOWN) + { + if (b == NULL || b->bv_ga.ga_len == 0) + { + semsg(_(e_reduceempty), "Blob"); + return; } + initial.v_type = VAR_NUMBER; + initial.vval.v_number = blob_get(b, 0); + i = 1; + } + else if (argvars[2].v_type != VAR_NUMBER) + { + emsg(_(e_number_expected)); + return; + } + else + { + initial = argvars[2]; + i = 0; + } + + copy_tv(&initial, rettv); + 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); + r = call_func(func_name, -1, rettv, 2, argv, funcexe); + clear_tv(&argv[0]); + if (r == FAIL || called_emsg != called_emsg_start) + return; + } +} + +/* + * "reverse({blob})" function + */ + void +blob_reverse(blob_T *b, typval_T *rettv) +{ + int i, len = blob_len(b); + + for (i = 0; i < len / 2; i++) + { + int tmp = blob_get(b, i); + + blob_set(b, i, blob_get(b, len - i - 1)); + blob_set(b, len - i - 1, tmp); } + rettv_blob_set(rettv, b); } /* diff --git a/src/dict.c b/src/dict.c index ea43d0be1c..f2989fff21 100644 --- a/src/dict.c +++ b/src/dict.c @@ -896,13 +896,11 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) int vim9script = in_vim9script(); int had_comma; - /* - * First check if it's not a curly-braces thing: {expr}. - * Must do this without evaluating, otherwise a function may be called - * twice. Unfortunately this means we need to call eval1() twice for the - * first item. - * But {} is an empty Dictionary. - */ + // First check if it's not a curly-braces thing: {expr}. + // Must do this without evaluating, otherwise a function may be called + // twice. Unfortunately this means we need to call eval1() twice for the + // first item. + // But {} is an empty Dictionary. if (!vim9script && *curly_expr != '}' && eval1(&curly_expr, &tv, NULL) == OK @@ -1184,6 +1182,251 @@ dict_equal( } /* + * Count the number of times item "needle" occurs in Dict "d". Case is ignored + * if "ic" is TRUE. + */ + long +dict_count(dict_T *d, typval_T *needle, int ic) +{ + int todo; + hashitem_T *hi; + long n = 0; + + if (d == NULL) + return 0; + + todo = (int)d->dv_hashtab.ht_used; + for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + if (tv_equal(&HI2DI(hi)->di_tv, needle, ic, FALSE)) + ++n; + } + } + + return n; +} + +/* + * extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the + * resulting Dict in "rettv". "is_new" is TRUE for extendnew(). + */ + void +dict_extend_func( + typval_T *argvars, + type_T *type, + char *func_name, + char_u *arg_errmsg, + int is_new, + typval_T *rettv) +{ + dict_T *d1, *d2; + char_u *action; + int i; + + d1 = argvars[0].vval.v_dict; + if (d1 == NULL) + { + emsg(_(e_cannot_extend_null_dict)); + return; + } + d2 = argvars[1].vval.v_dict; + if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE)) + && d2 != NULL) + { + if (is_new) + { + d1 = dict_copy(d1, FALSE, get_copyID()); + if (d1 == NULL) + return; + } + + // Check the third argument. + if (argvars[2].v_type != VAR_UNKNOWN) + { + static char *(av[]) = {"keep", "force", "error"}; + + action = tv_get_string_chk(&argvars[2]); + if (action == NULL) + return; + for (i = 0; i < 3; ++i) + if (STRCMP(action, av[i]) == 0) + break; + if (i == 3) + { + semsg(_(e_invarg2), action); + return; + } + } + else + action = (char_u *)"force"; + + if (type != NULL && check_typval_arg_type(type, &argvars[1], + func_name, 2) == FAIL) + return; + dict_extend(d1, d2, action, func_name); + + if (is_new) + { + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = d1; + rettv->v_lock = FALSE; + } + else + copy_tv(&argvars[0], rettv); + } +} + +/* + * Implementation of map() and filter() for a Dict. Apply "expr" to every + * item in Dict "d" and return the result in "rettv". + */ + void +dict_filter_map( + dict_T *d, + filtermap_T filtermap, + type_T *argtype, + char *func_name, + char_u *arg_errmsg, + typval_T *expr, + typval_T *rettv) +{ + int prev_lock; + dict_T *d_ret = NULL; + hashtab_T *ht; + hashitem_T *hi; + dictitem_T *di; + int todo; + int rem; + + if (filtermap == FILTERMAP_MAPNEW) + { + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = NULL; + } + if (d == NULL + || (filtermap == FILTERMAP_FILTER + && value_check_lock(d->dv_lock, arg_errmsg, TRUE))) + return; + + prev_lock = d->dv_lock; + + if (filtermap == FILTERMAP_MAPNEW) + { + if (rettv_dict_alloc(rettv) == FAIL) + return; + d_ret = rettv->vval.v_dict; + } + + if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0) + d->dv_lock = VAR_LOCKED; + ht = &d->dv_hashtab; + hash_lock(ht); + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + int r; + typval_T newtv; + + --todo; + di = HI2DI(hi); + if (filtermap == FILTERMAP_MAP + && (value_check_lock(di->di_tv.v_lock, + arg_errmsg, TRUE) + || var_check_ro(di->di_flags, + arg_errmsg, TRUE))) + break; + set_vim_var_string(VV_KEY, di->di_key, -1); + newtv.v_type = VAR_UNKNOWN; + r = filter_map_one(&di->di_tv, expr, filtermap, + &newtv, &rem); + clear_tv(get_vim_var_tv(VV_KEY)); + if (r == FAIL || did_emsg) + { + clear_tv(&newtv); + break; + } + if (filtermap == FILTERMAP_MAP) + { + if (argtype != NULL && check_typval_arg_type( + argtype->tt_member, &newtv, + func_name, 0) == FAIL) + { + clear_tv(&newtv); + break; + } + // map(): replace the dict item value + clear_tv(&di->di_tv); + newtv.v_lock = 0; + di->di_tv = newtv; + } + else if (filtermap == FILTERMAP_MAPNEW) + { + // mapnew(): add the item value to the new dict + r = dict_add_tv(d_ret, (char *)di->di_key, &newtv); + clear_tv(&newtv); + if (r == FAIL) + break; + } + else if (filtermap == FILTERMAP_FILTER && rem) + { + // filter(false): remove the item from the dict + if (var_check_fixed(di->di_flags, arg_errmsg, TRUE) + || var_check_ro(di->di_flags, arg_errmsg, TRUE)) + break; + dictitem_remove(d, di); + } + } + } + hash_unlock(ht); + d->dv_lock = prev_lock; +} + +/* + * "remove({dict})" function + */ + void +dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) +{ + dict_T *d; + char_u *key; + dictitem_T *di; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + semsg(_(e_too_many_arguments_for_function_str), "remove()"); + return; + } + + d = argvars[0].vval.v_dict; + if (d == NULL || value_check_lock(d->dv_lock, arg_errmsg, TRUE)) + return; + + key = tv_get_string_chk(&argvars[1]); + if (key == NULL) + return; + + di = dict_find(d, key, -1); + if (di == NULL) + { + semsg(_(e_dictkey), key); + return; + } + + if (var_check_fixed(di->di_flags, arg_errmsg, TRUE) + || var_check_ro(di->di_flags, arg_errmsg, TRUE)) + return; + + *rettv = di->di_tv; + init_tv(&di->di_tv); + dictitem_remove(d, di); +} + +/* * Turn a dict into a list: * "what" == 0: list of keys * "what" == 1: list of values @@ -1338,36 +1581,4 @@ f_has_key(typval_T *argvars, typval_T *rettv) tv_get_string(&argvars[1]), -1) != NULL; } -/* - * "remove({dict})" function - */ - void -dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) -{ - dict_T *d; - char_u *key; - dictitem_T *di; - - if (argvars[2].v_type != VAR_UNKNOWN) - semsg(_(e_too_many_arguments_for_function_str), "remove()"); - else if ((d = argvars[0].vval.v_dict) != NULL - && !value_check_lock(d->dv_lock, arg_errmsg, TRUE)) - { - key = tv_get_string_chk(&argvars[1]); - if (key != NULL) - { - di = dict_find(d, key, -1); - if (di == NULL) - semsg(_(e_dictkey), key); - else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE) - && !var_check_ro(di->di_flags, arg_errmsg, TRUE)) - { - *rettv = di->di_tv; - init_tv(&di->di_tv); - dictitem_remove(d, di); - } - } - } -} - #endif // defined(FEAT_EVAL) diff --git a/src/list.c b/src/list.c index 57a1a52746..61a26a35dd 100644 --- a/src/list.c +++ b/src/list.c @@ -314,28 +314,6 @@ listitem_alloc(void) } /* - * Make a typval_T of the first character of "input" and store it in "output". - * Return OK or FAIL. - */ - static int -tv_get_first_char(char_u *input, typval_T *output) -{ - char_u buf[MB_MAXBYTES + 1]; - int len; - - if (input == NULL || output == NULL) - return FAIL; - - len = has_mbyte ? mb_ptr2len(input) : 1; - STRNCPY(buf, input, len); - buf[len] = NUL; - output->v_type = VAR_STRING; - output->vval.v_string = vim_strsave(buf); - - return output->vval.v_string == NULL ? FAIL : OK; -} - -/* * Free a list item, unless it was allocated together with the list itself. * Does not clear the value. Does not notify watchers. */ @@ -876,9 +854,7 @@ list_assign_range( long idx; type_T *member_type = NULL; - /* - * Check whether any of the list items is locked before making any changes. - */ + // Check whether any of the list items is locked before making any changes. idx = idx1; dest_li = first_li; for (src_li = src->lv_first; src_li != NULL && dest_li != NULL; ) @@ -896,9 +872,7 @@ list_assign_range( && dest->lv_type->tt_member != NULL) member_type = dest->lv_type->tt_member; - /* - * Assign the List values to the list items. - */ + // Assign the List values to the list items. idx = idx1; dest_li = first_li; for (src_li = src->lv_first; src_li != NULL; ) @@ -1725,6 +1699,10 @@ f_list2str(typval_T *argvars, typval_T *rettv) rettv->vval.v_string = ga.ga_data; } +/* + * Remove item argvars[1] from List argvars[0]. If argvars[2] is supplied, then + * remove the range of items from argvars[1] to argvars[2] (inclusive). + */ static void list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) { @@ -1733,6 +1711,9 @@ list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) listitem_T *li; int error = FALSE; long idx; + long end; + int cnt = 0; + list_T *rl; if ((l = argvars[0].vval.v_list) == NULL || value_check_lock(l->lv_lock, arg_errmsg, TRUE)) @@ -1740,75 +1721,76 @@ list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) 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) + return; // type error: do nothing, errmsg already given + + if ((item = list_find(l, idx)) == NULL) + { semsg(_(e_listidx), idx); - else + return; + } + + if (argvars[2].v_type == VAR_UNKNOWN) { - if (argvars[2].v_type == VAR_UNKNOWN) - { - // Remove one item, return its value. - vimlist_remove(l, item, item); - *rettv = item->li_tv; - list_free_item(l, item); - } - else - { - // Remove range of items, return list with values. - long end = (long)tv_get_number_chk(&argvars[2], &error); + // Remove one item, return its value. + vimlist_remove(l, item, item); + *rettv = item->li_tv; + list_free_item(l, item); + return; + } - if (error) - ; // type error: do nothing - else if ((item2 = list_find(l, end)) == NULL) - semsg(_(e_listidx), end); - else - { - int cnt = 0; + // Remove range of items, return list with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) + return; // type error: do nothing - 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_invalid_range)); - else - { - vimlist_remove(l, item, item2); - if (rettv_list_alloc(rettv) == OK) - { - list_T *rl = rettv->vval.v_list; - - if (l->lv_with_items > 0) - { - // need to copy the list items and move the value - while (item != NULL) - { - li = listitem_alloc(); - if (li == NULL) - return; - li->li_tv = item->li_tv; - init_tv(&item->li_tv); - list_append(rl, li); - if (item == item2) - break; - item = item->li_next; - } - } - else - { - rl->lv_first = item; - rl->lv_u.mat.lv_last = item2; - item->li_prev = NULL; - item2->li_next = NULL; - rl->lv_len = cnt; - } - } - } - } + if ((item2 = list_find(l, end)) == NULL) + { + semsg(_(e_listidx), end); + return; + } + + 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_invalid_range)); + return; + } + + vimlist_remove(l, item, item2); + if (rettv_list_alloc(rettv) != OK) + return; + + rl = rettv->vval.v_list; + + if (l->lv_with_items > 0) + { + // need to copy the list items and move the value + while (item != NULL) + { + li = listitem_alloc(); + if (li == NULL) + return; + li->li_tv = item->li_tv; + init_tv(&item->li_tv); + list_append(rl, li); + if (item == item2) + break; + item = item->li_next; } } + else + { + rl->lv_first = item; + rl->lv_u.mat.lv_last = item2; + item->li_prev = NULL; + item2->li_next = NULL; + rl->lv_len = cnt; + } } static int item_compare(const void *s1, const void *s2); @@ -2101,8 +2083,8 @@ do_uniq(list_T *l, sortinfo_T *info) } /* - * Parse the optional arguments to sort() and uniq() and return the values in - * 'info'. + * Parse the optional arguments supplied to the sort() or uniq() function and + * return the values in "info". */ static int parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info) @@ -2272,17 +2254,11 @@ f_uniq(typval_T *argvars, typval_T *rettv) do_sort_uniq(argvars, rettv, FALSE); } -typedef enum { - FILTERMAP_FILTER, - FILTERMAP_MAP, - FILTERMAP_MAPNEW -} filtermap_T; - /* * Handle one item for map() and filter(). * Sets v:val to "tv". Caller must set v:key. */ - static int + int filter_map_one( typval_T *tv, // original value typval_T *expr, // callback @@ -2320,254 +2296,11 @@ theend: } /* - * Implementation of map() and filter() for a Dict. + * Implementation of map() and filter() for a List. Apply "expr" to every item + * in List "l" and return the result in "rettv". */ static void -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) -{ - int prev_lock; - dict_T *d_ret = NULL; - hashtab_T *ht; - hashitem_T *hi; - dictitem_T *di; - int todo; - int rem; - - if (filtermap == FILTERMAP_MAPNEW) - { - rettv->v_type = VAR_DICT; - rettv->vval.v_dict = NULL; - } - if (d == NULL - || (filtermap == FILTERMAP_FILTER - && value_check_lock(d->dv_lock, arg_errmsg, TRUE))) - return; - - prev_lock = d->dv_lock; - - if (filtermap == FILTERMAP_MAPNEW) - { - if (rettv_dict_alloc(rettv) == FAIL) - return; - d_ret = rettv->vval.v_dict; - } - - if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0) - d->dv_lock = VAR_LOCKED; - ht = &d->dv_hashtab; - hash_lock(ht); - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - int r; - typval_T newtv; - - --todo; - di = HI2DI(hi); - if (filtermap == FILTERMAP_MAP - && (value_check_lock(di->di_tv.v_lock, - arg_errmsg, TRUE) - || var_check_ro(di->di_flags, - arg_errmsg, TRUE))) - break; - set_vim_var_string(VV_KEY, di->di_key, -1); - newtv.v_type = VAR_UNKNOWN; - r = filter_map_one(&di->di_tv, expr, filtermap, - &newtv, &rem); - clear_tv(get_vim_var_tv(VV_KEY)); - if (r == FAIL || did_emsg) - { - clear_tv(&newtv); - break; - } - if (filtermap == FILTERMAP_MAP) - { - if (argtype != NULL && check_typval_arg_type( - argtype->tt_member, &newtv, - func_name, 0) == FAIL) - { - clear_tv(&newtv); - break; - } - // map(): replace the dict item value - clear_tv(&di->di_tv); - newtv.v_lock = 0; - di->di_tv = newtv; - } - else if (filtermap == FILTERMAP_MAPNEW) - { - // mapnew(): add the item value to the new dict - r = dict_add_tv(d_ret, (char *)di->di_key, &newtv); - clear_tv(&newtv); - if (r == FAIL) - break; - } - else if (filtermap == FILTERMAP_FILTER && rem) - { - // filter(false): remove the item from the dict - if (var_check_fixed(di->di_flags, arg_errmsg, TRUE) - || var_check_ro(di->di_flags, arg_errmsg, TRUE)) - break; - dictitem_remove(d, di); - } - } - } - hash_unlock(ht); - d->dv_lock = prev_lock; -} - -/* - * 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; - 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) - return; - b_ret = rettv->vval.v_blob; - } - - // set_vim_var_nr() doesn't set the type - set_vim_var_type(VV_KEY, VAR_NUMBER); - - for (i = 0; i < b->bv_ga.ga_len; i++) - { - typval_T newtv; - - tv.v_type = VAR_NUMBER; - val = blob_get(b, i); - tv.vval.v_number = val; - set_vim_var_nr(VV_KEY, idx); - if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL - || did_emsg) - break; - if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) - { - clear_tv(&newtv); - emsg(_(e_invalblob)); - break; - } - if (filtermap != FILTERMAP_FILTER) - { - if (newtv.vval.v_number != val) - blob_set(b_ret, i, newtv.vval.v_number); - } - else if (rem) - { - 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); - --b->bv_ga.ga_len; - --i; - } - ++idx; - } -} - -/* - * 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 = 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 = str; *p != NUL; p += len) - { - typval_T newtv; - - if (tv_get_first_char(p, &tv) == FAIL) - break; - len = (int)STRLEN(tv.vval.v_string); - - set_vim_var_nr(VV_KEY, idx); - if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL - || did_emsg) - break; - if (did_emsg) - { - clear_tv(&newtv); - clear_tv(&tv); - break; - } - else if (filtermap != FILTERMAP_FILTER) - { - if (newtv.v_type != VAR_STRING) - { - clear_tv(&newtv); - clear_tv(&tv); - emsg(_(e_stringreq)); - break; - } - else - ga_concat(&ga, newtv.vval.v_string); - } - else if (!rem) - ga_concat(&ga, tv.vval.v_string); - - clear_tv(&newtv); - clear_tv(&tv); - - ++idx; - } - ga_append(&ga, NUL); - rettv->vval.v_string = ga.ga_data; -} - -/* - * Implementation of map() and filter() for a List. - */ - static void -filter_map_list( +list_filter_map( list_T *l, filtermap_T filtermap, type_T *argtype, @@ -2775,15 +2508,15 @@ filter_map(typva |