Patch 8.2.1463
Problem: Vim9: list slice not supported yet.
Solution: Add support for list slicing.
Files: src/vim9compile.c, src/vim9.h, src/vim9execute.c, src/eval.c,
src/list.c, src/proto/
list.pro, src/testdir/test_vim9_expr.vim,
src/testdir/test_vim9_disassemble.vim
*** ../vim-8.2.1462/src/vim9compile.c 2020-08-15 21:09:03.281675788 +0200
--- src/vim9compile.c 2020-08-15 21:30:07.177766759 +0200
***************
*** 3171,3183 ****
{
if (is_slice)
{
! emsg("Sorry, list slice not implemented yet");
! return FAIL;
}
- if ((*typep)->tt_type == VAR_LIST)
- *typep = (*typep)->tt_member;
- if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
- return FAIL;
}
else
{
--- 3171,3186 ----
{
if (is_slice)
{
! 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;
}
}
else
{
***************
*** 7095,7100 ****
--- 7098,7104 ----
case ISN_EXECUTE:
case ISN_FOR:
case ISN_LISTINDEX:
+ case ISN_LISTSLICE:
case ISN_STRINDEX:
case ISN_STRSLICE:
case ISN_GETITEM:
*** ../vim-8.2.1462/src/vim9.h 2020-08-15 21:09:03.281675788 +0200
--- src/vim9.h 2020-08-15 21:29:53.329859397 +0200
***************
*** 119,124 ****
--- 119,125 ----
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]
*** ../vim-8.2.1462/src/vim9execute.c 2020-08-15 21:09:03.281675788 +0200
--- src/vim9execute.c 2020-08-15 22:02:02.665053762 +0200
***************
*** 2286,2299 ****
break;
case ISN_LISTINDEX:
{
list_T *list;
! varnumber_T n;
listitem_T *li;
- typval_T temp_tv;
// list index: list is at stack-2, index at stack-1
! tv = STACK_TV_BOT(-2);
if (tv->v_type != VAR_LIST)
{
SOURCING_LNUM = iptr->isn_lnum;
--- 2286,2302 ----
break;
case ISN_LISTINDEX:
+ case ISN_LISTSLICE:
{
+ int is_slice = iptr->isn_type == ISN_LISTSLICE;
list_T *list;
! varnumber_T n1, n2;
listitem_T *li;
// list index: list is at stack-2, index at stack-1
! // 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,2329 ****
emsg(_(e_number_exp));
goto on_error;
}
! n = tv->vval.v_number;
clear_tv(tv);
! if ((li = list_find(list, n)) == NULL)
{
! SOURCING_LNUM = iptr->isn_lnum;
! semsg(_(e_listidx), n);
! goto on_error;
}
! --ectx.ec_stack.ga_len;
! // Clear the list after getting the item, to avoid that it
! // makes the item invalid.
tv = STACK_TV_BOT(-1);
! temp_tv = *tv;
! copy_tv(&li->li_tv, tv);
! clear_tv(&temp_tv);
}
break;
--- 2312,2338 ----
emsg(_(e_number_exp));
goto on_error;
}
! n1 = n2 = tv->vval.v_number;
clear_tv(tv);
!
! if (is_slice)
{
! 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 -= is_slice ? 2 : 1;
tv = STACK_TV_BOT(-1);
! if (list_slice_or_index(list, is_slice, n1, n2, tv, TRUE)
! == FAIL)
! goto on_error;
}
break;
***************
*** 3162,3167 ****
--- 3171,3177 ----
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",
*** ../vim-8.2.1462/src/eval.c 2020-08-15 21:09:03.281675788 +0200
--- src/eval.c 2020-08-15 22:03:24.532503636 +0200
***************
*** 3803,3845 ****
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;
! }
break;
case VAR_DICT:
--- 3803,3815 ----
break;
case VAR_LIST:
! 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:
*** ../vim-8.2.1462/src/list.c 2020-08-13 22:47:20.373992741 +0200
--- src/list.c 2020-08-15 22:03:57.304283278 +0200
***************
*** 888,893 ****
--- 888,948 ----
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.
*** ../vim-8.2.1462/src/proto/
list.pro 2020-07-01 18:29:23.681143435 +0200
--- src/proto/
list.pro 2020-08-15 22:01:50.973132279 +0200
***************
*** 34,39 ****
--- 34,40 ----
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);
*** ../vim-8.2.1462/src/testdir/test_vim9_expr.vim 2020-08-15 21:09:03.281675788 +0200
--- src/testdir/test_vim9_expr.vim 2020-08-15 22:07:14.246957600 +0200
***************
*** 2121,2126 ****
--- 2121,2154 ----
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)
*** ../vim-8.2.1462/src/testdir/test_vim9_disassemble.vim 2020-08-15 21:09:03.281675788 +0200
--- src/testdir/test_vim9_disassemble.vim 2020-08-15 22:11:37.917180026 +0200
***************
*** 992,997 ****
--- 992,1019 ----
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,1021 ****
--- 1038,1068 ----
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
*** ../vim-8.2.1462/src/version.c 2020-08-15 21:09:03.281675788 +0200
--- src/version.c 2020-08-15 21:29:27.942029098 +0200
***************
*** 756,757 ****
--- 756,759 ----
{ /* Add new patch number below this line */
+ /**/
+ 1463,
/**/
--
hundred-and-one symptoms of being an internet addict:
214. Your MCI "Circle of Friends" are all Hayes-compatible.
/// Bram Moolenaar -- Br...@Moolenaar.net --
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 ///