Patch 8.2.0730
Problem:    Vim9: Assignment to dict member does not work.
Solution:   Parse dict assignment. Implement getting dict member.
Files:      src/vim9.h, src/vim9compile.c, src/vim9execute.c, src/globals.h,
            src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_cmd.vim,
            src/testdir/test_vim9_script.vim


*** ../vim-8.2.0729/src/vim9.h  2020-05-06 21:06:26.421435642 +0200
--- src/vim9.h  2020-05-10 18:30:53.796003961 +0200
***************
*** 48,53 ****
--- 48,55 ----
      // 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,116 ****
      // expression operations
      ISN_CONCAT,
      ISN_INDEX,            // [expr] list index
!     ISN_MEMBER,           // 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
--- 112,119 ----
      // expression operations
      ISN_CONCAT,
      ISN_INDEX,            // [expr] list index
!     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
*** ../vim-8.2.0729/src/vim9compile.c   2020-05-09 22:50:04.751323784 +0200
--- src/vim9compile.c   2020-05-10 19:03:25.626411542 +0200
***************
*** 1558,1574 ****
  }
  
  /*
!  * Generate an ISN_MEMBER instruction.
   */
      static int
! generate_MEMBER(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)
        return FAIL;
      isn->isn_arg.string = vim_strnsave(name, (int)len);
  
--- 1558,1574 ----
  }
  
  /*
!  * Generate an ISN_STRINGMEMBER instruction.
   */
      static int
! 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_STRINGMEMBER)) == NULL)
        return FAIL;
      isn->isn_arg.string = vim_strnsave(name, (int)len);
  
***************
*** 3485,3499 ****
        }
        else if (**arg == '[')
        {
!           garray_T    *stack;
            type_T      **typep;
  
            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;
--- 3485,3501 ----
        }
        else if (**arg == '[')
        {
!           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;
  
            *arg = skipwhite(*arg + 1);
            if (compile_expr0(arg, cctx) == FAIL)
                return FAIL;
***************
*** 3505,3521 ****
            }
            *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)
            {
!               emsg(_(e_listreq));
!               return FAIL;
            }
!           if ((*typep)->tt_type == VAR_LIST)
                *typep = (*typep)->tt_member;
        }
        else if (**arg == '.' && (*arg)[1] != '.')
        {
--- 3507,3533 ----
            }
            *arg = *arg + 1;
  
!           typep = ((type_T **)stack->ga_data) + stack->ga_len - 2;
!           if ((*typep)->tt_type == VAR_LIST || (*typep) == &t_any)
            {
!               if ((*typep)->tt_type == VAR_LIST)
!                   *typep = (*typep)->tt_member;
!               if (generate_instr_drop(cctx, ISN_INDEX, 1) == FAIL)
!                   return FAIL;
            }
!           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,3541 ****
                semsg(_(e_syntax_at), *arg);
                return FAIL;
            }
!           if (generate_MEMBER(cctx, *arg, p - *arg) == FAIL)
                return FAIL;
            *arg = p;
        }
--- 3547,3553 ----
                semsg(_(e_syntax_at), *arg);
                return FAIL;
            }
!           if (generate_STRINGMEMBER(cctx, *arg, p - *arg) == FAIL)
                return FAIL;
            *arg = p;
        }
***************
*** 4501,4518 ****
--- 4513,4586 ----
  } 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".
   */
      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,4536 ****
      int               oplen = 0;
      int               heredoc = FALSE;
      type_T    *type = &t_any;
      lvar_T    *lvar = NULL;
      char_u    *name;
      char_u    *sp;
      int               has_type = 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)
        return NULL;
      if (var_count > 0)
      {
--- 4590,4606 ----
      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;
  
!     var_end = skip_var_list(arg, FALSE, &var_count, &semicolon);
!     if (var_end == NULL)
        return NULL;
      if (var_count > 0)
      {
***************
*** 4539,4545 ****
--- 4609,4620 ----
        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,4723 ****
--- 4790,4807 ----
                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,4779 ****
--- 4858,4890 ----
        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,4848 ****
        }
        generate_NEWLIST(cctx, l->lv_len);
        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)
            {
!               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;
            }
        }
  
--- 4903,4926 ----
        }
        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;
  
        // for "+=", "*=", "..=" etc. first load the current value
        if (*op != '=')
        {
!           generate_loadvar(cctx, dest, name, lvar, type);
! 
!           if (has_index)
            {
!               // TODO: get member from list or dict
!               emsg("Index with operation not supported yet");
!               goto theend;
            }
        }
  
***************
*** 4860,4866 ****
  
        if (cctx->ctx_skip != TRUE)
        {
!           stack = &cctx->ctx_type_stack;
            stacktype = stack->ga_len == 0 ? &t_void
                              : ((type_T **)stack->ga_data)[stack->ga_len - 1];
            if (lvar != NULL && (is_decl || !has_type))
--- 4938,4945 ----
  
        if (cctx->ctx_skip != TRUE)
        {
!           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,4893 ****
                            lvar->lv_type = stacktype;
                    }
                }
!               else if (need_type(stacktype, lvar->lv_type, -1, cctx) == FAIL)
!                   goto theend;
            }
!           else if (*p != '=' && check_type(type, stacktype, TRUE) == FAIL)
                goto theend;
        }
      }
--- 4963,4984 ----
                            lvar->lv_type = stacktype;
                    }
                }
!               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(member_type, stacktype, TRUE)
!                                                                      == FAIL)
                goto theend;
        }
      }
***************
*** 4906,4912 ****
        // variables are always initialized
        if (ga_grow(instr, 1) == FAIL)
            goto theend;
!       switch (type->tt_type)
        {
            case VAR_BOOL:
                generate_PUSHBOOL(cctx, VVAL_FALSE);
--- 4997,5003 ----
        // variables are always initialized
        if (ga_grow(instr, 1) == FAIL)
            goto theend;
!       switch (member_type->tt_type)
        {
            case VAR_BOOL:
                generate_PUSHBOOL(cctx, VVAL_FALSE);
***************
*** 4947,4957 ****
                break;
        }
      }
  
      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
--- 5038,5048 ----
                break;
        }
      }
+     end = p;
  
      if (oplen > 0 && *op != '=')
      {
        type_T      *expected = &t_number;
        type_T      *stacktype;
  
        // TODO: if type is known use float or any operation
***************
*** 4981,5083 ****
        }
      }
  
!     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      *rawname = name + (name[1] == ':' ? 2 : 0);
!               imported_T  *import = NULL;
!               int         sid = current_sctx.sc_sid;
!               int         idx;
  
!               if (name[1] != ':')
!               {
!                   import = find_imported(name, 0, cctx);
!                   if (import != NULL)
!                       sid = import->imp_sid;
!               }
  
!               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);
!               }
!               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)
                {
!                   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;
                }
!               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;
      }
!     ret = p;
  
  theend:
      vim_free(name);
--- 5072,5229 ----
        }
      }
  
!     if (has_index)
      {
!       int r;
  
!       // 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);
  
!       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      *rawname = name + (name[1] == ':' ? 2 : 0);
!                   imported_T  *import = NULL;
!                   int         sid = current_sctx.sc_sid;
!                   int         idx;
  
                    if (name[1] != ':')
                    {
!                       import = find_imported(name, 0, cctx);
!                       if (import != NULL)
!                           sid = import->imp_sid;
!                   }
  
!                   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);
                    }
!                   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)
!                   {
!                       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);
                }
!               break;
!       }
      }
!     ret = end;
  
  theend:
      vim_free(name);
***************
*** 6252,6258 ****
      for (;;)
      {
        exarg_T ea;
!       int     is_ex_command = FALSE;
  
        // Bail out on the first error to avoid a flood of errors and report
        // the right line number when inside try/catch.
--- 6398,6404 ----
      for (;;)
      {
        exarg_T ea;
!       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,6333 ****
                break;
  
            case ':':
!               is_ex_command = TRUE;
                break;
        }
  
--- 6473,6479 ----
                break;
  
            case ':':
!               starts_with_colon = TRUE;
                break;
        }
  
***************
*** 6347,6366 ****
        if (checkforcmd(&ea.cmd, "call", 3))
            ea.cmd = skipwhite(ea.cmd);
  
!       if (!is_ex_command)
        {
            // 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 == '@')
                                                         ? ea.cmd + 1 : ea.cmd;
!           p = to_name_end(p, TRUE);
            if (p > ea.cmd && *p != NUL)
            {
!               int oplen;
!               int heredoc;
! 
!               oplen = assignment_len(skipwhite(p), &heredoc);
                if (oplen > 0)
                {
                    // Recognize an assignment if we recognize the variable
--- 6493,6518 ----
        if (checkforcmd(&ea.cmd, "call", 3))
            ea.cmd = skipwhite(ea.cmd);
  
!       if (!starts_with_colon)
        {
+           char_u *pskip;
+ 
            // Assuming the command starts with a variable or function name,
!           // 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(pskip, TRUE);
            if (p > ea.cmd && *p != NUL)
            {
!               char_u *var_end;
!               int     oplen;
!               int     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,6399 ****
         * COMMAND after range
         */
        ea.cmd = skip_range(ea.cmd, NULL);
!       p = find_ex_command(&ea, NULL, is_ex_command ? NULL
                   : (void *(*)(char_u *, size_t, cctx_T *))lookup_local,
                                                                        &cctx);
  
--- 6545,6551 ----
         * COMMAND after range
         */
        ea.cmd = skip_range(ea.cmd, NULL);
!       p = find_ex_command(&ea, NULL, starts_with_colon ? NULL
                   : (void *(*)(char_u *, size_t, cctx_T *))lookup_local,
                                                                        &cctx);
  
***************
*** 6675,6681 ****
        case ISN_LOADW:
        case ISN_LOADT:
        case ISN_LOADOPT:
!       case ISN_MEMBER:
        case ISN_PUSHEXC:
        case ISN_PUSHS:
        case ISN_STOREENV:
--- 6827,6833 ----
        case ISN_LOADW:
        case ISN_LOADT:
        case ISN_LOADOPT:
!       case ISN_STRINGMEMBER:
        case ISN_PUSHEXC:
        case ISN_PUSHS:
        case ISN_STOREENV:
***************
*** 6758,6763 ****
--- 6910,6916 ----
        case ISN_EXECUTE:
        case ISN_FOR:
        case ISN_INDEX:
+       case ISN_MEMBER:
        case ISN_JUMP:
        case ISN_LOAD:
        case ISN_LOADOUTER:
***************
*** 6783,6788 ****
--- 6936,6943 ----
        case ISN_STORENR:
        case ISN_STOREREG:
        case ISN_STORESCRIPT:
+       case ISN_STOREDICT:
+       case ISN_STORELIST:
        case ISN_THROW:
        case ISN_TRY:
            // nothing allocated
*** ../vim-8.2.0729/src/vim9execute.c   2020-05-09 23:20:16.997309756 +0200
--- src/vim9execute.c   2020-05-10 18:36:59.071020484 +0200
***************
*** 1252,1257 ****
--- 1252,1327 ----
                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,2028 ****
                }
                break;
  
-           // dict member with string key
            case ISN_MEMBER:
                {
                    dict_T      *dict;
                    dictitem_T  *di;
  
                    tv = STACK_TV_BOT(-1);
--- 2089,2132 ----
                }
                break;
  
            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;
  
                    tv = STACK_TV_BOT(-1);
***************
*** 2380,2385 ****
--- 2484,2497 ----
                                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,2662 ****
            // 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,
                                                  iptr->isn_arg.string); break;
            case ISN_NEGATENR: smsg("%4d NEGATENR", current); break;
  
--- 2768,2775 ----
            // expression operations
            case ISN_CONCAT: smsg("%4d CONCAT", current); break;
            case ISN_INDEX: smsg("%4d INDEX", current); break;
!           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;
  
*** ../vim-8.2.0729/src/globals.h       2020-05-07 18:37:00.128512594 +0200
--- src/globals.h       2020-05-10 18:29:33.540180038 +0200
***************
*** 1673,1678 ****
--- 1673,1679 ----
  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"));
*** ../vim-8.2.0729/src/testdir/test_vim9_expr.vim      2020-05-09 
15:43:58.322878209 +0200
--- src/testdir/test_vim9_expr.vim      2020-05-10 18:51:05.024719168 +0200
***************
*** 644,652 ****
    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:')
    if has('float')
!     call CheckDefFailure(["let x = 0.7[1]"], 'E714:')
    endif
  endfunc
  
--- 644,652 ----
    call CheckDefFailure(["let x = #{one: 1} / #{two: 2}"], 'E1036:')
    call CheckDefFailure(["let x = #{one: 1} % #{two: 2}"], 'E1035:')
  
!   call CheckDefFailure(["let x = 0xff[1]"], 'E1090:')
    if has('float')
!     call CheckDefFailure(["let x = 0.7[1]"], 'E1090:')
    endif
  endfunc
  
*** ../vim-8.2.0729/src/testdir/test_vim9_cmd.vim       2020-04-26 
13:50:38.436096831 +0200
--- src/testdir/test_vim9_cmd.vim       2020-05-10 18:43:25.457831506 +0200
***************
*** 44,48 ****
--- 44,70 ----
    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
*** ../vim-8.2.0729/src/testdir/test_vim9_script.vim    2020-05-10 
15:24:41.392262069 +0200
--- src/testdir/test_vim9_script.vim    2020-05-10 19:01:44.766743159 +0200
***************
*** 38,44 ****
  
    call CheckDefFailure(['let x:string'], 'E1069:')
    call CheckDefFailure(['let x:string = "x"'], 'E1069:')
!   call CheckDefFailure(['let a:string = "x"'], 'E1082:')
  
    let a: number = 6
    assert_equal(6, a)
--- 38,44 ----
  
    call CheckDefFailure(['let x:string'], 'E1069:')
    call CheckDefFailure(['let x:string = "x"'], 'E1069:')
!   call CheckDefFailure(['let a:string = "x"'], 'E1069:')
  
    let a: number = 6
    assert_equal(6, a)
*** ../vim-8.2.0729/src/version.c       2020-05-10 15:24:41.392262069 +0200
--- src/version.c       2020-05-10 17:31:58.034861889 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     730,
  /**/

-- 
>From "know your smileys":
 :^[/   mean-smiley-with-cigarette

 /// Bram Moolenaar -- [email protected] -- 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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202005101710.04AHAwEh015277%40masaka.moolenaar.net.

Raspunde prin e-mail lui