summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2022-05-22 19:13:49 +0100
committerBram Moolenaar <Bram@vim.org>2022-05-22 19:13:49 +0100
commita061f34191712df7dde7716705fe0ec074e9758e (patch)
treef8a3bf02c826aeb748d12bfa74ead3e9f74573a2
parent9b2edfd3bf2f14a1faaee9b62930598a2e77a798 (diff)
patch 8.2.5003: cannot do bitwise shiftsv8.2.5003
Problem: Cannot do bitwise shifts. Solution: Add the >> and << operators. (Yegappan Lakshmanan, closes #8457)
-rw-r--r--runtime/doc/eval.txt145
-rw-r--r--src/errors.h6
-rw-r--r--src/eval.c163
-rw-r--r--src/structs.h2
-rw-r--r--src/testdir/test_expr.vim62
-rw-r--r--src/testdir/test_vim9_disassemble.vim27
-rw-r--r--src/testdir/test_vim9_expr.vim123
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h1
-rw-r--r--src/vim9execute.c28
-rw-r--r--src/vim9expr.c155
11 files changed, 554 insertions, 160 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 86b9686346..f9443fd297 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -868,33 +868,36 @@ Expression syntax summary, from least to most significant:
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
- <type>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
+ <type>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 character. Examples:
"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-..*
+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 expr6 must be a list. The
+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 ".." are converted to String for simple
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*
-----
-<type>expr8
+<type>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. Examples:
--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 in an empty string (reason: backward
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 or higher than the length of the
error.
-expr9[expr1a : expr1b] substring or sublist *expr-[:]*
+expr10[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,
+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 String concatenation. To avoid confusion
always put spaces around the dot for String concatenation.
-expr9(expr1, ...) |Funcref| function call *E1085*
+expr10(expr1, ...) |Funcref| function call *E1085*
-When expr9 is a |Funcref| type variable, invoke the function it refers to.
+When expr10 is a |Funcref| type variable, invoke the function it refers to.
-expr9->name([args]) method call *method* *->*
-expr9->{lambda}([args])
+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 no white space between the } and the
(.
- *expr10*
+ *expr11*
number
------
number number constant *expr-number*
diff --git a/src/errors.h b/src/errors.h
index 7a21ceeca8..c819729eb4 100644
--- 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
index ac3c998a51..0ac2dfb358 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -49,10 +49,11 @@ static int eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
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, evalarg_T *evalarg)
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, evalarg_T *evalarg)
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, evalarg_T *evalarg)
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 *tv2)
}
/*
- * 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 *tv2)
* 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, evalarg_T *evalarg)
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, evalarg_T *evalarg)
}
/*
- * 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, evalarg_T *evalarg)
* 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, typval_T *rettv)
* 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
index 612c26cdc1..a367d05811 100644
--- 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
index 0b6b57e4a2..24daeb6655 100644
--- 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
index 78604950b9..c1800c3128 100644
--- 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
index 401ab076b0..df65d72472 100644
--- 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<string> = ['a', <string>g:string_empty]
var ln: list<number> = [<number>g:anint, <number>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<string> = v:errors
v9.CheckDefFailure(['var errs: list<number> = 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:dic