diff options
-rw-r--r-- | src/nvim/eval.c | 63 | ||||
-rw-r--r-- | src/nvim/eval/executor.c | 4 | ||||
-rw-r--r-- | src/nvim/testdir/test_getvar.vim | 44 | ||||
-rw-r--r-- | src/nvim/testdir/test_partial.vim | 5 | ||||
-rw-r--r-- | src/nvim/testdir/test_vimscript.vim | 39 |
5 files changed, 132 insertions, 23 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 225c7c615b..38df22a2cb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -526,6 +526,35 @@ const list_T *eval_msgpack_type_lists[] = { [kMPExt] = NULL, }; +// Return "n1" divided by "n2", taking care of dividing by zero. +varnumber_T num_divide(varnumber_T n1, varnumber_T n2) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + varnumber_T result; + + if (n2 == 0) { // give an error message? + if (n1 == 0) { + result = VARNUMBER_MIN; // similar to NaN + } else if (n1 < 0) { + result = -VARNUMBER_MAX; + } else { + result = VARNUMBER_MAX; + } + } else { + result = n1 / n2; + } + + return result; +} + +// Return "n1" modulus "n2", taking care of dividing by zero. +varnumber_T num_modulus(varnumber_T n1, varnumber_T n2) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Give an error when n2 is 0? + return (n2 == 0) ? 0 : (n1 % n2); +} + /* * Initialize the global and v: variables. */ @@ -2047,8 +2076,8 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, case '+': n = numval + n; break; case '-': n = numval - n; break; case '*': n = numval * n; break; - case '/': n = numval / n; break; - case '%': n = numval % n; break; + case '/': n = num_divide(numval, n); break; + case '%': n = num_modulus(numval, n); break; } } else if (opt_type == 0 && stringval != NULL) { // string char *const oldstringval = stringval; @@ -4178,22 +4207,9 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string) if (op == '*') { n1 = n1 * n2; } else if (op == '/') { - if (n2 == 0) { // give an error message? - if (n1 == 0) { - n1 = VARNUMBER_MIN; // similar to NaN - } else if (n1 < 0) { - n1 = -VARNUMBER_MAX; - } else { - n1 = VARNUMBER_MAX; - } - } else { - n1 = n1 / n2; - } + n1 = num_divide(n1, n2); } else { - if (n2 == 0) /* give an error message? */ - n1 = 0; - else - n1 = n1 % n2; + n1 = num_modulus(n1, n2); } rettv->v_type = VAR_NUMBER; rettv->vval.v_number = n1; @@ -9600,6 +9616,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; dict_T *d; typval_T *tv = NULL; + bool what_is_dict = false; if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL) { @@ -9641,7 +9658,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) func_ref(rettv->vval.v_string); } } else if (strcmp(what, "dict") == 0) { - tv_dict_set_ret(rettv, pt->pt_dict); + what_is_dict = true; + if (pt->pt_dict != NULL) { + tv_dict_set_ret(rettv, pt->pt_dict); + } } else if (strcmp(what, "args") == 0) { rettv->v_type = VAR_LIST; if (tv_list_alloc_ret(rettv, pt->pt_argc) != NULL) { @@ -9652,7 +9672,12 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { EMSG2(_(e_invarg2), what); } - return; + + // When {what} == "dict" and pt->pt_dict == NULL, evaluate the + // third argument + if (!what_is_dict) { + return; + } } } else { EMSG2(_(e_listdictarg), "get()"); diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index e972c506dd..8cd21f8d62 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -74,8 +74,8 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, case '+': n += tv_get_number(tv2); break; case '-': n -= tv_get_number(tv2); break; case '*': n *= tv_get_number(tv2); break; - case '/': n /= tv_get_number(tv2); break; - case '%': n %= tv_get_number(tv2); break; + case '/': n = num_divide(n, tv_get_number(tv2)); break; + case '%': n = num_modulus(n, tv_get_number(tv2)); break; } tv_clear(tv1); tv1->v_type = VAR_NUMBER; diff --git a/src/nvim/testdir/test_getvar.vim b/src/nvim/testdir/test_getvar.vim index d6b6b69aa8..3b61d68ebc 100644 --- a/src/nvim/testdir/test_getvar.vim +++ b/src/nvim/testdir/test_getvar.vim @@ -1,4 +1,5 @@ -" Tests for getwinvar(), gettabvar() and gettabwinvar(). +" Tests for getwinvar(), gettabvar(), gettabwinvar() and get(). + func Test_var() " Use strings to test for memory leaks. First, check that in an empty " window, gettabvar() returns the correct value @@ -102,3 +103,44 @@ func Test_gettabvar_in_tabline() close redrawstatus! endfunc + +" Test get() function using default value. + +" get({dict}, {key} [, {default}]) +func Test_get_dict() + let d = {'foo': 42} + call assert_equal(42, get(d, 'foo', 99)) + call assert_equal(999, get(d, 'bar', 999)) +endfunc + +" get({list}, {idx} [, {default}]) +func Test_get_list() + let l = [1,2,3] + call assert_equal(1, get(l, 0, 999)) + call assert_equal(3, get(l, -1, 999)) + call assert_equal(999, get(l, 3, 999)) +endfunc + +" get({blob}, {idx} [, {default}]) - in test_blob.vim + +" get({lambda}, {what} [, {default}]) +func Test_get_lambda() + let l:L = {-> 42} + call assert_match('^<lambda>', get(l:L, 'name')) + call assert_equal(l:L, get(l:L, 'func')) + call assert_equal({'lambda has': 'no dict'}, get(l:L, 'dict', {'lambda has': 'no dict'})) + call assert_equal(0, get(l:L, 'dict')) + call assert_equal([], get(l:L, 'args')) +endfunc + +" get({func}, {what} [, {default}]) +func Test_get_func() + let l:F = function('tr') + call assert_equal('tr', get(l:F, 'name')) + call assert_equal(l:F, get(l:F, 'func')) + call assert_equal({'func has': 'no dict'}, get(l:F, 'dict', {'func has': 'no dict'})) + call assert_equal(0, get(l:F, 'dict')) + call assert_equal([], get(l:F, 'args')) +endfunc + +" get({partial}, {what} [, {default}]) - in test_partial.vim diff --git a/src/nvim/testdir/test_partial.vim b/src/nvim/testdir/test_partial.vim index de5c26c2dd..590e18e024 100644 --- a/src/nvim/testdir/test_partial.vim +++ b/src/nvim/testdir/test_partial.vim @@ -285,6 +285,11 @@ func Test_get_partial_items() call assert_equal('MyDictFunc', get(Func, 'name')) call assert_equal([], get(Func, 'args')) call assert_true(empty( get(Func, 'dict'))) + + let P = function('substitute', ['hello there', 'there']) + let dict = {'partial has': 'no dict'} + call assert_equal(dict, get(P, 'dict', dict)) + call assert_equal(0, get(l:P, 'dict')) endfunc func Test_compare_partials() diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index f3e40e1210..f39e53d6dd 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -21,7 +21,7 @@ com! -nargs=1 Xout call Xout(<args>) " " Create a script that consists of the body of the function a:funcname. " Replace any ":return" by a ":finish", any argument variable by a global -" variable, and and every ":call" by a ":source" for the next following argument +" variable, and every ":call" by a ":source" for the next following argument " in the variable argument list. This function is useful if similar tests are " to be made for a ":return" from a function call or a ":finish" in a script " file. @@ -1310,6 +1310,43 @@ func Test_compound_assignment_operators() let x .= 'n' call assert_equal('2n', x) + " Test special cases: division or modulus with 0. + let x = 1 + let x /= 0 + if has('num64') + call assert_equal(0x7FFFFFFFFFFFFFFF, x) + else + call assert_equal(0x7fffffff, x) + endif + + let x = -1 + let x /= 0 + if has('num64') + call assert_equal(-0x7FFFFFFFFFFFFFFF, x) + else + call assert_equal(-0x7fffffff, x) + endif + + let x = 0 + let x /= 0 + if has('num64') + call assert_equal(-0x7FFFFFFFFFFFFFFF - 1, x) + else + call assert_equal(-0x7FFFFFFF - 1, x) + endif + + let x = 1 + let x %= 0 + call assert_equal(0, x) + + let x = -1 + let x %= 0 + call assert_equal(0, x) + + let x = 0 + let x %= 0 + call assert_equal(0, x) + " Test for string let x = 'str' let x .= 'ing' |