mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 8.2.1466: Vim9: cannot index or slice a variable with type "any"
Problem: Vim9: cannot index or slice a variable with type "any". Solution: Add runtime index and slice.
This commit is contained in:
398
src/eval.c
398
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);
|
||||
|
||||
clear_tv(rettv);
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = v;
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (verbose)
|
||||
emsg(_(e_dictrange));
|
||||
if (len == -1)
|
||||
clear_tv(&var1);
|
||||
return FAIL;
|
||||
}
|
||||
{
|
||||
dictitem_T *item;
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
key = tv_get_string_chk(&var1);
|
||||
if (key == NULL)
|
||||
if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL)
|
||||
{
|
||||
clear_tv(&var1);
|
||||
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);
|
||||
}
|
||||
|
||||
item = dict_find(rettv->vval.v_dict, key, (int)len);
|
||||
|
||||
if (item == NULL && verbose)
|
||||
semsg(_(e_dictkey), key);
|
||||
if (len == -1)
|
||||
clear_tv(&var1);
|
||||
if (item == NULL)
|
||||
return FAIL;
|
||||
|
||||
copy_tv(&item->di_tv, &var1);
|
||||
clear_tv(rettv);
|
||||
*rettv = var1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
int v = blob_get(rettv->vval.v_blob, n1);
|
||||
|
||||
clear_tv(rettv);
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = v;
|
||||
}
|
||||
else
|
||||
semsg(_(e_blobidx), n1);
|
||||
}
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
case VAR_DICT:
|
||||
{
|
||||
dictitem_T *item;
|
||||
typval_T tmp;
|
||||
|
||||
if (key == NULL)
|
||||
{
|
||||
key = tv_get_string_chk(var1);
|
||||
if (key == NULL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user