]> granicus.if.org Git - vim/commitdiff
patch 8.2.1463: Vim9: list slice not supported yet v8.2.1463
authorBram Moolenaar <Bram@vim.org>
Sat, 15 Aug 2020 20:14:53 +0000 (22:14 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 15 Aug 2020 20:14:53 +0000 (22:14 +0200)
Problem:    Vim9: list slice not supported yet.
Solution:   Add support for list slicing.

src/eval.c
src/list.c
src/proto/list.pro
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_expr.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index cbbb9002bb2f1ce6e204e2f2298e097ef33a7cf6..8f685eda47e814312b8a3f5293188a8fb5836244 100644 (file)
@@ -3803,43 +3803,13 @@ eval_index(
                break;
 
            case VAR_LIST:
-               len = list_len(rettv->vval.v_list);
-               if (n1 < 0)
-                   n1 = len + n1;
-               if (!empty1 && (n1 < 0 || n1 >= len))
-               {
-                   // For a range we allow invalid values and return an empty
-                   // list.  A list index out of range is an error.
-                   if (!range)
-                   {
-                       if (verbose)
-                           semsg(_(e_listidx), n1);
-                       return FAIL;
-                   }
-                   n1 = len;
-               }
-               if (range)
-               {
-                   list_T      *l;
-
-                   if (n2 < 0)
-                       n2 = len + n2;
-                   else if (n2 >= len)
-                       n2 = len - 1;
-                   if (!empty2 && (n2 < 0 || n2 + 1 < n1))
-                       n2 = -1;
-                   l = list_slice(rettv->vval.v_list, n1, n2);
-                   if (l == NULL)
-                       return FAIL;
-                   clear_tv(rettv);
-                   rettv_list_set(rettv, l);
-               }
-               else
-               {
-                   copy_tv(&list_find(rettv->vval.v_list, n1)->li_tv, &var1);
-                   clear_tv(rettv);
-                   *rettv = var1;
-               }
+               if (empty1)
+                   n1 = 0;
+               if (empty2)
+                   n2 = -1;
+               if (list_slice_or_index(rettv->vval.v_list,
+                                       range, n1, n2, rettv, verbose) == FAIL)
+                   return FAIL;
                break;
 
            case VAR_DICT:
index 24b49d83ce087cd825bf190190e4ce71c27a0a6a..955272c37bae1fd387c89563891a8cad2502f0da 100644 (file)
@@ -888,6 +888,61 @@ list_slice(list_T *ol, long n1, long n2)
     return l;
 }
 
+    int
+list_slice_or_index(
+           list_T      *list,
+           int         range,
+           long        n1_arg,
+           long        n2_arg,
+           typval_T    *rettv,
+           int         verbose)
+{
+    long       len = list_len(list);
+    long       n1 = n1_arg;
+    long       n2 = n2_arg;
+    typval_T   var1;
+
+    if (n1 < 0)
+       n1 = len + n1;
+    if (n1 < 0 || n1 >= len)
+    {
+       // For a range we allow invalid values and return an empty
+       // list.  A list index out of range is an error.
+       if (!range)
+       {
+           if (verbose)
+               semsg(_(e_listidx), n1);
+           return FAIL;
+       }
+       n1 = len;
+    }
+    if (range)
+    {
+       list_T  *l;
+
+       if (n2 < 0)
+           n2 = len + n2;
+       else if (n2 >= len)
+           n2 = len - 1;
+       if (n2 < 0 || n2 + 1 < n1)
+           n2 = -1;
+       l = list_slice(list, n1, n2);
+       if (l == NULL)
+           return FAIL;
+       clear_tv(rettv);
+       rettv_list_set(rettv, l);
+    }
+    else
+    {
+       // copy the item to "var1" to avoid that freeing the list makes it
+       // invalid.
+       copy_tv(&list_find(list, n1)->li_tv, &var1);
+       clear_tv(rettv);
+       *rettv = var1;
+    }
+    return OK;
+}
+
 /*
  * Make a copy of list "orig".  Shallow if "deep" is FALSE.
  * The refcount of the new list is set to 1.
index 53502ae72398951f7ecf2f0775f7204fbc6be909..5a2feea2e3a8caa45ffc88c7d113728077996f5b 100644 (file)
@@ -34,6 +34,7 @@ void f_flatten(typval_T *argvars, typval_T *rettv);
 int list_extend(list_T *l1, list_T *l2, listitem_T *bef);
 int list_concat(list_T *l1, list_T *l2, typval_T *tv);
 list_T *list_slice(list_T *ol, long n1, long n2);
+int list_slice_or_index(list_T *list, int range, long n1_arg, long n2_arg, typval_T *rettv, int verbose);
 list_T *list_copy(list_T *orig, int deep, int copyID);
 void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2);
 char_u *list2string(typval_T *tv, int copyID, int restore_copyID);
index d901cb033bab6cbea43a10860e70ba3d10feeb65..3ed36f3f8613c56ac8f6e49b8dce6a3cd00c5ab4 100644 (file)
@@ -992,6 +992,28 @@ def Test_disassemble_string_index()
   assert_equal('b', StringIndex())
 enddef
 
+def StringSlice(): string
+  let s = "abcd"
+  let res = s[1:8]
+  return res
+enddef
+
+def Test_disassemble_string_slice()
+  let instr = execute('disassemble StringSlice')
+  assert_match('StringSlice\_s*' ..
+        'let s = "abcd"\_s*' ..
+        '\d PUSHS "abcd"\_s*' ..
+        '\d STORE $0\_s*' ..
+        'let res = s\[1:8]\_s*' ..
+        '\d LOAD $0\_s*' ..
+        '\d PUSHNR 1\_s*' ..
+        '\d PUSHNR 8\_s*' ..
+        '\d STRSLICE\_s*' ..
+        '\d STORE $1\_s*',
+        instr)
+  assert_equal('bcd', StringSlice())
+enddef
+
 def ListIndex(): number
   let l = [1, 2, 3]
   let res = l[1]
@@ -1016,6 +1038,31 @@ def Test_disassemble_list_index()
   assert_equal(2, ListIndex())
 enddef
 
+def ListSlice(): list<number>
+  let l = [1, 2, 3]
+  let res = l[1:8]
+  return res
+enddef
+
+def Test_disassemble_list_slice()
+  let instr = execute('disassemble ListSlice')
+  assert_match('ListSlice\_s*' ..
+        'let l = \[1, 2, 3]\_s*' ..
+        '\d PUSHNR 1\_s*' ..
+        '\d PUSHNR 2\_s*' ..
+        '\d PUSHNR 3\_s*' ..
+        '\d NEWLIST size 3\_s*' ..
+        '\d STORE $0\_s*' ..
+        'let res = l\[1:8]\_s*' ..
+        '\d LOAD $0\_s*' ..
+        '\d PUSHNR 1\_s*' ..
+        '\d PUSHNR 8\_s*' ..
+        '\d LISTSLICE\_s*' ..
+        '\d STORE $1\_s*',
+        instr)
+  assert_equal([2, 3], ListSlice())
+enddef
+
 def DictMember(): number
   let d = #{item: 1}
   let res = d.item
index cae719d5ec7d5818507a0a4829d6587c62cccd46..6f3cedf902d4202d02a1307063d6ce6e2d4a99f2 100644 (file)
@@ -2121,6 +2121,34 @@ def Test_expr7_string_subscript()
   CheckScriptSuccess(['vim9script'] + lines)
 enddef
 
+def Test_expr7_list_subscript()
+  let lines =<< trim END
+    let list = [0, 1, 2, 3, 4]
+    assert_equal(0, list[0])
+    assert_equal(4, list[4])
+    assert_equal(4, list[-1])
+    assert_equal(0, list[-5])
+
+    assert_equal([0, 1, 2, 3, 4], list[0:4])
+    assert_equal([0, 1, 2, 3, 4], list[:])
+    assert_equal([1, 2, 3, 4], list[1:])
+    assert_equal([2, 3, 4], list[2:-1])
+    assert_equal([4], list[4:-1])
+    assert_equal([], list[5:-1])
+    assert_equal([], list[999:-1])
+
+    assert_equal([0, 1, 2, 3], list[0:3])
+    assert_equal([0], list[0:0])
+    assert_equal([0, 1, 2, 3, 4], list[0:-1])
+    assert_equal([0, 1, 2], list[0:-3])
+    assert_equal([0], list[0:-5])
+    assert_equal([], list[0:-6])
+    assert_equal([], list[0:-99])
+  END
+  CheckDefSuccess(lines)
+  CheckScriptSuccess(['vim9script'] + lines)
+enddef
+
 def Test_expr7_subscript_linebreak()
   let range = range(
                3)
index febb53b8aaa5cca5988ee0b17aa3a884928c78c4..864c14bd9fb13e2e7c96d4b94dd4c3969b48f8f1 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1463,
 /**/
     1462,
 /**/
index 5494486876745a2155228a59f470688243c28c57..ce92d036274e2d389eca8ded4180ac751d6aac5b 100644 (file)
@@ -119,6 +119,7 @@ typedef enum {
     ISN_STRINDEX,   // [expr] string index
     ISN_STRSLICE,   // [expr:expr] string slice
     ISN_LISTINDEX,  // [expr] list index
+    ISN_LISTSLICE,  // [expr:expr] list slice
     ISN_SLICE,     // drop isn_arg.number items from start of list
     ISN_GETITEM,    // push list item, isn_arg.number is the index
     ISN_MEMBER,            // dict[member]
index c086c1cf81b41e4dc34516e2fae90f1c10932396..da44770f3e74b4239fe3d2c6f6228b9baca61e60 100644 (file)
@@ -3171,13 +3171,16 @@ compile_subscript(
            {
                if (is_slice)
                {
-                   emsg("Sorry, list slice not implemented yet");
-                   return FAIL;
+                   if (generate_instr_drop(cctx, ISN_LISTSLICE, 2) == FAIL)
+                       return FAIL;
+               }
+               else
+               {
+                   if ((*typep)->tt_type == VAR_LIST)
+                       *typep = (*typep)->tt_member;
+                   if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
+                       return FAIL;
                }
-               if ((*typep)->tt_type == VAR_LIST)
-                   *typep = (*typep)->tt_member;
-               if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
-                   return FAIL;
            }
            else
            {
@@ -7095,6 +7098,7 @@ delete_instr(isn_T *isn)
        case ISN_EXECUTE:
        case ISN_FOR:
        case ISN_LISTINDEX:
+       case ISN_LISTSLICE:
        case ISN_STRINDEX:
        case ISN_STRSLICE:
        case ISN_GETITEM:
index a0a4ea2a65ada160f217e286983e010af9b3e463..7cf2d0c3b54b43cd60e101fef5af5a0298642f87 100644 (file)
@@ -2286,14 +2286,17 @@ call_def_function(
                break;
 
            case ISN_LISTINDEX:
+           case ISN_LISTSLICE:
                {
+                   int         is_slice = iptr->isn_type == ISN_LISTSLICE;
                    list_T      *list;
-                   varnumber_T n;
+                   varnumber_T n1, n2;
                    listitem_T  *li;
-                   typval_T    temp_tv;
 
                    // list index: list is at stack-2, index at stack-1
-                   tv = STACK_TV_BOT(-2);
+                   // list slice: list is at stack-3, indexes at stack-2 and
+                   // stack-1
+                   tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2);
                    if (tv->v_type != VAR_LIST)
                    {
                        SOURCING_LNUM = iptr->isn_lnum;
@@ -2309,21 +2312,27 @@ call_def_function(
                        emsg(_(e_number_exp));
                        goto on_error;
                    }
-                   n = tv->vval.v_number;
+                   n1 = n2 = tv->vval.v_number;
                    clear_tv(tv);
-                   if ((li = list_find(list, n)) == NULL)
+
+                   if (is_slice)
                    {
-                       SOURCING_LNUM = iptr->isn_lnum;
-                       semsg(_(e_listidx), n);
-                       goto on_error;
+                       tv = STACK_TV_BOT(-2);
+                       if (tv->v_type != VAR_NUMBER)
+                       {
+                           SOURCING_LNUM = iptr->isn_lnum;
+                           emsg(_(e_number_exp));
+                           goto on_error;
+                       }
+                       n1 = tv->vval.v_number;
+                       clear_tv(tv);
                    }
-                   --ectx.ec_stack.ga_len;
-                   // Clear the list after getting the item, to avoid that it
-                   // makes the item invalid.
+
+                   ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
                    tv = STACK_TV_BOT(-1);
-                   temp_tv = *tv;
-                   copy_tv(&li->li_tv, tv);
-                   clear_tv(&temp_tv);
+                   if (list_slice_or_index(list, is_slice, n1, n2, tv, TRUE)
+                                                                      == FAIL)
+                       goto on_error;
                }
                break;
 
@@ -3162,6 +3171,7 @@ ex_disassemble(exarg_T *eap)
            case ISN_STRINDEX: smsg("%4d STRINDEX", current); break;
            case ISN_STRSLICE: smsg("%4d STRSLICE", current); break;
            case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break;
+           case ISN_LISTSLICE: smsg("%4d LISTSLICE", current); break;
            case ISN_SLICE: smsg("%4d SLICE %lld",
                                         current, iptr->isn_arg.number); break;
            case ISN_GETITEM: smsg("%4d ITEM %lld",