Mercurial > vim
diff src/list.c @ 17964:6d4d3bce365d v8.1.1978
patch 8.1.1978: the eval.c file is too big
Commit: https://github.com/vim/vim/commit/1e1d30048e722906a13665bd6c3c24c87eb2fe25
Author: Bram Moolenaar <Bram@vim.org>
Date: Wed Sep 4 14:41:14 2019 +0200
patch 8.1.1978: the eval.c file is too big
Problem: The eval.c file is too big.
Solution: Move filter() and map() to list.c.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Wed, 04 Sep 2019 14:45:04 +0200 |
parents | 0f7ae8010787 |
children | 684a15da9929 |
line wrap: on
line diff
--- a/src/list.c +++ b/src/list.c @@ -1547,4 +1547,220 @@ f_uniq(typval_T *argvars, typval_T *rett do_sort_uniq(argvars, rettv, FALSE); } -#endif /* defined(FEAT_EVAL) */ +/* + * Handle one item for map() and filter(). + */ + static int +filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) +{ + typval_T rettv; + typval_T argv[3]; + int retval = FAIL; + + copy_tv(tv, get_vim_var_tv(VV_VAL)); + argv[0] = *get_vim_var_tv(VV_KEY); + argv[1] = *get_vim_var_tv(VV_VAL); + if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL) + goto theend; + if (map) + { + // map(): replace the list item value + clear_tv(tv); + rettv.v_lock = 0; + *tv = rettv; + } + else + { + int error = FALSE; + + // filter(): when expr is zero remove the item + *remp = (tv_get_number_chk(&rettv, &error) == 0); + clear_tv(&rettv); + // On type error, nothing has been removed; return FAIL to stop the + // loop. The error message was given by tv_get_number_chk(). + if (error) + goto theend; + } + retval = OK; +theend: + clear_tv(get_vim_var_tv(VV_VAL)); + return retval; +} + +/* + * Implementation of map() and filter(). + */ + static void +filter_map(typval_T *argvars, typval_T *rettv, int map) +{ + typval_T *expr; + listitem_T *li, *nli; + list_T *l = NULL; + dictitem_T *di; + hashtab_T *ht; + hashitem_T *hi; + dict_T *d = NULL; + blob_T *b = NULL; + int rem; + int todo; + char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); + char_u *arg_errmsg = (char_u *)(map ? N_("map() argument") + : N_("filter() argument")); + int save_did_emsg; + int idx = 0; + + if (argvars[0].v_type == VAR_BLOB) + { + if ((b = argvars[0].vval.v_blob) == NULL) + return; + } + else if (argvars[0].v_type == VAR_LIST) + { + if ((l = argvars[0].vval.v_list) == NULL + || (!map && var_check_lock(l->lv_lock, arg_errmsg, TRUE))) + return; + } + else if (argvars[0].v_type == VAR_DICT) + { + if ((d = argvars[0].vval.v_dict) == NULL + || (!map && var_check_lock(d->dv_lock, arg_errmsg, TRUE))) + return; + } + else + { + semsg(_(e_listdictarg), ermsg); + return; + } + + expr = &argvars[1]; + // On type errors, the preceding call has already displayed an error + // message. Avoid a misleading error message for an empty string that + // was not passed as argument. + if (expr->v_type != VAR_UNKNOWN) + { + typval_T save_val; + typval_T save_key; + + prepare_vimvar(VV_VAL, &save_val); + prepare_vimvar(VV_KEY, &save_key); + + // We reset "did_emsg" to be able to detect whether an error + // occurred during evaluation of the expression. + save_did_emsg = did_emsg; + did_emsg = FALSE; + + if (argvars[0].v_type == VAR_DICT) + { + 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; + + --todo; + di = HI2DI(hi); + if (map && (var_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); + r = filter_map_one(&di->di_tv, expr, map, &rem); + clear_tv(get_vim_var_tv(VV_KEY)); + if (r == FAIL || did_emsg) + break; + if (!map && rem) + { + 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); + } + else if (argvars[0].v_type == VAR_BLOB) + { + int i; + typval_T tv; + + // 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++) + { + tv.v_type = VAR_NUMBER; + tv.vval.v_number = blob_get(b, i); + set_vim_var_nr(VV_KEY, idx); + if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) + break; + if (tv.v_type != VAR_NUMBER) + { + emsg(_(e_invalblob)); + break; + } + tv.v_type = VAR_NUMBER; + blob_set(b, i, tv.vval.v_number); + if (!map && rem) + { + char_u *p = (char_u *)argvars[0].vval.v_blob->bv_ga.ga_data; + + mch_memmove(p + idx, p + i + 1, + (size_t)b->bv_ga.ga_len - i - 1); + --b->bv_ga.ga_len; + --i; + } + } + } + else // argvars[0].v_type == VAR_LIST + { + // set_vim_var_nr() doesn't set the type + set_vim_var_type(VV_KEY, VAR_NUMBER); + + for (li = l->lv_first; li != NULL; li = nli) + { + if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) + break; + nli = li->li_next; + set_vim_var_nr(VV_KEY, idx); + if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL + || did_emsg) + break; + if (!map && rem) + listitem_remove(l, li); + ++idx; + } + } + + restore_vimvar(VV_KEY, &save_key); + restore_vimvar(VV_VAL, &save_val); + + did_emsg |= save_did_emsg; + } + + copy_tv(&argvars[0], rettv); +} + +/* + * "filter()" function + */ + void +f_filter(typval_T *argvars, typval_T *rettv) +{ + filter_map(argvars, rettv, FALSE); +} + +/* + * "map()" function + */ + void +f_map(typval_T *argvars, typval_T *rettv) +{ + filter_map(argvars, rettv, TRUE); +} + +#endif // defined(FEAT_EVAL)