summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/errors.h13
-rw-r--r--src/eval.c386
-rw-r--r--src/list.c2
-rw-r--r--src/proto/eval.pro2
-rw-r--r--src/testdir/test_vim9_disassemble.vim44
-rw-r--r--src/testdir/test_vim9_expr.vim90
-rw-r--r--src/testdir/test_vim9_script.vim2
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h2
-rw-r--r--src/vim9compile.c44
-rw-r--r--src/vim9execute.c28
11 files changed, 410 insertions, 205 deletions
diff --git a/src/errors.h b/src/errors.h
index 0622042a8..60be6c17f 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -21,6 +21,10 @@ EXTERN char e_invalid_command[]
#ifdef FEAT_EVAL
EXTERN char e_invalid_command_str[]
INIT(= N_("E476: Invalid command: %s"));
+EXTERN char e_cannot_slice_dictionary[]
+ INIT(= N_("E719: cannot slice a Dictionary"));
+EXTERN char e_cannot_index_special_variable[]
+ INIT(= N_("E909: Cannot index a special variable"));
EXTERN char e_missing_let_str[]
INIT(= N_("E1100: Missing :let: %s"));
EXTERN char e_variable_not_found_str[]
@@ -69,9 +73,9 @@ EXTERN char e_const_requires_a_value[]
INIT(= N_("E1021: const requires a value"));
EXTERN char e_type_or_initialization_required[]
INIT(= N_("E1022: type or initialization required"));
-EXTERN char e_cannot_slice_dictionary[]
- INIT(= N_("E1023: cannot slice a dictionary"));
-// E1024 unused
+// E1023 unused
+EXTERN char e_using_number_as_string[]
+ INIT(= N_("E1024: Using a Number as a String"));
EXTERN char e_using_rcurly_outside_if_block_scope[]
INIT(= N_("E1025: using } outside of a block scope"));
EXTERN char e_missing_rcurly[]
@@ -146,7 +150,8 @@ EXTERN char e_expected_dot_after_name_str[]
INIT(= N_("E1060: expected dot after name: %s"));
EXTERN char e_cannot_find_function_str[]
INIT(= N_("E1061: Cannot find function %s"));
-// E1062 unused
+EXTERN char e_cannot_index_number[]
+ INIT(= N_("E1062: Cannot index a Number"));
EXTERN char e_type_mismatch_for_v_variable[]
INIT(= N_("E1063: type mismatch for v: variable"));
// E1064 unused
diff --git a/src/eval.c b/src/eval.c
index 02b5623fc..37ed5153e 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -20,8 +20,6 @@
# include <float.h>
#endif
-static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
-
#define NAMESPACE_CHAR (char_u *)"abglstvw"
/*
@@ -928,7 +926,7 @@ get_lval(
if (lp->ll_tv->v_type == VAR_DICT)
{
if (!quiet)
- emsg(_(e_dictrange));
+ emsg(_(e_cannot_slice_dictionary));
clear_tv(&var1);
return NULL;
}
@@ -3549,47 +3547,12 @@ eval_index(
&& (evalarg->eval_flags & EVAL_EVALUATE);
int empty1 = FALSE, empty2 = FALSE;
typval_T var1, var2;
- long i;
- long n1, n2 = 0;
- long len = -1;
int range = FALSE;
- char_u *s;
char_u *key = NULL;
+ int keylen = -1;
- switch (rettv->v_type)
- {
- case VAR_FUNC:
- case VAR_PARTIAL:
- if (verbose)
- emsg(_("E695: Cannot index a Funcref"));
- return FAIL;
- case VAR_FLOAT:
-#ifdef FEAT_FLOAT
- if (verbose)
- emsg(_(e_float_as_string));
- return FAIL;
-#endif
- case VAR_BOOL:
- case VAR_SPECIAL:
- case VAR_JOB:
- case VAR_CHANNEL:
- if (verbose)
- emsg(_("E909: Cannot index a special variable"));
- return FAIL;
- case VAR_UNKNOWN:
- case VAR_ANY:
- case VAR_VOID:
- if (evaluate)
- return FAIL;
- // FALLTHROUGH
-
- case VAR_STRING:
- case VAR_NUMBER:
- case VAR_LIST:
- case VAR_DICT:
- case VAR_BLOB:
- break;
- }
+ if (check_can_index(rettv, evaluate, verbose) == FAIL)
+ return FAIL;
init_tv(&var1);
init_tv(&var2);
@@ -3599,11 +3562,11 @@ eval_index(
* dict.name
*/
key = *arg + 1;
- for (len = 0; eval_isdictc(key[len]); ++len)
+ for (keylen = 0; eval_isdictc(key[keylen]); ++keylen)
;
- if (len == 0)
+ if (keylen == 0)
return FAIL;
- *arg = skipwhite(key + len);
+ *arg = skipwhite(key + keylen);
}
else
{
@@ -3666,49 +3629,132 @@ eval_index(
if (evaluate)
{
- n1 = 0;
- if (!empty1 && rettv->v_type != VAR_DICT)
- {
- n1 = tv_get_number(&var1);
+ int res = eval_index_inner(rettv, range,
+ empty1 ? NULL : &var1, empty2 ? NULL : &var2,
+ key, keylen, verbose);
+ if (!empty1)
clear_tv(&var1);
- }
if (range)
- {
- if (empty2)
- n2 = -1;
- else
+ clear_tv(&var2);
+ return res;
+ }
+ return OK;
+}
+
+/*
+ * Check if "rettv" can have an [index] or [sli:ce]
+ */
+ int
+check_can_index(typval_T *rettv, int evaluate, int verbose)
+{
+ switch (rettv->v_type)
+ {
+ case VAR_FUNC:
+ case VAR_PARTIAL:
+ if (verbose)
+ emsg(_("E695: Cannot index a Funcref"));
+ return FAIL;
+ case VAR_FLOAT:
+#ifdef FEAT_FLOAT
+ if (verbose)
+ emsg(_(e_float_as_string));
+ return FAIL;
+#endif
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ case VAR_JOB:
+ case VAR_CHANNEL:
+ if (verbose)
+ emsg(_(e_cannot_index_special_variable));
+ return FAIL;
+ case VAR_UNKNOWN:
+ case VAR_ANY:
+ case VAR_VOID:
+ if (evaluate)
{
- n2 = tv_get_number(&var2);
- clear_tv(&var2);
+ emsg(_(e_cannot_index_special_variable));
+ return FAIL;
}
- }
+ // FALLTHROUGH
- switch (rettv->v_type)
+ case VAR_STRING:
+ case VAR_LIST:
+ case VAR_DICT:
+ case VAR_BLOB:
+ break;
+ case VAR_NUMBER:
+ if (in_vim9script())
+ emsg(_(e_cannot_index_number));
+ break;
+ }
+ return OK;
+}
+
+/*
+ * Apply index or range to "rettv".
+ * "var1" is the first index, NULL for [:expr].
+ * "var2" is the second index, NULL for [expr] and [expr: ]
+ * Alternatively, "key" is not NULL, then key[keylen] is the dict index.
+ */
+ int
+eval_index_inner(
+ typval_T *rettv,
+ int is_range,
+ typval_T *var1,
+ typval_T *var2,
+ char_u *key,
+ int keylen,
+ int verbose)
+{
+ long n1, n2 = 0;
+ long len;
+
+ n1 = 0;
+ if (var1 != NULL && rettv->v_type != VAR_DICT)
+ n1 = tv_get_number(var1);
+
+ if (is_range)
+ {
+ if (rettv->v_type == VAR_DICT)
{
- case VAR_UNKNOWN:
- case VAR_ANY:
- case VAR_VOID:
- case VAR_FUNC:
- case VAR_PARTIAL:
- case VAR_FLOAT:
- case VAR_BOOL:
- case VAR_SPECIAL:
- case VAR_JOB:
- case VAR_CHANNEL:
- break; // not evaluating, skipping over subscript
+ if (verbose)
+ emsg(_(e_cannot_slice_dictionary));
+ return FAIL;
+ }
+ if (var2 == NULL)
+ n2 = -1;
+ else
+ n2 = tv_get_number(var2);
+ }
+
+ switch (rettv->v_type)
+ {
+ case VAR_UNKNOWN:
+ case VAR_ANY:
+ case VAR_VOID:
+ case VAR_FUNC:
+ case VAR_PARTIAL:
+ case VAR_FLOAT:
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ case VAR_JOB:
+ case VAR_CHANNEL:
+ break; // not evaluating, skipping over subscript
+
+ case VAR_NUMBER:
+ case VAR_STRING:
+ {
+ char_u *s = tv_get_string(rettv);
- case VAR_NUMBER:
- case VAR_STRING:
- s = tv_get_string(rettv);
len = (long)STRLEN(s);
if (in_vim9script())
{
- if (range)
+ if (is_range)
s = string_slice(s, n1, n2);
else
s = char_from_string(s, n1);
}
- else if (range)
+ else if (is_range)
{
// The resulting variable is a substring. If the indexes
// are out of range the result is empty.
@@ -3740,119 +3786,107 @@ eval_index(
clear_tv(rettv);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = s;
- break;
+ }
+ break;
- case VAR_BLOB:
- len = blob_len(rettv->vval.v_blob);
- if (range)
+ case VAR_BLOB:
+ len = blob_len(rettv->vval.v_blob);
+ if (is_range)
+ {
+ // The resulting variable is a sub-blob. If the indexes
+ // are out of range the result is empty.
+ if (n1 < 0)
{
- // The resulting variable is a sub-blob. If the indexes
- // are out of range the result is empty.
+ n1 = len + n1;
if (n1 < 0)
- {
- n1 = len + n1;
- if (n1 < 0)
- n1 = 0;
- }
- if (n2 < 0)
- n2 = len + n2;
- else if (n2 >= len)
- n2 = len - 1;
- if (n1 >= len || n2 < 0 || n1 > n2)
- {
- clear_tv(rettv);
- rettv->v_type = VAR_BLOB;
- rettv->vval.v_blob = NULL;
- }
- else
- {
- blob_T *blob = blob_alloc();
-
- if (blob != NULL)
- {
- if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL)
- {
- blob_free(blob);
- return FAIL;
- }
- blob->bv_ga.ga_len = n2 - n1 + 1;
- for (i = n1; i <= n2; i++)
- blob_set(blob, i - n1,
- blob_get(rettv->vval.v_blob, i));
-
- clear_tv(rettv);
- rettv_blob_set(rettv, blob);
- }
- }
+ n1 = 0;
+ }
+ if (n2 < 0)
+ n2 = len + n2;
+ else if (n2 >= len)
+ n2 = len - 1;
+ if (n1 >= len || n2 < 0 || n1 > n2)
+ {
+ clear_tv(rettv);
+ rettv->v_type = VAR_BLOB;
+ rettv->vval.v_blob = NULL;
}
else
{
- // The resulting variable is a byte value.
- // If the index is too big or negative that is an error.
- if (n1 < 0)
- n1 = len + n1;
- if (n1 < len && n1 >= 0)
+ blob_T *blob = blob_alloc();
+ long i;
+
+ if (blob != NULL)
{
- int v = blob_get(rettv->vval.v_blob, n1);
+ if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL)
+ {
+ blob_free(blob);
+ return FAIL;
+ }
+ blob->bv_ga.ga_len = n2 - n1 + 1;
+ for (i = n1; i <= n2; i++)
+ blob_set(blob, i - n1,
+ blob_get(rettv->vval.v_blob, i));
clear_tv(rettv);
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = v;
+ rettv_blob_set(rettv, blob);
}
- else
- semsg(_(e_blobidx), n1);
}
- break;
-
- case VAR_LIST:
- if (empty1)
- n1 = 0;
- if (empty2)
- n2 = -1;
- if (list_slice_or_index(rettv->vval.v_list,
- range, n1, n2, rettv, verbose) == FAIL)
- return FAIL;
- break;
-
- case VAR_DICT:
- if (range)
+ }
+ else
+ {
+ // The resulting variable is a byte value.
+ // If the index is too big or negative that is an error.
+ if (n1 < 0)
+ n1 = len + n1;
+ if (n1 < len && n1 >= 0)
{
- if (verbose)
- emsg(_(e_dictrange));
- if (len == -1)
- clear_tv(&var1);
- return FAIL;
+ int v = blob_get(rettv->vval.v_blob, n1);
+
+ clear_tv(rettv);
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = v;
}
- {
- dictitem_T *item;
+ else
+ semsg(_(e_blobidx), n1);
+ }
+ break;
- if (len == -1)
- {
- key = tv_get_string_chk(&var1);
- if (key == NULL)
- {
- clear_tv(&var1);
- return FAIL;
- }
- }
+ case VAR_LIST:
+ if (var1 == NULL)
+ n1 = 0;
+ if (var2 == NULL)
+ n2 = -1;
+ if (list_slice_or_index(rettv->vval.v_list,
+ is_range, n1, n2, rettv, verbose) == FAIL)
+ return FAIL;
+ break;
- item = dict_find(rettv->vval.v_dict, key, (int)len);
+ case VAR_DICT:
+ {
+ dictitem_T *item;
+ typval_T tmp;
- if (item == NULL && verbose)
- semsg(_(e_dictkey), key);
- if (len == -1)
- clear_tv(&var1);
- if (item == NULL)
+ if (key == NULL)
+ {
+ key = tv_get_string_chk(var1);
+ if (key == NULL)
return FAIL;
-
- copy_tv(&item->di_tv, &var1);
- clear_tv(rettv);
- *rettv = var1;
}
- break;
- }
- }
+ item = dict_find(rettv->vval.v_dict, key, (int)keylen);
+
+ if (item == NULL && verbose)
+ semsg(_(e_dictkey), key);
+ if (item == NULL)
+ return FAIL;
+
+ copy_tv(&item->di_tv, &tmp);
+ clear_tv(rettv);
+ *rettv = tmp;
+ }
+ break;
+ }
return OK;
}
@@ -5292,9 +5326,9 @@ char_from_string(char_u *str, varnumber_T index)
* "str_len".
* If going over the end return "str_len".
* If "idx" is negative count from the end, -1 is the last character.
- * When going over the start return zero.
+ * When going over the start return -1.
*/
- static size_t
+ static long
char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
{
varnumber_T nchar = idx;
@@ -5317,8 +5351,10 @@ char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
nbyte -= mb_head_off(str, str + nbyte);
++nchar;
}
+ if (nchar < 0)
+ return -1;
}
- return nbyte;
+ return (long)nbyte;
}
/*
@@ -5328,24 +5364,26 @@ char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
char_u *
string_slice(char_u *str, varnumber_T first, varnumber_T last)
{
- size_t start_byte, end_byte;
- size_t slen;
+ long start_byte, end_byte;
+ size_t slen;
if (str == NULL)
return NULL;
slen = STRLEN(str);
start_byte = char_idx2byte(str, slen, first);
+ if (start_byte < 0)
+ start_byte = 0; // first index very negative: use zero
if (last == -1)
end_byte = slen;
else
{
end_byte = char_idx2byte(str, slen, last);
- if (end_byte < slen)
+ if (end_byte >= 0 && end_byte < (long)slen)
// end index is inclusive
end_byte += MB_CPTR2LEN(str + end_byte);
}
- if (start_byte >= slen || end_byte <= start_byte)
+ if (start_byte >= (long)slen || end_byte <= start_byte)
return NULL;
return vim_strnsave(str + start_byte, end_byte - start_byte);
}
diff --git a/src/list.c b/src/list.c
index b09e87f5f..c730b1a80 100644
--- a/src/list.c
+++ b/src/list.c
@@ -914,7 +914,7 @@ list_slice_or_index(
semsg(_(e_listidx), n1);
return FAIL;
}
- n1 = len;
+ n1 = n1 < 0 ? 0 : len;
}
if (range)
{
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index 551dacbcc..c32403fd0 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -37,6 +37,8 @@ int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg);
int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
void eval_addblob(typval_T *tv1, typval_T *tv2);
int eval_addlist(typval_T *tv1, typval_T *tv2);
+int check_can_index(typval_T *rettv, int evaluate, int verbose);
+int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, char_u *key, int keylen, int verbose);
char_u *partial_name(partial_T *pt);
void partial_unref(partial_T *pt);
int get_copyID(void);
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 3ed36f3f8..4560da03c 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -1091,6 +1091,50 @@ def Test_disassemble_dict_member()
call assert_equal(1, DictMember())
enddef
+let somelist = [1, 2, 3, 4, 5]
+def AnyIndex(): number
+ let res = g:somelist[2]
+ return res
+enddef
+
+def Test_disassemble_any_index()
+ let instr = execute('disassemble AnyIndex')
+ assert_match('AnyIndex\_s*' ..
+ 'let res = g:somelist\[2\]\_s*' ..
+ '\d LOADG g:somelist\_s*' ..
+ '\d PUSHNR 2\_s*' ..
+ '\d ANYINDEX\_s*' ..
+ '\d STORE $0\_s*' ..
+ 'return res\_s*' ..
+ '\d LOAD $0\_s*' ..
+ '\d CHECKTYPE number stack\[-1\]\_s*' ..
+ '\d RETURN',
+ instr)
+ assert_equal(3, AnyIndex())
+enddef
+
+def AnySlice(): list<number>
+ let res = g:somelist[1:3]
+ return res
+enddef
+
+def Test_disassemble_any_slice()
+ let instr = execute('disassemble AnySlice')
+ assert_match('AnySlice\_s*' ..
+ 'let res = g:somelist\[1:3\]\_s*' ..
+ '\d LOADG g:somelist\_s*' ..
+ '\d PUSHNR 1\_s*' ..
+ '\d PUSHNR 3\_s*' ..
+ '\d ANYSLICE\_s*' ..
+ '\d STORE $0\_s*' ..
+ 'return res\_s*' ..
+ '\d LOAD $0\_s*' ..
+ '\d CHECKTYPE list stack\[-1\]\_s*' ..
+ '\d RETURN',
+ instr)
+ assert_equal([2, 3, 4], AnySlice())
+enddef
+
def NegateNumber(): number
let nr = 9
let plus = +nr
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index 720f2307f..01ac3e615 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -1457,7 +1457,7 @@ def Test_expr7_list()
4]
call CheckDefFailure(["let x = 1234[3]"], 'E1107:')
- call CheckDefExecFailure(["let x = g:anint[3]"], 'E1029:')
+ call CheckDefExecFailure(["let x = g:anint[3]"], 'E1062:')
call CheckDefFailure(["let x = g:list_mixed[xxx]"], 'E1001:')
@@ -1768,9 +1768,91 @@ def Test_expr_member()
call CheckDefExecFailure(["let d: dict<number>", "d = g:list_empty"], 'E1029: Expected dict but got list')
enddef
-def Test_expr_index()
- # getting the one member should clear the list only after getting the item
- assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1])
+def Test_expr7_any_index_slice()
+ let lines =<< trim END
+ # getting the one member should clear the list only after getting the item
+ assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1])
+
+ # string is permissive, index out of range accepted
+ g:teststring = 'abcdef'
+ assert_equal('b', g:teststring[1])
+ assert_equal('', g:teststring[-1])
+ assert_equal('', g:teststring[99])
+
+ assert_equal('b', g:teststring[1:1])
+ assert_equal('bcdef', g:teststring[1:])
+ assert_equal('abcd', g:teststring[:3])
+ assert_equal('cdef', g:teststring[-4:])
+ assert_equal('abcdef', g:teststring[-9:])
+ assert_equal('abcd', g:teststring[:-3])
+ assert_equal('', g:teststring[:-9])
+
+ # blob index cannot be out of range
+ g:testblob = 0z01ab
+ assert_equal(0x01, g:testblob[0])
+ assert_equal(0xab, g:testblob[1])
+ assert_equal(0xab, g:testblob[-1])
+ assert_equal(0x01, g:testblob[-2])
+
+ # blob slice accepts out of range
+ assert_equal(0z01ab, g:testblob[0:1])
+ assert_equal(0z01, g:testblob[0:0])
+ assert_equal(0z01, g:testblob[-2:-2])
+ assert_equal(0zab, g:testblob[1:1])
+ assert_equal(0zab, g:testblob[-1:-1])
+ assert_equal(0z, g:testblob[2:2])
+ assert_equal(0z, g:testblob[0:-3])
+
+ # list index cannot be out of range
+ g:testlist = [0, 1, 2, 3]
+ assert_equal(0, g:testlist[0])
+ assert_equal(1, g:testlist[1])
+ assert_equal(3, g:testlist[3])
+ assert_equal(3, g:testlist[-1])
+ assert_equal(0, g:testlist[-4])
+ assert_equal(1, g:testlist[g:theone])
+
+ # list slice accepts out of range
+ assert_equal([0], g:testlist[0:0])
+ assert_equal([3], g:testlist[3:3])
+ assert_equal([0, 1], g:testlist[0:1])
+ assert_equal([0, 1, 2, 3], g:testlist[0:3])
+ assert_equal([0, 1, 2, 3], g:testlist[0:9])
+ assert_equal([], g:testlist[-1:1])
+ assert_equal([1], g:testlist[-3:1])
+ assert_equal([0, 1], g:testlist[-4:1])
+ assert_equal([0, 1], g:testlist[-9:1])
+ assert_equal([1, 2, 3], g:testlist[1:-1])
+ assert_equal([1], g:testlist[1:-3])
+ assert_equal([], g:testlist[1:-4])
+ assert_equal([], g:testlist[1:-9])
+
+ g:testdict = #{a: 1, b: 2}
+ assert_equal(1, g:testdict['a'])
+ assert_equal(2, g:testdict['b'])
+ END
+
+ CheckDefSuccess(lines)
+ CheckScriptSuccess(['vim9script'] + lines)
+
+ CheckDefExecFailure(['echo g:testblob[2]'], 'E979:')
+ CheckScriptFailure(['vim9script', 'echo g:testblob[2]'], 'E979:')
+ CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:')
+ CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:')
+
+ CheckDefExecFailure(['echo g:testlist[4]'], 'E684:')
+ CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:')
+ CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:')
+ CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684:')
+
+ CheckDefExecFailure(['echo g:testdict["a":"b"]'], 'E719:')
+ CheckScriptFailure(['vim9script', 'echo g:testdict["a":"b"]'], 'E719:')
+ CheckDefExecFailure(['echo g:testdict[1]'], 'E716:')
+ CheckScriptFailure(['vim9script', 'echo g:testdict[1]'], 'E716:')
+
+ unlet g:teststring
+ unlet g:testblob
+ unlet g:testlist
enddef
def Test_expr_member_vim9script()
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index 1e265be0d..3b8264fba 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -793,8 +793,8 @@ def Test_try_catch()
endtry
assert_equal(99, n)
- # TODO: this will change when index on "any" works
try
+ # string slice returns a string, not a number
n = g:astring[3]
catch /E1029:/
n = 77
diff --git a/src/version.c b/src/version.c
index 7a6fcbfff..66506b22c 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 */
/**/
+ 1466,
+/**/
1465,
/**/
1464,
diff --git a/src/vim9.h b/src/vim9.h
index ce92d0362..b7bd84ed5 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -120,6 +120,8 @@ typedef enum {
ISN_STRSLICE, // [expr:expr] string slice
ISN_LISTINDEX, // [expr] list index
ISN_LISTSLICE, // [expr:expr] list slice
+ ISN_ANYINDEX, // [expr] runtime index
+ ISN_ANYSLICE, // [expr:expr] runtime slice
ISN_SLICE, // drop isn_arg.number items from start of list
ISN_GETITEM, // push list item, isn_arg.number is the index
ISN_MEMBER, // dict[member]
diff --git a/src/vim9compile.c b/src/vim9compile.c
index cdc63cfe1..579ccbc78 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -3179,20 +3179,20 @@ compile_subscript(
}
else if (vtype == VAR_LIST || *typep == &t_any)
{
- // TODO: any requires runtime code
- if (*typep == &t_any && need_type(*typep, &t_list_any,
- is_slice ? -3 : -2, cctx, FALSE) == FAIL)
- return FAIL;
if (is_slice)
{
- if (generate_instr_drop(cctx, ISN_LISTSLICE, 2) == FAIL)
+ if (generate_instr_drop(cctx,
+ vtype == VAR_LIST ? ISN_LISTSLICE : ISN_ANYSLICE,
+ 2) == FAIL)
return FAIL;
}
else
{
if ((*typep)->tt_type == VAR_LIST)
*typep = (*typep)->tt_member;
- if (generate_instr_drop(cctx, ISN_LISTINDEX, 1) == FAIL)
+ if (generate_instr_drop(cctx,
+ vtype == VAR_LIST ? ISN_LISTINDEX : ISN_ANYINDEX,
+ 1) == FAIL)
return FAIL;
}
}
@@ -7085,11 +7085,13 @@ delete_instr(isn_T *isn)
case ISN_2STRING_ANY:
case ISN_ADDBLOB:
case ISN_ADDLIST:
+ case ISN_ANYINDEX:
+ case ISN_ANYSLICE:
case ISN_BCALL:
case ISN_CATCH:
+ case ISN_CHECKLEN:
case ISN_CHECKNR:
case ISN_CHECKTYPE:
- case ISN_CHECKLEN:
case ISN_COMPAREANY:
case ISN_COMPAREBLOB:
case ISN_COMPAREBOOL:
@@ -7102,7 +7104,6 @@ delete_instr(isn_T *isn)
case ISN_COMPARESTRING:
case ISN_CONCAT:
case ISN_DCALL:
- case ISN_SHUFFLE:
case ISN_DROP:
case ISN_ECHO:
case ISN_ECHOERR:
@@ -7111,14 +7112,10 @@ delete_instr(isn_T *isn)
case ISN_EXECCONCAT:
case ISN_EXECUTE:
case ISN_FOR:
- case ISN_LISTINDEX:
- case ISN_LISTSLICE:
- case ISN_STRINDEX:
- case ISN_STRSLICE:
case ISN_GETITEM:
- case ISN_SLICE:
- case ISN_MEMBER:
case ISN_JUMP:
+ case ISN_LISTINDEX:
+ case ISN_LISTSLICE:
case ISN_LOAD:
case ISN_LOADBDICT:
case ISN_LOADGDICT:
@@ -7128,27 +7125,32 @@ delete_instr(isn_T *isn)
case ISN_LOADTDICT:
case ISN_LOADV:
case ISN_LOADWDICT:
+ case ISN_MEMBER:
case ISN_NEGATENR:
case ISN_NEWDICT:
case ISN_NEWLIST:
- case ISN_OPNR:
- case ISN_OPFLOAT:
case ISN_OPANY:
+ case ISN_OPFLOAT:
+ case ISN_OPNR:
case ISN_PCALL:
case ISN_PCALL_END:
+ case ISN_PUSHBOOL:
case ISN_PUSHF:
case ISN_PUSHNR:
- case ISN_PUSHBOOL:
case ISN_PUSHSPEC:
case ISN_RETURN:
+ case ISN_SHUFFLE:
+ case ISN_SLICE:
case ISN_STORE:
- case ISN_STOREOUTER:
- case ISN_STOREV:
+ case ISN_STOREDICT:
+ case ISN_STORELIST:
case ISN_STORENR:
+ case ISN_STOREOUTER:
case ISN_STOREREG:
case ISN_STORESCRIPT:
- case ISN_STOREDICT:
- case ISN_STORELIST:
+ case ISN_STOREV:
+ case ISN_STRINDEX:
+ case ISN_STRSLICE:
case ISN_THROW:
case ISN_TRY:
// nothing allocated
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 02a895ecb..f8156c8a1 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2297,6 +2297,32 @@ call_def_function(
}
break;
+ case ISN_ANYINDEX:
+ case ISN_ANYSLICE:
+ {
+ int is_slice = iptr->isn_type == ISN_ANYSLICE;
+ typval_T *var1, *var2;
+ int res;
+
+ // index: composite is at stack-2, index at stack-1
+ // slice: composite is at stack-3, indexes at stack-2 and
+ // stack-1
+ tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2);
+ if (check_can_index(tv, TRUE, TRUE) == FAIL)
+ goto on_error;
+ var1 = is_slice ? STACK_TV_BOT(-2) : STACK_TV_BOT(-1);
+ var2 = is_slice ? STACK_TV_BOT(-1) : NULL;
+ res = eval_index_inner(tv, is_slice,
+ var1, var2, NULL, -1, TRUE);
+ clear_tv(var1);
+ if (is_slice)
+ clear_tv(var2);
+ ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
+ if (res == FAIL)
+ goto on_error;
+ }
+ break;
+
case ISN_SLICE:
{
list_T *list;
@@ -3133,6 +3159,8 @@ ex_disassemble(exarg_T *eap)
case ISN_STRSLICE: smsg("%4d STRSLICE", current); break;
case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break;
case ISN_LISTSLICE: smsg("%4d LISTSLICE", current); break;
+ case ISN_ANYINDEX: smsg("%4d ANYINDEX", current); break;
+ case ISN_ANYSLICE: smsg("%4d ANYSLICE", current); break;
case ISN_SLICE: smsg("%4d SLICE %lld",
current, iptr->isn_arg.number); break;
case ISN_GETITEM: smsg("%4d ITEM %lld",