To: vim_dev@googlegroups.com Subject: Patch 8.2.1463 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 + 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 -- Bram@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 ///