]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.5003: cannot do bitwise shifts v8.2.5003
authorYegappan Lakshmanan <yegappan@yahoo.com>
Sun, 22 May 2022 18:13:49 +0000 (19:13 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 22 May 2022 18:13:49 +0000 (19:13 +0100)
Problem:    Cannot do bitwise shifts.
Solution:   Add the >> and << operators. (Yegappan Lakshmanan, closes #8457)

runtime/doc/eval.txt
src/errors.h
src/eval.c
src/structs.h
src/testdir/test_expr.vim
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_expr.vim
src/version.c
src/vim.h
src/vim9execute.c
src/vim9expr.c

index 86b9686346a15003199a6342c311e444b4a2ffbb..f9443fd297e4546633cb02490464ecf6258a2afa 100644 (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
-
-|expr10|  number               number constant
+       ! expr9                 logical NOT
+       - expr9                 unary minus
+       + expr9                 unary plus
+
+|expr10|  expr11
+       expr10[expr1]           byte of a String or item of a |List|
+       expr10[expr1 : expr1]   substring of a String or sublist of a |List|
+       expr10.name             entry in a |Dictionary|
+       expr10(expr1, ...)      function call with |Funcref| variable
+       expr10->name(expr1, ...)        |method| call
+
+|expr11|  number               number constant
        "string"                string constant, backslash is special
        'string'                string constant, ' is doubled
        [expr1, ...]            |List|
@@ -1128,14 +1131,26 @@ can be matched like an ordinary character.  Examples:
        "foo\nbar" =~ "\\n"     evaluates to 0
 
 
-expr5 and expr6                                *expr5* *expr6* *E1036* *E1051*
+expr5                                          *expr5* *bitwise-shift*
+-----
+expr6 << expr6 bitwise left shift                              *expr-<<*
+expr6 >> expr6 bitwise right shift                             *expr->>*
+                                                       *E1282* *E1283*
+The "<<" and ">>" operators can be used to perform bitwise left or right shift
+of the left operand by the number of bits specified by the right operand.  The
+operands must be positive numbers.  The topmost bit (sign bit) is always
+cleared for ">>".  If the right operand (shift amount) is more than the
+maximum number of bits in a number (|v:numbersize|) the result is zero.
+
+
+expr6 and expr7                                *expr6* *expr7* *E1036* *E1051*
 ---------------
-expr6 + expr6   Number addition, |List| or |Blob| concatenation        *expr-+*
-expr6 - expr6   Number subtraction                             *expr--*
-expr6 . expr6   String concatenation                           *expr-.*
-expr6 .. expr6  String concatenation                           *expr-..*
+expr7 + expr7   Number addition, |List| or |Blob| concatenation        *expr-+*
+expr7 - expr7   Number subtraction                             *expr--*
+expr7 . expr7   String concatenation                           *expr-.*
+expr7 .. expr7  String concatenation                           *expr-..*
 
-For |Lists| only "+" is possible and then both expr6 must be a list.  The
+For |Lists| only "+" is possible and then both expr7 must be a list.  The
 result is a new list with the two lists Concatenated.
 
 For String concatenation ".." is preferred, since "." is ambiguous, it is also
@@ -1147,9 +1162,9 @@ In |Vim9| script the arguments of ".." are converted to String for simple
 types: Number, Float, Special and Bool.  For other types |string()| should be
 used.
 
-expr7 * expr7  Number multiplication                           *expr-star*
-expr7 / expr7  Number division                                 *expr-/*
-expr7 % expr7  Number modulo                                   *expr-%*
+expr8 * expr8  Number multiplication                           *expr-star*
+expr8 / expr8  Number division                                 *expr-/*
+expr8 % expr8  Number modulo                                   *expr-%*
 
 In legacy script, for all operators except "." and "..", Strings are converted
 to Numbers.
@@ -1191,18 +1206,18 @@ None of these work for |Funcref|s.
 ".", ".." and "%" do not work for Float. *E804* *E1035*
 
 
-expr7                                                  *expr7*
+expr8                                                  *expr8*
 -----
-<type>expr8
+<type>expr9
 
 This is only available in |Vim9| script, see |type-casting|.
 
 
-expr8                                                  *expr8*
+expr9                                                  *expr9*
 -----
-! expr8                        logical NOT             *expr-!*
-- expr8                        unary minus             *expr-unary--*
-+ expr8                        unary plus              *expr-unary-+*
+! expr9                        logical NOT             *expr-!*
+- expr9                        unary minus             *expr-unary--*
++ expr9                        unary plus              *expr-unary-+*
 
 For '!' |TRUE| becomes |FALSE|, |FALSE| becomes |TRUE| (one).
 For '-' the sign of the number is changed.
@@ -1224,21 +1239,21 @@ These three can be repeated and mixed.  Examples:
        --9         == 9
 
 
-expr9                                                  *expr9*
------
-This expression is either |expr10| or a sequence of the alternatives below,
+expr10                                                 *expr10*
+------
+This expression is either |expr11| or a sequence of the alternatives below,
 in any order.  E.g., these are all possible:
-       expr9[expr1].name
-       expr9.name[expr1]
-       expr9(expr1, ...)[expr1].name
-       expr9->(expr1, ...)[expr1]
+       expr10[expr1].name
+       expr10.name[expr1]
+       expr10(expr1, ...)[expr1].name
+       expr10->(expr1, ...)[expr1]
 Evaluation is always from left to right.
 
-expr9[expr1]           item of String or |List|        *expr-[]* *E111*
+expr10[expr1]          item of String or |List|        *expr-[]* *E111*
                                                *E909* *subscript* *E1062*
 In legacy Vim script:
-If expr9 is a Number or String this results in a String that contains the
-expr1'th single byte from expr9.  expr9 is used as a String (a number is
+If expr10 is a Number or String this results in a String that contains the
+expr1'th single byte from expr10.  expr10 is used as a String (a number is
 automatically converted to a String), expr1 as a Number.  This doesn't
 recognize multibyte encodings, see `byteidx()` for an alternative, or use
 `split()` to turn the string into a list of characters.  Example, to get the
@@ -1246,8 +1261,8 @@ byte under the cursor: >
        :let c = getline(".")[col(".") - 1]
 
 In |Vim9| script:                                      *E1147* *E1148*
-If expr9 is a String this results in a String that contains the expr1'th
-single character (including any composing characters) from expr9.  To use byte
+If expr10 is a String this results in a String that contains the expr1'th
+single character (including any composing characters) from expr10.  To use byte
 indexes use |strpart()|.
 
 Index zero gives the first byte or character.  Careful: text column numbers
@@ -1258,7 +1273,7 @@ String.  A negative index always results in an empty string (reason: backward
 compatibility).  Use [-1:] to get the last byte or character.
 In Vim9 script a negative index is used like with a list: count from the end.
 
-If expr9 is a |List| then it results the item at index expr1.  See |list-index|
+If expr10 is a |List| then it results the item at index expr1.  See |list-index|
 for possible index values.  If the index is out of range this results in an
 error.  Example: >
        :let item = mylist[-1]          " get last item
@@ -1268,14 +1283,14 @@ Generally, if a |List| index is equal to or higher than the length of the
 error.
 
 
-expr9[expr1a : expr1b] substring or sublist            *expr-[:]*
+expr10[expr1a : expr1b]        substring or sublist            *expr-[:]*
 
-If expr9 is a String this results in the substring with the bytes or
-characters from expr1a to and including expr1b.  expr9 is used as a String,
+If expr10 is a String this results in the substring with the bytes or
+characters from expr1a to and including expr1b.  expr10 is used as a String,
 expr1a and expr1b are used as a Number.
 
 In legacy Vim script the indexes are byte indexes.  This doesn't recognize
-multibyte encodings, see |byteidx()| for computing the indexes.  If expr9 is
+multibyte encodings, see |byteidx()| for computing the indexes.  If expr10 is
 a Number it is first converted to a String.
 
 In Vim9 script the indexes are character indexes and include composing
@@ -1302,20 +1317,20 @@ Examples: >
        :let s = s[:-3]                 " remove last two bytes
 <
                                                        *slice*
-If expr9 is a |List| this results in a new |List| with the items indicated by
+If expr10 is a |List| this results in a new |List| with the items indicated by
 the indexes expr1a and expr1b.  This works like with a String, as explained
 just above. Also see |sublist| below.  Examples: >
        :let l = mylist[:3]             " first four items
        :let l = mylist[4:4]            " List with one item
        :let l = mylist[:]              " shallow copy of a List
 
-If expr9 is a |Blob| this results in a new |Blob| with the bytes in the
+If expr10 is a |Blob| this results in a new |Blob| with the bytes in the
 indexes expr1a and expr1b, inclusive.  Examples: >
        :let b = 0zDEADBEEF
        :let bs = b[1:2]                " 0zADBE
        :let bs = b[:]                  " copy of 0zDEADBEEF
 
-Using expr9[expr1] or expr9[expr1a : expr1b] on a |Funcref| results in an
+Using expr10[expr1] or expr10[expr1a : expr1b] on a |Funcref| results in an
 error.
 
 Watch out for confusion between a namespace and a variable followed by a colon
@@ -1324,11 +1339,11 @@ for a sublist: >
        mylist[s:]     " uses namespace s:, error!
 
 
-expr9.name             entry in a |Dictionary|         *expr-entry*
+expr10.name            entry in a |Dictionary|         *expr-entry*
                                                        *E1203* *E1229*
-If expr9 is a |Dictionary| and it is followed by a dot, then the following
+If expr10 is a |Dictionary| and it is followed by a dot, then the following
 name will be used as a key in the |Dictionary|.  This is just like:
-expr9[name].
+expr10[name].
 
 The name must consist of alphanumeric characters, just like a variable name,
 but it may start with a number.  Curly braces cannot be used.
@@ -1345,17 +1360,17 @@ Note that the dot is also used for String concatenation.  To avoid confusion
 always put spaces around the dot for String concatenation.
 
 
-expr9(expr1, ...)      |Funcref| function call         *E1085*
+expr10(expr1, ...)     |Funcref| function call         *E1085*
 
-When expr9 is a |Funcref| type variable, invoke the function it refers to.
+When expr10 is a |Funcref| type variable, invoke the function it refers to.
 
 
-expr9->name([args])    method call                     *method* *->*
-expr9->{lambda}([args])
+expr10->name([args])   method call                     *method* *->*
+expr10->{lambda}([args])
                                                        *E260* *E276* *E1265*
 For methods that are also available as global functions this is the same as: >
-       name(expr9 [, args])
-There can also be methods specifically for the type of "expr9".
+       name(expr10 [, args])
+There can also be methods specifically for the type of "expr10".
 
 This allows for chaining, passing the value that one method returns to the
 next method: >
@@ -1364,7 +1379,7 @@ next method: >
 Example of using a lambda: >
        GetPercentage()->{x -> x * 100}()->printf('%d%%')
 <
-When using -> the |expr8| operators will be applied first, thus: >
+When using -> the |expr9| operators will be applied first, thus: >
        -1.234->string()
 Is equivalent to: >
        (-1.234)->string()
@@ -1393,7 +1408,7 @@ When using the lambda form there must be no white space between the } and the
 (.
 
 
-                                                       *expr10*
+                                                       *expr11*
 number
 ------
 number                 number constant                 *expr-number*
index 7a21ceeca891bb5ae2d6c46745ba02960cf8d785..c819729eb4e2f56ea4c1efa6d6ecb4dcdbcc25f5 100644 (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
index ac3c998a512c08b98b3b947ab379b7a7b89171aa..0ac2dfb35830bf670f68758e9a673e7f487ac2f1 100644 (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,
index 612c26cdc1716b66067aec057d569be62cdb8191..a367d058114e796ea79df95280425b7512069629 100644 (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
index 0b6b57e4a2aee7d1d9d7ed3274be8e54427cd2cb..24daeb6655b6f84eb70cc2da9fd47eacef026720 100644 (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
index 78604950b94322ddde065c87ebcc6e1036587300..c1800c31285d4c5e9efc2955d9c5f471a0c73f65 100644 (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
index 401ab076b0baca29e08e5e67c5d7ab4dac5c4892..df65d724722181f295406275c1156e7a1744367f 100644 (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
index 6aef8ad539d8fe46451f06f4d4d37cd3b4455ce3..a6570a3cf2f417b5addc9b7e89467913a8b90bb7 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    5003,
 /**/
     5002,
 /**/
index 4e213b2c9ed2896e68f747287e23c0816900d35b..b7af316bf1dc9a5df2aaadf6924d80d8152f3fc1 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2808,4 +2808,5 @@ long elapsed(DWORD start_tick);
 #define FFED_IS_GLOBAL 1       // "g:" was used
 #define FFED_NO_GLOBAL 2       // only check for script-local functions
 
+#define MAX_LSHIFT_BITS (varnumber_T)((sizeof(uvarnumber_T) * 8) - 1)
 #endif // VIM__H
index b191dc998390bc713011523edf364eda50c3c1e4..b4fd9d591bb252fd4a5664692ec2352c230d4968 100644 (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)
index eaea089f98e712af9b0b872264758bb8e812ba69..c84be874af3581d17ca83ec0e7bd64beb6b6193b 100644 (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);
 
     /*