0
0
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:
Bram Moolenaar
2020-08-16 17:33:35 +02:00
parent 56acb0943e
commit cc673e746a
11 changed files with 417 additions and 212 deletions

View File

@@ -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);
}