0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 8.2.5003: cannot do bitwise shifts

Problem:    Cannot do bitwise shifts.
Solution:   Add the >> and << operators. (Yegappan Lakshmanan, closes #8457)
This commit is contained in:
Yegappan Lakshmanan 2022-05-22 19:13:49 +01:00 committed by Bram Moolenaar
parent 9b2edfd3bf
commit a061f34191
11 changed files with 554 additions and 160 deletions

View File

@ -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
! expr9 logical NOT
- expr9 unary minus
+ expr9 unary plus
|expr10| number number constant
|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*
---------------
expr6 + expr6 Number addition, |List| or |Blob| concatenation *expr-+*
expr6 - expr6 Number subtraction *expr--*
expr6 . expr6 String concatenation *expr-.*
expr6 .. expr6 String concatenation *expr-..*
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.
For |Lists| only "+" is possible and then both expr6 must be a list. The
expr6 and expr7 *expr6* *expr7* *E1036* *E1051*
---------------
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 ".." 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*

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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: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<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 @@ 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<number>): list<number>
@ -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

View File

@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
5003,
/**/
5002,
/**/

View File

@ -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

View File

@ -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 *instr, int instr_count, ufunc_T *ufunc)
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)

View File

@ -1748,7 +1748,7 @@ compile_parenthesis(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
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(
}
/*
* <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 @@ compile_expr7(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
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, ppconst_T *ppconst)
* % 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, ppconst_T *ppconst)
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, ppconst_T *ppconst)
* .. 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, ppconst_T *ppconst)
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, ppconst_T *ppconst)
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
@ -2620,6 +2620,136 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
return OK;
}
/*
* 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
@ -2652,6 +2782,7 @@ compile_expr4(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
return FAIL;
p = may_peek_next_line(cctx, *arg, &next);
type = get_compare_type(p, &len, &type_is);
/*