patch 8.2.5003: cannot do bitwise shifts
Problem: Cannot do bitwise shifts.
Solution: Add the >> and << operators. (Yegappan Lakshmanan, closes #8457)
diff --git a/src/errors.h b/src/errors.h
index 7a21cee..c819729 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3279,3 +3279,9 @@
#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 ac3c998..0ac2dfb 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -49,10 +49,11 @@
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 @@
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 @@
int getnext;
/*
- * Get the first variable.
+ * Get the first expression.
*/
if (eval3(arg, rettv, evalarg) == FAIL)
return FAIL;
@@ -2717,7 +2718,7 @@
int getnext;
/*
- * Get the first variable.
+ * Get the first expression.
*/
if (eval4(arg, rettv, evalarg) == FAIL)
return FAIL;
@@ -2856,12 +2857,13 @@
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 @@
}
/*
- * 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 @@
* 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 @@
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 @@
}
/*
- * Handle fifth level expression:
+ * Handle sixth level expression:
* * number multiplication
* / number division
* % number modulo
@@ -3232,7 +3347,7 @@
* Return OK or FAIL.
*/
static int
-eval6(
+eval7(
char_u **arg,
typval_T *rettv,
evalarg_T *evalarg,
@@ -3243,9 +3358,9 @@
#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 @@
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 @@
* Return OK or FAIL.
*/
static int
-eval7t(
+eval8(
char_u **arg,
typval_T *rettv,
evalarg_T *evalarg,
@@ -3453,7 +3568,7 @@
*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 @@
* Return OK or FAIL.
*/
static int
-eval7(
+eval9(
char_u **arg,
typval_T *rettv,
evalarg_T *evalarg,
@@ -3720,7 +3835,7 @@
// "->" 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 @@
* 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 612c26c..a367d05 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -4152,6 +4152,8 @@
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 0b6b57e..24daeb6 100644
--- a/src/testdir/test_expr.vim
+++ b/src/testdir/test_expr.vim
@@ -946,4 +946,66 @@
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 7860495..c1800c3 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -2864,4 +2864,31 @@
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 401ab07..df65d72 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -1483,8 +1483,13 @@
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 @@
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 @@
endfor
enddef
-def Test_expr5_vim9script_channel()
+def Test_expr6_vim9script_channel()
if !has('channel')
MissingFeature 'channel'
else
@@ -1713,7 +1718,7 @@
endif
enddef
-def Test_expr5_float()
+def Test_expr6_float()
if !has('float')
MissingFeature 'float'
else
@@ -1741,7 +1746,7 @@
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 @@
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 @@
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 @@
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 @@
v9.CheckDefAndScriptFailure(lines, 'E1004:', 1)
enddef
-def Test_expr6_float()
+def Test_expr7_float()
if !has('float')
MissingFeature 'float'
else
@@ -1975,7 +1980,7 @@
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 @@
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 $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 @@
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 @@
v9.CheckDefAndScriptSuccess(lines)
enddef
-def Test_expr8_float()
+def Test_expr9_float()
# float constant
if !has('float')
MissingFeature 'float'
@@ -2107,7 +2112,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
x == 2
enddef
-def Test_expr8_lambda()
+def Test_expr9_lambda()
var lines =<< trim END
var La = () => 'result'
# comment
@@ -2494,7 +2499,7 @@
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 @@
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 @@
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 @@
v9.CheckDefAndScriptSuccess(lines)
enddef
-def Test_expr8funcref()
+def Test_expr9funcref()
var lines =<< trim END
def RetNumber(): number
return 123
@@ -2730,7 +2735,7 @@
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 @@
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 @@
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 @@
delcommand YourCommand
enddef
-def Test_expr8_call_2bool()
+def Test_expr9_call_2bool()
var lines =<< trim END
vim9script
@@ -3052,7 +3057,7 @@
v9.CheckDefExecAndScriptFailure(["var d: dict<number>", "d = g:list_empty"], 'E1012: Type mismatch; expected dict<number> but got list<unknown>', 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 @@
b:someVar = &fdm
enddef
-def Test_expr8_option()
+def Test_expr9_option()
var lines =<< trim END
# option
set ts=11
@@ -3250,7 +3255,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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<number>): list<number>
@@ -3714,7 +3719,7 @@
enddef
-def Test_expr8_not()
+def Test_expr9_not()
var lines =<< trim END
assert_equal(true, !'')
assert_equal(true, ![])
@@ -3766,7 +3771,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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
index 6aef8ad..a6570a3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -735,6 +735,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 5003,
+/**/
5002,
/**/
5001,
diff --git a/src/vim.h b/src/vim.h
index 4e213b2..b7af316 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -2808,4 +2808,5 @@
#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
index b191dc9..b4fd9d5 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -4055,6 +4055,17 @@
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 @@
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 @@
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
index eaea089..c84be87 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -1748,7 +1748,7 @@
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 @@
// 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 @@
* trailing ->name() method call
*/
static int
-compile_expr8(
+compile_expr9(
char_u **arg,
cctx_T *cctx,
ppconst_T *ppconst)
@@ -2389,10 +2389,10 @@
}
/*
- * <type>expr8: runtime type check / conversion
+ * <type>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 @@
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 @@
* % 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 @@
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 @@
* .. 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 @@
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 @@
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 @@
}
/*
+ * 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 @@
return FAIL;
p = may_peek_next_line(cctx, *arg, &next);
+
type = get_compare_type(p, &len, &type_is);
/*