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| expr5 isnot expr5 different |List|, |Dictionary| or |Blob|
instance instance
|expr5| expr6 |expr5| expr6 << expr6 bitwise left shift
expr6 + expr6 ... number addition, list or blob concatenation expr6 >> expr6 bitwise right shift
expr6 - expr6 ... number subtraction
expr6 . expr6 ... string concatenation
expr6 .. expr6 ... string concatenation
|expr6| expr7 |expr6| expr7
expr7 * expr7 ... number multiplication expr7 + expr7 ... number addition, list or blob concatenation
expr7 / expr7 ... number division expr7 - expr7 ... number subtraction
expr7 % expr7 ... number modulo expr7 . expr7 ... string concatenation
expr7 .. expr7 ... string concatenation
|expr7| expr8 |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| expr9
! expr8 logical NOT <type>expr9 type check and conversion (|Vim9| only)
- expr8 unary minus
+ expr8 unary plus
|expr9| expr10 |expr9| expr10
expr9[expr1] byte of a String or item of a |List| ! expr9 logical NOT
expr9[expr1 : expr1] substring of a String or sublist of a |List| - expr9 unary minus
expr9.name entry in a |Dictionary| + expr9 unary plus
expr9(expr1, ...) function call with |Funcref| variable
expr9->name(expr1, ...) |method| call
|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, backslash is special
'string' string constant, ' is doubled 'string' string constant, ' is doubled
[expr1, ...] |List| [expr1, ...] |List|
@ -1128,14 +1131,26 @@ can be matched like an ordinary character. Examples:
"foo\nbar" =~ "\\n" evaluates to 0 "foo\nbar" =~ "\\n" evaluates to 0
expr5 and expr6 *expr5* *expr6* *E1036* *E1051* expr5 *expr5* *bitwise-shift*
--------------- -----
expr6 + expr6 Number addition, |List| or |Blob| concatenation *expr-+* expr6 << expr6 bitwise left shift *expr-<<*
expr6 - expr6 Number subtraction *expr--* expr6 >> expr6 bitwise right shift *expr->>*
expr6 . expr6 String concatenation *expr-.* *E1282* *E1283*
expr6 .. expr6 String concatenation *expr-..* 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. result is a new list with the two lists Concatenated.
For String concatenation ".." is preferred, since "." is ambiguous, it is also 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 types: Number, Float, Special and Bool. For other types |string()| should be
used. used.
expr7 * expr7 Number multiplication *expr-star* expr8 * expr8 Number multiplication *expr-star*
expr7 / expr7 Number division *expr-/* expr8 / expr8 Number division *expr-/*
expr7 % expr7 Number modulo *expr-%* expr8 % expr8 Number modulo *expr-%*
In legacy script, for all operators except "." and "..", Strings are converted In legacy script, for all operators except "." and "..", Strings are converted
to Numbers. to Numbers.
@ -1191,18 +1206,18 @@ None of these work for |Funcref|s.
".", ".." and "%" do not work for Float. *E804* *E1035* ".", ".." 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|. This is only available in |Vim9| script, see |type-casting|.
expr8 *expr8* expr9 *expr9*
----- -----
! expr8 logical NOT *expr-!* ! expr9 logical NOT *expr-!*
- expr8 unary minus *expr-unary--* - expr9 unary minus *expr-unary--*
+ expr8 unary plus *expr-unary-+* + expr9 unary plus *expr-unary-+*
For '!' |TRUE| becomes |FALSE|, |FALSE| becomes |TRUE| (one). For '!' |TRUE| becomes |FALSE|, |FALSE| becomes |TRUE| (one).
For '-' the sign of the number is changed. For '-' the sign of the number is changed.
@ -1224,21 +1239,21 @@ These three can be repeated and mixed. Examples:
--9 == 9 --9 == 9
expr9 *expr9* expr10 *expr10*
----- ------
This expression is either |expr10| or a sequence of the alternatives below, This expression is either |expr11| or a sequence of the alternatives below,
in any order. E.g., these are all possible: in any order. E.g., these are all possible:
expr9[expr1].name expr10[expr1].name
expr9.name[expr1] expr10.name[expr1]
expr9(expr1, ...)[expr1].name expr10(expr1, ...)[expr1].name
expr9->(expr1, ...)[expr1] expr10->(expr1, ...)[expr1]
Evaluation is always from left to right. 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* *E909* *subscript* *E1062*
In legacy Vim script: In legacy Vim script:
If expr9 is a Number or String this results in a String that contains the If expr10 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 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 automatically converted to a String), expr1 as a Number. This doesn't
recognize multibyte encodings, see `byteidx()` for an alternative, or use recognize multibyte encodings, see `byteidx()` for an alternative, or use
`split()` to turn the string into a list of characters. Example, to get the `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] :let c = getline(".")[col(".") - 1]
In |Vim9| script: *E1147* *E1148* In |Vim9| script: *E1147* *E1148*
If expr9 is a String this results in a String that contains the expr1'th If expr10 is a String this results in a String that contains the expr1'th
single character (including any composing characters) from expr9. To use byte single character (including any composing characters) from expr10. To use byte
indexes use |strpart()|. indexes use |strpart()|.
Index zero gives the first byte or character. Careful: text column numbers 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. 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. 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 for possible index values. If the index is out of range this results in an
error. Example: > error. Example: >
:let item = mylist[-1] " get last item :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. 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 If expr10 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, characters from expr1a to and including expr1b. expr10 is used as a String,
expr1a and expr1b are used as a Number. expr1a and expr1b are used as a Number.
In legacy Vim script the indexes are byte indexes. This doesn't recognize 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. a Number it is first converted to a String.
In Vim9 script the indexes are character indexes and include composing In Vim9 script the indexes are character indexes and include composing
@ -1302,20 +1317,20 @@ Examples: >
:let s = s[:-3] " remove last two bytes :let s = s[:-3] " remove last two bytes
< <
*slice* *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 the indexes expr1a and expr1b. This works like with a String, as explained
just above. Also see |sublist| below. Examples: > just above. Also see |sublist| below. Examples: >
:let l = mylist[:3] " first four items :let l = mylist[:3] " first four items
:let l = mylist[4:4] " List with one item :let l = mylist[4:4] " List with one item
:let l = mylist[:] " shallow copy of a List :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: > indexes expr1a and expr1b, inclusive. Examples: >
:let b = 0zDEADBEEF :let b = 0zDEADBEEF
:let bs = b[1:2] " 0zADBE :let bs = b[1:2] " 0zADBE
:let bs = b[:] " copy of 0zDEADBEEF :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. error.
Watch out for confusion between a namespace and a variable followed by a colon 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! mylist[s:] " uses namespace s:, error!
expr9.name entry in a |Dictionary| *expr-entry* expr10.name entry in a |Dictionary| *expr-entry*
*E1203* *E1229* *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: 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, The name must consist of alphanumeric characters, just like a variable name,
but it may start with a number. Curly braces cannot be used. 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. 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* *->* expr10->name([args]) method call *method* *->*
expr9->{lambda}([args]) expr10->{lambda}([args])
*E260* *E276* *E1265* *E260* *E276* *E1265*
For methods that are also available as global functions this is the same as: > For methods that are also available as global functions this is the same as: >
name(expr9 [, args]) name(expr10 [, args])
There can also be methods specifically for the type of "expr9". There can also be methods specifically for the type of "expr10".
This allows for chaining, passing the value that one method returns to the This allows for chaining, passing the value that one method returns to the
next method: > next method: >
@ -1364,7 +1379,7 @@ next method: >
Example of using a lambda: > Example of using a lambda: >
GetPercentage()->{x -> x * 100}()->printf('%d%%') 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() -1.234->string()
Is equivalent to: > Is equivalent to: >
(-1.234)->string() (-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 number constant *expr-number* number number constant *expr-number*

View File

@ -3279,3 +3279,9 @@ EXTERN char e_illegal_character_in_word[]
#endif #endif
EXTERN char e_atom_engine_must_be_at_start_of_pattern[] EXTERN char e_atom_engine_must_be_at_start_of_pattern[]
INIT(= N_("E1281: Atom '\\%%#=%c' must be at the start of the 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 eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
static int eval4(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 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 eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
static int eval7t(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string);
static int eval7(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string); 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 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); 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; char_u *name = *arg;
ref.v_type = VAR_UNKNOWN; ref.v_type = VAR_UNKNOWN;
if (eval7(arg, &ref, evalarg, FALSE) == FAIL) if (eval9(arg, &ref, evalarg, FALSE) == FAIL)
{ {
dictitem_T *v; dictitem_T *v;
@ -2591,7 +2592,7 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
int getnext; int getnext;
/* /*
* Get the first variable. * Get the first expression.
*/ */
if (eval3(arg, rettv, evalarg) == FAIL) if (eval3(arg, rettv, evalarg) == FAIL)
return FAIL; return FAIL;
@ -2717,7 +2718,7 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
int getnext; int getnext;
/* /*
* Get the first variable. * Get the first expression.
*/ */
if (eval4(arg, rettv, evalarg) == FAIL) if (eval4(arg, rettv, evalarg) == FAIL)
return FAIL; return FAIL;
@ -2856,12 +2857,13 @@ eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
int type_is = FALSE; int type_is = FALSE;
/* /*
* Get the first variable. * Get the first expression.
*/ */
if (eval5(arg, rettv, evalarg) == FAIL) if (eval5(arg, rettv, evalarg) == FAIL)
return FAIL; return FAIL;
p = eval_next_non_blank(*arg, evalarg, &getnext); p = eval_next_non_blank(*arg, evalarg, &getnext);
type = get_compare_type(p, &len, &type_is); 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 addition, concatenation of list or blob
* - number subtraction * - number subtraction
* . string concatenation (if script version is 1) * . string concatenation (if script version is 1)
@ -3003,12 +3118,12 @@ eval_addlist(typval_T *tv1, typval_T *tv2)
* Return OK or FAIL. * Return OK or FAIL.
*/ */
static int 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; return FAIL;
/* /*
@ -3086,7 +3201,7 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
return FAIL; return FAIL;
} }
*arg = skipwhite_and_linebreak(*arg + oplen, evalarg); *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); clear_tv(rettv);
return FAIL; 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 multiplication
* / number division * / number division
* % number modulo * % number modulo
@ -3232,7 +3347,7 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
* Return OK or FAIL. * Return OK or FAIL.
*/ */
static int static int
eval6( eval7(
char_u **arg, char_u **arg,
typval_T *rettv, typval_T *rettv,
evalarg_T *evalarg, evalarg_T *evalarg,
@ -3243,9 +3358,9 @@ eval6(
#endif #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; return FAIL;
/* /*
@ -3318,7 +3433,7 @@ eval6(
return FAIL; return FAIL;
} }
*arg = skipwhite_and_linebreak(*arg + 1, evalarg); *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
if (eval7t(arg, &var2, evalarg, FALSE) == FAIL) if (eval8(arg, &var2, evalarg, FALSE) == FAIL)
return FAIL; return FAIL;
if (evaluate) if (evaluate)
@ -3415,7 +3530,7 @@ eval6(
* Return OK or FAIL. * Return OK or FAIL.
*/ */
static int static int
eval7t( eval8(
char_u **arg, char_u **arg,
typval_T *rettv, typval_T *rettv,
evalarg_T *evalarg, evalarg_T *evalarg,
@ -3453,7 +3568,7 @@ eval7t(
*arg = skipwhite_and_linebreak(*arg, evalarg); *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) if (want_type != NULL && evaluate)
{ {
@ -3642,7 +3757,7 @@ handle_predefined(char_u *s, int len, typval_T *rettv)
* Return OK or FAIL. * Return OK or FAIL.
*/ */
static int static int
eval7( eval9(
char_u **arg, char_u **arg,
typval_T *rettv, typval_T *rettv,
evalarg_T *evalarg, evalarg_T *evalarg,
@ -3720,7 +3835,7 @@ eval7(
// "->" follows. // "->" follows.
if (ret == OK && evaluate && end_leader > start_leader if (ret == OK && evaluate && end_leader > start_leader
&& rettv->v_type != VAR_BLOB) && rettv->v_type != VAR_BLOB)
ret = eval7_leader(rettv, TRUE, start_leader, &end_leader); ret = eval9_leader(rettv, TRUE, start_leader, &end_leader);
break; break;
/* /*
@ -3920,19 +4035,19 @@ eval7(
* Apply logical NOT and unary '-', from right to left, ignore '+'. * Apply logical NOT and unary '-', from right to left, ignore '+'.
*/ */
if (ret == OK && evaluate && end_leader > start_leader) 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; --recurse;
return ret; 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 "-". * When "numeric_only" is TRUE only handle "+" and "-".
* Adjusts "end_leaderp" until it is at "start_leader". * Adjusts "end_leaderp" until it is at "start_leader".
*/ */
static int static int
eval7_leader( eval9_leader(
typval_T *rettv, typval_T *rettv,
int numeric_only, int numeric_only,
char_u *start_leader, char_u *start_leader,

View File

@ -4152,6 +4152,8 @@ typedef enum
EXPR_MULT, // * EXPR_MULT, // *
EXPR_DIV, // / EXPR_DIV, // /
EXPR_REM, // % EXPR_REM, // %
EXPR_LSHIFT, // <<
EXPR_RSHIFT, // >>
// used with ISN_ADDLIST // used with ISN_ADDLIST
EXPR_COPY, // create new list EXPR_COPY, // create new list
EXPR_APPEND, // append to first list EXPR_APPEND, // append to first list

View File

@ -946,4 +946,66 @@ func Test_string_interp()
call v9.CheckDefAndScriptSuccess(lines) call v9.CheckDefAndScriptSuccess(lines)
endfunc 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 " vim: shiftwidth=2 sts=2 expandtab

View File

@ -2864,4 +2864,31 @@ def Test_disassemble_string_interp()
instr) instr)
enddef 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 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@ -1483,8 +1483,13 @@ func Test_expr4_fails()
endif endif
endfunc 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 " test addition, subtraction, concatenation
def Test_expr5() def Test_expr6()
var lines =<< trim END var lines =<< trim END
assert_equal(66, 60 + 6) assert_equal(66, 60 + 6)
assert_equal(70, 60 + assert_equal(70, 60 +
@ -1549,7 +1554,7 @@ def Test_expr5()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr5_vim9script() def Test_expr6_vim9script()
# check line continuation # check line continuation
var lines =<< trim END var lines =<< trim END
var name = 11 var name = 11
@ -1698,7 +1703,7 @@ def Test_expr5_vim9script()
endfor endfor
enddef enddef
def Test_expr5_vim9script_channel() def Test_expr6_vim9script_channel()
if !has('channel') if !has('channel')
MissingFeature 'channel' MissingFeature 'channel'
else else
@ -1713,7 +1718,7 @@ def Test_expr5_vim9script_channel()
endif endif
enddef enddef
def Test_expr5_float() def Test_expr6_float()
if !has('float') if !has('float')
MissingFeature 'float' MissingFeature 'float'
else else
@ -1741,7 +1746,7 @@ def Test_expr5_float()
endif endif
enddef enddef
func Test_expr5_fails() func Test_expr6_fails()
let msg = "White space required before and after '+'" 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)
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) call v9.CheckDefAndScriptFailure(['var x = 1 + false'], ['E1051:', 'E1138:'], 1)
endfunc endfunc
func Test_expr5_fails_channel() func Test_expr6_fails_channel()
CheckFeature channel CheckFeature channel
call v9.CheckDefAndScriptFailure(["var x = 'a' .. test_null_job()"], ['E1105:', 'E908:'], 1) call v9.CheckDefAndScriptFailure(["var x = 'a' .. test_null_job()"], ['E1105:', 'E908:'], 1)
call v9.CheckDefAndScriptFailure(["var x = 'a' .. test_null_channel()"], ['E1105:', 'E908:'], 1) call v9.CheckDefAndScriptFailure(["var x = 'a' .. test_null_channel()"], ['E1105:', 'E908:'], 1)
endfunc endfunc
def Test_expr5_list_add() def Test_expr6_list_add()
var lines =<< trim END var lines =<< trim END
# concatenating two lists with same member types is OK # concatenating two lists with same member types is OK
var d = {} var d = {}
@ -1818,7 +1823,7 @@ def Test_expr5_list_add()
enddef enddef
" test multiply, divide, modulo " test multiply, divide, modulo
def Test_expr6() def Test_expr7()
var lines =<< trim END var lines =<< trim END
assert_equal(36, 6 * 6) assert_equal(36, 6 * 6)
assert_equal(24, 6 * assert_equal(24, 6 *
@ -1890,7 +1895,7 @@ def Test_expr6()
v9.CheckDefExecAndScriptFailure(lines, 'E1154', 2) v9.CheckDefExecAndScriptFailure(lines, 'E1154', 2)
enddef enddef
def Test_expr6_vim9script() def Test_expr7_vim9script()
# check line continuation # check line continuation
var lines =<< trim END var lines =<< trim END
var name = 11 var name = 11
@ -1942,7 +1947,7 @@ def Test_expr6_vim9script()
v9.CheckDefAndScriptFailure(lines, 'E1004:', 1) v9.CheckDefAndScriptFailure(lines, 'E1004:', 1)
enddef enddef
def Test_expr6_float() def Test_expr7_float()
if !has('float') if !has('float')
MissingFeature 'float' MissingFeature 'float'
else else
@ -1975,7 +1980,7 @@ def Test_expr6_float()
endif endif
enddef enddef
func Test_expr6_fails() func Test_expr7_fails()
let msg = "White space required before and after '*'" 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)
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 endfor
endfunc endfunc
func Test_expr6_float_fails() func Test_expr7_float_fails()
CheckFeature float CheckFeature float
call v9.CheckDefAndScriptFailure(["var x = 1.0 % 2"], ['E1035:', 'E804:'], 1) call v9.CheckDefAndScriptFailure(["var x = 1.0 % 2"], ['E1035:', 'E804:'], 1)
endfunc endfunc
@ -2053,7 +2058,7 @@ let g:dict_one = #{one: 1}
let $TESTVAR = 'testvar' let $TESTVAR = 'testvar'
" type casts " type casts
def Test_expr7() def Test_expr8()
var lines =<< trim END var lines =<< trim END
var ls: list<string> = ['a', <string>g:string_empty] var ls: list<string> = ['a', <string>g:string_empty]
var ln: list<number> = [<number>g:anint, <number>g:thefour] var ln: list<number> = [<number>g:anint, <number>g:thefour]
@ -2079,7 +2084,7 @@ def Test_expr7()
enddef enddef
" test low level expression " test low level expression
def Test_expr8_number() def Test_expr9_number()
# number constant # number constant
var lines =<< trim END var lines =<< trim END
assert_equal(0, 0) assert_equal(0, 0)
@ -2092,7 +2097,7 @@ def Test_expr8_number()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8_float() def Test_expr9_float()
# float constant # float constant
if !has('float') if !has('float')
MissingFeature 'float' MissingFeature 'float'
@ -2107,7 +2112,7 @@ def Test_expr8_float()
endif endif
enddef enddef
def Test_expr8_blob() def Test_expr9_blob()
# blob constant # blob constant
var lines =<< trim END var lines =<< trim END
assert_equal(g:blob_empty, 0z) assert_equal(g:blob_empty, 0z)
@ -2139,7 +2144,7 @@ def Test_expr8_blob()
v9.CheckDefAndScriptFailure(["var x = 0z123"], 'E973:', 1) v9.CheckDefAndScriptFailure(["var x = 0z123"], 'E973:', 1)
enddef enddef
def Test_expr8_string() def Test_expr9_string()
# string constant # string constant
var lines =<< trim END var lines =<< trim END
assert_equal(g:string_empty, '') assert_equal(g:string_empty, '')
@ -2180,7 +2185,7 @@ def Test_expr8_string()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8_vimvar() def Test_expr9_vimvar()
v:errors = [] v:errors = []
var errs: list<string> = v:errors var errs: list<string> = v:errors
v9.CheckDefFailure(['var errs: list<number> = v:errors'], 'E1012:') v9.CheckDefFailure(['var errs: list<number> = v:errors'], 'E1012:')
@ -2205,7 +2210,7 @@ def Test_expr8_vimvar()
bwipe! bwipe!
enddef enddef
def Test_expr8_special() def Test_expr9_special()
# special constant # special constant
var lines =<< trim END var lines =<< trim END
assert_equal(g:special_true, true) assert_equal(g:special_true, true)
@ -2242,7 +2247,7 @@ def Test_expr8_special()
v9.CheckDefAndScriptFailure(['v:none = 22'], 'E46:', 1) v9.CheckDefAndScriptFailure(['v:none = 22'], 'E46:', 1)
enddef enddef
def Test_expr8_list() def Test_expr9_list()
# list # list
var lines =<< trim END var lines =<< trim END
assert_equal(g:list_empty, []) assert_equal(g:list_empty, [])
@ -2320,7 +2325,7 @@ def Test_expr8_list()
v9.CheckDefAndScriptFailure(lines + ['echo numbers[a :b]'], 'E1004:', 4) v9.CheckDefAndScriptFailure(lines + ['echo numbers[a :b]'], 'E1004:', 4)
enddef enddef
def Test_expr8_list_vim9script() def Test_expr9_list_vim9script()
var lines =<< trim END var lines =<< trim END
var l = [ var l = [
11, 11,
@ -2408,7 +2413,7 @@ def LambdaUsingArg(x: number): func
x == 2 x == 2
enddef enddef
def Test_expr8_lambda() def Test_expr9_lambda()
var lines =<< trim END var lines =<< trim END
var La = () => 'result' var La = () => 'result'
# comment # comment
@ -2494,7 +2499,7 @@ def Test_expr8_lambda()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8_lambda_block() def Test_expr9_lambda_block()
var lines =<< trim END var lines =<< trim END
var Func = (s: string): string => { var Func = (s: string): string => {
return 'hello ' .. s return 'hello ' .. s
@ -2574,7 +2579,7 @@ def NewLambdaUsingArg(x: number): func
x == 2 x == 2
enddef enddef
def Test_expr8_new_lambda() def Test_expr9_new_lambda()
var lines =<< trim END var lines =<< trim END
var La = () => 'result' var La = () => 'result'
assert_equal('result', La()) assert_equal('result', La())
@ -2659,7 +2664,7 @@ def Test_expr8_new_lambda()
v9.CheckDefAndScriptFailure(['var Fx = (a) => [0', ' 1]'], 'E696:', 2) v9.CheckDefAndScriptFailure(['var Fx = (a) => [0', ' 1]'], 'E696:', 2)
enddef enddef
def Test_expr8_lambda_vim9script() def Test_expr9_lambda_vim9script()
var lines =<< trim END var lines =<< trim END
var v = 10->((a) => var v = 10->((a) =>
a a
@ -2678,7 +2683,7 @@ def Test_expr8_lambda_vim9script()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8funcref() def Test_expr9funcref()
var lines =<< trim END var lines =<< trim END
def RetNumber(): number def RetNumber(): number
return 123 return 123
@ -2730,7 +2735,7 @@ enddef
let g:test_space_dict = {'': 'empty', ' ': 'space'} let g:test_space_dict = {'': 'empty', ' ': 'space'}
let g:test_hash_dict = #{one: 1, two: 2} let g:test_hash_dict = #{one: 1, two: 2}
def Test_expr8_dict() def Test_expr9_dict()
# dictionary # dictionary
var lines =<< trim END var lines =<< trim END
assert_equal(g:dict_empty, {}) assert_equal(g:dict_empty, {})
@ -2850,7 +2855,7 @@ def Test_expr8_dict()
v9.CheckDefExecAndScriptFailure(['{}[getftype("file")]'], 'E716: Key not present in Dictionary: ""', 1) v9.CheckDefExecAndScriptFailure(['{}[getftype("file")]'], 'E716: Key not present in Dictionary: ""', 1)
enddef enddef
def Test_expr8_dict_vim9script() def Test_expr9_dict_vim9script()
var lines =<< trim END var lines =<< trim END
var d = { var d = {
['one']: ['one']:
@ -2981,7 +2986,7 @@ def Test_expr8_dict_vim9script()
v9.CheckScriptSuccess(lines) v9.CheckScriptSuccess(lines)
enddef enddef
def Test_expr8_dict_in_block() def Test_expr9_dict_in_block()
var lines =<< trim END var lines =<< trim END
vim9script vim9script
command MyCommand { command MyCommand {
@ -3004,7 +3009,7 @@ def Test_expr8_dict_in_block()
delcommand YourCommand delcommand YourCommand
enddef enddef
def Test_expr8_call_2bool() def Test_expr9_call_2bool()
var lines =<< trim END var lines =<< trim END
vim9script 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) v9.CheckDefExecAndScriptFailure(["var d: dict<number>", "d = g:list_empty"], 'E1012: Type mismatch; expected dict<number> but got list<unknown>', 2)
enddef enddef
def Test_expr8_any_index_slice() def Test_expr9_any_index_slice()
var lines =<< trim END var lines =<< trim END
# getting the one member should clear the list only after getting the item # getting the one member should clear the list only after getting the item
assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1]) assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1])
@ -3223,7 +3228,7 @@ def SetSomeVar()
b:someVar = &fdm b:someVar = &fdm
enddef enddef
def Test_expr8_option() def Test_expr9_option()
var lines =<< trim END var lines =<< trim END
# option # option
set ts=11 set ts=11
@ -3250,7 +3255,7 @@ def Test_expr8_option()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8_environment() def Test_expr9_environment()
var lines =<< trim END var lines =<< trim END
# environment variable # environment variable
assert_equal('testvar', $TESTVAR) assert_equal('testvar', $TESTVAR)
@ -3262,7 +3267,7 @@ def Test_expr8_environment()
v9.CheckDefAndScriptFailure(["$"], ['E1002:', 'E15:'], 1) v9.CheckDefAndScriptFailure(["$"], ['E1002:', 'E15:'], 1)
enddef enddef
def Test_expr8_register() def Test_expr9_register()
var lines =<< trim END var lines =<< trim END
@a = 'register a' @a = 'register a'
assert_equal('register a', @a) assert_equal('register a', @a)
@ -3288,7 +3293,7 @@ def Test_expr8_register()
enddef enddef
" This is slow when run under valgrind. " This is slow when run under valgrind.
def Test_expr8_namespace() def Test_expr9_namespace()
var lines =<< trim END var lines =<< trim END
g:some_var = 'some' g:some_var = 'some'
assert_equal('some', get(g:, 'some_var')) assert_equal('some', get(g:, 'some_var'))
@ -3317,7 +3322,7 @@ def Test_expr8_namespace()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8_namespace_loop_def() def Test_expr9_namespace_loop_def()
var lines =<< trim END var lines =<< trim END
# check using g: in a for loop more than DO_NOT_FREE_CNT times # check using g: in a for loop more than DO_NOT_FREE_CNT times
var exists = 0 var exists = 0
@ -3336,8 +3341,8 @@ def Test_expr8_namespace_loop_def()
enddef enddef
" NOTE: this is known to be slow. To skip use: " NOTE: this is known to be slow. To skip use:
" :let $TEST_SKIP_PAT = 'Test_expr8_namespace_loop_script' " :let $TEST_SKIP_PAT = 'Test_expr9_namespace_loop_script'
def Test_expr8_namespace_loop_script() def Test_expr9_namespace_loop_script()
var lines =<< trim END var lines =<< trim END
vim9script vim9script
# check using g: in a for loop more than DO_NOT_FREE_CNT times # 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) v9.CheckScriptSuccess(lines)
enddef enddef
def Test_expr8_parens() def Test_expr9_parens()
# (expr) # (expr)
var lines =<< trim END var lines =<< trim END
assert_equal(4, (6 * 4) / 6) assert_equal(4, (6 * 4) / 6)
@ -3403,7 +3408,7 @@ def Test_expr8_parens()
unlet g:result unlet g:result
enddef enddef
def Test_expr8_negate_add() def Test_expr9_negate_add()
var lines =<< trim END var lines =<< trim END
assert_equal(-99, -99) assert_equal(-99, -99)
assert_equal(-99, - 99) assert_equal(-99, - 99)
@ -3452,7 +3457,7 @@ def LegacyReturn(): string
legacy return #{key: 'ok'}.key legacy return #{key: 'ok'}.key
enddef enddef
def Test_expr8_legacy_script() def Test_expr9_legacy_script()
var lines =<< trim END var lines =<< trim END
let s:legacy = 'legacy' let s:legacy = 'legacy'
def GetLocal(): string def GetLocal(): string
@ -3495,7 +3500,7 @@ def s:Echo4Arg(arg: any): string
return arg return arg
enddef enddef
def Test_expr8_call() def Test_expr9_call()
var lines =<< trim END var lines =<< trim END
assert_equal('yes', 'yes'->g:Echo()) assert_equal('yes', 'yes'->g:Echo())
assert_equal(true, !range(5)->empty()) assert_equal(true, !range(5)->empty())
@ -3518,7 +3523,7 @@ def g:ExistingGlobal(): string
return 'existing' return 'existing'
enddef enddef
def Test_expr8_call_global() def Test_expr9_call_global()
assert_equal('existing', g:ExistingGlobal()) assert_equal('existing', g:ExistingGlobal())
def g:DefinedLater(): string def g:DefinedLater(): string
@ -3532,7 +3537,7 @@ def Test_expr8_call_global()
v9.CheckDefAndScriptFailure(lines, 'E117: Unknown function: ExistingGlobal') v9.CheckDefAndScriptFailure(lines, 'E117: Unknown function: ExistingGlobal')
enddef enddef
def Test_expr8_autoload_var() def Test_expr9_autoload_var()
var auto_lines =<< trim END var auto_lines =<< trim END
let autofile#var = 'found' let autofile#var = 'found'
END END
@ -3555,7 +3560,7 @@ def Test_expr8_autoload_var()
delete('Xruntime', 'rf') delete('Xruntime', 'rf')
enddef enddef
def Test_expr8_call_autoload() def Test_expr9_call_autoload()
var auto_lines =<< trim END var auto_lines =<< trim END
def g:some#func(): string def g:some#func(): string
return 'found' return 'found'
@ -3572,7 +3577,7 @@ def Test_expr8_call_autoload()
delete('Xruntime', 'rf') delete('Xruntime', 'rf')
enddef enddef
def Test_expr8_method_call() def Test_expr9_method_call()
var lines =<< trim END var lines =<< trim END
new new
setline(1, ['first', 'last']) setline(1, ['first', 'last'])
@ -3663,7 +3668,7 @@ def Test_expr8_method_call()
v9.CheckDefFailure(lines, 'E15: Invalid expression: "->SetList[0]x()"') v9.CheckDefFailure(lines, 'E15: Invalid expression: "->SetList[0]x()"')
enddef enddef
def Test_expr8_method_call_linebreak() def Test_expr9_method_call_linebreak()
# this was giving an error when skipping over the expression # this was giving an error when skipping over the expression
var lines =<< trim END var lines =<< trim END
vim9script vim9script
@ -3679,7 +3684,7 @@ def Test_expr8_method_call_linebreak()
v9.CheckScriptSuccess(lines) v9.CheckScriptSuccess(lines)
enddef enddef
def Test_expr8_method_call_import() def Test_expr9_method_call_import()
var lines =<< trim END var lines =<< trim END
vim9script vim9script
export def Square(items: list<number>): list<number> export def Square(items: list<number>): list<number>
@ -3714,7 +3719,7 @@ def Test_expr8_method_call_import()
enddef enddef
def Test_expr8_not() def Test_expr9_not()
var lines =<< trim END var lines =<< trim END
assert_equal(true, !'') assert_equal(true, !'')
assert_equal(true, ![]) assert_equal(true, ![])
@ -3766,7 +3771,7 @@ enddef
let g:anumber = 42 let g:anumber = 42
def Test_expr8_negate() def Test_expr9_negate()
var lines =<< trim END var lines =<< trim END
var nr = 1 var nr = 1
assert_equal(-1, -nr) assert_equal(-1, -nr)
@ -3775,7 +3780,7 @@ def Test_expr8_negate()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
func Test_expr8_fails() func Test_expr9_fails()
call v9.CheckDefFailure(["var x = (12"], "E1097:", 3) call v9.CheckDefFailure(["var x = (12"], "E1097:", 3)
call v9.CheckScriptFailure(['vim9script', "var x = (12"], 'E110:', 2) call v9.CheckScriptFailure(['vim9script', "var x = (12"], 'E110:', 2)
@ -3837,7 +3842,7 @@ func CallMe2(one, two)
return a:one .. a:two return a:one .. a:two
endfunc endfunc
def Test_expr8_trailing() def Test_expr9_trailing()
var lines =<< trim END var lines =<< trim END
# user function call # user function call
assert_equal(123, g:CallMe(123)) assert_equal(123, g:CallMe(123))
@ -3873,7 +3878,7 @@ def Test_expr8_trailing()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8_string_subscript() def Test_expr9_string_subscript()
var lines =<< trim END var lines =<< trim END
var text = 'abcdef' var text = 'abcdef'
assert_equal('f', text[-1]) 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) v9.CheckDefAndScriptFailure(lines, ['E1012: Type mismatch; expected number but got string', 'E1030: Using a String as a Number: "2"'], 1)
enddef enddef
def Test_expr8_list_subscript() def Test_expr9_list_subscript()
var lines =<< trim END var lines =<< trim END
var list = [0, 1, 2, 3, 4] var list = [0, 1, 2, 3, 4]
assert_equal(0, list[0]) assert_equal(0, list[0])
@ -4015,7 +4020,7 @@ def Test_expr8_list_subscript()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8_dict_subscript() def Test_expr9_dict_subscript()
var lines =<< trim END var lines =<< trim END
var l = [{lnum: 2}, {lnum: 1}] var l = [{lnum: 2}, {lnum: 1}]
var res = l[0].lnum > l[1].lnum var res = l[0].lnum > l[1].lnum
@ -4036,7 +4041,7 @@ def Test_expr8_dict_subscript()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8_blob_subscript() def Test_expr9_blob_subscript()
var lines =<< trim END var lines =<< trim END
var b = 0z112233 var b = 0z112233
assert_equal(0x11, b[0]) assert_equal(0x11, b[0])
@ -4048,7 +4053,7 @@ def Test_expr8_blob_subscript()
v9.CheckDefAndScriptSuccess(lines) v9.CheckDefAndScriptSuccess(lines)
enddef enddef
def Test_expr8_funcref_subscript() def Test_expr9_funcref_subscript()
var lines =<< trim END var lines =<< trim END
var l = function('len')("abc") var l = function('len')("abc")
assert_equal(3, l) 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) v9.CheckDefAndScriptFailure(["var l = function('len')(xxx)"], ['E1001: Variable not found: xxx', 'E121: Undefined variable: xxx'], 1)
enddef enddef
def Test_expr8_subscript_linebreak() def Test_expr9_subscript_linebreak()
var lines =<< trim END var lines =<< trim END
var range = range( var range = range(
3) 3)
@ -4101,7 +4106,7 @@ def Test_expr8_subscript_linebreak()
v9.CheckDefAndScriptFailure(lines, ['E1127:', 'E116:'], 2) v9.CheckDefAndScriptFailure(lines, ['E1127:', 'E116:'], 2)
enddef 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))'], 'E107:', 2)
call v9.CheckDefAndScriptFailure(['var l = [2]', 'l->((ll) => add(ll, 8)) ()'], 'E274:', 2) call v9.CheckDefAndScriptFailure(['var l = [2]', 'l->((ll) => add(ll, 8)) ()'], 'E274:', 2)
endfunc endfunc

View File

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

View File

@ -2808,4 +2808,5 @@ long elapsed(DWORD start_tick);
#define FFED_IS_GLOBAL 1 // "g:" was used #define FFED_IS_GLOBAL 1 // "g:" was used
#define FFED_NO_GLOBAL 2 // only check for script-local functions #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 #endif // VIM__H

View File

@ -4055,6 +4055,17 @@ exec_instructions(ectx_T *ectx)
varnumber_T res = 0; varnumber_T res = 0;
int div_zero = FALSE; 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) switch (iptr->isn_arg.op.op_type)
{ {
case EXPR_MULT: res = arg1 * arg2; break; 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_GEQUAL: res = arg1 >= arg2; break;
case EXPR_SMALLER: res = arg1 < arg2; break; case EXPR_SMALLER: res = arg1 < arg2; break;
case EXPR_SEQUAL: 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; 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_REM: what = "%"; break;
case EXPR_SUB: what = "-"; break; case EXPR_SUB: what = "-"; break;
case EXPR_ADD: what = "+"; break; case EXPR_ADD: what = "+"; break;
case EXPR_LSHIFT: what = "<<"; break;
case EXPR_RSHIFT: what = ">>"; break;
default: what = "???"; break; default: what = "???"; break;
} }
switch (iptr->isn_type) switch (iptr->isn_type)

View File

@ -1748,7 +1748,7 @@ compile_parenthesis(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
return ret; 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()". * Compile whatever comes after "name" or "name()".
@ -1909,7 +1909,7 @@ compile_subscript(
// do not look in the next line // do not look in the next line
cctx->ctx_ufunc->uf_lines.ga_len = 1; 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; || *skipwhite(*arg) != NUL;
*paren = '('; *paren = '(';
--paren_follows_after_expr; --paren_follows_after_expr;
@ -2143,7 +2143,7 @@ compile_subscript(
* trailing ->name() method call * trailing ->name() method call
*/ */
static int static int
compile_expr8( compile_expr9(
char_u **arg, char_u **arg,
cctx_T *cctx, cctx_T *cctx,
ppconst_T *ppconst) ppconst_T *ppconst)
@ -2389,10 +2389,10 @@ compile_expr8(
} }
/* /*
* <type>expr8: runtime type check / conversion * <type>expr9: runtime type check / conversion
*/ */
static int 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; type_T *want_type = NULL;
@ -2417,7 +2417,7 @@ compile_expr7(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
return FAIL; return FAIL;
} }
if (compile_expr8(arg, cctx, ppconst) == FAIL) if (compile_expr9(arg, cctx, ppconst) == FAIL)
return FAIL; return FAIL;
if (want_type != NULL) if (want_type != NULL)
@ -2444,14 +2444,14 @@ compile_expr7(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
* % number modulo * % number modulo
*/ */
static int 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 *op;
char_u *next; char_u *next;
int ppconst_used = ppconst->pp_used; int ppconst_used = ppconst->pp_used;
// get the first expression // get the first expression
if (compile_expr7(arg, cctx, ppconst) == FAIL) if (compile_expr8(arg, cctx, ppconst) == FAIL)
return FAIL; return FAIL;
/* /*
@ -2477,7 +2477,7 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
return FAIL; return FAIL;
// get the second expression // get the second expression
if (compile_expr7(arg, cctx, ppconst) == FAIL) if (compile_expr8(arg, cctx, ppconst) == FAIL)
return FAIL; return FAIL;
if (ppconst->pp_used == ppconst_used + 2 if (ppconst->pp_used == ppconst_used + 2
@ -2522,7 +2522,7 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
* .. string concatenation * .. string concatenation
*/ */
static int 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 *op;
char_u *next; char_u *next;
@ -2530,7 +2530,7 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
int ppconst_used = ppconst->pp_used; int ppconst_used = ppconst->pp_used;
// get the first variable // get the first variable
if (compile_expr6(arg, cctx, ppconst) == FAIL) if (compile_expr7(arg, cctx, ppconst) == FAIL)
return FAIL; return FAIL;
/* /*
@ -2562,7 +2562,7 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
return FAIL; return FAIL;
// get the second expression // get the second expression
if (compile_expr6(arg, cctx, ppconst) == FAIL) if (compile_expr7(arg, cctx, ppconst) == FAIL)
return FAIL; return FAIL;
if (ppconst->pp_used == ppconst_used + 2 if (ppconst->pp_used == ppconst_used + 2
@ -2620,6 +2620,136 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
return OK; 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
* expr5a =~ expr5b * expr5a =~ expr5b
@ -2652,6 +2782,7 @@ compile_expr4(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
return FAIL; return FAIL;
p = may_peek_next_line(cctx, *arg, &next); p = may_peek_next_line(cctx, *arg, &next);
type = get_compare_type(p, &len, &type_is); type = get_compare_type(p, &len, &type_is);
/* /*