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 -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///