# HG changeset patch # User Bram Moolenaar # Date 1550007008 -3600 # Node ID 05d836c8f1c44c3ad959d15a084764443c7f7b01 # Parent 9b7a86acea77e45538c723c2aae54109691d69c0 patch 8.1.0902: incomplete set of assignment operators commit https://github.com/vim/vim/commit/ff697e6cef8ced7717a21fd525ab3200b2f1724f Author: Bram Moolenaar Date: Tue Feb 12 22:28:33 2019 +0100 patch 8.1.0902: incomplete set of assignment operators Problem: Incomplete set of assignment operators. Solution: Add /=, *= and %=. (Ozaki Kiichi, closes https://github.com/vim/vim/issues/3931) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -10836,9 +10836,13 @@ 7. Commands *expression-commands* When the selected range of items is partly past the end of the list, items will be added. - *:let+=* *:let-=* *:let.=* *E734* + *:let+=* *:let-=* *:letstar=* + *:let/=* *:let%=* *:let.=* *E734* :let {var} += {expr1} Like ":let {var} = {var} + {expr1}". :let {var} -= {expr1} Like ":let {var} = {var} - {expr1}". +:let {var} *= {expr1} Like ":let {var} = {var} * {expr1}". +:let {var} /= {expr1} Like ":let {var} = {var} / {expr1}". +:let {var} %= {expr1} Like ":let {var} = {var} % {expr1}". :let {var} .= {expr1} Like ":let {var} = {var} . {expr1}". These fail if {var} was not set yet and when the type of {var} and {expr1} don't fit the operator. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -1197,6 +1197,9 @@ eval_foldexpr(char_u *arg, int *cp) * ":let var = expr" assignment command. * ":let var += expr" assignment command. * ":let var -= expr" assignment command. + * ":let var *= expr" assignment command. + * ":let var /= expr" assignment command. + * ":let var %= expr" assignment command. * ":let var .= expr" assignment command. * ":let [var1, var2] = expr" unpack list. */ @@ -1216,10 +1219,10 @@ ex_let(exarg_T *eap) argend = skip_var_list(arg, &var_count, &semicolon); if (argend == NULL) return; - if (argend > arg && argend[-1] == '.') /* for var.='str' */ + if (argend > arg && argend[-1] == '.') // for var.='str' --argend; expr = skipwhite(argend); - if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL + if (*expr != '=' && !(vim_strchr((char_u *)"+-*/%.", *expr) != NULL && expr[1] == '=')) { /* @@ -1249,8 +1252,8 @@ ex_let(exarg_T *eap) op[1] = NUL; if (*expr != '=') { - if (vim_strchr((char_u *)"+-.", *expr) != NULL) - op[0] = *expr; /* +=, -= or .= */ + if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL) + op[0] = *expr; // +=, -=, *=, /=, %= or .= expr = skipwhite(expr + 2); } else @@ -1671,7 +1674,7 @@ ex_let_one( semsg(_(e_invarg2), name - 1); else { - if (op != NULL && (*op == '+' || *op == '-')) + if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) semsg(_(e_letwrong), op); else if (endchars != NULL && vim_strchr(endchars, *skipwhite(arg)) == NULL) @@ -1744,18 +1747,22 @@ ex_let_one( || (opt_type == 0 && *op != '.')) { semsg(_(e_letwrong), op); - s = NULL; /* don't set the value */ + s = NULL; // don't set the value } else { - if (opt_type == 1) /* number */ + if (opt_type == 1) // number { - if (*op == '+') - n = numval + n; - else - n = numval - n; + switch (*op) + { + 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; + } } - else if (opt_type == 0 && stringval != NULL) /* string */ + else if (opt_type == 0 && stringval != NULL) // string { s = concat_str(stringval, s); vim_free(stringval); @@ -1779,7 +1786,7 @@ ex_let_one( else if (*arg == '@') { ++arg; - if (op != NULL && (*op == '+' || *op == '-')) + if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) semsg(_(e_letwrong), op); else if (endchars != NULL && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) @@ -2254,7 +2261,8 @@ clear_lval(lval_T *lp) /* * Set a variable that was parsed by get_lval() to "rettv". * "endp" points to just after the parsed name. - * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=". + * "op" is NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", + * "%" for "%=", "." for ".=" or "=" for "=". */ static void set_var_lval( @@ -2327,7 +2335,7 @@ set_var_lval( { typval_T tv; - /* handle +=, -= and .= */ + // handle +=, -=, *=, /=, %= and .= di = NULL; if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), &tv, &di, TRUE, FALSE) == OK) @@ -2448,7 +2456,8 @@ set_var_lval( } /* - * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2" + * Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2" + * and "tv1 .= tv2" * Returns OK or FAIL. */ static int @@ -2490,7 +2499,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char case VAR_LIST: if (*op != '+' || tv2->v_type != VAR_LIST) break; - /* List += List */ + // List += List if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); return OK; @@ -2499,19 +2508,24 @@ tv_op(typval_T *tv1, typval_T *tv2, char case VAR_STRING: if (tv2->v_type == VAR_LIST) break; - if (*op == '+' || *op == '-') - { - /* nr += nr or nr -= nr*/ + if (vim_strchr((char_u *)"+-*/%", *op) != NULL) + { + // nr += nr , nr -= nr , nr *=nr , nr /= nr , nr %= nr n = tv_get_number(tv1); #ifdef FEAT_FLOAT if (tv2->v_type == VAR_FLOAT) { float_T f = n; - if (*op == '+') - f += tv2->vval.v_float; - else - f -= tv2->vval.v_float; + if (*op == '%') + break; + switch (*op) + { + case '+': f += tv2->vval.v_float; break; + case '-': f -= tv2->vval.v_float; break; + case '*': f *= tv2->vval.v_float; break; + case '/': f /= tv2->vval.v_float; break; + } clear_tv(tv1); tv1->v_type = VAR_FLOAT; tv1->vval.v_float = f; @@ -2519,10 +2533,14 @@ tv_op(typval_T *tv1, typval_T *tv2, char else #endif { - if (*op == '+') - n += tv_get_number(tv2); - else - n -= tv_get_number(tv2); + switch (*op) + { + 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; + } clear_tv(tv1); tv1->v_type = VAR_NUMBER; tv1->vval.v_number = n; @@ -2533,7 +2551,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char if (tv2->v_type == VAR_FLOAT) break; - /* str .= str */ + // str .= str s = tv_get_string(tv1); s = concat_str(s, tv_get_string_buf(tv2, numbuf)); clear_tv(tv1); @@ -2547,7 +2565,8 @@ tv_op(typval_T *tv1, typval_T *tv2, char { float_T f; - if (*op == '.' || (tv2->v_type != VAR_FLOAT + if (*op == '%' || *op == '.' + || (tv2->v_type != VAR_FLOAT && tv2->v_type != VAR_NUMBER && tv2->v_type != VAR_STRING)) break; @@ -2555,10 +2574,13 @@ tv_op(typval_T *tv1, typval_T *tv2, char f = tv2->vval.v_float; else f = tv_get_number(tv2); - if (*op == '+') - tv1->vval.v_float += f; - else - tv1->vval.v_float -= f; + switch (*op) + { + case '+': tv1->vval.v_float += f; break; + case '-': tv1->vval.v_float -= f; break; + case '*': tv1->vval.v_float *= f; break; + case '/': tv1->vval.v_float /= f; break; + } } #endif return OK; diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -1441,6 +1441,84 @@ func Test_script_local_func() enew! | close endfunc +func Test_compound_assignment_operators() + " Test for number + let x = 1 + let x += 10 + call assert_equal(11, x) + let x -= 5 + call assert_equal(6, x) + let x *= 4 + call assert_equal(24, x) + let x /= 3 + call assert_equal(8, x) + let x %= 3 + call assert_equal(2, x) + let x .= 'n' + call assert_equal('2n', x) + + " Test for string + let x = 'str' + let x .= 'ing' + call assert_equal('string', x) + let x += 1 + call assert_equal(1, x) + let x -= 1.5 + call assert_equal(-0.5, x) + + if has('float') + " Test for float + let x = 0.5 + let x += 4.5 + call assert_equal(5.0, x) + let x -= 1.5 + call assert_equal(3.5, x) + let x *= 3.0 + call assert_equal(10.5, x) + let x /= 2.5 + call assert_equal(4.2, x) + call assert_fails('let x %= 0.5', 'E734') + call assert_fails('let x .= "f"', 'E734') + endif + + " Test for environment variable + let $FOO = 1 + call assert_fails('let $FOO += 1', 'E734') + call assert_fails('let $FOO -= 1', 'E734') + call assert_fails('let $FOO *= 1', 'E734') + call assert_fails('let $FOO /= 1', 'E734') + call assert_fails('let $FOO %= 1', 'E734') + let $FOO .= 's' + call assert_equal('1s', $FOO) + unlet $FOO + + " Test for option variable (type: number) + let &scrolljump = 1 + let &scrolljump += 5 + call assert_equal(6, &scrolljump) + let &scrolljump -= 2 + call assert_equal(4, &scrolljump) + let &scrolljump *= 3 + call assert_equal(12, &scrolljump) + let &scrolljump /= 2 + call assert_equal(6, &scrolljump) + let &scrolljump %= 5 + call assert_equal(1, &scrolljump) + call assert_fails('let &scrolljump .= "j"', 'E734') + set scrolljump&vim + + " Test for register + let @/ = 1 + call assert_fails('let @/ += 1', 'E734') + call assert_fails('let @/ -= 1', 'E734') + call assert_fails('let @/ *= 1', 'E734') + call assert_fails('let @/ /= 1', 'E734') + call assert_fails('let @/ %= 1', 'E734') + let @/ .= 's' + call assert_equal('1s', @/) + let @/ = '' +endfunc + "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=4 tw=80 fdm=marker diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -784,6 +784,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 902, +/**/ 901, /**/ 900,