]> granicus.if.org Git - vim/commitdiff
patch 8.2.2533: Vim9: cannot use a range with :unlet v8.2.2533
authorBram Moolenaar <Bram@vim.org>
Sat, 20 Feb 2021 16:04:02 +0000 (17:04 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 20 Feb 2021 16:04:02 +0000 (17:04 +0100)
Problem:    Vim9: cannot use a range with :unlet.
Solution:   Implement ISN_UNLETRANGE.

src/errors.h
src/eval.c
src/evalvars.c
src/list.c
src/proto/evalvars.pro
src/proto/list.pro
src/testdir/test_vim9_assign.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index 05f90b14b1fe3bd61499a5a555ad87b46a814b7a..775c5324b03945750314aa7e4f95272abf278701 100644 (file)
@@ -365,3 +365,7 @@ EXTERN char e_variable_nr_type_mismatch_expected_str_but_got_str[]
        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"));
index 444c0faf388d1c27d52503f87ac99bfc424eb368..bf01ae0fdff6facf9bb38c7c2f7cee12321fb6a5 100644 (file)
@@ -1213,15 +1213,7 @@ get_lval(
 
            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);
-               }
-           }
+           lp->ll_li = list_find_index(lp->ll_list, &lp->ll_n1);
            if (lp->ll_li == NULL)
            {
                clear_tv(&var2);
index ce21fb255100d7e4f6e4142ebc2b3234142d1c85..d5c993d8ca38ab40f286697f5ae7519b0007053b 100644 (file)
@@ -1656,27 +1656,9 @@ do_unlet_var(
        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;
-       }
+       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
     {
@@ -1691,6 +1673,43 @@ do_unlet_var(
     return ret;
 }
 
+/*
+ * 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.
index 484ce74c985581c6c1fc81e6e6524ff3e3710a23..2af3085cc304db859c91594c77407ff5fc2d9e66 100644 (file)
@@ -530,6 +530,26 @@ list_find_str(list_T *l, long idx)
     return tv_get_string(&li->li_tv);
 }
 
+/*
+ * 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.
index 144dc7cd8be52c41933e3e46bff4f3e760ddd181..cf4db0bf112256af59ae6c76b79811a1aca99d40 100644 (file)
@@ -23,6 +23,7 @@ void list_hashtable_vars(hashtab_T *ht, char *prefix, int empty, int *first);
 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);
index 4ccc897808a942fd6c4707ee4bc235958b8d7bac..192981d862619db92415a8b4ea5620a0a4e8aa1d 100644 (file)
@@ -20,6 +20,7 @@ int list_equal(list_T *l1, list_T *l2, int ic, int recursive);
 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);
index 392a091a1a0712b818c1ea9a258b7aa4cffb1153..bcddc4f162a0f33456ce96a4f3fca0cf184f4197 100644 (file)
@@ -9,6 +9,7 @@ let g:existing = 'yes'
 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,6 +1415,51 @@ def Test_unlet()
   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']
index a107650d5d61b46e42a47b13b49643fdd2bc1889..f83f03879e19ada5d0ad59195e9059e5d5c7c4fe 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2533,
 /**/
     2532,
 /**/
index 1cf4b3c6597d057fd2087e9bfc4cab4610681bfa..ca8e64d175dbeb8ce6e046679e864af75a0b1541 100644 (file)
@@ -61,6 +61,7 @@ typedef enum {
     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
 
index b12d887d991f817258600b1f7008f77d601a138c..648f4e031cfde52d481b60e5361fb132e35a73a2 100644 (file)
@@ -5865,6 +5865,7 @@ compile_assign_unlet(
     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,6 +5873,27 @@ compile_assign_unlet(
     {
        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,17 +5919,29 @@ compile_assign_unlet(
     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
-               && need_type(((type_T **)stack->ga_data)[stack->ga_len - 1],
+       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;
+               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,6 +5980,11 @@ compile_assign_unlet(
                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,6 +8946,7 @@ delete_instr(isn_T *isn)
        case ISN_TRY:
        case ISN_TRYCONT:
        case ISN_UNLETINDEX:
+       case ISN_UNLETRANGE:
        case ISN_UNPACK:
            // nothing allocated
            break;
index fc195337b0fbf386b847a41f08cc4d430bcde233..6734f4f7daa1227b49f25e66173b7fb1ae68f9c2 100644 (file)
@@ -878,6 +878,21 @@ error_if_locked(int lock, char *error)
     return FALSE;
 }
 
+/*
+ * 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,12 +2193,9 @@ call_def_function(
                    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;
+                       if (check_for_number(tv_idx) == FAIL)
                        {
-                           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
@@ -2219,6 +2231,58 @@ call_def_function(
                }
                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,6 +4215,9 @@ ex_disassemble(exarg_T *eap)
            case ISN_UNLETINDEX:
                smsg("%4d UNLETINDEX", current);
                break;
+           case ISN_UNLETRANGE:
+               smsg("%4d UNLETRANGE", current);
+               break;
            case ISN_LOCKCONST:
                smsg("%4d LOCKCONST", current);
                break;