To: vim_dev@googlegroups.com Subject: Patch 8.2.2533 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2533 Problem: Vim9: cannot use a range with :unlet. Solution: Implement ISN_UNLETRANGE. Files: src/errors.h, src/eval.c, src/evalvars.c, src/list.c, src/proto/evalvars.pro, src/proto/list.pro, src/vim9.h, src/vim9compile.c, src/vim9execute.c src/testdir/test_vim9_assign.vim *** ../vim-8.2.2532/src/errors.h 2021-02-14 12:57:32.552655477 +0100 --- src/errors.h 2021-02-20 16:34:28.669597296 +0100 *************** *** 365,367 **** --- 365,371 ---- INIT(= N_("E1163: Variable %d: type mismatch, expected %s but got %s")); EXTERN char e_vim9cmd_must_be_followed_by_command[] INIT(= N_("E1164: vim9cmd must be followed by a command")); + EXTERN char e_cannot_use_range_with_assignment_str[] + INIT(= N_("E1165: Cannot use a range with an assignment: %s")); + EXTERN char e_cannot_use_range_with_dictionary[] + INIT(= N_("E1166: Cannot use a range with a dictionary")); *** ../vim-8.2.2532/src/eval.c 2021-02-17 18:49:07.968110009 +0100 --- src/eval.c 2021-02-20 16:34:28.669597296 +0100 *************** *** 1213,1227 **** lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; ! lp->ll_li = list_find(lp->ll_list, lp->ll_n1); ! if (lp->ll_li == NULL) ! { ! if (lp->ll_n1 < 0) ! { ! lp->ll_n1 = 0; ! lp->ll_li = list_find(lp->ll_list, lp->ll_n1); ! } ! } if (lp->ll_li == NULL) { clear_tv(&var2); --- 1213,1219 ---- lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; ! lp->ll_li = list_find_index(lp->ll_list, &lp->ll_n1); if (lp->ll_li == NULL) { clear_tv(&var2); *** ../vim-8.2.2532/src/evalvars.c 2021-02-17 15:05:41.544996596 +0100 --- src/evalvars.c 2021-02-20 16:34:28.669597296 +0100 *************** *** 1656,1682 **** return FAIL; else if (lp->ll_range) { ! listitem_T *li; ! listitem_T *ll_li = lp->ll_li; ! int ll_n1 = lp->ll_n1; ! ! while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) ! { ! li = ll_li->li_next; ! if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) ! return FAIL; ! ll_li = li; ! ++ll_n1; ! } ! ! // Delete a range of List items. ! while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) ! { ! li = lp->ll_li->li_next; ! listitem_remove(lp->ll_list, lp->ll_li); ! lp->ll_li = li; ! ++lp->ll_n1; ! } } else { --- 1656,1664 ---- return FAIL; else if (lp->ll_range) { ! if (list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_name, lp->ll_n1, ! !lp->ll_empty2, lp->ll_n2) == FAIL) ! return FAIL; } else { *************** *** 1692,1697 **** --- 1674,1716 ---- } /* + * Unlet one item or a range of items from a list. + * Return OK or FAIL. + */ + int + list_unlet_range( + list_T *l, + listitem_T *li_first, + char_u *name, + long n1_arg, + int has_n2, + long n2) + { + listitem_T *li = li_first; + int n1 = n1_arg; + + while (li != NULL && (!has_n2 || n2 >= n1)) + { + if (value_check_lock(li->li_tv.v_lock, name, FALSE)) + return FAIL; + li = li->li_next; + ++n1; + } + + // Delete a range of List items. + li = li_first; + n1 = n1_arg; + while (li != NULL && (!has_n2 || n2 >= n1)) + { + listitem_T *next = li->li_next; + + listitem_remove(l, li); + li = next; + ++n1; + } + return OK; + } + /* * "unlet" a variable. Return OK if it existed, FAIL if not. * When "forceit" is TRUE don't complain if the variable doesn't exist. */ *** ../vim-8.2.2532/src/list.c 2021-02-11 21:19:30.522147936 +0100 --- src/list.c 2021-02-20 16:34:28.673597284 +0100 *************** *** 531,536 **** --- 531,556 ---- } /* + * Like list_find() but when a negative index is used that is not found use + * zero and set "idx" to zero. Used for first index of a range. + */ + listitem_T * + list_find_index(list_T *l, long *idx) + { + listitem_T *li = list_find(l, *idx); + + if (li == NULL) + { + if (*idx < 0) + { + *idx = 0; + li = list_find(l, *idx); + } + } + return li; + } + + /* * Locate "item" list "l" and return its index. * Returns -1 when "item" is not in the list. */ *** ../vim-8.2.2532/src/proto/evalvars.pro 2021-02-17 15:05:41.544996596 +0100 --- src/proto/evalvars.pro 2021-02-20 16:44:23.779591812 +0100 *************** *** 23,28 **** --- 23,29 ---- void ex_unlet(exarg_T *eap); void ex_lockvar(exarg_T *eap); void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie); + int list_unlet_range(list_T *l, listitem_T *li_first, char_u *name, long n1_arg, int has_n2, long n2); int do_unlet(char_u *name, int forceit); void item_lock(typval_T *tv, int deep, int lock, int check_refcount); void del_menutrans_vars(void); *** ../vim-8.2.2532/src/proto/list.pro 2021-02-01 20:14:44.566705066 +0100 --- src/proto/list.pro 2021-02-20 16:44:18.515604903 +0100 *************** *** 20,25 **** --- 20,26 ---- listitem_T *list_find(list_T *l, long n); long list_find_nr(list_T *l, long idx, int *errorp); char_u *list_find_str(list_T *l, long idx); + listitem_T *list_find_index(list_T *l, long *idx); long list_idx_of_item(list_T *l, listitem_T *item); void list_append(list_T *l, listitem_T *item); int list_append_tv(list_T *l, typval_T *tv); *** ../vim-8.2.2532/src/vim9.h 2021-02-17 17:00:23.483706812 +0100 --- src/vim9.h 2021-02-20 16:34:28.673597284 +0100 *************** *** 61,66 **** --- 61,67 ---- ISN_UNLET, // unlet variable isn_arg.unlet.ul_name ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name ISN_UNLETINDEX, // unlet item of list or dict + ISN_UNLETRANGE, // unlet items of list ISN_LOCKCONST, // lock constant value *** ../vim-8.2.2532/src/vim9compile.c 2021-02-20 08:16:33.823363221 +0100 --- src/vim9compile.c 2021-02-20 16:34:28.673597284 +0100 *************** *** 5865,5870 **** --- 5865,5871 ---- vartype_T dest_type; size_t varlen = lhs->lhs_varlen; garray_T *stack = &cctx->ctx_type_stack; + int range = FALSE; // Compile the "idx" in "var[idx]" or "key" in "var.key". p = var_start + varlen; *************** *** 5872,5877 **** --- 5873,5899 ---- { p = skipwhite(p + 1); r = compile_expr0(&p, cctx); + + if (r == OK && *skipwhite(p) == ':') + { + // unlet var[idx : idx] + if (is_assign) + { + semsg(_(e_cannot_use_range_with_assignment_str), p); + return FAIL; + } + range = TRUE; + p = skipwhite(p); + if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1])) + { + semsg(_(e_white_space_required_before_and_after_str_at_str), + ":", p); + return FAIL; + } + p = skipwhite(p + 1); + r = compile_expr0(&p, cctx); + } + if (r == OK && *skipwhite(p) != ']') { // this should not happen *************** *** 5897,5913 **** else { dest_type = lhs->lhs_type->tt_type; if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL) return FAIL; ! if (dest_type == VAR_LIST ! && need_type(((type_T **)stack->ga_data)[stack->ga_len - 1], &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) ! return FAIL; } // Load the dict or list. On the stack we then have: // - value (for assignment, not for :unlet) // - index // - variable if (lhs->lhs_dest == dest_expr) { --- 5919,5947 ---- else { dest_type = lhs->lhs_type->tt_type; + if (dest_type == VAR_DICT && range) + { + emsg(e_cannot_use_range_with_dictionary); + return FAIL; + } if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL) return FAIL; ! if (dest_type == VAR_LIST) ! { ! if (range ! && need_type(((type_T **)stack->ga_data)[stack->ga_len - 2], &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) ! return FAIL; ! if (need_type(((type_T **)stack->ga_data)[stack->ga_len - 1], ! &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) ! return FAIL; ! } } // Load the dict or list. On the stack we then have: // - value (for assignment, not for :unlet) // - index + // - for [a : b] second index // - variable if (lhs->lhs_dest == dest_expr) { *************** *** 5946,5951 **** --- 5980,5990 ---- return FAIL; isn->isn_arg.vartype = dest_type; } + else if (range) + { + if (generate_instr_drop(cctx, ISN_UNLETRANGE, 3) == NULL) + return FAIL; + } else { if (generate_instr_drop(cctx, ISN_UNLETINDEX, 2) == NULL) *************** *** 8907,8912 **** --- 8946,8952 ---- case ISN_TRY: case ISN_TRYCONT: case ISN_UNLETINDEX: + case ISN_UNLETRANGE: case ISN_UNPACK: // nothing allocated break; *** ../vim-8.2.2532/src/vim9execute.c 2021-02-19 19:13:13.087904339 +0100 --- src/vim9execute.c 2021-02-20 16:34:28.673597284 +0100 *************** *** 879,884 **** --- 879,899 ---- } /* + * Give an error if "tv" is not a number and return FAIL. + */ + static int + check_for_number(typval_T *tv) + { + if (tv->v_type != VAR_NUMBER) + { + semsg(_(e_expected_str_but_got_str), + vartype_name(VAR_NUMBER), vartype_name(tv->v_type)); + return FAIL; + } + return OK; + } + + /* * Store "tv" in variable "name". * This is for s: and g: variables. */ *************** *** 2178,2189 **** else if (tv_dest->v_type == VAR_LIST) { // unlet a List item, index must be a number ! if (tv_idx->v_type != VAR_NUMBER) { - SOURCING_LNUM = iptr->isn_lnum; - semsg(_(e_expected_str_but_got_str), - vartype_name(VAR_NUMBER), - vartype_name(tv_idx->v_type)); status = FAIL; } else --- 2193,2201 ---- else if (tv_dest->v_type == VAR_LIST) { // unlet a List item, index must be a number ! SOURCING_LNUM = iptr->isn_lnum; ! if (check_for_number(tv_idx) == FAIL) { status = FAIL; } else *************** *** 2219,2224 **** --- 2231,2288 ---- } break; + // unlet range of items in list variable + case ISN_UNLETRANGE: + { + // Stack contains: + // -3 index1 + // -2 index2 + // -1 dict or list + typval_T *tv_idx1 = STACK_TV_BOT(-3); + typval_T *tv_idx2 = STACK_TV_BOT(-2); + typval_T *tv_dest = STACK_TV_BOT(-1); + int status = OK; + + if (tv_dest->v_type == VAR_LIST) + { + // indexes must be a number + SOURCING_LNUM = iptr->isn_lnum; + if (check_for_number(tv_idx1) == FAIL + || check_for_number(tv_idx2) == FAIL) + { + status = FAIL; + } + else + { + list_T *l = tv_dest->vval.v_list; + long n1 = (long)tv_idx1->vval.v_number; + long n2 = (long)tv_idx2->vval.v_number; + listitem_T *li; + + li = list_find_index(l, &n1); + if (li == NULL + || list_unlet_range(l, li, NULL, n1, + TRUE, n2) == FAIL) + status = FAIL; + } + } + else + { + status = FAIL; + SOURCING_LNUM = iptr->isn_lnum; + semsg(_(e_cannot_index_str), + vartype_name(tv_dest->v_type)); + } + + clear_tv(tv_idx1); + clear_tv(tv_idx2); + clear_tv(tv_dest); + ectx.ec_stack.ga_len -= 3; + if (status == FAIL) + goto on_error; + } + break; + // push constant case ISN_PUSHNR: case ISN_PUSHBOOL: *************** *** 4151,4156 **** --- 4215,4223 ---- case ISN_UNLETINDEX: smsg("%4d UNLETINDEX", current); break; + case ISN_UNLETRANGE: + smsg("%4d UNLETRANGE", current); + break; case ISN_LOCKCONST: smsg("%4d LOCKCONST", current); break; *** ../vim-8.2.2532/src/testdir/test_vim9_assign.vim 2021-02-17 18:49:07.968110009 +0100 --- src/testdir/test_vim9_assign.vim 2021-02-20 16:34:28.673597284 +0100 *************** *** 9,14 **** --- 9,15 ---- let g:inc_counter = 1 let $SOME_ENV_VAR = 'some' let g:alist = [7] + let g:adict = #{a: 1} let g:astring = 'text' def Test_assignment_bool() *************** *** 1414,1419 **** --- 1415,1465 ---- unlet ll[-1] assert_equal([1, 3], ll) + ll = [1, 2, 3, 4] + unlet ll[0 : 1] + assert_equal([3, 4], ll) + + ll = [1, 2, 3, 4] + unlet ll[2 : 8] + assert_equal([1, 2], ll) + + ll = [1, 2, 3, 4] + unlet ll[-2 : -1] + assert_equal([1, 2], ll) + + CheckDefFailure([ + 'var ll = [1, 2]', + 'll[1 : 2] = 7', + ], 'E1165:', 2) + CheckDefFailure([ + 'var dd = {a: 1}', + 'unlet dd["a" : "a"]', + ], 'E1166:', 2) + CheckDefExecFailure([ + 'unlet g:adict[0 : 1]', + ], 'E1148:', 1) + CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll[0:1]', + ], 'E1004:', 2) + CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll[0 :1]', + ], 'E1004:', 2) + CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll[0: 1]', + ], 'E1004:', 2) + + CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll["x" : 1]', + ], 'E1012:', 2) + CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll[0 : "x"]', + ], 'E1012:', 2) + # list of dict unlet var dl = [{a: 1, b: 2}, {c: 3}] unlet dl[0]['b'] *** ../vim-8.2.2532/src/version.c 2021-02-20 08:16:33.823363221 +0100 --- src/version.c 2021-02-20 16:42:10.316036025 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2533, /**/ -- Q: How do you tell the difference between a female cat and a male cat? A: You ask it a question and if HE answers, it's a male but, if SHE answers, it's a female. /// 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 ///