changeset 21828:af5db9b6d210

patch 8.2.1463: Vim9: list slice not supported yet Commit: https://github.com/vim/vim/commit/ed5918771fcf9877d8445e74c62ab1ce6b8e28c1 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Aug 15 22:14:53 2020 +0200 patch 8.2.1463: Vim9: list slice not supported yet Problem: Vim9: list slice not supported yet. Solution: Add support for list slicing.
author Bram Moolenaar <Bram@vim.org>
date Sat, 15 Aug 2020 22:15:03 +0200
parents fbafd1638f76
children 21c552fb7da5
files 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
diffstat 9 files changed, 175 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -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:
--- a/src/list.c
+++ b/src/list.c
@@ -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.
--- a/src/proto/list.pro
+++ b/src/proto/list.pro
@@ -34,6 +34,7 @@ void f_flatten(typval_T *argvars, typval
 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);
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -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
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -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)
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1463,
+/**/
     1462,
 /**/
     1461,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -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]
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -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;
 		}
-		if ((*typep)->tt_type == VAR_LIST)
-		    *typep = (*typep)->tt_member;
-		if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == 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;
+		}
 	    }
 	    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:
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -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",