To: vim_dev@googlegroups.com Subject: Patch 8.2.2719 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2719 Problem: Vim9: appending to dict item doesn't work in a :def function. Solution: Implement assignment with operator on indexed item. Files: src/vim9compile.c, src/testdir/test_vim9_assign.vim *** ../vim-8.2.2718/src/vim9compile.c 2021-04-04 20:49:46.626430253 +0200 --- src/vim9compile.c 2021-04-05 16:50:46.497420275 +0200 *************** *** 2650,2655 **** --- 2650,2761 ---- } /* + * Compile getting a member from a list/dict/string/blob. Stack has the + * indexable value and the index. + */ + static int + compile_member(int is_slice, cctx_T *cctx) + { + type_T **typep; + garray_T *stack = &cctx->ctx_type_stack; + vartype_T vtype; + type_T *valtype; + + // We can index a list and a dict. If we don't know the type + // we can use the index value type. + // TODO: If we don't know use an instruction to figure it out at + // runtime. + typep = ((type_T **)stack->ga_data) + stack->ga_len + - (is_slice ? 3 : 2); + vtype = (*typep)->tt_type; + valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; + // If the index is a string, the variable must be a Dict. + if (*typep == &t_any && valtype == &t_string) + vtype = VAR_DICT; + if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB) + { + if (need_type(valtype, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) + return FAIL; + if (is_slice) + { + valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2]; + if (need_type(valtype, &t_number, -2, 0, cctx, + FALSE, FALSE) == FAIL) + return FAIL; + } + } + + if (vtype == VAR_DICT) + { + if (is_slice) + { + emsg(_(e_cannot_slice_dictionary)); + return FAIL; + } + if ((*typep)->tt_type == VAR_DICT) + { + *typep = (*typep)->tt_member; + if (*typep == &t_unknown) + // empty dict was used + *typep = &t_any; + } + else + { + if (need_type(*typep, &t_dict_any, -2, 0, cctx, + FALSE, FALSE) == FAIL) + return FAIL; + *typep = &t_any; + } + if (may_generate_2STRING(-1, cctx) == FAIL) + return FAIL; + if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL) + return FAIL; + } + else if (vtype == VAR_STRING) + { + *typep = &t_string; + if ((is_slice + ? generate_instr_drop(cctx, ISN_STRSLICE, 2) + : generate_instr_drop(cctx, ISN_STRINDEX, 1)) == FAIL) + return FAIL; + } + else if (vtype == VAR_BLOB) + { + emsg("Sorry, blob index and slice not implemented yet"); + return FAIL; + } + else if (vtype == VAR_LIST || *typep == &t_any) + { + if (is_slice) + { + if (generate_instr_drop(cctx, + vtype == VAR_LIST ? ISN_LISTSLICE : ISN_ANYSLICE, + 2) == FAIL) + return FAIL; + } + else + { + if ((*typep)->tt_type == VAR_LIST) + { + *typep = (*typep)->tt_member; + if (*typep == &t_unknown) + // empty list was used + *typep = &t_any; + } + if (generate_instr_drop(cctx, + vtype == VAR_LIST ? ISN_LISTINDEX : ISN_ANYINDEX, 1) == FAIL) + return FAIL; + } + } + else + { + emsg(_(e_string_list_dict_or_blob_required)); + return FAIL; + } + return OK; + } + + /* * Generate an instruction to load script-local variable "name", without the * leading "s:". * Also finds imported variables. *************** *** 3934,3943 **** } else if (**arg == '[') { - garray_T *stack = &cctx->ctx_type_stack; - type_T **typep; - type_T *valtype; - vartype_T vtype; int is_slice = FALSE; // list index: list[123] --- 4040,4045 ---- *************** *** 4004,4102 **** } *arg = *arg + 1; ! // We can index a list and a dict. If we don't know the type ! // we can use the index value type. ! // TODO: If we don't know use an instruction to figure it out at ! // runtime. ! typep = ((type_T **)stack->ga_data) + stack->ga_len ! - (is_slice ? 3 : 2); ! vtype = (*typep)->tt_type; ! valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; ! // If the index is a string, the variable must be a Dict. ! if (*typep == &t_any && valtype == &t_string) ! vtype = VAR_DICT; ! if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB) ! { ! if (need_type(valtype, &t_number, -1, 0, cctx, ! FALSE, FALSE) == FAIL) ! return FAIL; ! if (is_slice) ! { ! valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2]; ! if (need_type(valtype, &t_number, -2, 0, cctx, ! FALSE, FALSE) == FAIL) ! return FAIL; ! } ! } ! ! if (vtype == VAR_DICT) ! { ! if (is_slice) ! { ! emsg(_(e_cannot_slice_dictionary)); ! return FAIL; ! } ! if ((*typep)->tt_type == VAR_DICT) ! { ! *typep = (*typep)->tt_member; ! if (*typep == &t_unknown) ! // empty dict was used ! *typep = &t_any; ! } ! else ! { ! if (need_type(*typep, &t_dict_any, -2, 0, cctx, ! FALSE, FALSE) == FAIL) ! return FAIL; ! *typep = &t_any; ! } ! if (may_generate_2STRING(-1, cctx) == FAIL) ! return FAIL; ! if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL) ! return FAIL; ! } ! else if (vtype == VAR_STRING) ! { ! *typep = &t_string; ! if ((is_slice ! ? generate_instr_drop(cctx, ISN_STRSLICE, 2) ! : generate_instr_drop(cctx, ISN_STRINDEX, 1)) == FAIL) ! return FAIL; ! } ! else if (vtype == VAR_BLOB) ! { ! emsg("Sorry, blob index and slice not implemented yet"); return FAIL; - } - else if (vtype == VAR_LIST || *typep == &t_any) - { - if (is_slice) - { - if (generate_instr_drop(cctx, - vtype == VAR_LIST ? ISN_LISTSLICE : ISN_ANYSLICE, - 2) == FAIL) - return FAIL; - } - else - { - if ((*typep)->tt_type == VAR_LIST) - { - *typep = (*typep)->tt_member; - if (*typep == &t_unknown) - // empty list was used - *typep = &t_any; - } - if (generate_instr_drop(cctx, - vtype == VAR_LIST ? ISN_LISTINDEX : ISN_ANYINDEX, - 1) == FAIL) - return FAIL; - } - } - else - { - emsg(_(e_string_list_dict_or_blob_required)); - return FAIL; - } } else if (*p == '.' && p[1] != '.') { --- 4106,4113 ---- } *arg = *arg + 1; ! if (compile_member(is_slice, cctx) == FAIL) return FAIL; } else if (*p == '.' && p[1] != '.') { *************** *** 5905,5913 **** lhs->lhs_type = lhs->lhs_lvar->lv_type; } ! if (oplen == 3 && !heredoc && lhs->lhs_dest != dest_global ! && lhs->lhs_type->tt_type != VAR_STRING ! && lhs->lhs_type->tt_type != VAR_ANY) { emsg(_(e_can_only_concatenate_to_string)); return FAIL; --- 5916,5926 ---- lhs->lhs_type = lhs->lhs_lvar->lv_type; } ! if (oplen == 3 && !heredoc ! && lhs->lhs_dest != dest_global ! && !lhs->lhs_has_index ! && lhs->lhs_type->tt_type != VAR_STRING ! && lhs->lhs_type->tt_type != VAR_ANY) { emsg(_(e_can_only_concatenate_to_string)); return FAIL; *************** *** 5993,6018 **** } /* ! * Assignment to a list or dict member, or ":unlet" for the item, using the ! * information in "lhs". ! * Returns OK or FAIL. */ static int ! compile_assign_unlet( char_u *var_start, lhs_T *lhs, int is_assign, ! type_T *rhs_type, cctx_T *cctx) { - char_u *p; - int r; - 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; if (*p == '[') { --- 6006,6026 ---- } /* ! * For an assignment with an index, compile the "idx" in "var[idx]" or "key" in ! * "var.key". */ static int ! compile_assign_index( char_u *var_start, lhs_T *lhs, int is_assign, ! int *range, cctx_T *cctx) { size_t varlen = lhs->lhs_varlen; ! char_u *p; ! int r = OK; p = var_start + varlen; if (*p == '[') { *************** *** 6027,6033 **** 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])) { --- 6035,6041 ---- 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])) { *************** *** 6053,6059 **** r = generate_PUSHS(cctx, key); } ! if (r == FAIL) return FAIL; if (lhs->lhs_type == &t_any) --- 6061,6089 ---- r = generate_PUSHS(cctx, key); } ! return r; ! } ! ! /* ! * Assignment to a list or dict member, or ":unlet" for the item, using the ! * information in "lhs". ! * Returns OK or FAIL. ! */ ! static int ! compile_assign_unlet( ! char_u *var_start, ! lhs_T *lhs, ! int is_assign, ! type_T *rhs_type, ! cctx_T *cctx) ! { ! char_u *p; ! vartype_T dest_type; ! size_t varlen = lhs->lhs_varlen; ! garray_T *stack = &cctx->ctx_type_stack; ! int range = FALSE; ! ! if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL) return FAIL; if (lhs->lhs_type == &t_any) *************** *** 6330,6338 **** if (lhs.lhs_has_index) { ! // TODO: get member from list or dict ! emsg("Index with operation not supported yet"); ! goto theend; } } --- 6360,6376 ---- if (lhs.lhs_has_index) { ! int range = FALSE; ! ! // Get member from list or dict. First compile the ! // index value. ! if (compile_assign_index(var_start, &lhs, ! TRUE, &range, cctx) == FAIL) ! goto theend; ! ! // Get the member. ! if (compile_member(FALSE, cctx) == FAIL) ! goto theend; } } *** ../vim-8.2.2718/src/testdir/test_vim9_assign.vim 2021-04-03 21:00:57.668184656 +0200 --- src/testdir/test_vim9_assign.vim 2021-04-05 17:09:23.738039270 +0200 *************** *** 1116,1122 **** def Test_assign_dict_with_op() var lines =<< trim END - vim9script var ds: dict = {a: 'x'} ds['a'] ..= 'y' ds.a ..= 'z' --- 1116,1121 ---- *************** *** 1148,1155 **** dn.a %= 6 assert_equal(2, dn.a) END ! # TODO: this should also work with a :def function ! CheckScriptSuccess(lines) enddef def Test_assign_lambda() --- 1147,1192 ---- dn.a %= 6 assert_equal(2, dn.a) END ! CheckDefAndScriptSuccess(lines) ! enddef ! ! def Test_assign_list_with_op() ! var lines =<< trim END ! var ls: list = ['x'] ! ls[0] ..= 'y' ! assert_equal('xy', ls[0]) ! ! var ln: list = [9] ! ln[0] += 2 ! assert_equal(11, ln[0]) ! ! ln[0] -= 3 ! assert_equal(8, ln[0]) ! ! ln[0] *= 2 ! assert_equal(16, ln[0]) ! ! ln[0] /= 3 ! assert_equal(5, ln[0]) ! ! ln[0] %= 3 ! assert_equal(2, ln[0]) ! END ! CheckDefAndScriptSuccess(lines) ! enddef ! ! def Test_assign_with_op_fails() ! var lines =<< trim END ! var s = 'abc' ! s[1] += 'x' ! END ! CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2) ! ! lines =<< trim END ! var s = 'abc' ! s[1] ..= 'x' ! END ! CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2) enddef def Test_assign_lambda() *** ../vim-8.2.2718/src/version.c 2021-04-05 15:38:47.857581963 +0200 --- src/version.c 2021-04-05 15:42:34.036987624 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2719, /**/ -- hundred-and-one symptoms of being an internet addict: 62. If your doorbell rings, you think that new mail has arrived. And then you're disappointed that it's only someone at the door. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///