forked from aniani/vim
patch 8.2.0730: Vim9: Assignment to dict member does not work
Problem: Vim9: Assignment to dict member does not work. Solution: Parse dict assignment. Implement getting dict member.
This commit is contained in:
parent
89483d4043
commit
1cc2a94f80
@ -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_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_dictkey[] INIT(= N_("E716: Key not present in Dictionary: %s"));
|
||||||
EXTERN char e_listreq[] INIT(= N_("E714: List required"));
|
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_listblobreq[] INIT(= N_("E897: List or Blob required"));
|
||||||
EXTERN char e_list_end[] INIT(= N_("E697: Missing end of List ']': %s"));
|
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"));
|
EXTERN char e_listdictarg[] INIT(= N_("E712: Argument of %s must be a List or Dictionary"));
|
||||||
|
@ -44,5 +44,27 @@ def Test_syn_include_wildcards()
|
|||||||
delete('Xthemine.vim')
|
delete('Xthemine.vim')
|
||||||
enddef
|
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: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@ -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}"], 'E1036:')
|
||||||
call CheckDefFailure(["let x = #{one: 1} % #{two: 2}"], 'E1035:')
|
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')
|
if has('float')
|
||||||
call CheckDefFailure(["let x = 0.7[1]"], 'E714:')
|
call CheckDefFailure(["let x = 0.7[1]"], 'E1090:')
|
||||||
endif
|
endif
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ def Test_assignment()
|
|||||||
|
|
||||||
call CheckDefFailure(['let x:string'], 'E1069:')
|
call CheckDefFailure(['let x:string'], 'E1069:')
|
||||||
call CheckDefFailure(['let x:string = "x"'], '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
|
let a: number = 6
|
||||||
assert_equal(6, a)
|
assert_equal(6, a)
|
||||||
|
@ -746,6 +746,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
730,
|
||||||
/**/
|
/**/
|
||||||
729,
|
729,
|
||||||
/**/
|
/**/
|
||||||
|
@ -48,6 +48,8 @@ typedef enum {
|
|||||||
// ISN_STOREOTHER, // pop into other script variable isn_arg.other.
|
// ISN_STOREOTHER, // pop into other script variable isn_arg.other.
|
||||||
|
|
||||||
ISN_STORENR, // store number into local variable isn_arg.storenr.stnr_idx
|
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_UNLET, // unlet variable isn_arg.unlet.ul_name
|
||||||
ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name
|
ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name
|
||||||
@ -110,7 +112,8 @@ typedef enum {
|
|||||||
// expression operations
|
// expression operations
|
||||||
ISN_CONCAT,
|
ISN_CONCAT,
|
||||||
ISN_INDEX, // [expr] list index
|
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_2BOOL, // convert value to bool, invert if isn_arg.number != 0
|
||||||
ISN_2STRING, // convert value to string at isn_arg.number on stack
|
ISN_2STRING, // convert value to string at isn_arg.number on stack
|
||||||
ISN_NEGATENR, // apply "-" to number
|
ISN_NEGATENR, // apply "-" to number
|
||||||
|
@ -1558,17 +1558,17 @@ generate_PCALL(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an ISN_MEMBER instruction.
|
* Generate an ISN_STRINGMEMBER instruction.
|
||||||
*/
|
*/
|
||||||
static int
|
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;
|
isn_T *isn;
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
type_T *type;
|
type_T *type;
|
||||||
|
|
||||||
RETURN_OK_IF_SKIP(cctx);
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
if ((isn = generate_instr(cctx, ISN_MEMBER)) == NULL)
|
if ((isn = generate_instr(cctx, ISN_STRINGMEMBER)) == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
isn->isn_arg.string = vim_strnsave(name, (int)len);
|
isn->isn_arg.string = vim_strnsave(name, (int)len);
|
||||||
|
|
||||||
@ -3485,15 +3485,17 @@ compile_subscript(
|
|||||||
}
|
}
|
||||||
else if (**arg == '[')
|
else if (**arg == '[')
|
||||||
{
|
{
|
||||||
garray_T *stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
type_T **typep;
|
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)
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
// list index: list[123]
|
|
||||||
// TODO: more arguments
|
|
||||||
// TODO: dict member dict['name']
|
|
||||||
*arg = skipwhite(*arg + 1);
|
*arg = skipwhite(*arg + 1);
|
||||||
if (compile_expr0(arg, cctx) == FAIL)
|
if (compile_expr0(arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@ -3505,17 +3507,27 @@ compile_subscript(
|
|||||||
}
|
}
|
||||||
*arg = *arg + 1;
|
*arg = *arg + 1;
|
||||||
|
|
||||||
if (generate_instr_drop(cctx, ISN_INDEX, 1) == FAIL)
|
typep = ((type_T **)stack->ga_data) + stack->ga_len - 2;
|
||||||
return FAIL;
|
if ((*typep)->tt_type == VAR_LIST || (*typep) == &t_any)
|
||||||
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));
|
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;
|
return FAIL;
|
||||||
}
|
}
|
||||||
if ((*typep)->tt_type == VAR_LIST)
|
|
||||||
*typep = (*typep)->tt_member;
|
|
||||||
}
|
}
|
||||||
else if (**arg == '.' && (*arg)[1] != '.')
|
else if (**arg == '.' && (*arg)[1] != '.')
|
||||||
{
|
{
|
||||||
@ -3535,7 +3547,7 @@ compile_subscript(
|
|||||||
semsg(_(e_syntax_at), *arg);
|
semsg(_(e_syntax_at), *arg);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
if (generate_MEMBER(cctx, *arg, p - *arg) == FAIL)
|
if (generate_STRINGMEMBER(cctx, *arg, p - *arg) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
*arg = p;
|
*arg = p;
|
||||||
}
|
}
|
||||||
@ -4500,6 +4512,59 @@ typedef enum {
|
|||||||
dest_reg,
|
dest_reg,
|
||||||
} assign_dest_T;
|
} 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"
|
* compile "let var [= expr]", "const var = expr" and "var = expr"
|
||||||
* "arg" points to "var".
|
* "arg" points to "var".
|
||||||
@ -4507,12 +4572,15 @@ typedef enum {
|
|||||||
static char_u *
|
static char_u *
|
||||||
compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
||||||
{
|
{
|
||||||
|
char_u *var_end;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
char_u *end = arg;
|
||||||
char_u *ret = NULL;
|
char_u *ret = NULL;
|
||||||
int var_count = 0;
|
int var_count = 0;
|
||||||
int semicolon = 0;
|
int semicolon = 0;
|
||||||
size_t varlen;
|
size_t varlen;
|
||||||
garray_T *instr = &cctx->ctx_instr;
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
int new_local = FALSE;
|
int new_local = FALSE;
|
||||||
char_u *op;
|
char_u *op;
|
||||||
int opt_type;
|
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 oplen = 0;
|
||||||
int heredoc = FALSE;
|
int heredoc = FALSE;
|
||||||
type_T *type = &t_any;
|
type_T *type = &t_any;
|
||||||
|
type_T *member_type = &t_any;
|
||||||
lvar_T *lvar = NULL;
|
lvar_T *lvar = NULL;
|
||||||
char_u *name;
|
char_u *name;
|
||||||
char_u *sp;
|
char_u *sp;
|
||||||
int has_type = FALSE;
|
int has_type = FALSE;
|
||||||
|
int has_index = FALSE;
|
||||||
int is_decl = cmdidx == CMD_let || cmdidx == CMD_const;
|
int is_decl = cmdidx == CMD_let || cmdidx == CMD_const;
|
||||||
int instr_count = -1;
|
int instr_count = -1;
|
||||||
|
|
||||||
p = skip_var_list(arg, FALSE, &var_count, &semicolon);
|
var_end = skip_var_list(arg, FALSE, &var_count, &semicolon);
|
||||||
if (p == NULL)
|
if (var_end == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (var_count > 0)
|
if (var_count > 0)
|
||||||
{
|
{
|
||||||
@ -4539,7 +4609,12 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
return NULL;
|
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:".
|
// "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] == ':')
|
if (is_decl && p == arg + 2 && p[-1] == ':')
|
||||||
--p;
|
--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);
|
semsg(_("E1082: Cannot use a namespaced variable: %s"), name);
|
||||||
goto theend;
|
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 (dest != dest_option)
|
||||||
{
|
{
|
||||||
if (is_decl && *p == ':')
|
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;
|
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)
|
if (heredoc)
|
||||||
{
|
{
|
||||||
list_T *l;
|
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);
|
generate_NEWLIST(cctx, l->lv_len);
|
||||||
type = &t_list_string;
|
type = &t_list_string;
|
||||||
|
member_type = &t_list_string;
|
||||||
list_free(l);
|
list_free(l);
|
||||||
p += STRLEN(p);
|
p += STRLEN(p);
|
||||||
}
|
}
|
||||||
else if (oplen > 0)
|
else if (oplen > 0)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
type_T *stacktype;
|
|
||||||
garray_T *stack;
|
|
||||||
|
|
||||||
// for "+=", "*=", "..=" etc. first load the current value
|
// for "+=", "*=", "..=" etc. first load the current value
|
||||||
if (*op != '=')
|
if (*op != '=')
|
||||||
{
|
{
|
||||||
switch (dest)
|
generate_loadvar(cctx, dest, name, lvar, type);
|
||||||
|
|
||||||
|
if (has_index)
|
||||||
{
|
{
|
||||||
case dest_option:
|
// TODO: get member from list or dict
|
||||||
// TODO: check the option exists
|
emsg("Index with operation not supported yet");
|
||||||
generate_LOAD(cctx, ISN_LOADOPT, 0, name, type);
|
goto theend;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4860,7 +4938,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
|
|
||||||
if (cctx->ctx_skip != TRUE)
|
if (cctx->ctx_skip != TRUE)
|
||||||
{
|
{
|
||||||
stack = &cctx->ctx_type_stack;
|
type_T *stacktype;
|
||||||
|
|
||||||
stacktype = stack->ga_len == 0 ? &t_void
|
stacktype = stack->ga_len == 0 ? &t_void
|
||||||
: ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
: ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
||||||
if (lvar != NULL && (is_decl || !has_type))
|
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;
|
lvar->lv_type = stacktype;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (need_type(stacktype, lvar->lv_type, -1, cctx) == FAIL)
|
else
|
||||||
goto theend;
|
{
|
||||||
|
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;
|
goto theend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4906,7 +4997,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
// variables are always initialized
|
// variables are always initialized
|
||||||
if (ga_grow(instr, 1) == FAIL)
|
if (ga_grow(instr, 1) == FAIL)
|
||||||
goto theend;
|
goto theend;
|
||||||
switch (type->tt_type)
|
switch (member_type->tt_type)
|
||||||
{
|
{
|
||||||
case VAR_BOOL:
|
case VAR_BOOL:
|
||||||
generate_PUSHBOOL(cctx, VVAL_FALSE);
|
generate_PUSHBOOL(cctx, VVAL_FALSE);
|
||||||
@ -4947,11 +5038,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
end = p;
|
||||||
|
|
||||||
if (oplen > 0 && *op != '=')
|
if (oplen > 0 && *op != '=')
|
||||||
{
|
{
|
||||||
type_T *expected = &t_number;
|
type_T *expected = &t_number;
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
|
||||||
type_T *stacktype;
|
type_T *stacktype;
|
||||||
|
|
||||||
// TODO: if type is known use float or any operation
|
// 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:
|
int r;
|
||||||
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] != ':')
|
// 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:
|
||||||
{
|
{
|
||||||
import = find_imported(name, 0, cctx);
|
char_u *rawname = name + (name[1] == ':' ? 2 : 0);
|
||||||
if (import != NULL)
|
imported_T *import = NULL;
|
||||||
sid = import->imp_sid;
|
int sid = current_sctx.sc_sid;
|
||||||
}
|
int idx;
|
||||||
|
|
||||||
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] != ':')
|
if (name[1] != ':')
|
||||||
{
|
{
|
||||||
int len = (int)STRLEN(name) + 3;
|
import = find_imported(name, 0, cctx);
|
||||||
|
if (import != NULL)
|
||||||
name_s = alloc(len);
|
sid = import->imp_sid;
|
||||||
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)
|
idx = get_script_item_idx(sid, rawname, TRUE);
|
||||||
vim_free(name_s);
|
// TODO: specific type
|
||||||
}
|
if (idx < 0)
|
||||||
else
|
{
|
||||||
generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
|
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);
|
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)
|
break;
|
||||||
generate_STORE(cctx, ISN_STOREOUTER, lvar->lv_idx, NULL);
|
case dest_local:
|
||||||
else
|
if (lvar != NULL)
|
||||||
generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
|
{
|
||||||
}
|
isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
|
||||||
break;
|
|
||||||
|
// 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 = p;
|
ret = end;
|
||||||
|
|
||||||
theend:
|
theend:
|
||||||
vim_free(name);
|
vim_free(name);
|
||||||
@ -6252,7 +6398,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
exarg_T ea;
|
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
|
// Bail out on the first error to avoid a flood of errors and report
|
||||||
// the right line number when inside try/catch.
|
// 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;
|
break;
|
||||||
|
|
||||||
case ':':
|
case ':':
|
||||||
is_ex_command = TRUE;
|
starts_with_colon = TRUE;
|
||||||
break;
|
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))
|
if (checkforcmd(&ea.cmd, "call", 3))
|
||||||
ea.cmd = skipwhite(ea.cmd);
|
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,
|
// Assuming the command starts with a variable or function name,
|
||||||
// find what follows. Also "&opt = val", "$ENV = val" and "@r =
|
// find what follows.
|
||||||
// val".
|
// Skip over "var.member", "var[idx]" and the like.
|
||||||
p = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@')
|
// Also "&opt = val", "$ENV = val" and "@r = val".
|
||||||
|
pskip = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@')
|
||||||
? ea.cmd + 1 : 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)
|
if (p > ea.cmd && *p != NUL)
|
||||||
{
|
{
|
||||||
int oplen;
|
char_u *var_end;
|
||||||
int heredoc;
|
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)
|
if (oplen > 0)
|
||||||
{
|
{
|
||||||
// Recognize an assignment if we recognize the variable
|
// 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
|
* COMMAND after range
|
||||||
*/
|
*/
|
||||||
ea.cmd = skip_range(ea.cmd, NULL);
|
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,
|
: (void *(*)(char_u *, size_t, cctx_T *))lookup_local,
|
||||||
&cctx);
|
&cctx);
|
||||||
|
|
||||||
@ -6675,7 +6827,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_LOADW:
|
case ISN_LOADW:
|
||||||
case ISN_LOADT:
|
case ISN_LOADT:
|
||||||
case ISN_LOADOPT:
|
case ISN_LOADOPT:
|
||||||
case ISN_MEMBER:
|
case ISN_STRINGMEMBER:
|
||||||
case ISN_PUSHEXC:
|
case ISN_PUSHEXC:
|
||||||
case ISN_PUSHS:
|
case ISN_PUSHS:
|
||||||
case ISN_STOREENV:
|
case ISN_STOREENV:
|
||||||
@ -6758,6 +6910,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_EXECUTE:
|
case ISN_EXECUTE:
|
||||||
case ISN_FOR:
|
case ISN_FOR:
|
||||||
case ISN_INDEX:
|
case ISN_INDEX:
|
||||||
|
case ISN_MEMBER:
|
||||||
case ISN_JUMP:
|
case ISN_JUMP:
|
||||||
case ISN_LOAD:
|
case ISN_LOAD:
|
||||||
case ISN_LOADOUTER:
|
case ISN_LOADOUTER:
|
||||||
@ -6783,6 +6936,8 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_STORENR:
|
case ISN_STORENR:
|
||||||
case ISN_STOREREG:
|
case ISN_STOREREG:
|
||||||
case ISN_STORESCRIPT:
|
case ISN_STORESCRIPT:
|
||||||
|
case ISN_STOREDICT:
|
||||||
|
case ISN_STORELIST:
|
||||||
case ISN_THROW:
|
case ISN_THROW:
|
||||||
case ISN_TRY:
|
case ISN_TRY:
|
||||||
// nothing allocated
|
// nothing allocated
|
||||||
|
@ -1252,6 +1252,76 @@ call_def_function(
|
|||||||
tv->vval.v_number = iptr->isn_arg.storenr.stnr_val;
|
tv->vval.v_number = iptr->isn_arg.storenr.stnr_val;
|
||||||
break;
|
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
|
// push constant
|
||||||
case ISN_PUSHNR:
|
case ISN_PUSHNR:
|
||||||
case ISN_PUSHBOOL:
|
case ISN_PUSHBOOL:
|
||||||
@ -2019,8 +2089,42 @@ call_def_function(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// dict member with string key
|
|
||||||
case ISN_MEMBER:
|
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;
|
dict_T *dict;
|
||||||
dictitem_T *di;
|
dictitem_T *di;
|
||||||
@ -2380,6 +2484,14 @@ ex_disassemble(exarg_T *eap)
|
|||||||
iptr->isn_arg.storenr.stnr_idx);
|
iptr->isn_arg.storenr.stnr_idx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ISN_STORELIST:
|
||||||
|
smsg("%4d STORELIST", current);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ISN_STOREDICT:
|
||||||
|
smsg("%4d STOREDICT", current);
|
||||||
|
break;
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
case ISN_PUSHNR:
|
case ISN_PUSHNR:
|
||||||
smsg("%4d PUSHNR %lld", current,
|
smsg("%4d PUSHNR %lld", current,
|
||||||
@ -2656,7 +2768,8 @@ ex_disassemble(exarg_T *eap)
|
|||||||
// expression operations
|
// expression operations
|
||||||
case ISN_CONCAT: smsg("%4d CONCAT", current); break;
|
case ISN_CONCAT: smsg("%4d CONCAT", current); break;
|
||||||
case ISN_INDEX: smsg("%4d INDEX", 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;
|
iptr->isn_arg.string); break;
|
||||||
case ISN_NEGATENR: smsg("%4d NEGATENR", current); break;
|
case ISN_NEGATENR: smsg("%4d NEGATENR", current); break;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user