# HG changeset patch # User Bram Moolenaar # Date 1606586404 -3600 # Node ID b98003d73150619c4410c9738a5d0701ec018b40 # Parent 52326852b9411ddd88a2be6e099d95dd186b7155 patch 8.2.2063: Vim9: only one level of indexing supported Commit: https://github.com/vim/vim/commit/dc234caff22131bdc1ff9ea50d67e11843d0d73e Author: Bram Moolenaar Date: Sat Nov 28 18:52:33 2020 +0100 patch 8.2.2063: Vim9: only one level of indexing supported Problem: Vim9: only one level of indexing supported. Solution: Handle more than one index in an assignment. diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -311,3 +311,5 @@ EXTERN char e_missing_matching_bracket_a INIT(= N_("E1139: Missing matching bracket after dict key")); EXTERN char e_for_argument_must_be_sequence_of_lists[] INIT(= N_("E1140: For argument must be a sequence of lists")); +EXTERN char e_indexable_type_required[] + INIT(= N_("E1141: Indexable type required")); diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -225,6 +225,78 @@ def Test_assignment() END enddef +def Test_assign_index() + # list of list + var l1: list + l1[0] = 123 + assert_equal([123], l1) + + var l2: list> + l2[0] = [] + l2[0][0] = 123 + assert_equal([[123]], l2) + + var l3: list>> + l3[0] = [] + l3[0][0] = [] + l3[0][0][0] = 123 + assert_equal([[[123]]], l3) + + var lines =<< trim END + var l3: list> + l3[0] = [] + l3[0][0] = [] + END + CheckDefFailure(lines, 'E1012: Type mismatch; expected number but got list', 3) + + # dict of dict + var d1: dict + d1.one = 1 + assert_equal({one: 1}, d1) + + var d2: dict> + d2.one = {} + d2.one.two = 123 + assert_equal({one: {two: 123}}, d2) + + var d3: dict>> + d3.one = {} + d3.one.two = {} + d3.one.two.three = 123 + assert_equal({one: {two: {three: 123}}}, d3) + + lines =<< trim END + var d3: dict> + d3.one = {} + d3.one.two = {} + END + CheckDefFailure(lines, 'E1012: Type mismatch; expected number but got dict', 3) + + # list of dict + var ld: list> + ld[0] = {} + ld[0].one = 123 + assert_equal([{one: 123}], ld) + + lines =<< trim END + var ld: list> + ld[0] = [] + END + CheckDefFailure(lines, 'E1012: Type mismatch; expected dict but got list', 2) + + # dict of list + var dl: dict> + dl.one = [] + dl.one[0] = 123 + assert_equal({one: [123]}, dl) + + lines =<< trim END + var dl: dict> + dl.one = {} + END + CheckDefFailure(lines, 'E1012: Type mismatch; expected list but got dict', 2) +enddef + def Test_extend_list() var lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2063, +/**/ 2062, /**/ 2061, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4961,6 +4961,7 @@ typedef enum { dest_vimvar, dest_script, dest_reg, + dest_expr, } assign_dest_T; /* @@ -5013,7 +5014,32 @@ generate_loadvar( else generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type); break; - } + case dest_expr: + // list or dict value should already be on the stack. + break; + } +} + +/* + * Skip over "[expr]" or ".member". + * Does not check for any errors. + */ + static char_u * +skip_index(char_u *start) +{ + char_u *p = start; + + if (*p == '[') + { + p = skipwhite(p + 1); + (void)skip_expr(&p, NULL); + p = skipwhite(p); + if (*p == ']') + return p + 1; + return p; + } + // if (*p == '.') + return to_name_end(p + 1, TRUE); } void @@ -5069,6 +5095,7 @@ compile_assignment(char_u *arg, exarg_T int heredoc = FALSE; type_T *type = &t_any; type_T *member_type = &t_any; + type_T *rhs_type = &t_any; char_u *name = NULL; char_u *sp; int is_decl = cmdidx == CMD_let || cmdidx == CMD_var @@ -5157,6 +5184,8 @@ compile_assignment(char_u *arg, exarg_T // TODO: check the length of a constant list here generate_CHECKLEN(cctx, semicolon ? var_count - 1 : var_count, semicolon); + if (stacktype->tt_member != NULL) + rhs_type = stacktype->tt_member; } } @@ -5467,6 +5496,7 @@ compile_assignment(char_u *arg, exarg_T if (var_end > var_start + varlen) { // Something follows after the variable: "var[idx]" or "var.key". + // TODO: should we also handle "->func()" here? if (is_decl) { emsg(_(e_cannot_use_index_when_declaring_variable)); @@ -5475,6 +5505,27 @@ compile_assignment(char_u *arg, exarg_T if (var_start[varlen] == '[' || var_start[varlen] == '.') { + char_u *after = var_start + varlen; + + // Only the last index is used below, if there are others + // before it generate code for the expression. Thus for + // "ll[1][2]" the expression is "ll[1]" and "[2]" is the index. + for (;;) + { + p = skip_index(after); + if (*p != '[' && *p != '.') + break; + after = p; + } + if (after > var_start + varlen) + { + varlen = after - var_start; + dest = dest_expr; + // We don't know the type before evaluating the expression, + // use "any" until then. + type = &t_any; + } + has_index = TRUE; if (type->tt_member == NULL) member_type = &t_any; @@ -5511,7 +5562,6 @@ compile_assignment(char_u *arg, exarg_T } else if (oplen > 0) { - type_T *stacktype; int is_const = FALSE; // For "var = expr" evaluate the expression. @@ -5558,18 +5608,18 @@ compile_assignment(char_u *arg, exarg_T return FAIL; } - stacktype = stack->ga_len == 0 ? &t_void + rhs_type = stack->ga_len == 0 ? &t_void : ((type_T **)stack->ga_data)[stack->ga_len - 1]; if (lvar != NULL && (is_decl || !has_type)) { - if ((stacktype->tt_type == VAR_FUNC - || stacktype->tt_type == VAR_PARTIAL) + if ((rhs_type->tt_type == VAR_FUNC + || rhs_type->tt_type == VAR_PARTIAL) && var_wrong_func_name(name, TRUE)) goto theend; if (new_local && !has_type) { - if (stacktype->tt_type == VAR_VOID) + if (rhs_type->tt_type == VAR_VOID) { emsg(_(e_cannot_use_void_value)); goto theend; @@ -5578,14 +5628,14 @@ compile_assignment(char_u *arg, exarg_T { // An empty list or dict has a &t_unknown member, // for a variable that implies &t_any. - if (stacktype == &t_list_empty) + if (rhs_type == &t_list_empty) lvar->lv_type = &t_list_any; - else if (stacktype == &t_dict_empty) + else if (rhs_type == &t_dict_empty) lvar->lv_type = &t_dict_any; - else if (stacktype == &t_unknown) + else if (rhs_type == &t_unknown) lvar->lv_type = &t_any; else - lvar->lv_type = stacktype; + lvar->lv_type = rhs_type; } } else if (*op == '=') @@ -5595,17 +5645,17 @@ compile_assignment(char_u *arg, exarg_T // without operator check type here, otherwise below if (has_index) { - use_type = use_type->tt_member; - if (use_type == NULL) + use_type = member_type; + if (member_type == NULL) // could be indexing "any" use_type = &t_any; } - if (need_type(stacktype, use_type, -1, cctx, + if (need_type(rhs_type, use_type, -1, cctx, FALSE, is_const) == FAIL) goto theend; } } - else if (*p != '=' && need_type(stacktype, member_type, -1, + else if (*p != '=' && need_type(rhs_type, member_type, -1, cctx, FALSE, FALSE) == FAIL) goto theend; } @@ -5771,7 +5821,31 @@ compile_assignment(char_u *arg, exarg_T // - value // - index // - variable - generate_loadvar(cctx, dest, name, lvar, type); + if (dest == dest_expr) + { + int c = var_start[varlen]; + + // Evaluate "ll[expr]" of "ll[expr][idx]" + p = var_start; + var_start[varlen] = NUL; + if (compile_expr0(&p, cctx) == OK && p != var_start + varlen) + { + // this should not happen + emsg(_(e_missbrac)); + goto theend; + } + var_start[varlen] = c; + + type = stack->ga_len == 0 ? &t_void + : ((type_T **)stack->ga_data)[stack->ga_len - 1]; + // now we can properly check the type + if (type->tt_member != NULL + && need_type(rhs_type, type->tt_member, -2, cctx, + FALSE, FALSE) == FAIL) + goto theend; + } + else + generate_loadvar(cctx, dest, name, lvar, type); if (type->tt_type == VAR_LIST) { @@ -5785,7 +5859,7 @@ compile_assignment(char_u *arg, exarg_T } else { - emsg(_(e_listreq)); + emsg(_(e_indexable_type_required)); goto theend; } } @@ -5882,6 +5956,9 @@ compile_assignment(char_u *arg, exarg_T generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); } break; + case dest_expr: + // cannot happen + break; } }