Patch 8.2.2533
Problem:    Vim9: cannot use a range with :unlet.
Solution:   Implement ISN_UNLETRANGE.
Files:      src/errors.h, src/eval.c, src/evalvars.c, src/list.c,
            src/proto/evalvars.pro, src/proto/list.pro, src/vim9.h,
            src/vim9compile.c, src/vim9execute.c
            src/testdir/test_vim9_assign.vim


*** ../vim-8.2.2532/src/errors.h        2021-02-14 12:57:32.552655477 +0100
--- src/errors.h        2021-02-20 16:34:28.669597296 +0100
***************
*** 365,367 ****
--- 365,371 ----
        INIT(= N_("E1163: Variable %d: type mismatch, expected %s but got %s"));
  EXTERN char e_vim9cmd_must_be_followed_by_command[]
        INIT(= N_("E1164: vim9cmd must be followed by a command"));
+ EXTERN char e_cannot_use_range_with_assignment_str[]
+       INIT(= N_("E1165: Cannot use a range with an assignment: %s"));
+ EXTERN char e_cannot_use_range_with_dictionary[]
+       INIT(= N_("E1166: Cannot use a range with a dictionary"));
*** ../vim-8.2.2532/src/eval.c  2021-02-17 18:49:07.968110009 +0100
--- src/eval.c  2021-02-20 16:34:28.669597296 +0100
***************
*** 1213,1227 ****
  
            lp->ll_dict = NULL;
            lp->ll_list = lp->ll_tv->vval.v_list;
!           lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
!           if (lp->ll_li == NULL)
!           {
!               if (lp->ll_n1 < 0)
!               {
!                   lp->ll_n1 = 0;
!                   lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
!               }
!           }
            if (lp->ll_li == NULL)
            {
                clear_tv(&var2);
--- 1213,1219 ----
  
            lp->ll_dict = NULL;
            lp->ll_list = lp->ll_tv->vval.v_list;
!           lp->ll_li = list_find_index(lp->ll_list, &lp->ll_n1);
            if (lp->ll_li == NULL)
            {
                clear_tv(&var2);
*** ../vim-8.2.2532/src/evalvars.c      2021-02-17 15:05:41.544996596 +0100
--- src/evalvars.c      2021-02-20 16:34:28.669597296 +0100
***************
*** 1656,1682 ****
        return FAIL;
      else if (lp->ll_range)
      {
!       listitem_T    *li;
!       listitem_T    *ll_li = lp->ll_li;
!       int           ll_n1 = lp->ll_n1;
! 
!       while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1))
!       {
!           li = ll_li->li_next;
!           if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE))
!               return FAIL;
!           ll_li = li;
!           ++ll_n1;
!       }
! 
!       // Delete a range of List items.
!       while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1))
!       {
!           li = lp->ll_li->li_next;
!           listitem_remove(lp->ll_list, lp->ll_li);
!           lp->ll_li = li;
!           ++lp->ll_n1;
!       }
      }
      else
      {
--- 1656,1664 ----
        return FAIL;
      else if (lp->ll_range)
      {
!       if (list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_name, lp->ll_n1,
!                                          !lp->ll_empty2, lp->ll_n2) == FAIL)
!           return FAIL;
      }
      else
      {
***************
*** 1692,1697 ****
--- 1674,1716 ----
  }
  
  /*
+  * Unlet one item or a range of items from a list.
+  * Return OK or FAIL.
+  */
+     int
+ list_unlet_range(
+       list_T      *l,
+       listitem_T  *li_first,
+       char_u      *name,
+       long        n1_arg,
+       int         has_n2,
+       long        n2)
+ {
+     listitem_T  *li = li_first;
+     int               n1 = n1_arg;
+ 
+     while (li != NULL && (!has_n2 || n2 >= n1))
+     {
+       if (value_check_lock(li->li_tv.v_lock, name, FALSE))
+           return FAIL;
+       li = li->li_next;
+       ++n1;
+     }
+ 
+     // Delete a range of List items.
+     li = li_first;
+     n1 = n1_arg;
+     while (li != NULL && (!has_n2 || n2 >= n1))
+     {
+       listitem_T *next = li->li_next;
+ 
+       listitem_remove(l, li);
+       li = next;
+       ++n1;
+     }
+     return OK;
+ }
+ /*
   * "unlet" a variable.  Return OK if it existed, FAIL if not.
   * When "forceit" is TRUE don't complain if the variable doesn't exist.
   */
*** ../vim-8.2.2532/src/list.c  2021-02-11 21:19:30.522147936 +0100
--- src/list.c  2021-02-20 16:34:28.673597284 +0100
***************
*** 531,536 ****
--- 531,556 ----
  }
  
  /*
+  * Like list_find() but when a negative index is used that is not found use
+  * zero and set "idx" to zero.  Used for first index of a range.
+  */
+     listitem_T *
+ list_find_index(list_T *l, long *idx)
+ {
+     listitem_T *li = list_find(l, *idx);
+ 
+     if (li == NULL)
+     {
+       if (*idx < 0)
+       {
+           *idx = 0;
+           li = list_find(l, *idx);
+       }
+     }
+     return li;
+ }
+ 
+ /*
   * Locate "item" list "l" and return its index.
   * Returns -1 when "item" is not in the list.
   */
*** ../vim-8.2.2532/src/proto/evalvars.pro      2021-02-17 15:05:41.544996596 
+0100
--- src/proto/evalvars.pro      2021-02-20 16:44:23.779591812 +0100
***************
*** 23,28 ****
--- 23,29 ----
  void ex_unlet(exarg_T *eap);
  void ex_lockvar(exarg_T *eap);
  void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, 
int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie);
+ int list_unlet_range(list_T *l, listitem_T *li_first, char_u *name, long 
n1_arg, int has_n2, long n2);
  int do_unlet(char_u *name, int forceit);
  void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
  void del_menutrans_vars(void);
*** ../vim-8.2.2532/src/proto/list.pro  2021-02-01 20:14:44.566705066 +0100
--- src/proto/list.pro  2021-02-20 16:44:18.515604903 +0100
***************
*** 20,25 ****
--- 20,26 ----
  listitem_T *list_find(list_T *l, long n);
  long list_find_nr(list_T *l, long idx, int *errorp);
  char_u *list_find_str(list_T *l, long idx);
+ listitem_T *list_find_index(list_T *l, long *idx);
  long list_idx_of_item(list_T *l, listitem_T *item);
  void list_append(list_T *l, listitem_T *item);
  int list_append_tv(list_T *l, typval_T *tv);
*** ../vim-8.2.2532/src/vim9.h  2021-02-17 17:00:23.483706812 +0100
--- src/vim9.h  2021-02-20 16:34:28.673597284 +0100
***************
*** 61,66 ****
--- 61,67 ----
      ISN_UNLET,                // unlet variable isn_arg.unlet.ul_name
      ISN_UNLETENV,     // unlet environment variable isn_arg.unlet.ul_name
      ISN_UNLETINDEX,   // unlet item of list or dict
+     ISN_UNLETRANGE,   // unlet items of list
  
      ISN_LOCKCONST,    // lock constant value
  
*** ../vim-8.2.2532/src/vim9compile.c   2021-02-20 08:16:33.823363221 +0100
--- src/vim9compile.c   2021-02-20 16:34:28.673597284 +0100
***************
*** 5865,5870 ****
--- 5865,5871 ----
      vartype_T dest_type;
      size_t    varlen = lhs->lhs_varlen;
      garray_T    *stack = &cctx->ctx_type_stack;
+     int               range = FALSE;
  
      // Compile the "idx" in "var[idx]" or "key" in "var.key".
      p = var_start + varlen;
***************
*** 5872,5877 ****
--- 5873,5899 ----
      {
        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) != ']')
        {
            // this should not happen
***************
*** 5897,5913 ****
      else
      {
        dest_type = lhs->lhs_type->tt_type;
        if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL)
            return FAIL;
!       if (dest_type == VAR_LIST
!               && need_type(((type_T **)stack->ga_data)[stack->ga_len - 1],
                                 &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
!           return FAIL;
      }
  
      // Load the dict or list.  On the stack we then have:
      // - value (for assignment, not for :unlet)
      // - index
      // - variable
      if (lhs->lhs_dest == dest_expr)
      {
--- 5919,5947 ----
      else
      {
        dest_type = lhs->lhs_type->tt_type;
+       if (dest_type == VAR_DICT && range)
+       {
+           emsg(e_cannot_use_range_with_dictionary);
+           return FAIL;
+       }
        if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL)
            return FAIL;
!       if (dest_type == VAR_LIST)
!       {
!           if (range
!                 && need_type(((type_T **)stack->ga_data)[stack->ga_len - 2],
                                 &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
!               return FAIL;
!           if (need_type(((type_T **)stack->ga_data)[stack->ga_len - 1],
!                                &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
!               return FAIL;
!       }
      }
  
      // Load the dict or list.  On the stack we then have:
      // - value (for assignment, not for :unlet)
      // - index
+     // - for [a : b] second index
      // - variable
      if (lhs->lhs_dest == dest_expr)
      {
***************
*** 5946,5951 ****
--- 5980,5990 ----
                return FAIL;
            isn->isn_arg.vartype = dest_type;
        }
+       else if (range)
+       {
+           if (generate_instr_drop(cctx, ISN_UNLETRANGE, 3) == NULL)
+               return FAIL;
+       }
        else
        {
            if (generate_instr_drop(cctx, ISN_UNLETINDEX, 2) == NULL)
***************
*** 8907,8912 ****
--- 8946,8952 ----
        case ISN_TRY:
        case ISN_TRYCONT:
        case ISN_UNLETINDEX:
+       case ISN_UNLETRANGE:
        case ISN_UNPACK:
            // nothing allocated
            break;
*** ../vim-8.2.2532/src/vim9execute.c   2021-02-19 19:13:13.087904339 +0100
--- src/vim9execute.c   2021-02-20 16:34:28.673597284 +0100
***************
*** 879,884 ****
--- 879,899 ----
  }
  
  /*
+  * Give an error if "tv" is not a number and return FAIL.
+  */
+     static int
+ check_for_number(typval_T *tv)
+ {
+     if (tv->v_type != VAR_NUMBER)
+     {
+       semsg(_(e_expected_str_but_got_str),
+               vartype_name(VAR_NUMBER), vartype_name(tv->v_type));
+       return FAIL;
+     }
+     return OK;
+ }
+ 
+ /*
   * Store "tv" in variable "name".
   * This is for s: and g: variables.
   */
***************
*** 2178,2189 ****
                    else if (tv_dest->v_type == VAR_LIST)
                    {
                        // unlet a List item, index must be a number
!                       if (tv_idx->v_type != VAR_NUMBER)
                        {
-                           SOURCING_LNUM = iptr->isn_lnum;
-                           semsg(_(e_expected_str_but_got_str),
-                                       vartype_name(VAR_NUMBER),
-                                       vartype_name(tv_idx->v_type));
                            status = FAIL;
                        }
                        else
--- 2193,2201 ----
                    else if (tv_dest->v_type == VAR_LIST)
                    {
                        // unlet a List item, index must be a number
!                       SOURCING_LNUM = iptr->isn_lnum;
!                       if (check_for_number(tv_idx) == FAIL)
                        {
                            status = FAIL;
                        }
                        else
***************
*** 2219,2224 ****
--- 2231,2288 ----
                }
                break;
  
+           // unlet range of items in list variable
+           case ISN_UNLETRANGE:
+               {
+                   // Stack contains:
+                   // -3 index1
+                   // -2 index2
+                   // -1 dict or list
+                   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;
+ 
+                   if (tv_dest->v_type == VAR_LIST)
+                   {
+                       // indexes must be a number
+                       SOURCING_LNUM = iptr->isn_lnum;
+                       if (check_for_number(tv_idx1) == FAIL
+                               || check_for_number(tv_idx2) == FAIL)
+                       {
+                           status = FAIL;
+                       }
+                       else
+                       {
+                           list_T      *l = tv_dest->vval.v_list;
+                           long        n1 = (long)tv_idx1->vval.v_number;
+                           long        n2 = (long)tv_idx2->vval.v_number;
+                           listitem_T  *li;
+ 
+                           li = list_find_index(l, &n1);
+                           if (li == NULL
+                                    || list_unlet_range(l, li, NULL, n1,
+                                                           TRUE, n2) == FAIL)
+                               status = FAIL;
+                       }
+                   }
+                   else
+                   {
+                       status = FAIL;
+                       SOURCING_LNUM = iptr->isn_lnum;
+                       semsg(_(e_cannot_index_str),
+                                               vartype_name(tv_dest->v_type));
+                   }
+ 
+                   clear_tv(tv_idx1);
+                   clear_tv(tv_idx2);
+                   clear_tv(tv_dest);
+                   ectx.ec_stack.ga_len -= 3;
+                   if (status == FAIL)
+                       goto on_error;
+               }
+               break;
+ 
            // push constant
            case ISN_PUSHNR:
            case ISN_PUSHBOOL:
***************
*** 4151,4156 ****
--- 4215,4223 ----
            case ISN_UNLETINDEX:
                smsg("%4d UNLETINDEX", current);
                break;
+           case ISN_UNLETRANGE:
+               smsg("%4d UNLETRANGE", current);
+               break;
            case ISN_LOCKCONST:
                smsg("%4d LOCKCONST", current);
                break;
*** ../vim-8.2.2532/src/testdir/test_vim9_assign.vim    2021-02-17 
18:49:07.968110009 +0100
--- src/testdir/test_vim9_assign.vim    2021-02-20 16:34:28.673597284 +0100
***************
*** 9,14 ****
--- 9,15 ----
  let g:inc_counter = 1
  let $SOME_ENV_VAR = 'some'
  let g:alist = [7]
+ let g:adict = #{a: 1}
  let g:astring = 'text'
  
  def Test_assignment_bool()
***************
*** 1414,1419 ****
--- 1415,1465 ----
    unlet ll[-1]
    assert_equal([1, 3], ll)
  
+   ll = [1, 2, 3, 4]
+   unlet ll[0 : 1]
+   assert_equal([3, 4], ll)
+ 
+   ll = [1, 2, 3, 4]
+   unlet ll[2 : 8]
+   assert_equal([1, 2], ll)
+ 
+   ll = [1, 2, 3, 4]
+   unlet ll[-2 : -1]
+   assert_equal([1, 2], ll)
+ 
+   CheckDefFailure([
+     'var ll = [1, 2]',
+     'll[1 : 2] = 7',
+     ], 'E1165:', 2)
+   CheckDefFailure([
+     'var dd = {a: 1}',
+     'unlet dd["a" : "a"]',
+     ], 'E1166:', 2)
+   CheckDefExecFailure([
+     'unlet g:adict[0 : 1]',
+     ], 'E1148:', 1)
+   CheckDefFailure([
+     'var ll = [1, 2]',
+     'unlet ll[0:1]',
+     ], 'E1004:', 2)
+   CheckDefFailure([
+     'var ll = [1, 2]',
+     'unlet ll[0 :1]',
+     ], 'E1004:', 2)
+   CheckDefFailure([
+     'var ll = [1, 2]',
+     'unlet ll[0: 1]',
+     ], 'E1004:', 2)
+ 
+   CheckDefFailure([
+     'var ll = [1, 2]',
+     'unlet ll["x" : 1]',
+     ], 'E1012:', 2)
+   CheckDefFailure([
+     'var ll = [1, 2]',
+     'unlet ll[0 : "x"]',
+     ], 'E1012:', 2)
+ 
    # list of dict unlet
    var dl = [{a: 1, b: 2}, {c: 3}]
    unlet dl[0]['b']
*** ../vim-8.2.2532/src/version.c       2021-02-20 08:16:33.823363221 +0100
--- src/version.c       2021-02-20 16:42:10.316036025 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2533,
  /**/

-- 
Q: How do you tell the difference between a female cat and a male cat?
A: You ask it a question and if HE answers, it's a male but, if SHE
   answers, it's a female.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202102201605.11KG5AMD2997355%40masaka.moolenaar.net.

Raspunde prin e-mail lui