summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-06-14 23:05:10 +0200
committerBram Moolenaar <Bram@vim.org>2020-06-14 23:05:10 +0200
commit47a519a933e8bcaf703a5feaac5c01491a658ee3 (patch)
treee684ae57e04c729278f3248e9131768d8a957060
parenta65d8b5bb9e9267c6e4500c67d26c839a64f30eb (diff)
downloadvim-git-8.2.0981.tar.gz
patch 8.2.0981: Vim9: cannot compile "[var, var] = list"v8.2.0981
Problem: Vim9: cannot compile "[var, var] = list". Solution: Implement list assignment.
-rw-r--r--src/eval.c2
-rw-r--r--src/evalvars.c28
-rw-r--r--src/proto/evalvars.pro3
-rw-r--r--src/testdir/test_vim9_script.vim8
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h1
-rw-r--r--src/vim9compile.c1206
-rw-r--r--src/vim9execute.c27
8 files changed, 730 insertions, 547 deletions
diff --git a/src/eval.c b/src/eval.c
index 6ca362886..dfd82429f 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1431,7 +1431,7 @@ eval_for_line(
if (fi == NULL)
return NULL;
- expr = skip_var_list(arg, TRUE, &fi->fi_varcount, &fi->fi_semicolon);
+ expr = skip_var_list(arg, TRUE, &fi->fi_varcount, &fi->fi_semicolon, FALSE);
if (expr == NULL)
return fi;
diff --git a/src/evalvars.c b/src/evalvars.c
index 31cd01d25..d2c71d290 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -164,7 +164,6 @@ static dict_T vimvardict; // Dictionary with v: variables
// for VIM_VERSION_ defines
#include "version.h"
-static char_u *skip_var_one(char_u *arg, int include_type);
static void list_glob_vars(int *first);
static void list_buf_vars(int *first);
static void list_win_vars(int *first);
@@ -709,7 +708,7 @@ ex_let(exarg_T *eap)
if (eap->arg == eap->cmd)
flags |= LET_NO_COMMAND;
- argend = skip_var_list(arg, TRUE, &var_count, &semicolon);
+ argend = skip_var_list(arg, TRUE, &var_count, &semicolon, FALSE);
if (argend == NULL)
return;
if (argend > arg && argend[-1] == '.') // for var.='str'
@@ -916,7 +915,8 @@ ex_let_vars(
* Skip over assignable variable "var" or list of variables "[var, var]".
* Used for ":let varvar = expr" and ":for varvar in expr".
* For "[var, var]" increment "*var_count" for each variable.
- * for "[var, var; var]" set "semicolon".
+ * for "[var, var; var]" set "semicolon" to 1.
+ * If "silent" is TRUE do not give an "invalid argument" error message.
* Return NULL for an error.
*/
char_u *
@@ -924,7 +924,8 @@ skip_var_list(
char_u *arg,
int include_type,
int *var_count,
- int *semicolon)
+ int *semicolon,
+ int silent)
{
char_u *p, *s;
@@ -935,10 +936,11 @@ skip_var_list(
for (;;)
{
p = skipwhite(p + 1); // skip whites after '[', ';' or ','
- s = skip_var_one(p, TRUE);
+ s = skip_var_one(p, FALSE);
if (s == p)
{
- semsg(_(e_invarg2), p);
+ if (!silent)
+ semsg(_(e_invarg2), p);
return NULL;
}
++*var_count;
@@ -957,7 +959,8 @@ skip_var_list(
}
else if (*p != ',')
{
- semsg(_(e_invarg2), p);
+ if (!silent)
+ semsg(_(e_invarg2), p);
return NULL;
}
}
@@ -972,7 +975,7 @@ skip_var_list(
* l[idx].
* In Vim9 script also skip over ": type" if "include_type" is TRUE.
*/
- static char_u *
+ char_u *
skip_var_one(char_u *arg, int include_type)
{
char_u *end;
@@ -981,10 +984,13 @@ skip_var_one(char_u *arg, int include_type)
return arg + 2;
end = find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
- if (include_type && current_sctx.sc_version == SCRIPT_VERSION_VIM9
- && *end == ':')
+ if (include_type && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
{
- end = skip_type(skipwhite(end + 1));
+ // "a: type" is declaring variable "a" with a type, not "a:".
+ if (end == arg + 2 && end[-1] == ':')
+ --end;
+ if (*end == ':')
+ end = skip_type(skipwhite(end + 1));
}
return end;
}
diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro
index 97641b5ab..eab82c8cf 100644
--- a/src/proto/evalvars.pro
+++ b/src/proto/evalvars.pro
@@ -16,7 +16,8 @@ void restore_vimvar(int idx, typval_T *save_tv);
list_T *heredoc_get(exarg_T *eap, char_u *cmd, int script_get);
void ex_let(exarg_T *eap);
int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, int var_count, int flags, char_u *op);
-char_u *skip_var_list(char_u *arg, int include_type, int *var_count, int *semicolon);
+char_u *skip_var_list(char_u *arg, int include_type, int *var_count, int *semicolon, int silent);
+char_u *skip_var_one(char_u *arg, int include_type);
void list_hashtable_vars(hashtab_T *ht, char *prefix, int empty, int *first);
void ex_unlet(exarg_T *eap);
void ex_lockvar(exarg_T *eap);
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index 58b2ba255..a030686dc 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -223,6 +223,14 @@ def Test_assignment_default()
assert_equal(5678, nr)
enddef
+def Test_assignment_var_list()
+ let v1: string
+ let v2: string
+ [v1, v2] = ['one', 'two']
+ assert_equal('one', v1)
+ assert_equal('two', v2)
+enddef
+
def Mess(): string
v:foldstart = 123
return 'xxx'
diff --git a/src/version.c b/src/version.c
index eabdafc5f..b811ae09b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 981,
+/**/
980,
/**/
979,
diff --git a/src/vim9.h b/src/vim9.h
index 27914af7e..66b088290 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -112,6 +112,7 @@ typedef enum {
// expression operations
ISN_CONCAT,
ISN_INDEX, // [expr] list index
+ ISN_GETITEM, // push list item, isn_arg.number is the 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
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 01af95e74..12b125a68 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -136,6 +136,7 @@ struct cctx_S {
static char e_var_notfound[] = N_("E1001: variable not found: %s");
static char e_syntax_at[] = N_("E1002: Syntax error at %s");
static char e_used_as_arg[] = N_("E1006: %s is used as an argument");
+static char e_cannot_use_void[] = N_("E1031: Cannot use void value");
static void delete_def_function_contents(dfunc_T *dfunc);
static void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
@@ -1053,6 +1054,38 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
}
/*
+ * Generate an ISN_GETITEM instruction with "index".
+ */
+ static int
+generate_GETITEM(cctx_T *cctx, int index)
+{
+ isn_T *isn;
+ garray_T *stack = &cctx->ctx_type_stack;
+ type_T *type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+ type_T *item_type = &t_any;
+
+ RETURN_OK_IF_SKIP(cctx);
+
+ if (type->tt_type == VAR_LIST)
+ item_type = type->tt_member;
+ else if (type->tt_type != VAR_ANY)
+ {
+ emsg(_(e_listreq));
+ return FAIL;
+ }
+ if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL)
+ return FAIL;
+ isn->isn_arg.number = index;
+
+ // add the item type to the type stack
+ if (ga_grow(stack, 1) == FAIL)
+ return FAIL;
+ ((type_T **)stack->ga_data)[stack->ga_len] = item_type;
+ ++stack->ga_len;
+ return OK;
+}
+
+/*
* Generate an ISN_STORE instruction.
*/
static int
@@ -4573,684 +4606,776 @@ generate_loadvar(
}
/*
- * compile "let var [= expr]", "const var = expr" and "var = expr"
+ * Compile declaration and assignment:
+ * "let var", "let var = expr", "const var = expr" and "var = expr"
* "arg" points to "var".
+ * Return NULL for an error.
+ * Return "arg" if it does not look like a variable list.
*/
static char_u *
compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
{
- char_u *var_end;
+ char_u *var_start;
char_u *p;
char_u *end = arg;
char_u *ret = NULL;
int var_count = 0;
+ int var_idx;
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;
- assign_dest_T dest = dest_local;
- int opt_flags = 0;
- int vimvaridx = -1;
int oplen = 0;
int heredoc = FALSE;
type_T *type = &t_any;
type_T *member_type = &t_any;
- lvar_T *lvar = NULL;
- lvar_T arg_lvar;
- char_u *name;
+ char_u *name = NULL;
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)
+ // Skip over the "var" or "[var, var]" to get to any "=".
+ p = skip_var_list(arg, TRUE, &var_count, &semicolon, TRUE);
+ if (p == NULL)
+ return *arg == '[' ? arg : NULL;
+
+ if (var_count > 0 && is_decl)
{
- // TODO: let [var, var] = list
- emsg("Cannot handle a list yet");
+ emsg(_("E1092: Cannot use a list for a declaration"));
return NULL;
}
- p = (*arg == '&' || *arg == '$' || *arg == '@') ? arg + 1 : arg;
- p = to_name_end(p, TRUE);
+ sp = p;
+ p = skipwhite(p);
+ op = p;
+ oplen = assignment_len(p, &heredoc);
- // "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;
+ if (var_count > 0 && oplen == 0)
+ // can be something like "[1, 2]->func()"
+ return arg;
- varlen = p - arg;
- name = vim_strnsave(arg, varlen);
- if (name == NULL)
+ if (oplen > 0 && (!VIM_ISWHITE(*sp) || !VIM_ISWHITE(op[oplen])))
+ {
+ char_u buf[4];
+
+ vim_strncpy(buf, op, oplen);
+ semsg(_(e_white_both), buf);
return NULL;
+ }
- if (cctx->ctx_skip != TRUE)
+ if (heredoc)
{
- if (*arg == '&')
+ list_T *l;
+ listitem_T *li;
+
+ // [let] varname =<< [trim] {end}
+ eap->getline = exarg_getline;
+ eap->cookie = cctx;
+ l = heredoc_get(eap, op + 3, FALSE);
+
+ // Push each line and the create the list.
+ FOR_ALL_LIST_ITEMS(l, li)
{
- int cc;
- long numval;
+ generate_PUSHS(cctx, li->li_tv.vval.v_string);
+ li->li_tv.vval.v_string = NULL;
+ }
+ generate_NEWLIST(cctx, l->lv_len);
+ type = &t_list_string;
+ member_type = &t_list_string;
+ list_free(l);
+ p += STRLEN(p);
+ end = p;
+ }
+ else if (var_count > 0)
+ {
+ // for "[var, var] = expr" evaluate the expression here, loop over the
+ // list of variables below.
- dest = dest_option;
- if (cmdidx == CMD_const)
+ p = skipwhite(op + oplen);
+ if (compile_expr0(&p, cctx) == FAIL)
+ return NULL;
+ end = p;
+
+ 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 (stacktype->tt_type == VAR_VOID)
{
- emsg(_(e_const_option));
+ emsg(_(e_cannot_use_void));
goto theend;
}
- if (is_decl)
- {
- semsg(_("E1052: Cannot declare an option: %s"), arg);
+ if (need_type(stacktype, &t_list_any, -1, cctx) == FAIL)
goto theend;
- }
- p = arg;
- p = find_option_end(&p, &opt_flags);
- if (p == NULL)
+ // TODO: check length of list to be var_count (or more if
+ // "semicolon" set)
+ }
+ }
+
+ /*
+ * Loop over variables in "[var, var] = expr".
+ * For "var = expr" and "let var: type" this is done only once.
+ */
+ if (var_count > 0)
+ var_start = skipwhite(arg + 1); // skip over the "["
+ else
+ var_start = arg;
+ for (var_idx = 0; var_idx == 0 || var_idx < var_count; var_idx++)
+ {
+ char_u *var_end = skip_var_one(var_start, FALSE);
+ size_t varlen;
+ int new_local = FALSE;
+ int opt_type;
+ int opt_flags = 0;
+ assign_dest_T dest = dest_local;
+ int vimvaridx = -1;
+ lvar_T *lvar = NULL;
+ lvar_T arg_lvar;
+ int has_type = FALSE;
+ int has_index = FALSE;
+ int instr_count = -1;
+
+ p = (*var_start == '&' || *var_start == '$'
+ || *var_start == '@') ? var_start + 1 : var_start;
+ p = to_name_end(p, TRUE);
+
+ // "a: type" is declaring variable "a" with a type, not "a:".
+ if (is_decl && var_end == var_start + 2 && var_end[-1] == ':')
+ --var_end;
+ if (is_decl && p == var_start + 2 && p[-1] == ':')
+ --p;
+
+ varlen = p - var_start;
+ vim_free(name);
+ name = vim_strnsave(var_start, varlen);
+ if (name == NULL)
+ return NULL;
+ if (!heredoc)
+ type = &t_any;
+
+ if (cctx->ctx_skip != TRUE)
+ {
+ if (*var_start == '&')
{
- // cannot happen?
- emsg(_(e_letunexp));
- goto theend;
+ int cc;
+ long numval;
+
+ dest = dest_option;
+ if (cmdidx == CMD_const)
+ {
+ emsg(_(e_const_option));
+ goto theend;
+ }
+ if (is_decl)
+ {
+ semsg(_("E1052: Cannot declare an option: %s"), var_start);
+ goto theend;
+ }
+ p = var_start;
+ p = find_option_end(&p, &opt_flags);
+ if (p == NULL)
+ {
+ // cannot happen?
+ emsg(_(e_letunexp));
+ goto theend;
+ }
+ cc = *p;
+ *p = NUL;
+ opt_type = get_option_value(var_start + 1, &numval,
+ NULL, opt_flags);
+ *p = cc;
+ if (opt_type == -3)
+ {
+ semsg(_(e_unknown_option), var_start);
+ goto theend;
+ }
+ if (opt_type == -2 || opt_type == 0)
+ type = &t_string;
+ else
+ type = &t_number; // both number and boolean option
}
- cc = *p;
- *p = NUL;
- opt_type = get_option_value(arg + 1, &numval, NULL, opt_flags);
- *p = cc;
- if (opt_type == -3)
+ else if (*var_start == '$')
{
- semsg(_(e_unknown_option), arg);
- goto theend;
- }
- if (opt_type == -2 || opt_type == 0)
+ dest = dest_env;
type = &t_string;
- else
- type = &t_number; // both number and boolean option
- }
- else if (*arg == '$')
- {
- dest = dest_env;
- type = &t_string;
- if (is_decl)
- {
- semsg(_("E1065: Cannot declare an environment variable: %s"),
+ if (is_decl)
+ {
+ semsg(_("E1065: Cannot declare an environment variable: %s"),
name);
- goto theend;
+ goto theend;
+ }
}
- }
- else if (*arg == '@')
- {
- if (!valid_yank_reg(arg[1], TRUE))
+ else if (*var_start == '@')
{
- emsg_invreg(arg[1]);
- goto theend;
+ if (!valid_yank_reg(var_start[1], TRUE))
+ {
+ emsg_invreg(var_start[1]);
+ goto theend;
+ }
+ dest = dest_reg;
+ type = &t_string;
+ if (is_decl)
+ {
+ semsg(_("E1066: Cannot declare a register: %s"), name);
+ goto theend;
+ }
}
- dest = dest_reg;
- type = &t_string;
- if (is_decl)
+ else if (STRNCMP(var_start, "g:", 2) == 0)
{
- semsg(_("E1066: Cannot declare a register: %s"), name);
- goto theend;
+ dest = dest_global;
+ if (is_decl)
+ {
+ semsg(_("E1016: Cannot declare a global variable: %s"),
+ name);
+ goto theend;
+ }
}
- }
- else if (STRNCMP(arg, "g:", 2) == 0)
- {
- dest = dest_global;
- if (is_decl)
+ else if (STRNCMP(var_start, "b:", 2) == 0)
{
- semsg(_("E1016: Cannot declare a global variable: %s"), name);
- goto theend;
+ dest = dest_buffer;
+ if (is_decl)
+ {
+ semsg(_("E1078: Cannot declare a buffer variable: %s"),
+ name);
+ goto theend;
+ }
}
- }
- else if (STRNCMP(arg, "b:", 2) == 0)
- {
- dest = dest_buffer;
- if (is_decl)
+ else if (STRNCMP(var_start, "w:", 2) == 0)
{
- semsg(_("E1078: Cannot declare a buffer variable: %s"), name);
- goto theend;
+ dest = dest_window;
+ if (is_decl)
+ {
+ semsg(_("E1079: Cannot declare a window variable: %s"),
+ name);
+ goto theend;
+ }
}
- }
- else if (STRNCMP(arg, "w:", 2) == 0)
- {
- dest = dest_window;
- if (is_decl)
+ else if (STRNCMP(var_start, "t:", 2) == 0)
{
- semsg(_("E1079: Cannot declare a window variable: %s"), name);
- goto theend;
+ dest = dest_tab;
+ if (is_decl)
+ {
+ semsg(_("E1080: Cannot declare a tab variable: %s"), name);
+ goto theend;
+ }
}
- }
- else if (STRNCMP(arg, "t:", 2) == 0)
- {
- dest = dest_tab;
- if (is_decl)
+ else if (STRNCMP(var_start, "v:", 2) == 0)
{
- semsg(_("E1080: Cannot declare a tab variable: %s"), name);
- goto theend;
- }
- }
- else if (STRNCMP(arg, "v:", 2) == 0)
- {
- typval_T *vtv;
- int di_flags;
+ typval_T *vtv;
+ int di_flags;
- vimvaridx = find_vim_var(name + 2, &di_flags);
- if (vimvaridx < 0)
- {
- semsg(_(e_var_notfound), arg);
- goto theend;
+ vimvaridx = find_vim_var(name + 2, &di_flags);
+ if (vimvaridx < 0)
+ {
+ semsg(_(e_var_notfound), var_start);
+ goto theend;
+ }
+ // We use the current value of "sandbox" here, is that OK?
+ if (var_check_ro(di_flags, name, FALSE))
+ goto theend;
+ dest = dest_vimvar;
+ vtv = get_vim_var_tv(vimvaridx);
+ type = typval2type(vtv);
+ if (is_decl)
+ {
+ semsg(_("E1064: Cannot declare a v: variable: %s"), name);
+ goto theend;
+ }
}
- // We use the current value of "sandbox" here, is that OK?
- if (var_check_ro(di_flags, name, FALSE))
- goto theend;
- dest = dest_vimvar;
- vtv = get_vim_var_tv(vimvaridx);
- type = typval2type(vtv);
- if (is_decl)
+ else
{
- semsg(_("E1064: Cannot declare a v: variable: %s"), name);
- goto theend;
- }
- }
- else
- {
- int idx;
+ int idx;
+
+ for (idx = 0; reserved[idx] != NULL; ++idx)
+ if (STRCMP(reserved[idx], name) == 0)
+ {
+ semsg(_("E1034: Cannot use reserved name %s"), name);
+ goto theend;
+ }
- for (idx = 0; reserved[idx] != NULL; ++idx)
- if (STRCMP(reserved[idx], name) == 0)
+ lvar = lookup_local(var_start, varlen, cctx);
+ if (lvar == NULL)
{
- semsg(_("E1034: Cannot use reserved name %s"), name);
- goto theend;
+ CLEAR_FIELD(arg_lvar);
+ if (lookup_arg(var_start, varlen,
+ &arg_lvar.lv_idx, &arg_lvar.lv_type,
+ &arg_lvar.lv_from_outer, cctx) == OK)
+ {
+ if (is_decl)
+ {
+ semsg(_(e_used_as_arg), name);
+ goto theend;
+ }
+ lvar = &arg_lvar;
+ }
}
-
- lvar = lookup_local(arg, varlen, cctx);
- if (lvar == NULL)
- {
- CLEAR_FIELD(arg_lvar);
- if (lookup_arg(arg, varlen,
- &arg_lvar.lv_idx, &arg_lvar.lv_type,
- &arg_lvar.lv_from_outer, cctx) == OK)
+ if (lvar != NULL)
{
if (is_decl)
{
- semsg(_(e_used_as_arg), name);
+ semsg(_("E1017: Variable already declared: %s"), name);
+ goto theend;
+ }
+ else if (lvar->lv_const)
+ {
+ semsg(_("E1018: Cannot assign to a constant: %s"),
+ name);
goto theend;
}
- lvar = &arg_lvar;
}
- }
- if (lvar != NULL)
- {
- if (is_decl)
+ else if (STRNCMP(var_start, "s:", 2) == 0
+ || lookup_script(var_start, varlen) == OK
+ || find_imported(var_start, varlen, cctx) != NULL)
{
- semsg(_("E1017: Variable already declared: %s"), name);
- goto theend;
+ dest = dest_script;
+ if (is_decl)
+ {
+ semsg(_("E1054: Variable already declared in the script: %s"),
+ name);
+ goto theend;
+ }
}
- else if (lvar->lv_const)
+ else if (name[1] == ':' && name[2] != NUL)
{
- semsg(_("E1018: Cannot assign to a constant: %s"), name);
+ semsg(_("E1082: Cannot use a namespaced variable: %s"),
+ name);
goto theend;
}
- }
- else if (STRNCMP(arg, "s:", 2) == 0
- || lookup_script(arg, varlen) == OK
- || find_imported(arg, varlen, cctx) != NULL)
- {
- dest = dest_script;
- if (is_decl)
+ else if (!is_decl)
{
- semsg(_("E1054: Variable already declared in the script: %s"),
- name);
+ semsg(_("E1089: unknown variable: %s"), name);
goto theend;
}
}
- else if (name[1] == ':' && name[2] != NUL)
- {
- 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;
+ // handle "a:name" as a name, not index "name" on "a"
+ if (varlen > 1 || var_start[varlen] != ':')
+ p = var_end;
- if (dest != dest_option)
- {
- if (is_decl && *p == ':')
+ if (dest != dest_option)
{
- // parse optional type: "let var: type = expr"
- if (!VIM_ISWHITE(p[1]))
+ if (is_decl && *p == ':')
{
- semsg(_(e_white_after), ":");
- goto theend;
+ // parse optional type: "let var: type = expr"
+ if (!VIM_ISWHITE(p[1]))
+ {
+ semsg(_(e_white_after), ":");
+ goto theend;
+ }
+ p = skipwhite(p + 1);
+ type = parse_type(&p, cctx->ctx_type_list);
+ has_type = TRUE;
}
- p = skipwhite(p + 1);
- type = parse_type(&p, cctx->ctx_type_list);
- has_type = TRUE;
- }
- else if (lvar != NULL)
- type = lvar->lv_type;
- }
-
- sp = p;
- p = skipwhite(p);
- op = p;
- oplen = assignment_len(p, &heredoc);
- if (oplen > 0 && (!VIM_ISWHITE(*sp) || !VIM_ISWHITE(op[oplen])))
- {
- char_u buf[4];
-
- vim_strncpy(buf, op, oplen);
- semsg(_(e_white_both), buf);
- }
-
- if (oplen == 3 && !heredoc && dest != dest_global
- && type->tt_type != VAR_STRING && type->tt_type != VAR_ANY)
- {
- emsg(_("E1019: Can only concatenate to string"));
- goto theend;
- }
-
- if (lvar == NULL && dest == dest_local && cctx->ctx_skip != TRUE)
- {
- if (oplen > 1 && !heredoc)
- {
- // +=, /=, etc. require an existing variable
- semsg(_("E1020: cannot use an operator on a new variable: %s"),
- name);
- goto theend;
+ else if (lvar != NULL)
+ type = lvar->lv_type;
}
- // new local variable
- if (type->tt_type == VAR_FUNC && var_check_func_name(name, TRUE))
- goto theend;
- lvar = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type);
- if (lvar == NULL)
- goto theend;
- new_local = TRUE;
- }
-
- member_type = type;
- if (var_end > arg + varlen)
- {
- // Something follows after the variable: "var[idx]".
- if (is_decl)
+ if (oplen == 3 && !heredoc && dest != dest_global
+ && type->tt_type != VAR_STRING
+ && type->tt_type != VAR_ANY)
{
- emsg(_("E1087: cannot use an index when declaring a variable"));
+ emsg(_("E1019: Can only concatenate to string"));
goto theend;
}
- if (arg[varlen] == '[')
+ if (lvar == NULL && dest == dest_local && cctx->ctx_skip != TRUE)
{
- has_index = TRUE;
- if (type->tt_member == NULL)
+ if (oplen > 1 && !heredoc)
{
- semsg(_("E1088: cannot use an index on %s"), name);
+ // +=, /=, etc. require an existing variable
+ semsg(_("E1020: cannot use an operator on a new variable: %s"),
+ name);
goto theend;
}
- member_type = type->tt_member;
- }
- else
- {
- semsg("Not supported yet: %s", arg);
- goto theend;
- }
- }
- else if (lvar == &arg_lvar)
- {
- semsg(_("E1090: Cannot assign to argument %s"), name);
- goto theend;
- }
-
- if (heredoc)
- {
- list_T *l;
- listitem_T *li;
- // [let] varname =<< [trim] {end}
- eap->getline = exarg_getline;
- eap->cookie = cctx;
- l = heredoc_get(eap, op + 3, FALSE);
-
- // Push each line and the create the list.
- FOR_ALL_LIST_ITEMS(l, li)
- {
- generate_PUSHS(cctx, li->li_tv.vval.v_string);
- li->li_tv.vval.v_string = NULL;
+ // new local variable
+ if (type->tt_type == VAR_FUNC && var_check_func_name(name, TRUE))
+ goto theend;
+ lvar = reserve_local(cctx, var_start, varlen,
+ cmdidx == CMD_const, type);
+ if (lvar == NULL)
+ goto theend;
+ new_local = TRUE;
}
- 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 != '=')
+ member_type = type;
+ if (var_end > var_start + varlen)
{
- generate_loadvar(cctx, dest, name, lvar, type);
+ // Something follows after the variable: "var[idx]".
+ if (is_decl)
+ {
+ emsg(_("E1087: cannot use an index when declaring a variable"));
+ goto theend;
+ }
- if (has_index)
+ if (var_start[varlen] == '[')
{
- // TODO: get member from list or dict
- emsg("Index with operation not supported yet");
+ 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", var_start);
goto theend;
}
}
-
- // Compile the expression. Temporarily hide the new local variable
- // here, it is not available to this expression.
- if (new_local)
- --cctx->ctx_locals.ga_len;
- instr_count = instr->ga_len;
- p = skipwhite(p + oplen);
- r = compile_expr0(&p, cctx);
- if (new_local)
- ++cctx->ctx_locals.ga_len;
- if (r == FAIL)
+ else if (lvar == &arg_lvar)
+ {
+ semsg(_("E1090: Cannot assign to argument %s"), name);
goto theend;
+ }
- if (cctx->ctx_skip != TRUE)
+ if (!heredoc)
{
- 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))
+ if (oplen > 0)
{
- if (new_local && !has_type)
+ // For "var = expr" evaluate the expression.
+ if (var_count == 0)
{
- if (stacktype->tt_type == VAR_VOID)
- {
- emsg(_("E1031: Cannot use void value"));
- goto theend;
- }
- else
+ int r;
+
+ // for "+=", "*=", "..=" etc. first load the current value
+ if (*op != '=')
{
- // An empty list or dict has a &t_void member, for a
- // variable that implies &t_any.
- if (stacktype == &t_list_empty)
- lvar->lv_type = &t_list_any;
- else if (stacktype == &t_dict_empty)
- lvar->lv_type = &t_dict_any;
- else
- lvar->lv_type = stacktype;
+ 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;
+ }
}
+
+ // Compile the expression. Temporarily hide the new local
+ // variable here, it is not available to this expression.
+ if (new_local)
+ --cctx->ctx_locals.ga_len;
+ instr_count = instr->ga_len;
+ p = skipwhite(op + oplen);
+ r = compile_expr0(&p, cctx);
+ if (new_local)
+ ++cctx->ctx_locals.ga_len;
+ if (r == FAIL)
+ goto theend;
}
else
{
- type_T *use_type = lvar->lv_type;
+ // For "[var, var] = expr" get the "var_idx" item from the
+ // list.
+ if (generate_GETITEM(cctx, var_idx) == FAIL)
+ return FAIL;
+ }
+
+ if (cctx->ctx_skip != TRUE)
+ {
+ type_T *stacktype;
- if (has_index)
+ stacktype = stack->ga_len == 0 ? &t_void
+ : ((type_T **)stack->ga_data)[stack->ga_len - 1];
+ if (lvar != NULL && (is_decl || !has_type))
{
- use_type = use_type->tt_member;
- if (use_type == NULL)
- use_type = &t_void;
+ if (new_local && !has_type)
+ {
+ if (stacktype->tt_type == VAR_VOID)
+ {
+ emsg(_(e_cannot_use_void));
+ goto theend;
+ }
+ else
+ {
+ // An empty list or dict has a &t_void member,
+ // for a variable that implies &t_any.
+ if (stacktype == &t_list_empty)
+ lvar->lv_type = &t_list_any;
+ else if (stacktype == &t_dict_empty)
+ lvar->lv_type = &t_dict_any;
+ else
+ 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;
+ }
}
- if (need_type(stacktype, use_type, -1, cctx) == FAIL)
+ else if (*p != '=' && need_type(stacktype, member_type, -1,
+ cctx) == FAIL)
goto theend;
}
}
- else if (*p != '=' && need_type(stacktype, member_type, -1,
- cctx) == FAIL)
+ else if (cmdidx == CMD_const)
+ {
+ emsg(_(e_const_req_value));
goto theend;
- }
- }
- else if (cmdidx == CMD_const)
- {
- emsg(_(e_const_req_value));
- goto theend;
- }
- else if (!has_type || dest == dest_option)
- {
- emsg(_(e_type_req));
- goto theend;
- }
- else
- {
- // 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);
- break;
- case VAR_FLOAT:
+ }
+ else if (!has_type || dest == dest_option)
+ {
+ emsg(_(e_type_req));
+ goto theend;
+ }
+ else
+ {
+ // 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);
+ break;
+ case VAR_FLOAT:
#ifdef FEAT_FLOAT
- generate_PUSHF(cctx, 0.0);
+ generate_PUSHF(cctx, 0.0);
#endif
- break;
- case VAR_STRING:
- generate_PUSHS(cctx, NULL);
- break;
- case VAR_BLOB:
- generate_PUSHBLOB(cctx, NULL);
- break;
- case VAR_FUNC:
- generate_PUSHFUNC(cctx, NULL, &t_func_void);
- break;
- case VAR_LIST:
- generate_NEWLIST(cctx, 0);
- break;
- case VAR_DICT:
- generate_NEWDICT(cctx, 0);
- break;
- case VAR_JOB:
- generate_PUSHJOB(cctx, NULL);
- break;
- case VAR_CHANNEL:
- generate_PUSHCHANNEL(cctx, NULL);
- break;
- case VAR_NUMBER:
- case VAR_UNKNOWN:
- case VAR_ANY:
- case VAR_PARTIAL:
- case VAR_VOID:
- case VAR_SPECIAL: // cannot happen
- generate_PUSHNR(cctx, 0);
- break;
+ break;
+ case VAR_STRING:
+ generate_PUSHS(cctx, NULL);
+ break;
+ case VAR_BLOB:
+ generate_PUSHBLOB(cctx, NULL);
+ break;
+ case VAR_FUNC:
+ generate_PUSHFUNC(cctx, NULL, &t_func_void);
+ break;
+ case VAR_LIST:
+ generate_NEWLIST(cctx, 0);
+ break;
+ case VAR_DICT:
+ generate_NEWDICT(cctx, 0);
+ break;
+ case VAR_JOB:
+ generate_PUSHJOB(cctx, NULL);
+ break;
+ case VAR_CHANNEL:
+ generate_PUSHCHANNEL(cctx, NULL);
+ break;
+ case VAR_NUMBER:
+ case VAR_UNKNOWN:
+ case VAR_ANY:
+ case VAR_PARTIAL:
+ case VAR_VOID:
+ case VAR_SPECIAL: // cannot happen
+ generate_PUSHNR(cctx, 0);
+ break;
+ }
+ }
+ if (var_count == 0)
+ end = p;
}
- }
- end = p;
- if (oplen > 0 && *op != '=')
- {
- type_T *expected = &t_number;
- type_T *stacktype;
-
- // TODO: if type is known use float or any operation
-
- if (*op == '.')
- expected = &t_string;
- stacktype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
- if (need_type(stacktype, expected, -1, cctx) == FAIL)
- goto theend;
-
- if (*op == '.')
- generate_instr_drop(cctx, ISN_CONCAT, 1);
- else
+ if (oplen > 0 && *op != '=')
{
- isn_T *isn = generate_instr_drop(cctx, ISN_OPNR, 1);
+ type_T *expected = &t_number;
+ type_T *stacktype;
- if (isn == NULL)
+ // TODO: if type is known use float or any operation
+
+ if (*op == '.')
+ expected = &t_string;
+ stacktype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+ if (need_type(stacktype, expected, -1, cctx) == FAIL)
goto theend;
- switch (*op)
+
+ if (*op == '.')
+ generate_instr_drop(cctx, ISN_CONCAT, 1);
+ else
{
- case '+': isn->isn_arg.op.op_type = EXPR_ADD; break;
- case '-': isn->isn_arg.op.op_type = EXPR_SUB; break;
- case '*': isn->isn_arg.op.op_type = EXPR_MULT; break;
- case '/': isn->isn_arg.op.op_type = EXPR_DIV; break;
- case '%': isn->isn_arg.op.op_type = EXPR_REM; break;
+ isn_T *isn = generate_instr_drop(cctx, ISN_OPNR, 1);
+
+ if (isn == NULL)
+ goto theend;
+ switch (*op)
+ {
+ case '+': isn->isn_arg.op.op_type = EXPR_ADD; break;
+ case '-': isn->isn_arg.op.op_type = EXPR_SUB; break;
+ case '*': isn->isn_arg.op.op_type = EXPR_MULT; break;
+ case '/': isn->isn_arg.op.op_type = EXPR_DIV; break;
+ case '%': isn->isn_arg.op.op_type = EXPR_REM; break;
+ }
}
}
- }
-
- 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) != ']')
+ if (has_index)
{
- 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
+ int r;
+
+ // Compile the "idx" in "var[idx]".
+ if (new_local)
+ --cctx->ctx_locals.ga_len;
+ p = skipwhite(var_start + 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;
- }
+ {
+ 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);
+ // 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;
+ 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
{
- 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)
+ switch (dest)
+ {
+ case dest_option:
+ generate_STOREOPT(cctx, name + 1, opt_flags);
+ break;
+ case dest_global:
+ // include g: with the name, easier to execute that way
+ generate_STORE(cctx, ISN_STOREG, 0, name);
+ break;
+ case dest_buffer:
+ // include b: with the name, easier to execute that way
+ generate_STORE(cctx, ISN_STOREB, 0, name);
+ break;
+ case dest_window:
+ // include w: with the name, easier to execute that way
+ generate_STORE(cctx, ISN_STOREW, 0, name);
+ break;
+ case dest_tab:
+ // include t: with the name, easier to execute that way
+ generate_STORE(cctx, ISN_STORET, 0, name);
+ break;
+ case dest_env:
+ generate_STORE(cctx, ISN_STOREENV, 0, name + 1);
+ break;
+ case dest_reg:
+ generate_STORE(cctx, ISN_STOREREG, name[1], NULL);
+ break;
+ case dest_vimvar:
+ generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
+ break;
+ case dest_script:
{
- char_u *name_s = name;
+ char_u *rawname = name + (name[1] == ':' ? 2 : 0);
+ imported_T *import = NULL;
+ int sid = current_sctx.sc_sid;
+ int idx;
- // Include s: in the name for store_var()
if (name[1] != ':')
{
- int len = (int)STRLEN(name) + 3;
-
- name_s = alloc(len);
- if (name_s == NULL)
- name_s = name;
- else
- vim_snprintf((char *)name_s, len, "s:%s", name);
+ import = find_imported(name, 0, cctx);
+ if (import != NULL)
+ sid = import->imp_sid;
}
- generate_OLDSCRIPT(cctx, ISN_STORES, name_s, 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,
+ 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)
+ }
+ break;
+ case dest_local:
+ if (lvar != NULL)
{
- varnumber_T val = isn->isn_arg.number;
+ 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,
+ 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;
+ else
+ generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
+ }
+ break;
+ }
}
+
+ if (var_idx + 1 < var_count)
+ var_start = skipwhite(var_end + 1);
}
+
+ // for "[var, var] = expr" drop the "expr" value
+ if (var_count > 0 && generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
+ goto theend;
+
ret = end;
theend:
@@ -6575,12 +6700,22 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|| find_imported(ea.cmd, len, &cctx) != NULL)
{
line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
- if (line == NULL)
+ if (line == NULL || line == ea.cmd)
goto erret;
continue;
}
}
}
+
+ if (*ea.cmd == '[')
+ {
+ // [var, var] = expr
+ line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
+ if (line == NULL)
+ goto erret;
+ if (line != ea.cmd)
+ continue;
+ }
}
/*
@@ -6646,6 +6781,8 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
case CMD_let:
case CMD_const:
line = compile_assignment(p, &ea, ea.cmdidx, &cctx);
+ if (line == p)
+ line = NULL;
break;
case CMD_unlet:
@@ -6957,6 +7094,7 @@ delete_instr(isn_T *isn)
case ISN_EXECUTE:
case ISN_FOR:
case ISN_INDEX:
+ case ISN_GETITEM:
case ISN_MEMBER:
case ISN_JUMP:
case ISN_LOAD:
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 1a87476dc..7462b68c9 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2114,6 +2114,31 @@ call_def_function(
}
break;
+ case ISN_GETITEM:
+ {
+ listitem_T *li;
+ int index = iptr->isn_arg.number;
+
+ // get list item: list is at stack-1, push item
+ tv = STACK_TV_BOT(-1);
+ if (tv->v_type != VAR_LIST)
+ {
+ emsg(_(e_listreq));
+ goto failed;
+ }
+ if ((li = list_find(tv->vval.v_list, index)) == NULL)
+ {
+ semsg(_(e_listidx), index);
+ goto failed;
+ }
+
+ if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
+ goto failed;
+ ++ectx.ec_stack.ga_len;
+ copy_tv(&li->li_tv, STACK_TV_BOT(-1));
+ }
+ break;
+
case ISN_MEMBER:
{
dict_T *dict;
@@ -2789,6 +2814,8 @@ ex_disassemble(exarg_T *eap)
// expression operations
case ISN_CONCAT: smsg("%4d CONCAT", current); break;
case ISN_INDEX: smsg("%4d INDEX", current); break;
+ case ISN_GETITEM: smsg("%4d ITEM %lld",
+ current, iptr->isn_arg.number); break;
case ISN_MEMBER: smsg("%4d MEMBER", current); break;
case ISN_STRINGMEMBER: smsg("%4d MEMBER %s", current,
iptr->isn_arg.string); break;