# HG changeset patch # User Bram Moolenaar # Date 1653243303 -7200 # Node ID abdea69d21b625ac0f1661eeab05a313e10452f4 # Parent 789ff659637a9759c7c1e7111422f3772f1f77cd patch 8.2.5003: cannot do bitwise shifts Commit: https://github.com/vim/vim/commit/a061f34191712df7dde7716705fe0ec074e9758e Author: Yegappan Lakshmanan Date: Sun May 22 19:13:49 2022 +0100 patch 8.2.5003: cannot do bitwise shifts Problem: Cannot do bitwise shifts. Solution: Add the >> and << operators. (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/8457) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -868,33 +868,36 @@ Expression syntax summary, from least to expr5 isnot expr5 different |List|, |Dictionary| or |Blob| instance -|expr5| expr6 - expr6 + expr6 ... number addition, list or blob concatenation - expr6 - expr6 ... number subtraction - expr6 . expr6 ... string concatenation - expr6 .. expr6 ... string concatenation +|expr5| expr6 << expr6 bitwise left shift + expr6 >> expr6 bitwise right shift |expr6| expr7 - expr7 * expr7 ... number multiplication - expr7 / expr7 ... number division - expr7 % expr7 ... number modulo + expr7 + expr7 ... number addition, list or blob concatenation + expr7 - expr7 ... number subtraction + expr7 . expr7 ... string concatenation + expr7 .. expr7 ... string concatenation |expr7| expr8 - expr8 type check and conversion (|Vim9| only) + expr8 * expr8 ... number multiplication + expr8 / expr8 ... number division + expr8 % expr8 ... number modulo |expr8| expr9 - ! expr8 logical NOT - - expr8 unary minus - + expr8 unary plus + expr9 type check and conversion (|Vim9| only) |expr9| expr10 - expr9[expr1] byte of a String or item of a |List| - expr9[expr1 : expr1] substring of a String or sublist of a |List| - expr9.name entry in a |Dictionary| - expr9(expr1, ...) function call with |Funcref| variable - expr9->name(expr1, ...) |method| call - -|expr10| number number constant + ! expr9 logical NOT + - expr9 unary minus + + expr9 unary plus + +|expr10| expr11 + expr10[expr1] byte of a String or item of a |List| + expr10[expr1 : expr1] substring of a String or sublist of a |List| + expr10.name entry in a |Dictionary| + expr10(expr1, ...) function call with |Funcref| variable + expr10->name(expr1, ...) |method| call + +|expr11| number number constant "string" string constant, backslash is special 'string' string constant, ' is doubled [expr1, ...] |List| @@ -1128,14 +1131,26 @@ can be matched like an ordinary characte "foo\nbar" =~ "\\n" evaluates to 0 -expr5 and expr6 *expr5* *expr6* *E1036* *E1051* +expr5 *expr5* *bitwise-shift* +----- +expr6 << expr6 bitwise left shift *expr-<<* +expr6 >> expr6 bitwise right shift *expr->>* + *E1282* *E1283* +The "<<" and ">>" operators can be used to perform bitwise left or right shift +of the left operand by the number of bits specified by the right operand. The +operands must be positive numbers. The topmost bit (sign bit) is always +cleared for ">>". If the right operand (shift amount) is more than the +maximum number of bits in a number (|v:numbersize|) the result is zero. + + +expr6 and expr7 *expr6* *expr7* *E1036* *E1051* --------------- -expr6 + expr6 Number addition, |List| or |Blob| concatenation *expr-+* -expr6 - expr6 Number subtraction *expr--* -expr6 . expr6 String concatenation *expr-.* -expr6 .. expr6 String concatenation *expr-..* - -For |Lists| only "+" is possible and then both expr6 must be a list. The +expr7 + expr7 Number addition, |List| or |Blob| concatenation *expr-+* +expr7 - expr7 Number subtraction *expr--* +expr7 . expr7 String concatenation *expr-.* +expr7 .. expr7 String concatenation *expr-..* + +For |Lists| only "+" is possible and then both expr7 must be a list. The result is a new list with the two lists Concatenated. For String concatenation ".." is preferred, since "." is ambiguous, it is also @@ -1147,9 +1162,9 @@ In |Vim9| script the arguments of ".." a types: Number, Float, Special and Bool. For other types |string()| should be used. -expr7 * expr7 Number multiplication *expr-star* -expr7 / expr7 Number division *expr-/* -expr7 % expr7 Number modulo *expr-%* +expr8 * expr8 Number multiplication *expr-star* +expr8 / expr8 Number division *expr-/* +expr8 % expr8 Number modulo *expr-%* In legacy script, for all operators except "." and "..", Strings are converted to Numbers. @@ -1191,18 +1206,18 @@ None of these work for |Funcref|s. ".", ".." and "%" do not work for Float. *E804* *E1035* -expr7 *expr7* +expr8 *expr8* ----- -expr8 +expr9 This is only available in |Vim9| script, see |type-casting|. -expr8 *expr8* +expr9 *expr9* ----- -! expr8 logical NOT *expr-!* -- expr8 unary minus *expr-unary--* -+ expr8 unary plus *expr-unary-+* +! expr9 logical NOT *expr-!* +- expr9 unary minus *expr-unary--* ++ expr9 unary plus *expr-unary-+* For '!' |TRUE| becomes |FALSE|, |FALSE| becomes |TRUE| (one). For '-' the sign of the number is changed. @@ -1224,21 +1239,21 @@ These three can be repeated and mixed. --9 == 9 -expr9 *expr9* ------ -This expression is either |expr10| or a sequence of the alternatives below, +expr10 *expr10* +------ +This expression is either |expr11| or a sequence of the alternatives below, in any order. E.g., these are all possible: - expr9[expr1].name - expr9.name[expr1] - expr9(expr1, ...)[expr1].name - expr9->(expr1, ...)[expr1] + expr10[expr1].name + expr10.name[expr1] + expr10(expr1, ...)[expr1].name + expr10->(expr1, ...)[expr1] Evaluation is always from left to right. -expr9[expr1] item of String or |List| *expr-[]* *E111* +expr10[expr1] item of String or |List| *expr-[]* *E111* *E909* *subscript* *E1062* In legacy Vim script: -If expr9 is a Number or String this results in a String that contains the -expr1'th single byte from expr9. expr9 is used as a String (a number is +If expr10 is a Number or String this results in a String that contains the +expr1'th single byte from expr10. expr10 is used as a String (a number is automatically converted to a String), expr1 as a Number. This doesn't recognize multibyte encodings, see `byteidx()` for an alternative, or use `split()` to turn the string into a list of characters. Example, to get the @@ -1246,8 +1261,8 @@ byte under the cursor: > :let c = getline(".")[col(".") - 1] In |Vim9| script: *E1147* *E1148* -If expr9 is a String this results in a String that contains the expr1'th -single character (including any composing characters) from expr9. To use byte +If expr10 is a String this results in a String that contains the expr1'th +single character (including any composing characters) from expr10. To use byte indexes use |strpart()|. Index zero gives the first byte or character. Careful: text column numbers @@ -1258,7 +1273,7 @@ String. A negative index always results compatibility). Use [-1:] to get the last byte or character. In Vim9 script a negative index is used like with a list: count from the end. -If expr9 is a |List| then it results the item at index expr1. See |list-index| +If expr10 is a |List| then it results the item at index expr1. See |list-index| for possible index values. If the index is out of range this results in an error. Example: > :let item = mylist[-1] " get last item @@ -1268,14 +1283,14 @@ Generally, if a |List| index is equal to error. -expr9[expr1a : expr1b] substring or sublist *expr-[:]* - -If expr9 is a String this results in the substring with the bytes or -characters from expr1a to and including expr1b. expr9 is used as a String, +expr10[expr1a : expr1b] substring or sublist *expr-[:]* + +If expr10 is a String this results in the substring with the bytes or +characters from expr1a to and including expr1b. expr10 is used as a String, expr1a and expr1b are used as a Number. In legacy Vim script the indexes are byte indexes. This doesn't recognize -multibyte encodings, see |byteidx()| for computing the indexes. If expr9 is +multibyte encodings, see |byteidx()| for computing the indexes. If expr10 is a Number it is first converted to a String. In Vim9 script the indexes are character indexes and include composing @@ -1302,20 +1317,20 @@ Examples: > :let s = s[:-3] " remove last two bytes < *slice* -If expr9 is a |List| this results in a new |List| with the items indicated by +If expr10 is a |List| this results in a new |List| with the items indicated by the indexes expr1a and expr1b. This works like with a String, as explained just above. Also see |sublist| below. Examples: > :let l = mylist[:3] " first four items :let l = mylist[4:4] " List with one item :let l = mylist[:] " shallow copy of a List -If expr9 is a |Blob| this results in a new |Blob| with the bytes in the +If expr10 is a |Blob| this results in a new |Blob| with the bytes in the indexes expr1a and expr1b, inclusive. Examples: > :let b = 0zDEADBEEF :let bs = b[1:2] " 0zADBE :let bs = b[:] " copy of 0zDEADBEEF -Using expr9[expr1] or expr9[expr1a : expr1b] on a |Funcref| results in an +Using expr10[expr1] or expr10[expr1a : expr1b] on a |Funcref| results in an error. Watch out for confusion between a namespace and a variable followed by a colon @@ -1324,11 +1339,11 @@ for a sublist: > mylist[s:] " uses namespace s:, error! -expr9.name entry in a |Dictionary| *expr-entry* +expr10.name entry in a |Dictionary| *expr-entry* *E1203* *E1229* -If expr9 is a |Dictionary| and it is followed by a dot, then the following +If expr10 is a |Dictionary| and it is followed by a dot, then the following name will be used as a key in the |Dictionary|. This is just like: -expr9[name]. +expr10[name]. The name must consist of alphanumeric characters, just like a variable name, but it may start with a number. Curly braces cannot be used. @@ -1345,17 +1360,17 @@ Note that the dot is also used for Strin always put spaces around the dot for String concatenation. -expr9(expr1, ...) |Funcref| function call *E1085* - -When expr9 is a |Funcref| type variable, invoke the function it refers to. - - -expr9->name([args]) method call *method* *->* -expr9->{lambda}([args]) +expr10(expr1, ...) |Funcref| function call *E1085* + +When expr10 is a |Funcref| type variable, invoke the function it refers to. + + +expr10->name([args]) method call *method* *->* +expr10->{lambda}([args]) *E260* *E276* *E1265* For methods that are also available as global functions this is the same as: > - name(expr9 [, args]) -There can also be methods specifically for the type of "expr9". + name(expr10 [, args]) +There can also be methods specifically for the type of "expr10". This allows for chaining, passing the value that one method returns to the next method: > @@ -1364,7 +1379,7 @@ next method: > Example of using a lambda: > GetPercentage()->{x -> x * 100}()->printf('%d%%') < -When using -> the |expr8| operators will be applied first, thus: > +When using -> the |expr9| operators will be applied first, thus: > -1.234->string() Is equivalent to: > (-1.234)->string() @@ -1393,7 +1408,7 @@ When using the lambda form there must be (. - *expr10* + *expr11* number ------ number number constant *expr-number* diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -3279,3 +3279,9 @@ EXTERN char e_illegal_character_in_word[ #endif EXTERN char e_atom_engine_must_be_at_start_of_pattern[] INIT(= N_("E1281: Atom '\\%%#=%c' must be at the start of the pattern")); +#ifdef FEAT_EVAL +EXTERN char e_bitshift_ops_must_be_number[] + INIT(= N_("E1282: bitshift operands must be numbers")); +EXTERN char e_bitshift_ops_must_be_postive[] + INIT(= N_("E1283: bitshift amount must be a positive number")); +#endif diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -49,10 +49,11 @@ static int eval2(char_u **arg, typval_T static int eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg); static int eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg); static int eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg); -static int eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); -static int eval7t(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); +static int eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg); static int eval7(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); -static int eval7_leader(typval_T *rettv, int numeric_only, char_u *start_leader, char_u **end_leaderp); +static int eval8(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); +static int eval9(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); +static int eval9_leader(typval_T *rettv, int numeric_only, char_u *start_leader, char_u **end_leaderp); static int free_unref_items(int copyID); static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end); @@ -638,7 +639,7 @@ deref_function_name( char_u *name = *arg; ref.v_type = VAR_UNKNOWN; - if (eval7(arg, &ref, evalarg, FALSE) == FAIL) + if (eval9(arg, &ref, evalarg, FALSE) == FAIL) { dictitem_T *v; @@ -2591,7 +2592,7 @@ eval2(char_u **arg, typval_T *rettv, eva int getnext; /* - * Get the first variable. + * Get the first expression. */ if (eval3(arg, rettv, evalarg) == FAIL) return FAIL; @@ -2717,7 +2718,7 @@ eval3(char_u **arg, typval_T *rettv, eva int getnext; /* - * Get the first variable. + * Get the first expression. */ if (eval4(arg, rettv, evalarg) == FAIL) return FAIL; @@ -2856,12 +2857,13 @@ eval4(char_u **arg, typval_T *rettv, eva int type_is = FALSE; /* - * Get the first variable. + * Get the first expression. */ if (eval5(arg, rettv, evalarg) == FAIL) return FAIL; p = eval_next_non_blank(*arg, evalarg, &getnext); + type = get_compare_type(p, &len, &type_is); /* @@ -2991,7 +2993,120 @@ eval_addlist(typval_T *tv1, typval_T *tv } /* - * Handle fourth level expression: + * Handle the bitwise left/right shift operator expression: + * var1 << var2 + * var1 >> var2 + * + * "arg" must point to the first non-white of the expression. + * "arg" is advanced to just after the recognized expression. + * + * Return OK or FAIL. + */ + static int +eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) +{ + /* + * Get the first expression. + */ + if (eval6(arg, rettv, evalarg) == FAIL) + return FAIL; + + /* + * Repeat computing, until no '<<' or '>>' is following. + */ + for (;;) + { + char_u *p; + int getnext; + exprtype_T type; + int evaluate; + typval_T var2; + int vim9script; + + p = eval_next_non_blank(*arg, evalarg, &getnext); + if (p[0] == '<' && p[1] == '<') + type = EXPR_LSHIFT; + else if (p[0] == '>' && p[1] == '>') + type = EXPR_RSHIFT; + else + return OK; + + // Handle a bitwise left or right shift operator + if (rettv->v_type != VAR_NUMBER) + { + // left operand should be a number + emsg(_(e_bitshift_ops_must_be_number)); + clear_tv(rettv); + return FAIL; + } + + evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); + vim9script = in_vim9script(); + if (getnext) + { + *arg = eval_next_line(*arg, evalarg); + p = *arg; + } + else if (evaluate && vim9script && !VIM_ISWHITE(**arg)) + { + error_white_both(*arg, 2); + clear_tv(rettv); + return FAIL; + } + + /* + * Get the second variable. + */ + if (evaluate && vim9script && !IS_WHITE_OR_NUL(p[2])) + { + error_white_both(p, 2); + clear_tv(rettv); + return FAIL; + } + *arg = skipwhite_and_linebreak(p + 2, evalarg); + if (eval6(arg, &var2, evalarg) == FAIL) + { + clear_tv(rettv); + return FAIL; + } + + if (var2.v_type != VAR_NUMBER || var2.vval.v_number < 0) + { + // right operand should be a positive number + if (var2.v_type != VAR_NUMBER) + emsg(_(e_bitshift_ops_must_be_number)); + else + emsg(_(e_bitshift_ops_must_be_postive)); + clear_tv(rettv); + clear_tv(&var2); + return FAIL; + } + + if (evaluate) + { + if (var2.vval.v_number > MAX_LSHIFT_BITS) + // shifting more bits than we have always results in zero + rettv->vval.v_number = 0; + else if (type == EXPR_LSHIFT) + rettv->vval.v_number = + rettv->vval.v_number << var2.vval.v_number; + else + { + rettv->vval.v_number = + rettv->vval.v_number >> var2.vval.v_number; + // clear the topmost sign bit + rettv->vval.v_number &= ~((uvarnumber_T)1 << MAX_LSHIFT_BITS); + } + } + + clear_tv(&var2); + } + + return OK; +} + +/* + * Handle fifth level expression: * + number addition, concatenation of list or blob * - number subtraction * . string concatenation (if script version is 1) @@ -3003,12 +3118,12 @@ eval_addlist(typval_T *tv1, typval_T *tv * Return OK or FAIL. */ static int -eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) +eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { /* - * Get the first variable. + * Get the first expression. */ - if (eval6(arg, rettv, evalarg, FALSE) == FAIL) + if (eval7(arg, rettv, evalarg, FALSE) == FAIL) return FAIL; /* @@ -3086,7 +3201,7 @@ eval5(char_u **arg, typval_T *rettv, eva return FAIL; } *arg = skipwhite_and_linebreak(*arg + oplen, evalarg); - if (eval6(arg, &var2, evalarg, !vim9script && op == '.') == FAIL) + if (eval7(arg, &var2, evalarg, !vim9script && op == '.') == FAIL) { clear_tv(rettv); return FAIL; @@ -3221,7 +3336,7 @@ eval5(char_u **arg, typval_T *rettv, eva } /* - * Handle fifth level expression: + * Handle sixth level expression: * * number multiplication * / number division * % number modulo @@ -3232,7 +3347,7 @@ eval5(char_u **arg, typval_T *rettv, eva * Return OK or FAIL. */ static int -eval6( +eval7( char_u **arg, typval_T *rettv, evalarg_T *evalarg, @@ -3243,9 +3358,9 @@ eval6( #endif /* - * Get the first variable. + * Get the first expression. */ - if (eval7t(arg, rettv, evalarg, want_string) == FAIL) + if (eval8(arg, rettv, evalarg, want_string) == FAIL) return FAIL; /* @@ -3318,7 +3433,7 @@ eval6( return FAIL; } *arg = skipwhite_and_linebreak(*arg + 1, evalarg); - if (eval7t(arg, &var2, evalarg, FALSE) == FAIL) + if (eval8(arg, &var2, evalarg, FALSE) == FAIL) return FAIL; if (evaluate) @@ -3415,7 +3530,7 @@ eval6( * Return OK or FAIL. */ static int -eval7t( +eval8( char_u **arg, typval_T *rettv, evalarg_T *evalarg, @@ -3453,7 +3568,7 @@ eval7t( *arg = skipwhite_and_linebreak(*arg, evalarg); } - res = eval7(arg, rettv, evalarg, want_string); + res = eval9(arg, rettv, evalarg, want_string); if (want_type != NULL && evaluate) { @@ -3642,7 +3757,7 @@ handle_predefined(char_u *s, int len, ty * Return OK or FAIL. */ static int -eval7( +eval9( char_u **arg, typval_T *rettv, evalarg_T *evalarg, @@ -3720,7 +3835,7 @@ eval7( // "->" follows. if (ret == OK && evaluate && end_leader > start_leader && rettv->v_type != VAR_BLOB) - ret = eval7_leader(rettv, TRUE, start_leader, &end_leader); + ret = eval9_leader(rettv, TRUE, start_leader, &end_leader); break; /* @@ -3920,19 +4035,19 @@ eval7( * Apply logical NOT and unary '-', from right to left, ignore '+'. */ if (ret == OK && evaluate && end_leader > start_leader) - ret = eval7_leader(rettv, FALSE, start_leader, &end_leader); + ret = eval9_leader(rettv, FALSE, start_leader, &end_leader); --recurse; return ret; } /* - * Apply the leading "!" and "-" before an eval7 expression to "rettv". + * Apply the leading "!" and "-" before an eval9 expression to "rettv". * When "numeric_only" is TRUE only handle "+" and "-". * Adjusts "end_leaderp" until it is at "start_leader". */ static int -eval7_leader( +eval9_leader( typval_T *rettv, int numeric_only, char_u *start_leader, diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -4152,6 +4152,8 @@ typedef enum EXPR_MULT, // * EXPR_DIV, // / EXPR_REM, // % + EXPR_LSHIFT, // << + EXPR_RSHIFT, // >> // used with ISN_ADDLIST EXPR_COPY, // create new list EXPR_APPEND, // append to first list diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim --- a/src/testdir/test_expr.vim +++ b/src/testdir/test_expr.vim @@ -946,4 +946,66 @@ func Test_string_interp() call v9.CheckDefAndScriptSuccess(lines) endfunc +" Test for bitwise left and right shift (<< and >>) +func Test_bitwise_shift() + let lines =<< trim END + call assert_equal(16, 1 << 4) + call assert_equal(2, 16 >> 3) + call assert_equal(0, 0 << 2) + call assert_equal(0, 0 >> 4) + call assert_equal(3, 3 << 0) + call assert_equal(3, 3 >> 0) + call assert_equal(0, 0 >> 4) + call assert_equal(0, 999999 >> 100) + call assert_equal(0, 999999 << 100) + VAR a = 8 + VAR b = 2 + call assert_equal(2, a >> b) + call assert_equal(32, a << b) + #" operator precedence + call assert_equal(48, 1 + 2 << 5 - 1) + call assert_equal(3, 8 + 4 >> 4 - 2) + call assert_true(1 << 2 < 1 << 3) + call assert_true(1 << 4 > 1 << 3) + VAR val = 0 + for i in range(0, v:numbersize - 2) + LET val = or(val, 1 << i) + endfor + call assert_equal(v:numbermax, val) + LET val = v:numbermax + for i in range(0, v:numbersize - 2) + LET val = and(val, invert(1 << i)) + endfor + call assert_equal(0, val) + #" multiple operators + call assert_equal(16, 1 << 2 << 2) + call assert_equal(4, 64 >> 2 >> 2) + call assert_true(1 << 2 << 2 == 256 >> 2 >> 2) + END + call v9.CheckLegacyAndVim9Success(lines) + + call v9.CheckLegacyAndVim9Failure(['VAR v = 2 << -1'], ['E1283:', 'E1283:', 'E1283:']) + call v9.CheckLegacyAndVim9Failure(['VAR a = 2', 'VAR b = -1', 'VAR v = a << b'], ['E1283:', 'E1283:', 'E1283:']) + call v9.CheckLegacyAndVim9Failure(['VAR v = "8" >> 2'], ['E1282:', 'E1282:', 'E1282:']) + call v9.CheckLegacyAndVim9Failure(['VAR v = 1 << "2"'], ['E1282:', 'E1282:', 'E1282:']) + call v9.CheckLegacyAndVim9Failure(['VAR a = "8"', 'VAR b = 2', 'VAR v = a << b'], ['E1282:', 'E1012:', 'E1282:']) + call v9.CheckLegacyAndVim9Failure(['VAR a = 8', 'VAR b = "2"', 'VAR v = a >> b'], ['E1282:', 'E1012:', 'E1282:']) + call v9.CheckLegacyAndVim9Failure(['VAR v = ![] << 1'], ['E745:', 'E1012:', 'E1282:']) + call v9.CheckLegacyAndVim9Failure(['VAR v = 1 << ![]'], ['E745:', 'E1012:', 'E1282:']) + call v9.CheckLegacyAndVim9Failure(['VAR v = ![] >> 1'], ['E745:', 'E1012:', 'E1282:']) + call v9.CheckLegacyAndVim9Failure(['VAR v = 1 >> ![]'], ['E745:', 'E1012:', 'E1282:']) + call v9.CheckDefAndScriptFailure(['echo 1<< 2'], ['E1004:', 'E1004:']) + call v9.CheckDefAndScriptFailure(['echo 1 <<2'], ['E1004:', 'E1004:']) + call v9.CheckDefAndScriptFailure(['echo 1>> 2'], ['E1004:', 'E1004:']) + call v9.CheckDefAndScriptFailure(['echo 1 >>2'], ['E1004:', 'E1004:']) + + let lines =<< trim END + var a = 1 + << + 4 + assert_equal(16, a) + END + call v9.CheckDefAndScriptSuccess(lines) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -2864,4 +2864,31 @@ def Test_disassemble_string_interp() instr) enddef +def BitShift() + var a = 1 << 2 + var b = 8 >> 1 + var c = a << b + var d = b << a +enddef + +def Test_disassemble_bitshift() + var instr = execute('disassemble BitShift') + assert_match('BitShift\_s*' .. + 'var a = 1 << 2\_s*' .. + '0 STORE 4 in $0\_s*' .. + 'var b = 8 >> 1\_s*' .. + '1 STORE 4 in $1\_s*' .. + 'var c = a << b\_s*' .. + '2 LOAD $0\_s*' .. + '3 LOAD $1\_s*' .. + '4 OPNR <<\_s*' .. + '5 STORE $2\_s*' .. + 'var d = b << a\_s*' .. + '6 LOAD $1\_s*' .. + '7 LOAD $0\_s*' .. + '8 OPNR <<\_s*' .. + '9 STORE $3\_s*' .. + '10 RETURN void', instr) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1483,8 +1483,13 @@ func Test_expr4_fails() endif endfunc +" test bitwise left and right shift operators +" The tests for this is in test_expr.vim (Test_bitwise_shift) +" def Test_expr5() +" enddef + " test addition, subtraction, concatenation -def Test_expr5() +def Test_expr6() var lines =<< trim END assert_equal(66, 60 + 6) assert_equal(70, 60 + @@ -1549,7 +1554,7 @@ def Test_expr5() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr5_vim9script() +def Test_expr6_vim9script() # check line continuation var lines =<< trim END var name = 11 @@ -1698,7 +1703,7 @@ def Test_expr5_vim9script() endfor enddef -def Test_expr5_vim9script_channel() +def Test_expr6_vim9script_channel() if !has('channel') MissingFeature 'channel' else @@ -1713,7 +1718,7 @@ def Test_expr5_vim9script_channel() endif enddef -def Test_expr5_float() +def Test_expr6_float() if !has('float') MissingFeature 'float' else @@ -1741,7 +1746,7 @@ def Test_expr5_float() endif enddef -func Test_expr5_fails() +func Test_expr6_fails() let msg = "White space required before and after '+'" call v9.CheckDefAndScriptFailure(["var x = 1+2"], msg, 1) call v9.CheckDefAndScriptFailure(["var x = 1 +2"], msg, 1) @@ -1780,14 +1785,14 @@ func Test_expr5_fails() call v9.CheckDefAndScriptFailure(['var x = 1 + false'], ['E1051:', 'E1138:'], 1) endfunc -func Test_expr5_fails_channel() +func Test_expr6_fails_channel() CheckFeature channel call v9.CheckDefAndScriptFailure(["var x = 'a' .. test_null_job()"], ['E1105:', 'E908:'], 1) call v9.CheckDefAndScriptFailure(["var x = 'a' .. test_null_channel()"], ['E1105:', 'E908:'], 1) endfunc -def Test_expr5_list_add() +def Test_expr6_list_add() var lines =<< trim END # concatenating two lists with same member types is OK var d = {} @@ -1818,7 +1823,7 @@ def Test_expr5_list_add() enddef " test multiply, divide, modulo -def Test_expr6() +def Test_expr7() var lines =<< trim END assert_equal(36, 6 * 6) assert_equal(24, 6 * @@ -1890,7 +1895,7 @@ def Test_expr6() v9.CheckDefExecAndScriptFailure(lines, 'E1154', 2) enddef -def Test_expr6_vim9script() +def Test_expr7_vim9script() # check line continuation var lines =<< trim END var name = 11 @@ -1942,7 +1947,7 @@ def Test_expr6_vim9script() v9.CheckDefAndScriptFailure(lines, 'E1004:', 1) enddef -def Test_expr6_float() +def Test_expr7_float() if !has('float') MissingFeature 'float' else @@ -1975,7 +1980,7 @@ def Test_expr6_float() endif enddef -func Test_expr6_fails() +func Test_expr7_fails() let msg = "White space required before and after '*'" call v9.CheckDefAndScriptFailure(["var x = 1*2"], msg, 1) call v9.CheckDefAndScriptFailure(["var x = 1 *2"], msg, 1) @@ -2019,7 +2024,7 @@ func Test_expr6_fails() endfor endfunc -func Test_expr6_float_fails() +func Test_expr7_float_fails() CheckFeature float call v9.CheckDefAndScriptFailure(["var x = 1.0 % 2"], ['E1035:', 'E804:'], 1) endfunc @@ -2053,7 +2058,7 @@ let g:dict_one = #{one: 1} let $TESTVAR = 'testvar' " type casts -def Test_expr7() +def Test_expr8() var lines =<< trim END var ls: list = ['a', g:string_empty] var ln: list = [g:anint, g:thefour] @@ -2079,7 +2084,7 @@ def Test_expr7() enddef " test low level expression -def Test_expr8_number() +def Test_expr9_number() # number constant var lines =<< trim END assert_equal(0, 0) @@ -2092,7 +2097,7 @@ def Test_expr8_number() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_float() +def Test_expr9_float() # float constant if !has('float') MissingFeature 'float' @@ -2107,7 +2112,7 @@ def Test_expr8_float() endif enddef -def Test_expr8_blob() +def Test_expr9_blob() # blob constant var lines =<< trim END assert_equal(g:blob_empty, 0z) @@ -2139,7 +2144,7 @@ def Test_expr8_blob() v9.CheckDefAndScriptFailure(["var x = 0z123"], 'E973:', 1) enddef -def Test_expr8_string() +def Test_expr9_string() # string constant var lines =<< trim END assert_equal(g:string_empty, '') @@ -2180,7 +2185,7 @@ def Test_expr8_string() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_vimvar() +def Test_expr9_vimvar() v:errors = [] var errs: list = v:errors v9.CheckDefFailure(['var errs: list = v:errors'], 'E1012:') @@ -2205,7 +2210,7 @@ def Test_expr8_vimvar() bwipe! enddef -def Test_expr8_special() +def Test_expr9_special() # special constant var lines =<< trim END assert_equal(g:special_true, true) @@ -2242,7 +2247,7 @@ def Test_expr8_special() v9.CheckDefAndScriptFailure(['v:none = 22'], 'E46:', 1) enddef -def Test_expr8_list() +def Test_expr9_list() # list var lines =<< trim END assert_equal(g:list_empty, []) @@ -2320,7 +2325,7 @@ def Test_expr8_list() v9.CheckDefAndScriptFailure(lines + ['echo numbers[a :b]'], 'E1004:', 4) enddef -def Test_expr8_list_vim9script() +def Test_expr9_list_vim9script() var lines =<< trim END var l = [ 11, @@ -2408,7 +2413,7 @@ def LambdaUsingArg(x: number): func x == 2 enddef -def Test_expr8_lambda() +def Test_expr9_lambda() var lines =<< trim END var La = () => 'result' # comment @@ -2494,7 +2499,7 @@ def Test_expr8_lambda() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_lambda_block() +def Test_expr9_lambda_block() var lines =<< trim END var Func = (s: string): string => { return 'hello ' .. s @@ -2574,7 +2579,7 @@ def NewLambdaUsingArg(x: number): func x == 2 enddef -def Test_expr8_new_lambda() +def Test_expr9_new_lambda() var lines =<< trim END var La = () => 'result' assert_equal('result', La()) @@ -2659,7 +2664,7 @@ def Test_expr8_new_lambda() v9.CheckDefAndScriptFailure(['var Fx = (a) => [0', ' 1]'], 'E696:', 2) enddef -def Test_expr8_lambda_vim9script() +def Test_expr9_lambda_vim9script() var lines =<< trim END var v = 10->((a) => a @@ -2678,7 +2683,7 @@ def Test_expr8_lambda_vim9script() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8funcref() +def Test_expr9funcref() var lines =<< trim END def RetNumber(): number return 123 @@ -2730,7 +2735,7 @@ enddef let g:test_space_dict = {'': 'empty', ' ': 'space'} let g:test_hash_dict = #{one: 1, two: 2} -def Test_expr8_dict() +def Test_expr9_dict() # dictionary var lines =<< trim END assert_equal(g:dict_empty, {}) @@ -2850,7 +2855,7 @@ def Test_expr8_dict() v9.CheckDefExecAndScriptFailure(['{}[getftype("file")]'], 'E716: Key not present in Dictionary: ""', 1) enddef -def Test_expr8_dict_vim9script() +def Test_expr9_dict_vim9script() var lines =<< trim END var d = { ['one']: @@ -2981,7 +2986,7 @@ def Test_expr8_dict_vim9script() v9.CheckScriptSuccess(lines) enddef -def Test_expr8_dict_in_block() +def Test_expr9_dict_in_block() var lines =<< trim END vim9script command MyCommand { @@ -3004,7 +3009,7 @@ def Test_expr8_dict_in_block() delcommand YourCommand enddef -def Test_expr8_call_2bool() +def Test_expr9_call_2bool() var lines =<< trim END vim9script @@ -3052,7 +3057,7 @@ def Test_expr_member() v9.CheckDefExecAndScriptFailure(["var d: dict", "d = g:list_empty"], 'E1012: Type mismatch; expected dict but got list', 2) enddef -def Test_expr8_any_index_slice() +def Test_expr9_any_index_slice() var lines =<< trim END # getting the one member should clear the list only after getting the item assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1]) @@ -3223,7 +3228,7 @@ def SetSomeVar() b:someVar = &fdm enddef -def Test_expr8_option() +def Test_expr9_option() var lines =<< trim END # option set ts=11 @@ -3250,7 +3255,7 @@ def Test_expr8_option() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_environment() +def Test_expr9_environment() var lines =<< trim END # environment variable assert_equal('testvar', $TESTVAR) @@ -3262,7 +3267,7 @@ def Test_expr8_environment() v9.CheckDefAndScriptFailure(["$"], ['E1002:', 'E15:'], 1) enddef -def Test_expr8_register() +def Test_expr9_register() var lines =<< trim END @a = 'register a' assert_equal('register a', @a) @@ -3288,7 +3293,7 @@ def Test_expr8_register() enddef " This is slow when run under valgrind. -def Test_expr8_namespace() +def Test_expr9_namespace() var lines =<< trim END g:some_var = 'some' assert_equal('some', get(g:, 'some_var')) @@ -3317,7 +3322,7 @@ def Test_expr8_namespace() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_namespace_loop_def() +def Test_expr9_namespace_loop_def() var lines =<< trim END # check using g: in a for loop more than DO_NOT_FREE_CNT times var exists = 0 @@ -3336,8 +3341,8 @@ def Test_expr8_namespace_loop_def() enddef " NOTE: this is known to be slow. To skip use: -" :let $TEST_SKIP_PAT = 'Test_expr8_namespace_loop_script' -def Test_expr8_namespace_loop_script() +" :let $TEST_SKIP_PAT = 'Test_expr9_namespace_loop_script' +def Test_expr9_namespace_loop_script() var lines =<< trim END vim9script # check using g: in a for loop more than DO_NOT_FREE_CNT times @@ -3356,7 +3361,7 @@ def Test_expr8_namespace_loop_script() v9.CheckScriptSuccess(lines) enddef -def Test_expr8_parens() +def Test_expr9_parens() # (expr) var lines =<< trim END assert_equal(4, (6 * 4) / 6) @@ -3403,7 +3408,7 @@ def Test_expr8_parens() unlet g:result enddef -def Test_expr8_negate_add() +def Test_expr9_negate_add() var lines =<< trim END assert_equal(-99, -99) assert_equal(-99, - 99) @@ -3452,7 +3457,7 @@ def LegacyReturn(): string legacy return #{key: 'ok'}.key enddef -def Test_expr8_legacy_script() +def Test_expr9_legacy_script() var lines =<< trim END let s:legacy = 'legacy' def GetLocal(): string @@ -3495,7 +3500,7 @@ def s:Echo4Arg(arg: any): string return arg enddef -def Test_expr8_call() +def Test_expr9_call() var lines =<< trim END assert_equal('yes', 'yes'->g:Echo()) assert_equal(true, !range(5)->empty()) @@ -3518,7 +3523,7 @@ def g:ExistingGlobal(): string return 'existing' enddef -def Test_expr8_call_global() +def Test_expr9_call_global() assert_equal('existing', g:ExistingGlobal()) def g:DefinedLater(): string @@ -3532,7 +3537,7 @@ def Test_expr8_call_global() v9.CheckDefAndScriptFailure(lines, 'E117: Unknown function: ExistingGlobal') enddef -def Test_expr8_autoload_var() +def Test_expr9_autoload_var() var auto_lines =<< trim END let autofile#var = 'found' END @@ -3555,7 +3560,7 @@ def Test_expr8_autoload_var() delete('Xruntime', 'rf') enddef -def Test_expr8_call_autoload() +def Test_expr9_call_autoload() var auto_lines =<< trim END def g:some#func(): string return 'found' @@ -3572,7 +3577,7 @@ def Test_expr8_call_autoload() delete('Xruntime', 'rf') enddef -def Test_expr8_method_call() +def Test_expr9_method_call() var lines =<< trim END new setline(1, ['first', 'last']) @@ -3663,7 +3668,7 @@ def Test_expr8_method_call() v9.CheckDefFailure(lines, 'E15: Invalid expression: "->SetList[0]x()"') enddef -def Test_expr8_method_call_linebreak() +def Test_expr9_method_call_linebreak() # this was giving an error when skipping over the expression var lines =<< trim END vim9script @@ -3679,7 +3684,7 @@ def Test_expr8_method_call_linebreak() v9.CheckScriptSuccess(lines) enddef -def Test_expr8_method_call_import() +def Test_expr9_method_call_import() var lines =<< trim END vim9script export def Square(items: list): list @@ -3714,7 +3719,7 @@ def Test_expr8_method_call_import() enddef -def Test_expr8_not() +def Test_expr9_not() var lines =<< trim END assert_equal(true, !'') assert_equal(true, ![]) @@ -3766,7 +3771,7 @@ enddef let g:anumber = 42 -def Test_expr8_negate() +def Test_expr9_negate() var lines =<< trim END var nr = 1 assert_equal(-1, -nr) @@ -3775,7 +3780,7 @@ def Test_expr8_negate() v9.CheckDefAndScriptSuccess(lines) enddef -func Test_expr8_fails() +func Test_expr9_fails() call v9.CheckDefFailure(["var x = (12"], "E1097:", 3) call v9.CheckScriptFailure(['vim9script', "var x = (12"], 'E110:', 2) @@ -3837,7 +3842,7 @@ func CallMe2(one, two) return a:one .. a:two endfunc -def Test_expr8_trailing() +def Test_expr9_trailing() var lines =<< trim END # user function call assert_equal(123, g:CallMe(123)) @@ -3873,7 +3878,7 @@ def Test_expr8_trailing() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_string_subscript() +def Test_expr9_string_subscript() var lines =<< trim END var text = 'abcdef' assert_equal('f', text[-1]) @@ -3972,7 +3977,7 @@ def Test_expr8_string_subscript() v9.CheckDefAndScriptFailure(lines, ['E1012: Type mismatch; expected number but got string', 'E1030: Using a String as a Number: "2"'], 1) enddef -def Test_expr8_list_subscript() +def Test_expr9_list_subscript() var lines =<< trim END var list = [0, 1, 2, 3, 4] assert_equal(0, list[0]) @@ -4015,7 +4020,7 @@ def Test_expr8_list_subscript() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_dict_subscript() +def Test_expr9_dict_subscript() var lines =<< trim END var l = [{lnum: 2}, {lnum: 1}] var res = l[0].lnum > l[1].lnum @@ -4036,7 +4041,7 @@ def Test_expr8_dict_subscript() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_blob_subscript() +def Test_expr9_blob_subscript() var lines =<< trim END var b = 0z112233 assert_equal(0x11, b[0]) @@ -4048,7 +4053,7 @@ def Test_expr8_blob_subscript() v9.CheckDefAndScriptSuccess(lines) enddef -def Test_expr8_funcref_subscript() +def Test_expr9_funcref_subscript() var lines =<< trim END var l = function('len')("abc") assert_equal(3, l) @@ -4058,7 +4063,7 @@ def Test_expr8_funcref_subscript() v9.CheckDefAndScriptFailure(["var l = function('len')(xxx)"], ['E1001: Variable not found: xxx', 'E121: Undefined variable: xxx'], 1) enddef -def Test_expr8_subscript_linebreak() +def Test_expr9_subscript_linebreak() var lines =<< trim END var range = range( 3) @@ -4101,7 +4106,7 @@ def Test_expr8_subscript_linebreak() v9.CheckDefAndScriptFailure(lines, ['E1127:', 'E116:'], 2) enddef -func Test_expr8_trailing_fails() +func Test_expr9_trailing_fails() call v9.CheckDefAndScriptFailure(['var l = [2]', 'l->((ll) => add(ll, 8))'], 'E107:', 2) call v9.CheckDefAndScriptFailure(['var l = [2]', 'l->((ll) => add(ll, 8)) ()'], 'E274:', 2) endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -735,6 +735,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 5003, +/**/ 5002, /**/ 5001, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2808,4 +2808,5 @@ long elapsed(DWORD start_tick); #define FFED_IS_GLOBAL 1 // "g:" was used #define FFED_NO_GLOBAL 2 // only check for script-local functions +#define MAX_LSHIFT_BITS (varnumber_T)((sizeof(uvarnumber_T) * 8) - 1) #endif // VIM__H diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -4055,6 +4055,17 @@ exec_instructions(ectx_T *ectx) varnumber_T res = 0; int div_zero = FALSE; + if (iptr->isn_arg.op.op_type == EXPR_LSHIFT + || iptr->isn_arg.op.op_type == EXPR_RSHIFT) + { + if (arg2 < 0) + { + SOURCING_LNUM = iptr->isn_lnum; + emsg(_(e_bitshift_ops_must_be_postive)); + goto on_error; + } + } + switch (iptr->isn_arg.op.op_type) { case EXPR_MULT: res = arg1 * arg2; break; @@ -4077,6 +4088,21 @@ exec_instructions(ectx_T *ectx) case EXPR_GEQUAL: res = arg1 >= arg2; break; case EXPR_SMALLER: res = arg1 < arg2; break; case EXPR_SEQUAL: res = arg1 <= arg2; break; + case EXPR_LSHIFT: if (arg2 > MAX_LSHIFT_BITS) + res = 0; + else + res = arg1 << arg2; + break; + case EXPR_RSHIFT: if (arg2 > MAX_LSHIFT_BITS) + res = 0; + else + { + res = arg1 >> arg2; + // clear the topmost sign bit + res &= ~((uvarnumber_T)1 + << MAX_LSHIFT_BITS); + } + break; default: break; } @@ -6016,6 +6042,8 @@ list_instructions(char *pfx, isn_T *inst case EXPR_REM: what = "%"; break; case EXPR_SUB: what = "-"; break; case EXPR_ADD: what = "+"; break; + case EXPR_LSHIFT: what = "<<"; break; + case EXPR_RSHIFT: what = ">>"; break; default: what = "???"; break; } switch (iptr->isn_type) diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -1748,7 +1748,7 @@ compile_parenthesis(char_u **arg, cctx_T return ret; } -static int compile_expr8(char_u **arg, cctx_T *cctx, ppconst_T *ppconst); +static int compile_expr9(char_u **arg, cctx_T *cctx, ppconst_T *ppconst); /* * Compile whatever comes after "name" or "name()". @@ -1909,7 +1909,7 @@ compile_subscript( // do not look in the next line cctx->ctx_ufunc->uf_lines.ga_len = 1; - fail = compile_expr8(arg, cctx, ppconst) == FAIL + fail = compile_expr9(arg, cctx, ppconst) == FAIL || *skipwhite(*arg) != NUL; *paren = '('; --paren_follows_after_expr; @@ -2143,7 +2143,7 @@ compile_subscript( * trailing ->name() method call */ static int -compile_expr8( +compile_expr9( char_u **arg, cctx_T *cctx, ppconst_T *ppconst) @@ -2389,10 +2389,10 @@ compile_expr8( } /* - * expr8: runtime type check / conversion + * expr9: runtime type check / conversion */ static int -compile_expr7(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) +compile_expr8(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) { type_T *want_type = NULL; @@ -2417,7 +2417,7 @@ compile_expr7(char_u **arg, cctx_T *cctx return FAIL; } - if (compile_expr8(arg, cctx, ppconst) == FAIL) + if (compile_expr9(arg, cctx, ppconst) == FAIL) return FAIL; if (want_type != NULL) @@ -2444,14 +2444,14 @@ compile_expr7(char_u **arg, cctx_T *cctx * % number modulo */ static int -compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) +compile_expr7(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) { char_u *op; char_u *next; int ppconst_used = ppconst->pp_used; // get the first expression - if (compile_expr7(arg, cctx, ppconst) == FAIL) + if (compile_expr8(arg, cctx, ppconst) == FAIL) return FAIL; /* @@ -2477,7 +2477,7 @@ compile_expr6(char_u **arg, cctx_T *cctx return FAIL; // get the second expression - if (compile_expr7(arg, cctx, ppconst) == FAIL) + if (compile_expr8(arg, cctx, ppconst) == FAIL) return FAIL; if (ppconst->pp_used == ppconst_used + 2 @@ -2522,7 +2522,7 @@ compile_expr6(char_u **arg, cctx_T *cctx * .. string concatenation */ static int -compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) +compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) { char_u *op; char_u *next; @@ -2530,7 +2530,7 @@ compile_expr5(char_u **arg, cctx_T *cctx int ppconst_used = ppconst->pp_used; // get the first variable - if (compile_expr6(arg, cctx, ppconst) == FAIL) + if (compile_expr7(arg, cctx, ppconst) == FAIL) return FAIL; /* @@ -2562,7 +2562,7 @@ compile_expr5(char_u **arg, cctx_T *cctx return FAIL; // get the second expression - if (compile_expr6(arg, cctx, ppconst) == FAIL) + if (compile_expr7(arg, cctx, ppconst) == FAIL) return FAIL; if (ppconst->pp_used == ppconst_used + 2 @@ -2621,6 +2621,136 @@ compile_expr5(char_u **arg, cctx_T *cctx } /* + * expr6a >> expr6b + * expr6a << expr6b + * + * Produces instructions: + * OPNR bitwise left or right shift + */ + static int +compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) +{ + exprtype_T type = EXPR_UNKNOWN; + char_u *p; + char_u *next; + int len = 2; + int ppconst_used = ppconst->pp_used; + typval_T *tv1; + typval_T *tv2; + isn_T *isn; + + // get the first variable + if (compile_expr6(arg, cctx, ppconst) == FAIL) + return FAIL; + + /* + * Repeat computing, until no "+", "-" or ".." is following. + */ + for (;;) + { + type = EXPR_UNKNOWN; + + p = may_peek_next_line(cctx, *arg, &next); + if (p[0] == '<' && p[1] == '<') + type = EXPR_LSHIFT; + else if (p[0] == '>' && p[1] == '>') + type = EXPR_RSHIFT; + + if (type == EXPR_UNKNOWN) + return OK; + + // Handle a bitwise left or right shift operator + if (ppconst->pp_used == ppconst_used + 1) + { + tv1 = &ppconst->pp_tv[ppconst->pp_used - 1]; + if (tv1->v_type != VAR_NUMBER) + { + // left operand should be a number + emsg(_(e_bitshift_ops_must_be_number)); + return FAIL; + } + } + else + { + type_T *t = get_type_on_stack(cctx, 0); + + if (need_type(t, &t_number, 0, 0, cctx, FALSE, FALSE) == FAIL) + { + emsg(_(e_bitshift_ops_must_be_number)); + return FAIL; + } + } + + if (next != NULL) + { + *arg = next_line_from_context(cctx, TRUE); + p = skipwhite(*arg); + } + + if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[len])) + { + error_white_both(p, len); + return FAIL; + } + + // get the second variable + if (may_get_next_line_error(p + len, arg, cctx) == FAIL) + return FAIL; + + if (compile_expr6(arg, cctx, ppconst) == FAIL) + return FAIL; + + if (ppconst->pp_used == ppconst_used + 2) + { + // Both sides are a constant, compute the result now. + tv2 = &ppconst->pp_tv[ppconst->pp_used - 1]; + if (tv2->v_type != VAR_NUMBER || tv2->vval.v_number < 0) + { + // right operand should be a positive number + if (tv2->v_type != VAR_NUMBER) + emsg(_(e_bitshift_ops_must_be_number)); + else + emsg(_(e_bitshift_ops_must_be_postive)); + return FAIL; + } + + if (tv2->vval.v_number > MAX_LSHIFT_BITS) + tv1->vval.v_number = 0; + else if (type == EXPR_LSHIFT) + tv1->vval.v_number = tv1->vval.v_number << tv2->vval.v_number; + else + { + tv1->vval.v_number = tv1->vval.v_number >> tv2->vval.v_number; + // clear the topmost sign bit + tv1->vval.v_number &= ~((uvarnumber_T)1 << MAX_LSHIFT_BITS); + } + clear_tv(tv2); + --ppconst->pp_used; + } + else + { + if (need_type(get_type_on_stack(cctx, 0), &t_number, 0, 0, cctx, + FALSE, FALSE) == FAIL) + { + emsg(_(e_bitshift_ops_must_be_number)); + return FAIL; + } + + generate_ppconst(cctx, ppconst); + + isn = generate_instr_drop(cctx, ISN_OPNR, 1); + if (isn == NULL) + return FAIL; + + if (isn != NULL) + isn->isn_arg.op.op_type = type; + } + } + + return OK; +} + +/* * expr5a == expr5b * expr5a =~ expr5b * expr5a != expr5b @@ -2652,6 +2782,7 @@ compile_expr4(char_u **arg, cctx_T *cctx return FAIL; p = may_peek_next_line(cctx, *arg, &next); + type = get_compare_type(p, &len, &type_is); /*