]> granicus.if.org Git - vim/commitdiff
patch 8.2.2757: Vim9: blob tests for legacy and Vim9 script are separate v8.2.2757
authorBram Moolenaar <Bram@vim.org>
Mon, 12 Apr 2021 19:21:02 +0000 (21:21 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 12 Apr 2021 19:21:02 +0000 (21:21 +0200)
Problem:    Vim9: blob tests for legacy and Vim9 script are separate.
Solution:   Add CheckLegacyAndVim9Success().  Make blob index assign work.

src/blob.c
src/errors.h
src/eval.c
src/ex_docmd.c
src/proto/blob.pro
src/testdir/test_blob.vim
src/testdir/vim9.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index 8d260f172ba961e8965d96466932aa863f259dac..d758beb19db2a7de875d2b83b527dc6361ff0fcf 100644 (file)
@@ -336,6 +336,28 @@ blob_slice_or_index(
     return OK;
 }
 
+/*
+ * Set bytes "n1" to "n2" (inclusive) in "dest" to the value of "src".
+ * Caller must make sure "src" is a blob.
+ * Returns FAIL if the number of bytes does not match.
+ */
+    int
+blob_set_range(blob_T *dest, long n1, long n2, typval_T *src)
+{
+    int        il, ir;
+
+    if (n2 - n1 + 1 != blob_len(src->vval.v_blob))
+    {
+       emsg(_("E972: Blob value does not have the right number of bytes"));
+       return FAIL;
+    }
+
+    ir = 0;
+    for (il = n1; il <= n2; il++)
+       blob_set(dest, il, blob_get(src->vval.v_blob, ir++));
+    return OK;
+}
+
 /*
  * "remove({blob})" function
  */
index d58da735aa3d89f51e020049e566dddb5bb04d57..d38562cfab7e742871cdf0ee84e0b0271a0b7553 100644 (file)
@@ -399,3 +399,5 @@ EXTERN char e_variable_arguments_type_must_be_list_str[]
        INIT(= N_("E1180: Variable arguments type must be a list: %s"));
 EXTERN char e_cannot_use_underscore_here[]
        INIT(= N_("E1181: Cannot use an underscore here"));
+EXTERN char e_blob_required[]
+       INIT(= N_("E1182: Blob required"));
index 1291b364a93229e1522350c081bf1f5fdae2c5c3..2e2ca23b6974641a974c29484cff17b25c0c438c 100644 (file)
@@ -1319,23 +1319,12 @@ set_var_lval(
 
            if (lp->ll_range && rettv->v_type == VAR_BLOB)
            {
-               int     il, ir;
-
                if (lp->ll_empty2)
                    lp->ll_n2 = blob_len(lp->ll_blob) - 1;
 
-               if (lp->ll_n2 - lp->ll_n1 + 1 != blob_len(rettv->vval.v_blob))
-               {
-                   emsg(_("E972: Blob value does not have the right number of bytes"));
+               if (blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2,
+                                                               rettv) == FAIL)
                    return;
-               }
-               if (lp->ll_empty2)
-                   lp->ll_n2 = blob_len(lp->ll_blob);
-
-               ir = 0;
-               for (il = lp->ll_n1; il <= lp->ll_n2; il++)
-                   blob_set(lp->ll_blob, il,
-                           blob_get(rettv->vval.v_blob, ir++));
            }
            else
            {
index 7c1c448551182789c6cbe118f543aa7e1f00c899..83f49d0d378b375edabbf0e810495286b5e8a48c 100644 (file)
@@ -3429,22 +3429,25 @@ find_ex_command(
                            // "varname.key" is an expression.
                         || (*p == '.' && ASCII_ISALPHA(p[1]))))
            {
-               char_u  *after = p;
+               char_u  *after = eap->cmd;
 
                // When followed by "=" or "+=" then it is an assignment.
+               // Skip over the whole thing, it can be:
+               //      name.member = val
+               //      name[a : b] = val
+               //      name[idx] = val
+               //      name[idx].member = val
+               //      etc.
+               eap->cmdidx = CMD_eval;
                ++emsg_silent;
-               if (*after == '.')
-                   after = skipwhite(after + 1);
                if (skip_expr(&after, NULL) == OK)
+               {
                    after = skipwhite(after);
-               else
-                   after = (char_u *)"";
-               if (*after == '=' || (*after != NUL && after[1] == '=')
+                   if (*after == '=' || (*after != NUL && after[1] == '=')
                                         || (after[0] == '.' && after[1] == '.'
                                                           && after[2] == '='))
-                   eap->cmdidx = CMD_var;
-               else
-                   eap->cmdidx = CMD_eval;
+                       eap->cmdidx = CMD_var;
+               }
                --emsg_silent;
                return eap->cmd;
            }
index 6be7f0bea5115895c05ae2b6174ed4f5b3c9a19a..7da269c35f043072fafa477b1be7288c97026b50 100644 (file)
@@ -14,5 +14,6 @@ int write_blob(FILE *fd, blob_T *blob);
 char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf);
 blob_T *string2blob(char_u *str);
 int blob_slice_or_index(blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, int exclusive, typval_T *rettv);
+int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src);
 void blob_remove(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
index 3eff715d05e3ba70e96a8aaf9b63f2a564b56a5d..34ed0caf2fc771873198a6a8c9c76d6aa49917bd 100644 (file)
@@ -1,5 +1,7 @@
 " Tests for the Blob types
 
+source vim9.vim
+
 func TearDown()
   " Run garbage collection after every test
   call test_garbagecollect_now()
@@ -9,73 +11,81 @@ endfunc
 
 " Blob creation from constant
 func Test_blob_create()
-  let b = 0zDEADBEEF
-  call assert_equal(v:t_blob, type(b))
-  call assert_equal(4, len(b))
-  call assert_equal(0xDE, b[0])
-  call assert_equal(0xAD, b[1])
-  call assert_equal(0xBE, b[2])
-  call assert_equal(0xEF, b[3])
-  call assert_fails('let x = b[4]')
-
-  call assert_equal(0xDE, get(b, 0))
-  call assert_equal(0xEF, get(b, 3))
-
-  call assert_fails('let b = 0z1', 'E973:')
-  call assert_fails('let b = 0z1x', 'E973:')
-  call assert_fails('let b = 0z12345', 'E973:')
-
-  call assert_equal(0z, test_null_blob())
-
-  let b = 0z001122.33445566.778899.aabbcc.dd
-  call assert_equal(0z00112233445566778899aabbccdd, b)
-  call assert_fails('let b = 0z1.1')
-  call assert_fails('let b = 0z.')
-  call assert_fails('let b = 0z001122.')
-  call assert_fails('call get("", 1)', 'E896:')
-  call assert_equal(0, len(test_null_blob()))
+  let lines =<< trim END
+      VAR b = 0zDEADBEEF
+      call assert_equal(v:t_blob, type(b))
+      call assert_equal(4, len(b))
+      call assert_equal(0xDE, b[0])
+      call assert_equal(0xAD, b[1])
+      call assert_equal(0xBE, b[2])
+      call assert_equal(0xEF, b[3])
+      call assert_fails('VAR x = b[4]')
+
+      call assert_equal(0xDE, get(b, 0))
+      call assert_equal(0xEF, get(b, 3))
+
+      call assert_fails('VAR b = 0z1', 'E973:')
+      call assert_fails('VAR b = 0z1x', 'E973:')
+      call assert_fails('VAR b = 0z12345', 'E973:')
+
+      call assert_equal(0z, test_null_blob())
+
+      LET b = 0z001122.33445566.778899.aabbcc.dd
+      call assert_equal(0z00112233445566778899aabbccdd, b)
+      call assert_fails('VAR b = 0z1.1')
+      call assert_fails('VAR b = 0z.')
+      call assert_fails('VAR b = 0z001122.')
+      call assert_fails('call get("", 1)', 'E896:')
+      call assert_equal(0, len(test_null_blob()))
+  END
+  call CheckLegacyAndVim9Success(lines)
 endfunc
 
 " assignment to a blob
 func Test_blob_assign()
+  let lines =<< trim END
+      VAR b = 0zDEADBEEF
+      VAR b2 = b[1 : 2]
+      call assert_equal(0zADBE, b2)
+
+      VAR bcopy = b[:]
+      call assert_equal(b, bcopy)
+      call assert_false(b is bcopy)
+
+      LET b = 0zDEADBEEF
+      LET b2 = b
+      call assert_true(b is b2)
+      LET b[:] = 0z11223344
+      call assert_equal(0z11223344, b)
+      call assert_equal(0z11223344, b2)
+      call assert_true(b is b2)
+
+      LET b = 0zDEADBEEF
+      LET b[3 :] = 0z66
+      call assert_equal(0zDEADBE66, b)
+      LET b[: 1] = 0z8899
+      call assert_equal(0z8899BE66, b)
+
+      LET b = 0zDEADBEEF
+      LET b += 0z99
+      call assert_equal(0zDEADBEEF99, b)
+
+      VAR l = [0z12]
+      VAR m = deepcopy(l)
+      LET m[0] = 0z34  #" E742 or E741 should not occur.
+  END
+  call CheckLegacyAndVim9Success(lines)
+
+  " TODO: move to above once it works
   let b = 0zDEADBEEF
-  let b2 = b[1:2]
-  call assert_equal(0zADBE, b2)
-
-  let bcopy = b[:]
-  call assert_equal(b, bcopy)
-  call assert_false(b is bcopy)
-
-  let b = 0zDEADBEEF
-  let b2 = b
-  call assert_true(b is b2)
-  let b[:] = 0z11223344
-  call assert_equal(0z11223344, b)
-  call assert_equal(0z11223344, b2)
-  call assert_true(b is b2)
+  call assert_fails('let b[2 : 3] = 0z112233', 'E972:')
+  call assert_fails('let b[2 : 3] = 0z11', 'E972:')
+  call assert_fails('let b[3 : 2] = 0z', 'E979:')
 
-  let b = 0zDEADBEEF
-  let b[3:] = 0z66
-  call assert_equal(0zDEADBE66, b)
-  let b[:1] = 0z8899
-  call assert_equal(0z8899BE66, b)
-
-  call assert_fails('let b[2:3] = 0z112233', 'E972:')
-  call assert_fails('let b[2:3] = 0z11', 'E972:')
-  call assert_fails('let b[3:2] = 0z', 'E979:')
-
-  let b = 0zDEADBEEF
-  let b += 0z99
-  call assert_equal(0zDEADBEEF99, b)
-
-  call assert_fails('let b .= 0z33', 'E734:')
-  call assert_fails('let b .= "xx"', 'E734:')
+  call assert_fails('let b ..= 0z33', 'E734:')
+  call assert_fails('let b ..= "xx"', 'E734:')
   call assert_fails('let b += "xx"', 'E734:')
-  call assert_fails('let b[1:1] .= 0z55', 'E734:')
-
-  let l = [0z12]
-  let m = deepcopy(l)
-  let m[0] = 0z34      " E742 or E741 should not occur.
+  call assert_fails('let b[1 : 1] ..= 0z55', 'E734:')
 endfunc
 
 func Test_blob_get_range()
index f17f141dba979c2e9ffd2283a9c01719719173ab..e05f8590bbbe85940d3c7ffa79bb1676e8e23f06 100644 (file)
@@ -133,3 +133,38 @@ def CheckDefExecAndScriptFailure2(
   CheckDefExecFailure(lines, errorDef, lnum)
   CheckScriptFailure(['vim9script'] + lines, errorScript, lnum + 1)
 enddef
+
+
+" Check that "lines" inside a legacy function has no error.
+func CheckLegacySuccess(lines)
+  let cwd = getcwd()
+  let fname = 'XlegacySuccess' .. s:sequence
+  let s:sequence += 1
+  call writefile(['func Func()'] + a:lines + ['endfunc'], fname)
+  try
+    exe 'so ' .. fname
+    call Func()
+    delfunc! Func
+  finally
+    call chdir(cwd)
+    call delete(fname)
+  endtry
+endfunc
+
+" Execute "lines" in a legacy function, :def function and Vim9 script.
+" Use 'VAR' for a declaration.
+" Use 'LET' for an assignment
+" Use ' #"' for a comment
+def CheckLegacyAndVim9Success(lines: list<string>)
+  var legacylines = lines->mapnew((_, v) =>
+                               v->substitute('\<VAR\>', 'let', 'g')
+                                ->substitute('\<LET\>', 'let', 'g')
+                                ->substitute('#"', ' "', 'g'))
+  CheckLegacySuccess(legacylines)
+
+  var vim9lines = lines->mapnew((_, v) =>
+                               v->substitute('\<VAR\>', 'var', 'g')
+                                ->substitute('\<LET ', '', 'g'))
+  CheckDefSuccess(vim9lines)
+  CheckScriptSuccess(['vim9script'] + vim9lines)
+enddef
index 0dc382f43b8d9b08e144a7a32b2d2f1795cec203..3728f320e6081c9b4c465c9ada552fba11c56569 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2757,
 /**/
     2756,
 /**/
index c30e597adbde0c8a7ecf11b3b10a8a55aa3c66af..4a10e12b1cad365b0a467ccf08a5ae20be09a85c 100644 (file)
@@ -57,6 +57,8 @@ typedef enum {
     ISN_STORENR,    // store number into local variable isn_arg.storenr.stnr_idx
     ISN_STOREINDEX,    // store into list or dictionary, type isn_arg.vartype,
                        // value/index/variable on stack
+    ISN_STORERANGE,    // store into blob,
+                       // value/index 1/index 2/variable on stack
 
     ISN_UNLET,         // unlet variable isn_arg.unlet.ul_name
     ISN_UNLETENV,      // unlet environment variable isn_arg.unlet.ul_name
index 9263659640f0fea090b99bfde89698ba82ac06be..bcba1e3224696d7c7faf09414d2599c3fb7d029e 100644 (file)
@@ -6064,38 +6064,48 @@ compile_lhs(
 compile_assign_index(
        char_u  *var_start,
        lhs_T   *lhs,
-       int     is_assign,
        int     *range,
        cctx_T  *cctx)
 {
     size_t     varlen = lhs->lhs_varlen;
     char_u     *p;
     int                r = OK;
+    int                need_white_before = TRUE;
+    int                empty_second;
 
     p = var_start + varlen;
     if (*p == '[')
     {
        p = skipwhite(p + 1);
-       r = compile_expr0(&p, cctx);
+       if (*p == ':')
+       {
+           // empty first index, push zero
+           r = generate_PUSHNR(cctx, 0);
+           need_white_before = FALSE;
+       }
+       else
+           r = compile_expr0(&p, cctx);
 
        if (r == OK && *skipwhite(p) == ':')
        {
            // unlet var[idx : idx]
-           if (is_assign)
-           {
-               semsg(_(e_cannot_use_range_with_assignment_str), p);
-               return FAIL;
-           }
+           // blob[idx : idx] = value
            *range = TRUE;
            p = skipwhite(p);
-           if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
+           empty_second = *skipwhite(p + 1) == ']';
+           if ((need_white_before && !IS_WHITE_OR_NUL(p[-1]))
+                   || (!empty_second && !IS_WHITE_OR_NUL(p[1])))
            {
                semsg(_(e_white_space_required_before_and_after_str_at_str),
                                                                      ":", p);
                return FAIL;
            }
            p = skipwhite(p + 1);
-           r = compile_expr0(&p, cctx);
+           if (*p == ']')
+               // empty second index, push "none"
+               r = generate_PUSHSPEC(cctx, VVAL_NONE);
+           else
+               r = compile_expr0(&p, cctx);
        }
 
        if (r == OK && *skipwhite(p) != ']')
@@ -6175,8 +6185,14 @@ compile_assign_unlet(
     garray_T    *stack = &cctx->ctx_type_stack;
     int                range = FALSE;
 
-    if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL)
+    if (compile_assign_index(var_start, lhs, &range, cctx) == FAIL)
        return FAIL;
+    if (is_assign && range && lhs->lhs_type != &t_blob
+                                                   && lhs->lhs_type != &t_any)
+    {
+       semsg(_(e_cannot_use_range_with_assignment_str), var_start);
+       return FAIL;
+    }
 
     if (lhs->lhs_type == &t_any)
     {
@@ -6213,15 +6229,24 @@ compile_assign_unlet(
     if (compile_load_lhs(lhs, var_start, rhs_type, cctx) == FAIL)
        return FAIL;
 
-    if (dest_type == VAR_LIST || dest_type == VAR_DICT || dest_type == VAR_ANY)
+    if (dest_type == VAR_LIST || dest_type == VAR_DICT
+                             || dest_type == VAR_BLOB || dest_type == VAR_ANY)
     {
        if (is_assign)
        {
-           isn_T       *isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3);
+           if (range)
+           {
+               if (generate_instr_drop(cctx, ISN_STORERANGE, 4) == NULL)
+                   return FAIL;
+           }
+           else
+           {
+               isn_T   *isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3);
 
-           if (isn == NULL)
-               return FAIL;
-           isn->isn_arg.vartype = dest_type;
+               if (isn == NULL)
+                   return FAIL;
+               isn->isn_arg.vartype = dest_type;
+           }
        }
        else if (range)
        {
@@ -6443,8 +6468,14 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                            // Get member from list or dict.  First compile the
                            // index value.
                            if (compile_assign_index(var_start, &lhs,
-                                                  TRUE, &range, cctx) == FAIL)
+                                                        &range, cctx) == FAIL)
                                goto theend;
+                           if (range)
+                           {
+                               semsg(_(e_cannot_use_range_with_assignment_str),
+                                                                   var_start);
+                               return FAIL;
+                           }
 
                            // Get the member.
                            if (compile_member(FALSE, cctx) == FAIL)
@@ -9315,6 +9346,7 @@ delete_instr(isn_T *isn)
        case ISN_SLICE:
        case ISN_STORE:
        case ISN_STOREINDEX:
+       case ISN_STORERANGE:
        case ISN_STORENR:
        case ISN_STOREOUTER:
        case ISN_STOREREG:
index 94d6f45232daf534e289000312e18230c5e7915b..026a9ee45d88a0adeb6f727e3f86c219d35bc533 100644 (file)
@@ -2219,6 +2219,10 @@ call_def_function(
                            clear_tv(tv);
                        }
                    }
+                   else if (status == OK && dest_type == VAR_BLOB)
+                   {
+                       // TODO
+                   }
                    else
                    {
                        status = FAIL;
@@ -2236,6 +2240,60 @@ call_def_function(
                }
                break;
 
+           // store value in blob range
+           case ISN_STORERANGE:
+               {
+                   typval_T    *tv_idx1 = STACK_TV_BOT(-3);
+                   typval_T    *tv_idx2 = STACK_TV_BOT(-2);
+                   typval_T    *tv_dest = STACK_TV_BOT(-1);
+                   int         status = OK;
+
+                   // Stack contains:
+                   // -4 value to be stored
+                   // -3 first index or "none"
+                   // -2 second index or "none"
+                   // -1 destination blob
+                   tv = STACK_TV_BOT(-4);
+                   if (tv_dest->v_type != VAR_BLOB)
+                   {
+                       status = FAIL;
+                       emsg(_(e_blob_required));
+                   }
+                   else
+                   {
+                       varnumber_T n1;
+                       varnumber_T n2;
+                       int         error = FALSE;
+
+                       n1 = tv_get_number_chk(tv_idx1, &error);
+                       if (error)
+                           status = FAIL;
+                       else
+                       {
+                           if (tv_idx2->v_type == VAR_SPECIAL
+                                       && tv_idx2->vval.v_number == VVAL_NONE)
+                               n2 = blob_len(tv_dest->vval.v_blob) - 1;
+                           else
+                               n2 = tv_get_number_chk(tv_idx2, &error);
+                           if (error)
+                               status = FAIL;
+                           else
+                               status = blob_set_range(tv_dest->vval.v_blob,
+                                                                  n1, n2, tv);
+                       }
+                   }
+
+                   clear_tv(tv_idx1);
+                   clear_tv(tv_idx2);
+                   clear_tv(tv_dest);
+                   ectx.ec_stack.ga_len -= 4;
+                   clear_tv(tv);
+
+                   if (status == FAIL)
+                       goto on_error;
+               }
+               break;
+
            // load or store variable or argument from outer scope
            case ISN_LOADOUTER:
            case ISN_STOREOUTER:
@@ -4362,6 +4420,10 @@ ex_disassemble(exarg_T *eap)
                }
                break;
 
+           case ISN_STORERANGE:
+               smsg("%4d STORERANGE", current);
+               break;
+
            // constants
            case ISN_PUSHNR:
                smsg("%4d PUSHNR %lld", current,