Patch 8.2.2757
Problem:    Vim9: blob tests for legacy and Vim9 script are separate.
Solution:   Add CheckLegacyAndVim9Success().  Make blob index assign work.
Files:      src/vim9compile.c, src/vim9.h, src/vim9execute.c, src/errors.h,
            src/blob.c, src/proto/blob.pro, src/eval.c, src/ex_docmd.c,
            src/testdir/vim9.vim, src/testdir/test_blob.vim


*** ../vim-8.2.2756/src/vim9compile.c   2021-04-11 20:26:30.486312262 +0200
--- src/vim9compile.c   2021-04-12 21:17:06.523249070 +0200
***************
*** 6064,6101 ****
  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;
  
      p = var_start + varlen;
      if (*p == '[')
      {
        p = skipwhite(p + 1);
!       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;
!           }
            *range = TRUE;
            p = skipwhite(p);
!           if (!IS_WHITE_OR_NUL(p[-1]) || !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 (r == OK && *skipwhite(p) != ']')
--- 6064,6111 ----
  compile_assign_index(
        char_u  *var_start,
        lhs_T   *lhs,
        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);
!       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]
!           // blob[idx : idx] = value
            *range = TRUE;
            p = skipwhite(p);
!           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);
!           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,6182 ****
      garray_T    *stack = &cctx->ctx_type_stack;
      int               range = FALSE;
  
!     if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL)
        return FAIL;
  
      if (lhs->lhs_type == &t_any)
      {
--- 6185,6198 ----
      garray_T    *stack = &cctx->ctx_type_stack;
      int               range = FALSE;
  
!     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,6227 ****
      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 (is_assign)
        {
!           isn_T       *isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3);
  
!           if (isn == NULL)
!               return FAIL;
!           isn->isn_arg.vartype = dest_type;
        }
        else if (range)
        {
--- 6229,6252 ----
      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_BLOB || dest_type == VAR_ANY)
      {
        if (is_assign)
        {
!           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;
!           }
        }
        else if (range)
        {
***************
*** 6443,6450 ****
                            // Get member from list or dict.  First compile the
                            // index value.
                            if (compile_assign_index(var_start, &lhs,
!                                                  TRUE, &range, cctx) == FAIL)
                                goto theend;
  
                            // Get the member.
                            if (compile_member(FALSE, cctx) == FAIL)
--- 6468,6481 ----
                            // Get member from list or dict.  First compile the
                            // index value.
                            if (compile_assign_index(var_start, &lhs,
!                                                        &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,9320 ****
--- 9346,9352 ----
        case ISN_SLICE:
        case ISN_STORE:
        case ISN_STOREINDEX:
+       case ISN_STORERANGE:
        case ISN_STORENR:
        case ISN_STOREOUTER:
        case ISN_STOREREG:
*** ../vim-8.2.2756/src/vim9.h  2021-04-11 20:26:30.486312262 +0200
--- src/vim9.h  2021-04-11 22:08:58.545439336 +0200
***************
*** 57,62 ****
--- 57,64 ----
      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
*** ../vim-8.2.2756/src/vim9execute.c   2021-04-11 20:26:30.486312262 +0200
--- src/vim9execute.c   2021-04-12 20:52:21.741867635 +0200
***************
*** 2219,2224 ****
--- 2219,2228 ----
                            clear_tv(tv);
                        }
                    }
+                   else if (status == OK && dest_type == VAR_BLOB)
+                   {
+                       // TODO
+                   }
                    else
                    {
                        status = FAIL;
***************
*** 2236,2241 ****
--- 2240,2299 ----
                }
                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,4367 ****
--- 4420,4429 ----
                }
                break;
  
+           case ISN_STORERANGE:
+               smsg("%4d STORERANGE", current);
+               break;
+ 
            // constants
            case ISN_PUSHNR:
                smsg("%4d PUSHNR %lld", current,
*** ../vim-8.2.2756/src/errors.h        2021-04-10 17:17:33.431942839 +0200
--- src/errors.h        2021-04-11 22:18:38.077671376 +0200
***************
*** 399,401 ****
--- 399,403 ----
        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"));
*** ../vim-8.2.2756/src/blob.c  2021-04-11 20:26:30.486312262 +0200
--- src/blob.c  2021-04-12 20:48:33.234630874 +0200
***************
*** 337,342 ****
--- 337,364 ----
  }
  
  /*
+  * 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
   */
      void
*** ../vim-8.2.2756/src/proto/blob.pro  2021-04-11 20:26:30.486312262 +0200
--- src/proto/blob.pro  2021-04-12 20:48:02.110737084 +0200
***************
*** 14,18 ****
--- 14,19 ----
  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 : */
*** ../vim-8.2.2756/src/eval.c  2021-04-11 20:26:30.486312262 +0200
--- src/eval.c  2021-04-12 20:47:13.250905213 +0200
***************
*** 1319,1341 ****
  
            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"));
                    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
            {
--- 1319,1330 ----
  
            if (lp->ll_range && rettv->v_type == VAR_BLOB)
            {
                if (lp->ll_empty2)
                    lp->ll_n2 = blob_len(lp->ll_blob) - 1;
  
!               if (blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2,
!                                                               rettv) == FAIL)
                    return;
            }
            else
            {
*** ../vim-8.2.2756/src/ex_docmd.c      2021-04-11 13:29:13.194824346 +0200
--- src/ex_docmd.c      2021-04-12 21:05:51.644473199 +0200
***************
*** 3429,3450 ****
                            // "varname.key" is an expression.
                         || (*p == '.' && ASCII_ISALPHA(p[1]))))
            {
!               char_u  *after = p;
  
                // When followed by "=" or "+=" then it is an assignment.
                ++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] == '=')
                                         || (after[0] == '.' && after[1] == '.'
                                                           && after[2] == '='))
!                   eap->cmdidx = CMD_var;
!               else
!                   eap->cmdidx = CMD_eval;
                --emsg_silent;
                return eap->cmd;
            }
--- 3429,3453 ----
                            // "varname.key" is an expression.
                         || (*p == '.' && ASCII_ISALPHA(p[1]))))
            {
!               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 (skip_expr(&after, NULL) == OK)
+               {
                    after = skipwhite(after);
!                   if (*after == '=' || (*after != NUL && after[1] == '=')
                                         || (after[0] == '.' && after[1] == '.'
                                                           && after[2] == '='))
!                       eap->cmdidx = CMD_var;
!               }
                --emsg_silent;
                return eap->cmd;
            }
*** ../vim-8.2.2756/src/testdir/vim9.vim        2021-04-04 20:49:46.626430253 
+0200
--- src/testdir/vim9.vim        2021-04-11 21:26:33.678728448 +0200
***************
*** 133,135 ****
--- 133,170 ----
    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
*** ../vim-8.2.2756/src/testdir/test_blob.vim   2020-10-15 22:29:13.566726912 
+0200
--- src/testdir/test_blob.vim   2021-04-12 21:07:56.612309041 +0200
***************
*** 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,81 ****
  
  " 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()))
  endfunc
  
  " assignment to a blob
  func Test_blob_assign()
!   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)
  
!   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 += "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.
  endfunc
  
  func Test_blob_get_range()
--- 11,91 ----
  
  " Blob creation from constant
  func Test_blob_create()
!   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
!   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:')
  
!   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:')
  endfunc
  
  func Test_blob_get_range()
*** ../vim-8.2.2756/src/version.c       2021-04-11 20:26:30.486312262 +0200
--- src/version.c       2021-04-11 21:09:35.779093423 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2757,
  /**/

-- 
Momento mori, ergo carpe diem

 /// Bram Moolenaar -- b...@moolenaar.net -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202104121921.13CJLb8B782832%40masaka.moolenaar.net.

Raspunde prin e-mail lui