]> granicus.if.org Git - vim/commitdiff
patch 8.2.0730: Vim9: Assignment to dict member does not work v8.2.0730
authorBram Moolenaar <Bram@vim.org>
Sun, 10 May 2020 17:10:31 +0000 (19:10 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 10 May 2020 17:10:31 +0000 (19:10 +0200)
Problem:    Vim9: Assignment to dict member does not work.
Solution:   Parse dict assignment. Implement getting dict member.

src/globals.h
src/testdir/test_vim9_cmd.vim
src/testdir/test_vim9_expr.vim
src/testdir/test_vim9_script.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index 5dfb6ff3f1642781fd5a5437ce415f02f707784e..11e06be7ee9032a9e605080bc85e59cf3d93b1f2 100644 (file)
@@ -1673,6 +1673,7 @@ EXTERN char e_toofewarg[] INIT(= N_("E119: Not enough arguments for function: %s
 EXTERN char e_func_deleted[]   INIT(= N_("E933: Function was deleted: %s"));
 EXTERN char e_dictkey[]                INIT(= N_("E716: Key not present in Dictionary: %s"));
 EXTERN char e_listreq[]                INIT(= N_("E714: List required"));
+EXTERN char e_listdictblobreq[]        INIT(= N_("E1090: List, Dict or Blob required"));
 EXTERN char e_listblobreq[]    INIT(= N_("E897: List or Blob required"));
 EXTERN char e_list_end[]       INIT(= N_("E697: Missing end of List ']': %s"));
 EXTERN char e_listdictarg[]    INIT(= N_("E712: Argument of %s must be a List or Dictionary"));
index 70437b901f70f6c11279f8b2a24acc07a7bb7573..12260c355856886fd5fa18c8f1ccd88d57f840b7 100644 (file)
@@ -44,5 +44,27 @@ def Test_syn_include_wildcards()
   delete('Xthemine.vim')
 enddef
 
+def Test_assign_list()
+  let l: list<string> = []
+  l[0] = 'value'
+  assert_equal('value', l[0])
+
+  l[1] = 'asdf'
+  assert_equal('value', l[0])
+  assert_equal('asdf', l[1])
+  assert_equal('asdf', l[-1])
+  assert_equal('value', l[-2])
+enddef
+
+def Test_assign_dict()
+  let d: dict<string> = {}
+  d['key'] = 'value'
+  assert_equal('value', d['key'])
+
+  d[123] = 'qwerty'
+  assert_equal('qwerty', d[123])
+  assert_equal('qwerty', d['123'])
+enddef
+
 
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 86d1715734740ee23a2688c209d7d0567328507e..6e8278850f5793db79d59a9c595e37c576daabff 100644 (file)
@@ -644,9 +644,9 @@ func Test_expr6_fails()
   call CheckDefFailure(["let x = #{one: 1} / #{two: 2}"], 'E1036:')
   call CheckDefFailure(["let x = #{one: 1} % #{two: 2}"], 'E1035:')
 
-  call CheckDefFailure(["let x = 0xff[1]"], 'E714:')
+  call CheckDefFailure(["let x = 0xff[1]"], 'E1090:')
   if has('float')
-    call CheckDefFailure(["let x = 0.7[1]"], 'E714:')
+    call CheckDefFailure(["let x = 0.7[1]"], 'E1090:')
   endif
 endfunc
 
index d1cc1f9de344b6463325ce228d266ca7e6108e3b..0b99eec5d38162779e63435926c04e1f18c4bc4d 100644 (file)
@@ -38,7 +38,7 @@ def Test_assignment()
 
   call CheckDefFailure(['let x:string'], 'E1069:')
   call CheckDefFailure(['let x:string = "x"'], 'E1069:')
-  call CheckDefFailure(['let a:string = "x"'], 'E1082:')
+  call CheckDefFailure(['let a:string = "x"'], 'E1069:')
 
   let a: number = 6
   assert_equal(6, a)
index 4bf6242645039675a70510b0d2fa620d4f937058..d89763bfc191737e0790e3c33af8883e509ce3c2 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    730,
 /**/
     729,
 /**/
index 709716ee3bbb8d160cf509b190a66bf309a89bbe..27914af7e46d4190a2917593f48d94d26097e3d5 100644 (file)
@@ -48,6 +48,8 @@ typedef enum {
     // ISN_STOREOTHER, // pop into other script variable isn_arg.other.
 
     ISN_STORENR,    // store number into local variable isn_arg.storenr.stnr_idx
+    ISN_STORELIST,     // store into list, value/index/varable on stack
+    ISN_STOREDICT,     // store into dictionary, value/index/variable on stack
 
     ISN_UNLET,         // unlet variable isn_arg.unlet.ul_name
     ISN_UNLETENV,      // unlet environment variable isn_arg.unlet.ul_name
@@ -110,7 +112,8 @@ typedef enum {
     // expression operations
     ISN_CONCAT,
     ISN_INDEX,     // [expr] list index
-    ISN_MEMBER,            // dict.member using isn_arg.string
+    ISN_MEMBER,            // dict[member]
+    ISN_STRINGMEMBER, // dict.member using isn_arg.string
     ISN_2BOOL,     // convert value to bool, invert if isn_arg.number != 0
     ISN_2STRING,    // convert value to string at isn_arg.number on stack
     ISN_NEGATENR,   // apply "-" to number
index 5b016e63c41609121079dab01e0e62e6ce72ce35..09e71aca357637028ef90b92cdf8a36f2b897f57 100644 (file)
@@ -1558,17 +1558,17 @@ generate_PCALL(
 }
 
 /*
- * Generate an ISN_MEMBER instruction.
+ * Generate an ISN_STRINGMEMBER instruction.
  */
     static int
-generate_MEMBER(cctx_T *cctx, char_u *name, size_t len)
+generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len)
 {
     isn_T      *isn;
     garray_T   *stack = &cctx->ctx_type_stack;
     type_T     *type;
 
     RETURN_OK_IF_SKIP(cctx);
-    if ((isn = generate_instr(cctx, ISN_MEMBER)) == NULL)
+    if ((isn = generate_instr(cctx, ISN_STRINGMEMBER)) == NULL)
        return FAIL;
     isn->isn_arg.string = vim_strnsave(name, (int)len);
 
@@ -3485,15 +3485,17 @@ compile_subscript(
        }
        else if (**arg == '[')
        {
-           garray_T    *stack;
+           garray_T    *stack = &cctx->ctx_type_stack;
            type_T      **typep;
 
+           // list index: list[123]
+           // list member: dict[key]
+           // TODO: blob index
+           // TODO: more arguments
+           // TODO: recognize list or dict at runtime
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
 
-           // list index: list[123]
-           // TODO: more arguments
-           // TODO: dict member  dict['name']
            *arg = skipwhite(*arg + 1);
            if (compile_expr0(arg, cctx) == FAIL)
                return FAIL;
@@ -3505,17 +3507,27 @@ compile_subscript(
            }
            *arg = *arg + 1;
 
-           if (generate_instr_drop(cctx, ISN_INDEX, 1) == FAIL)
-               return FAIL;
-           stack = &cctx->ctx_type_stack;
-           typep = ((type_T **)stack->ga_data) + stack->ga_len - 1;
-           if ((*typep)->tt_type != VAR_LIST && *typep != &t_any)
+           typep = ((type_T **)stack->ga_data) + stack->ga_len - 2;
+           if ((*typep)->tt_type == VAR_LIST || (*typep) == &t_any)
            {
-               emsg(_(e_listreq));
-               return FAIL;
+               if ((*typep)->tt_type == VAR_LIST)
+                   *typep = (*typep)->tt_member;
+               if (generate_instr_drop(cctx, ISN_INDEX, 1) == FAIL)
+                   return FAIL;
            }
-           if ((*typep)->tt_type == VAR_LIST)
+           else if ((*typep)->tt_type == VAR_DICT)
+           {
                *typep = (*typep)->tt_member;
+               if (may_generate_2STRING(-1, cctx) == FAIL)
+                   return FAIL;
+               if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
+                   return FAIL;
+           }
+           else
+           {
+               emsg(_(e_listdictblobreq));
+               return FAIL;
+           }
        }
        else if (**arg == '.' && (*arg)[1] != '.')
        {
@@ -3535,7 +3547,7 @@ compile_subscript(
                semsg(_(e_syntax_at), *arg);
                return FAIL;
            }
-           if (generate_MEMBER(cctx, *arg, p - *arg) == FAIL)
+           if (generate_STRINGMEMBER(cctx, *arg, p - *arg) == FAIL)
                return FAIL;
            *arg = p;
        }
@@ -4500,6 +4512,59 @@ typedef enum {
     dest_reg,
 } assign_dest_T;
 
+/*
+ * Generate the load instruction for "name".
+ */
+    static void
+generate_loadvar(
+       cctx_T          *cctx,
+       assign_dest_T   dest,
+       char_u          *name,
+       lvar_T          *lvar,
+       type_T          *type)
+{
+    switch (dest)
+    {
+       case dest_option:
+           // TODO: check the option exists
+           generate_LOAD(cctx, ISN_LOADOPT, 0, name, type);
+           break;
+       case dest_global:
+           generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type);
+           break;
+       case dest_buffer:
+           generate_LOAD(cctx, ISN_LOADB, 0, name + 2, type);
+           break;
+       case dest_window:
+           generate_LOAD(cctx, ISN_LOADW, 0, name + 2, type);
+           break;
+       case dest_tab:
+           generate_LOAD(cctx, ISN_LOADT, 0, name + 2, type);
+           break;
+       case dest_script:
+           compile_load_scriptvar(cctx,
+                   name + (name[1] == ':' ? 2 : 0), NULL, NULL, TRUE);
+           break;
+       case dest_env:
+           // Include $ in the name here
+           generate_LOAD(cctx, ISN_LOADENV, 0, name, type);
+           break;
+       case dest_reg:
+           generate_LOAD(cctx, ISN_LOADREG, name[1], NULL, &t_string);
+           break;
+       case dest_vimvar:
+           generate_LOADV(cctx, name + 2, TRUE);
+           break;
+       case dest_local:
+           if (lvar->lv_from_outer)
+               generate_LOAD(cctx, ISN_LOADOUTER, lvar->lv_idx,
+                                                          NULL, type);
+           else
+               generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
+           break;
+    }
+}
+
 /*
  * compile "let var [= expr]", "const var = expr" and "var = expr"
  * "arg" points to "var".
@@ -4507,12 +4572,15 @@ typedef enum {
     static char_u *
 compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
 {
+    char_u     *var_end;
     char_u     *p;
+    char_u     *end = arg;
     char_u     *ret = NULL;
     int                var_count = 0;
     int                semicolon = 0;
     size_t     varlen;
     garray_T   *instr = &cctx->ctx_instr;
+    garray_T    *stack = &cctx->ctx_type_stack;
     int                new_local = FALSE;
     char_u     *op;
     int                opt_type;
@@ -4522,15 +4590,17 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
     int                oplen = 0;
     int                heredoc = FALSE;
     type_T     *type = &t_any;
+    type_T     *member_type = &t_any;
     lvar_T     *lvar = NULL;
     char_u     *name;
     char_u     *sp;
     int                has_type = FALSE;
+    int                has_index = FALSE;
     int                is_decl = cmdidx == CMD_let || cmdidx == CMD_const;
     int                instr_count = -1;
 
-    p = skip_var_list(arg, FALSE, &var_count, &semicolon);
-    if (p == NULL)
+    var_end = skip_var_list(arg, FALSE, &var_count, &semicolon);
+    if (var_end == NULL)
        return NULL;
     if (var_count > 0)
     {
@@ -4539,7 +4609,12 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
        return NULL;
     }
 
+    p = (*arg == '&' || *arg == '$' || *arg == '@') ? arg + 1 : arg;
+    p = to_name_end(p, TRUE);
+
     // "a: type" is declaring variable "a" with a type, not "a:".
+    if (is_decl && var_end == arg + 2 && var_end[-1] == ':')
+       --var_end;
     if (is_decl && p == arg + 2 && p[-1] == ':')
        --p;
 
@@ -4715,9 +4790,18 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                semsg(_("E1082: Cannot use a namespaced variable: %s"), name);
                goto theend;
            }
+           else if (!is_decl)
+           {
+               semsg(_("E1089: unknown variable: %s"), name);
+               goto theend;
+           }
        }
     }
 
+    // handle "a:name" as a name, not index "name" on "a"
+    if (varlen > 1 || arg[varlen] != ':')
+       p = var_end;
+
     if (dest != dest_option)
     {
        if (is_decl && *p == ':')
@@ -4774,6 +4858,33 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
        new_local = TRUE;
     }
 
+    member_type = type;
+    if (var_end > arg + varlen)
+    {
+       if (is_decl)
+       {
+           emsg(_("E1087: cannot use an index when declaring a variable"));
+           goto theend;
+       }
+
+       // Something follows after the variable: "var[idx]".
+       if (arg[varlen] == '[')
+       {
+           has_index = TRUE;
+           if (type->tt_member == NULL)
+           {
+               semsg(_("E1088: cannot use an index on %s"), name);
+               goto theend;
+           }
+           member_type = type->tt_member;
+       }
+       else
+       {
+           semsg("Not supported yet: %s", arg);
+           goto theend;
+       }
+    }
+
     if (heredoc)
     {
        list_T     *l;
@@ -4792,57 +4903,24 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
        }
        generate_NEWLIST(cctx, l->lv_len);
        type = &t_list_string;
+       member_type = &t_list_string;
        list_free(l);
        p += STRLEN(p);
     }
     else if (oplen > 0)
     {
        int     r;
-       type_T  *stacktype;
-       garray_T *stack;
 
        // for "+=", "*=", "..=" etc. first load the current value
        if (*op != '=')
        {
-           switch (dest)
+           generate_loadvar(cctx, dest, name, lvar, type);
+
+           if (has_index)
            {
-               case dest_option:
-                   // TODO: check the option exists
-                   generate_LOAD(cctx, ISN_LOADOPT, 0, name, type);
-                   break;
-               case dest_global:
-                   generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type);
-                   break;
-               case dest_buffer:
-                   generate_LOAD(cctx, ISN_LOADB, 0, name + 2, type);
-                   break;
-               case dest_window:
-                   generate_LOAD(cctx, ISN_LOADW, 0, name + 2, type);
-                   break;
-               case dest_tab:
-                   generate_LOAD(cctx, ISN_LOADT, 0, name + 2, type);
-                   break;
-               case dest_script:
-                   compile_load_scriptvar(cctx,
-                           name + (name[1] == ':' ? 2 : 0), NULL, NULL, TRUE);
-                   break;
-               case dest_env:
-                   // Include $ in the name here
-                   generate_LOAD(cctx, ISN_LOADENV, 0, name, type);
-                   break;
-               case dest_reg:
-                   generate_LOAD(cctx, ISN_LOADREG, arg[1], NULL, &t_string);
-                   break;
-               case dest_vimvar:
-                   generate_LOADV(cctx, name + 2, TRUE);
-                   break;
-               case dest_local:
-                   if (lvar->lv_from_outer)
-                       generate_LOAD(cctx, ISN_LOADOUTER, lvar->lv_idx,
-                                                                  NULL, type);
-                   else
-                       generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
-                   break;
+               // TODO: get member from list or dict
+               emsg("Index with operation not supported yet");
+               goto theend;
            }
        }
 
@@ -4860,7 +4938,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
 
        if (cctx->ctx_skip != TRUE)
        {
-           stack = &cctx->ctx_type_stack;
+           type_T      *stacktype;
+
            stacktype = stack->ga_len == 0 ? &t_void
                              : ((type_T **)stack->ga_data)[stack->ga_len - 1];
            if (lvar != NULL && (is_decl || !has_type))
@@ -4884,10 +4963,22 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                            lvar->lv_type = stacktype;
                    }
                }
-               else if (need_type(stacktype, lvar->lv_type, -1, cctx) == FAIL)
-                   goto theend;
+               else
+               {
+                   type_T *use_type = lvar->lv_type;
+
+                   if (has_index)
+                   {
+                       use_type = use_type->tt_member;
+                       if (use_type == NULL)
+                           use_type = &t_void;
+                   }
+                   if (need_type(stacktype, use_type, -1, cctx) == FAIL)
+                       goto theend;
+               }
            }
-           else if (*p != '=' && check_type(type, stacktype, TRUE) == FAIL)
+           else if (*p != '=' && check_type(member_type, stacktype, TRUE)
+                                                                      == FAIL)
                goto theend;
        }
     }
@@ -4906,7 +4997,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
        // variables are always initialized
        if (ga_grow(instr, 1) == FAIL)
            goto theend;
-       switch (type->tt_type)
+       switch (member_type->tt_type)
        {
            case VAR_BOOL:
                generate_PUSHBOOL(cctx, VVAL_FALSE);
@@ -4947,11 +5038,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                break;
        }
     }
+    end = p;
 
     if (oplen > 0 && *op != '=')
     {
        type_T      *expected = &t_number;
-       garray_T    *stack = &cctx->ctx_type_stack;
        type_T      *stacktype;
 
        // TODO: if type is known use float or any operation
@@ -4981,103 +5072,158 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
        }
     }
 
-    switch (dest)
+    if (has_index)
     {
-       case dest_option:
-           generate_STOREOPT(cctx, name + 1, opt_flags);
-           break;
-       case dest_global:
-           // include g: with the name, easier to execute that way
-           generate_STORE(cctx, ISN_STOREG, 0, name);
-           break;
-       case dest_buffer:
-           // include b: with the name, easier to execute that way
-           generate_STORE(cctx, ISN_STOREB, 0, name);
-           break;
-       case dest_window:
-           // include w: with the name, easier to execute that way
-           generate_STORE(cctx, ISN_STOREW, 0, name);
-           break;
-       case dest_tab:
-           // include t: with the name, easier to execute that way
-           generate_STORE(cctx, ISN_STORET, 0, name);
-           break;
-       case dest_env:
-           generate_STORE(cctx, ISN_STOREENV, 0, name + 1);
-           break;
-       case dest_reg:
-           generate_STORE(cctx, ISN_STOREREG, name[1], NULL);
-           break;
-       case dest_vimvar:
-           generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
-           break;
-       case dest_script:
-           {
-               char_u      *rawname = name + (name[1] == ':' ? 2 : 0);
-               imported_T  *import = NULL;
-               int         sid = current_sctx.sc_sid;
-               int         idx;
+       int r;
 
-               if (name[1] != ':')
-               {
-                   import = find_imported(name, 0, cctx);
-                   if (import != NULL)
-                       sid = import->imp_sid;
-               }
+       // Compile the "idx" in "var[idx]".
+       if (new_local)
+           --cctx->ctx_locals.ga_len;
+       p = skipwhite(arg + varlen + 1);
+       r = compile_expr0(&p, cctx);
+       if (new_local)
+           ++cctx->ctx_locals.ga_len;
+       if (r == FAIL)
+           goto theend;
+       if (*skipwhite(p) != ']')
+       {
+           emsg(_(e_missbrac));
+           goto theend;
+       }
+       if (type->tt_type == VAR_DICT
+               && may_generate_2STRING(-1, cctx) == FAIL)
+           goto theend;
+       if (type->tt_type == VAR_LIST
+               && ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type
+                                                                != VAR_NUMBER)
+       {
+           emsg(_(e_number_exp));
+           goto theend;
+       }
+
+       // Load the dict or list.  On the stack we then have:
+       // - value
+       // - index
+       // - variable
+       generate_loadvar(cctx, dest, name, lvar, type);
 
-               idx = get_script_item_idx(sid, rawname, TRUE);
-               // TODO: specific type
-               if (idx < 0)
+       if (type->tt_type == VAR_LIST)
+       {
+           if (generate_instr_drop(cctx, ISN_STORELIST, 3) == FAIL)
+               return FAIL;
+       }
+       else if (type->tt_type == VAR_DICT)
+       {
+           if (generate_instr_drop(cctx, ISN_STOREDICT, 3) == FAIL)
+               return FAIL;
+       }
+       else
+       {
+           emsg(_(e_listreq));
+           goto theend;
+       }
+    }
+    else
+    {
+       switch (dest)
+       {
+           case dest_option:
+               generate_STOREOPT(cctx, name + 1, opt_flags);
+               break;
+           case dest_global:
+               // include g: with the name, easier to execute that way
+               generate_STORE(cctx, ISN_STOREG, 0, name);
+               break;
+           case dest_buffer:
+               // include b: with the name, easier to execute that way
+               generate_STORE(cctx, ISN_STOREB, 0, name);
+               break;
+           case dest_window:
+               // include w: with the name, easier to execute that way
+               generate_STORE(cctx, ISN_STOREW, 0, name);
+               break;
+           case dest_tab:
+               // include t: with the name, easier to execute that way
+               generate_STORE(cctx, ISN_STORET, 0, name);
+               break;
+           case dest_env:
+               generate_STORE(cctx, ISN_STOREENV, 0, name + 1);
+               break;
+           case dest_reg:
+               generate_STORE(cctx, ISN_STOREREG, name[1], NULL);
+               break;
+           case dest_vimvar:
+               generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
+               break;
+           case dest_script:
                {
-                   char_u *name_s = name;
+                   char_u      *rawname = name + (name[1] == ':' ? 2 : 0);
+                   imported_T  *import = NULL;
+                   int         sid = current_sctx.sc_sid;
+                   int         idx;
 
-                   // Include s: in the name for store_var()
                    if (name[1] != ':')
                    {
-                       int len = (int)STRLEN(name) + 3;
+                       import = find_imported(name, 0, cctx);
+                       if (import != NULL)
+                           sid = import->imp_sid;
+                   }
 
-                       name_s = alloc(len);
-                       if (name_s == NULL)
-                           name_s = name;
-                       else
-                           vim_snprintf((char *)name_s, len, "s:%s", name);
+                   idx = get_script_item_idx(sid, rawname, TRUE);
+                   // TODO: specific type
+                   if (idx < 0)
+                   {
+                       char_u *name_s = name;
+
+                       // Include s: in the name for store_var()
+                       if (name[1] != ':')
+                       {
+                           int len = (int)STRLEN(name) + 3;
+
+                           name_s = alloc(len);
+                           if (name_s == NULL)
+                               name_s = name;
+                           else
+                               vim_snprintf((char *)name_s, len, "s:%s", name);
+                       }
+                       generate_OLDSCRIPT(cctx, ISN_STORES, name_s, sid,
+                                                                      &t_any);
+                       if (name_s != name)
+                           vim_free(name_s);
                    }
-                   generate_OLDSCRIPT(cctx, ISN_STORES, name_s, sid, &t_any);
-                   if (name_s != name)
-                       vim_free(name_s);
-               }
-               else
-                   generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
+                   else
+                       generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
                                                             sid, idx, &t_any);
-           }
-           break;
-       case dest_local:
-           if (lvar != NULL)
-           {
-               isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
-
-               // optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE
-               // into ISN_STORENR
-               if (!lvar->lv_from_outer && instr->ga_len == instr_count + 1
-                                        && isn->isn_type == ISN_PUSHNR)
+               }
+               break;
+           case dest_local:
+               if (lvar != NULL)
                {
-                   varnumber_T val = isn->isn_arg.number;
-                   garray_T    *stack = &cctx->ctx_type_stack;
-
-                   isn->isn_type = ISN_STORENR;
-                   isn->isn_arg.storenr.stnr_idx = lvar->lv_idx;
-                   isn->isn_arg.storenr.stnr_val = val;
-                   if (stack->ga_len > 0)
-                       --stack->ga_len;
+                   isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
+
+                   // optimization: turn "var = 123" from ISN_PUSHNR +
+                   // ISN_STORE into ISN_STORENR
+                   if (!lvar->lv_from_outer && instr->ga_len == instr_count + 1
+                                            && isn->isn_type == ISN_PUSHNR)
+                   {
+                       varnumber_T val = isn->isn_arg.number;
+
+                       isn->isn_type = ISN_STORENR;
+                       isn->isn_arg.storenr.stnr_idx = lvar->lv_idx;
+                       isn->isn_arg.storenr.stnr_val = val;
+                       if (stack->ga_len > 0)
+                           --stack->ga_len;
+                   }
+                   else if (lvar->lv_from_outer)
+                       generate_STORE(cctx, ISN_STOREOUTER, lvar->lv_idx,
+                                                                        NULL);
+                   else
+                       generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
                }
-               else if (lvar->lv_from_outer)
-                   generate_STORE(cctx, ISN_STOREOUTER, lvar->lv_idx, NULL);
-               else
-                   generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
-           }
-           break;
+               break;
+       }
     }
-    ret = p;
+    ret = end;
 
 theend:
     vim_free(name);
@@ -6252,7 +6398,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
     for (;;)
     {
        exarg_T ea;
-       int     is_ex_command = FALSE;
+       int     starts_with_colon = FALSE;
 
        // Bail out on the first error to avoid a flood of errors and report
        // the right line number when inside try/catch.
@@ -6327,7 +6473,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
                break;
 
            case ':':
-               is_ex_command = TRUE;
+               starts_with_colon = TRUE;
                break;
        }
 
@@ -6347,20 +6493,26 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
        if (checkforcmd(&ea.cmd, "call", 3))
            ea.cmd = skipwhite(ea.cmd);
 
-       if (!is_ex_command)
+       if (!starts_with_colon)
        {
+           char_u *pskip;
+
            // Assuming the command starts with a variable or function name,
-           // find what follows.  Also "&opt = val", "$ENV = val" and "@r =
-           // val".
-           p = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@')
+           // find what follows.
+           // Skip over "var.member", "var[idx]" and the like.
+           // Also "&opt = val", "$ENV = val" and "@r = val".
+           pskip = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@')
                                                         ? ea.cmd + 1 : ea.cmd;
-           p = to_name_end(p, TRUE);
+           p = to_name_end(pskip, TRUE);
            if (p > ea.cmd && *p != NUL)
            {
-               int oplen;
-               int heredoc;
+               char_u *var_end;
+               int     oplen;
+               int     heredoc;
 
-               oplen = assignment_len(skipwhite(p), &heredoc);
+               var_end = find_name_end(pskip, NULL, NULL,
+                                               FNE_CHECK_START | FNE_INCL_BR);
+               oplen = assignment_len(skipwhite(var_end), &heredoc);
                if (oplen > 0)
                {
                    // Recognize an assignment if we recognize the variable
@@ -6393,7 +6545,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
         * COMMAND after range
         */
        ea.cmd = skip_range(ea.cmd, NULL);
-       p = find_ex_command(&ea, NULL, is_ex_command ? NULL
+       p = find_ex_command(&ea, NULL, starts_with_colon ? NULL
                   : (void *(*)(char_u *, size_t, cctx_T *))lookup_local,
                                                                        &cctx);
 
@@ -6675,7 +6827,7 @@ delete_instr(isn_T *isn)
        case ISN_LOADW:
        case ISN_LOADT:
        case ISN_LOADOPT:
-       case ISN_MEMBER:
+       case ISN_STRINGMEMBER:
        case ISN_PUSHEXC:
        case ISN_PUSHS:
        case ISN_STOREENV:
@@ -6758,6 +6910,7 @@ delete_instr(isn_T *isn)
        case ISN_EXECUTE:
        case ISN_FOR:
        case ISN_INDEX:
+       case ISN_MEMBER:
        case ISN_JUMP:
        case ISN_LOAD:
        case ISN_LOADOUTER:
@@ -6783,6 +6936,8 @@ delete_instr(isn_T *isn)
        case ISN_STORENR:
        case ISN_STOREREG:
        case ISN_STORESCRIPT:
+       case ISN_STOREDICT:
+       case ISN_STORELIST:
        case ISN_THROW:
        case ISN_TRY:
            // nothing allocated
index 4d4df0ab60d6f28cf02dd75dd79484b90866b58f..ba4ae478719d489fba7fac3184358a5676296d20 100644 (file)
@@ -1252,6 +1252,76 @@ call_def_function(
                tv->vval.v_number = iptr->isn_arg.storenr.stnr_val;
                break;
 
+           // store value in list variable
+           case ISN_STORELIST:
+               {
+                   typval_T    *tv_idx = STACK_TV_BOT(-2);
+                   varnumber_T lidx = tv_idx->vval.v_number;
+                   typval_T    *tv_list = STACK_TV_BOT(-1);
+                   list_T      *list = tv_list->vval.v_list;
+
+                   if (lidx < 0 && list->lv_len + lidx >= 0)
+                       // negative index is relative to the end
+                       lidx = list->lv_len + lidx;
+                   if (lidx < 0 || lidx > list->lv_len)
+                   {
+                       semsg(_(e_listidx), lidx);
+                       goto failed;
+                   }
+                   tv = STACK_TV_BOT(-3);
+                   if (lidx < list->lv_len)
+                   {
+                       listitem_T *li = list_find(list, lidx);
+
+                       // overwrite existing list item
+                       clear_tv(&li->li_tv);
+                       li->li_tv = *tv;
+                   }
+                   else
+                   {
+                       // append to list
+                       if (list_append_tv(list, tv) == FAIL)
+                           goto failed;
+                       clear_tv(tv);
+                   }
+                   clear_tv(tv_idx);
+                   clear_tv(tv_list);
+               }
+               break;
+
+           // store value in dict variable
+           case ISN_STOREDICT:
+               {
+                   typval_T    *tv_key = STACK_TV_BOT(-2);
+                   char_u      *key = tv_key->vval.v_string;
+                   typval_T    *tv_dict = STACK_TV_BOT(-1);
+                   dict_T      *dict = tv_dict->vval.v_dict;
+                   dictitem_T  *di;
+
+                   if (key == NULL || *key == NUL)
+                   {
+                       emsg(_(e_emptykey));
+                       goto failed;
+                   }
+                   tv = STACK_TV_BOT(-3);
+                   di = dict_find(dict, key, -1);
+                   if (di != NULL)
+                   {
+                       clear_tv(&di->di_tv);
+                       di->di_tv = *tv;
+                   }
+                   else
+                   {
+                       // add to dict
+                       if (dict_add_tv(dict, (char *)key, tv) == FAIL)
+                           goto failed;
+                       clear_tv(tv);
+                   }
+                   clear_tv(tv_key);
+                   clear_tv(tv_dict);
+               }
+               break;
+
            // push constant
            case ISN_PUSHNR:
            case ISN_PUSHBOOL:
@@ -2019,8 +2089,42 @@ call_def_function(
                }
                break;
 
-           // dict member with string key
            case ISN_MEMBER:
+               {
+                   dict_T      *dict;
+                   char_u      *key;
+                   dictitem_T  *di;
+
+                   // dict member: dict is at stack-2, key at stack-1
+                   tv = STACK_TV_BOT(-2);
+                   if (tv->v_type != VAR_DICT)
+                   {
+                       emsg(_(e_dictreq));
+                       goto failed;
+                   }
+                   dict = tv->vval.v_dict;
+
+                   tv = STACK_TV_BOT(-1);
+                   if (tv->v_type != VAR_STRING)
+                   {
+                       emsg(_(e_stringreq));
+                       goto failed;
+                   }
+                   key = tv->vval.v_string;
+                   if ((di = dict_find(dict, key, -1)) == NULL)
+                   {
+                       semsg(_(e_dictkey), key);
+                       goto failed;
+                   }
+                   --ectx.ec_stack.ga_len;
+                   clear_tv(tv);
+                   clear_tv(STACK_TV_BOT(-1));
+                   copy_tv(&di->di_tv, STACK_TV_BOT(-1));
+               }
+               break;
+
+           // dict member with string key
+           case ISN_STRINGMEMBER:
                {
                    dict_T      *dict;
                    dictitem_T  *di;
@@ -2380,6 +2484,14 @@ ex_disassemble(exarg_T *eap)
                                iptr->isn_arg.storenr.stnr_idx);
                break;
 
+           case ISN_STORELIST:
+               smsg("%4d STORELIST", current);
+               break;
+
+           case ISN_STOREDICT:
+               smsg("%4d STOREDICT", current);
+               break;
+
            // constants
            case ISN_PUSHNR:
                smsg("%4d PUSHNR %lld", current,
@@ -2656,7 +2768,8 @@ ex_disassemble(exarg_T *eap)
            // expression operations
            case ISN_CONCAT: smsg("%4d CONCAT", current); break;
            case ISN_INDEX: smsg("%4d INDEX", current); break;
-           case ISN_MEMBER: smsg("%4d MEMBER %s", current,
+           case ISN_MEMBER: smsg("%4d MEMBER", current); break;
+           case ISN_STRINGMEMBER: smsg("%4d MEMBER %s", current,
                                                  iptr->isn_arg.string); break;
            case ISN_NEGATENR: smsg("%4d NEGATENR", current); break;