aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Dewar <seandewar@users.noreply.github.com>2021-12-06 20:50:29 +0000
committerzeertzjq <zeertzjq@outlook.com>2022-01-31 17:38:45 +0800
commitd746f5aa418f86828aef689a2c4f8d5b53c9f7de (patch)
tree7367709069bdea893e58ba979e5a2e68e00883e1 /src
parent2870311a37314135b73e0ee54b41da86c9540a39 (diff)
downloadrneovim-d746f5aa418f86828aef689a2c4f8d5b53c9f7de.tar.gz
rneovim-d746f5aa418f86828aef689a2c4f8d5b53c9f7de.tar.bz2
rneovim-d746f5aa418f86828aef689a2c4f8d5b53c9f7de.zip
feat(eval): partially port v8.2.0878
Problem: No reduce() function. Solution: Add a reduce() function. (closes vim/vim#5481) https://github.com/vim/vim/commit/85629985b71035608a37ba3bde86968481490d46 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.
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/funcs.c85
-rw-r--r--src/nvim/testdir/test_listdict.vim31
3 files changed, 117 insertions, 0 deletions
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 18967b80f2..e00a14fca7 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -276,6 +276,7 @@ return {
range={args={1, 3}, base=1},
readdir={args={1, 2}, base=1},
readfile={args={1, 3}, base=1},
+ reduce={args={2, 3}, base=1},
reg_executing={},
reg_recording={},
reg_recorded={},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 29619f62e9..1a5f6e08bc 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -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
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index f6c404d390..42b46fc76c 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -620,6 +620,37 @@ func Test_reverse_sort_uniq()
call assert_fails('call reverse("")', 'E899:')
endfunc
+" reduce a list or a blob
+func Test_reduce()
+ call assert_equal(1, reduce([], { acc, val -> acc + val }, 1))
+ call assert_equal(10, reduce([1, 3, 5], { acc, val -> acc + val }, 1))
+ call assert_equal(2 * (2 * ((2 * 1) + 2) + 3) + 4, reduce([2, 3, 4], { acc, val -> 2 * acc + val }, 1))
+ call assert_equal('a x y z', ['x', 'y', 'z']->reduce({ acc, val -> acc .. ' ' .. val}, 'a'))
+ call assert_equal(#{ x: 1, y: 1, z: 1 }, ['x', 'y', 'z']->reduce({ acc, val -> extend(acc, { val: 1 }) }, {}))
+ call assert_equal([0, 1, 2, 3], reduce([1, 2, 3], function('add'), [0]))
+
+ let l = ['x', 'y', 'z']
+ call assert_equal(42, reduce(l, function('get'), #{ x: #{ y: #{ z: 42 } } }))
+ call assert_equal(['x', 'y', 'z'], l)
+
+ call assert_equal(1, reduce([1], { acc, val -> acc + val }))
+ call assert_equal('x y z', reduce(['x', 'y', 'z'], { acc, val -> acc .. ' ' .. val }))
+ call assert_equal(120, range(1, 5)->reduce({ acc, val -> acc * val }))
+ call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value')
+
+ call assert_equal(1, reduce(0z, { acc, val -> acc + val }, 1))
+ call assert_equal(1 + 0xaf + 0xbf + 0xcf, reduce(0zAFBFCF, { acc, val -> acc + val }, 1))
+ call assert_equal(2 * (2 * 1 + 0xaf) + 0xbf, 0zAFBF->reduce({ acc, val -> 2 * acc + val }, 1))
+
+ call assert_equal(0xff, reduce(0zff, { acc, val -> acc + val }))
+ call assert_equal(2 * (2 * 0xaf + 0xbf) + 0xcf, reduce(0zAFBFCF, { acc, val -> 2 * acc + val }))
+ call assert_fails("call reduce(0z, { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value')
+
+ call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:')
+ call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:')
+ call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:')
+endfunc
+
" splitting a string to a List
func Test_str_split()
call assert_equal(['aa', 'bb'], split(' aa bb '))