summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-08-15 22:14:53 +0200
committerBram Moolenaar <Bram@vim.org>2020-08-15 22:14:53 +0200
commited5918771fcf9877d8445e74c62ab1ce6b8e28c1 (patch)
tree6b2c25678e1d0b606ad7e6b82d2d6528b6b86be8
parent11107bab7ead9124f46a7ddf6aa3bb66b43a8246 (diff)
downloadvim-git-ed5918771fcf9877d8445e74c62ab1ce6b8e28c1.tar.gz
patch 8.2.1463: Vim9: list slice not supported yetv8.2.1463
Problem: Vim9: list slice not supported yet. Solution: Add support for list slicing.
-rw-r--r--src/eval.c44
-rw-r--r--src/list.c55
-rw-r--r--src/proto/list.pro1
-rw-r--r--src/testdir/test_vim9_disassemble.vim47
-rw-r--r--src/testdir/test_vim9_expr.vim28
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h1
-rw-r--r--src/vim9compile.c16
-rw-r--r--src/vim9execute.c38
9 files changed, 175 insertions, 57 deletions
diff --git a/src/eval.c b/src/eval.c
index cbbb9002b..8f685eda4 100644
--- 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:
diff --git a/src/list.c b/src/list.c
index 24b49d83c..955272c37 100644
--- 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.
diff --git a/src/proto/list.pro b/src/proto/list.pro
index 53502ae72..5a2feea2e 100644
--- a/src/proto/list.pro
+++ b/src/proto/list.pro
@@ -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);
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index d901cb033..3ed36f3f8 100644
--- 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
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index cae719d5e..6f3cedf90 100644
--- 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)
diff --git a/src/version.c b/src/version.c
index febb53b8a..864c14bd9 100644
--- 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,
diff --git a/src/vim9.h b/src/vim9.h
index 549448687..ce92d0362 100644
--- 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]
diff --git a/src/vim9compile.c b/src/vim9compile.c
index c086c1cf8..da44770f3 100644
--- 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;
+ }
+ 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:
diff --git a/src/vim9execute.c b/src/vim9execute.c
index a0a4ea2a6..7cf2d0c3b 100644
--- 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",