aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/eval.c361
-rw-r--r--src/nvim/eval/typval.c4
-rw-r--r--test/old/testdir/test_listdict.vim19
3 files changed, 214 insertions, 170 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index e72287031d..941bb8b2b3 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -88,7 +88,10 @@
static const char *e_missbrac = N_("E111: Missing ']'");
static const char *e_list_end = N_("E697: Missing end of List ']': %s");
-static const char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
+static const char *e_cannot_slice_dictionary
+ = N_("E719: cannot slice a Dictionary");
+static const char e_cannot_index_special_variable[]
+ = N_("E909: Cannot index a special variable");
static const char *e_nowhitespace
= N_("E274: No white space allowed before parenthesis");
static const char *e_write2 = N_("E80: Error while writing: %s");
@@ -1438,7 +1441,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
if (*p == ':') {
if (lp->ll_tv->v_type == VAR_DICT) {
if (!quiet) {
- emsg(_(e_dictrange));
+ emsg(_(e_cannot_slice_dictionary));
}
tv_clear(&var1);
return NULL;
@@ -3469,39 +3472,12 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
bool empty1 = false;
bool empty2 = false;
- ptrdiff_t len = -1;
bool range = false;
const char *key = NULL;
+ ptrdiff_t 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:
- if (verbose) {
- emsg(_(e_float_as_string));
- }
+ if (check_can_index(rettv, evaluate, verbose) == FAIL) {
return FAIL;
- case VAR_BOOL:
- case VAR_SPECIAL:
- if (verbose) {
- emsg(_("E909: Cannot index a special variable"));
- }
- return FAIL;
- case VAR_UNKNOWN:
- if (evaluate) {
- return FAIL;
- }
- FALLTHROUGH;
- case VAR_STRING:
- case VAR_NUMBER:
- case VAR_LIST:
- case VAR_DICT:
- case VAR_BLOB:
- break;
}
typval_T var1 = TV_INITIAL_VALUE;
@@ -3509,11 +3485,11 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
if (**arg == '.') {
// dict.name
key = *arg + 1;
- for (len = 0; eval_isdictc(key[len]); len++) {}
- if (len == 0) {
+ for (keylen = 0; eval_isdictc(key[keylen]); keylen++) {}
+ if (keylen == 0) {
return FAIL;
}
- *arg = skipwhite(key + len);
+ *arg = skipwhite(key + keylen);
} else {
// something[idx]
//
@@ -3565,165 +3541,214 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
}
if (evaluate) {
- int n2 = 0;
- int n1 = 0;
- if (!empty1 && rettv->v_type != VAR_DICT && !tv_is_luafunc(rettv)) {
- n1 = (int)tv_get_number(&var1);
+ int res = eval_index_inner(rettv, range,
+ empty1 ? NULL : &var1, empty2 ? NULL : &var2,
+ key, keylen, verbose);
+ if (!empty1) {
tv_clear(&var1);
}
if (range) {
- if (empty2) {
- n2 = -1;
- } else {
- n2 = (int)tv_get_number(&var2);
- tv_clear(&var2);
- }
+ tv_clear(&var2);
}
+ return res;
+ }
+ return OK;
+}
- switch (rettv->v_type) {
- case VAR_NUMBER:
- case VAR_STRING: {
- const char *const s = tv_get_string(rettv);
- char *v;
- len = (ptrdiff_t)strlen(s);
- if (range) {
- // The resulting variable is a substring. If the indexes
- // are out of range the result is empty.
- if (n1 < 0) {
- n1 = (int)len + n1;
- if (n1 < 0) {
- n1 = 0;
- }
- }
- if (n2 < 0) {
- n2 = (int)len + n2;
- } else if (n2 >= len) {
- n2 = (int)len;
- }
- if (n1 >= len || n2 < 0 || n1 > n2) {
- v = NULL;
- } else {
- v = xmemdupz(s + n1, (size_t)n2 - (size_t)n1 + 1);
- }
- } else {
- // The resulting variable is a string of a single
- // character. If the index is too big or negative the
- // result is empty.
- if (n1 >= len || n1 < 0) {
- v = NULL;
- } else {
- v = xmemdupz(s + n1, 1);
- }
+/// Check if "rettv" can have an [index] or [sli:ce]
+static int check_can_index(typval_T *rettv, bool evaluate, bool verbose)
+{
+ switch (rettv->v_type) {
+ case VAR_FUNC:
+ case VAR_PARTIAL:
+ if (verbose) {
+ emsg(_("E695: Cannot index a Funcref"));
+ }
+ return FAIL;
+ case VAR_FLOAT:
+ if (verbose) {
+ emsg(_(e_float_as_string));
+ }
+ return FAIL;
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ if (verbose) {
+ emsg(_(e_cannot_index_special_variable));
+ }
+ return FAIL;
+ case VAR_UNKNOWN:
+ if (evaluate) {
+ emsg(_(e_cannot_index_special_variable));
+ return FAIL;
+ }
+ FALLTHROUGH;
+ case VAR_STRING:
+ case VAR_NUMBER:
+ case VAR_LIST:
+ case VAR_DICT:
+ case VAR_BLOB:
+ 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.
+static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typval_T *var2,
+ const char *key, ptrdiff_t keylen, bool verbose)
+{
+ int n1 = 0;
+ int n2 = 0;
+ if (var1 != NULL && rettv->v_type != VAR_DICT) {
+ n1 = (int)tv_get_number(var1);
+ }
+
+ if (is_range) {
+ if (rettv->v_type == VAR_DICT) {
+ if (verbose) {
+ emsg(_(e_cannot_slice_dictionary));
}
- tv_clear(rettv);
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = v;
- break;
+ return FAIL;
}
- case VAR_BLOB:
- len = tv_blob_len(rettv->vval.v_blob);
- if (range) {
- // The resulting variable is a sub-blob. If the indexes
- // are out of range the result is empty.
- if (n1 < 0) {
- n1 = (int)len + n1;
- if (n1 < 0) {
- n1 = 0;
- }
- }
- if (n2 < 0) {
- n2 = (int)len + n2;
- } else if (n2 >= len) {
- n2 = (int)len - 1;
- }
- if (n1 >= len || n2 < 0 || n1 > n2) {
- tv_clear(rettv);
- rettv->v_type = VAR_BLOB;
- rettv->vval.v_blob = NULL;
- } else {
- blob_T *const blob = tv_blob_alloc();
- ga_grow(&blob->bv_ga, n2 - n1 + 1);
- blob->bv_ga.ga_len = n2 - n1 + 1;
- for (long i = n1; i <= n2; i++) {
- tv_blob_set(blob, (int)(i - n1), tv_blob_get(rettv->vval.v_blob, (int)i));
- }
- tv_clear(rettv);
- tv_blob_set_ret(rettv, blob);
- }
- } else {
- // The resulting variable is a byte value.
- // If the index is too big or negative that is an error.
+ if (var2 == NULL) {
+ n2 = -1;
+ } else {
+ n2 = (int)tv_get_number(var2);
+ }
+ }
+
+ switch (rettv->v_type) {
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ case VAR_FUNC:
+ case VAR_FLOAT:
+ case VAR_PARTIAL:
+ case VAR_UNKNOWN:
+ break; // Not evaluating, skipping over subscript
+ case VAR_NUMBER:
+ case VAR_STRING: {
+ const char *const s = tv_get_string(rettv);
+ char *v;
+ int len = (int)strlen(s);
+ if (is_range) {
+ // The resulting variable is a substring. If the indexes
+ // are out of range the result is empty.
+ if (n1 < 0) {
+ n1 = len + n1;
if (n1 < 0) {
- n1 = (int)len + n1;
- }
- if (n1 < len && n1 >= 0) {
- const int v = (int)tv_blob_get(rettv->vval.v_blob, n1);
- tv_clear(rettv);
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = v;
- } else {
- semsg(_(e_blobidx), (int64_t)n1);
+ n1 = 0;
}
}
- break;
- case VAR_LIST:
- if (empty1) {
- n1 = 0;
+ if (n2 < 0) {
+ n2 = len + n2;
+ } else if (n2 >= len) {
+ n2 = len;
}
- if (empty2) {
- n2 = -1;
+ if (n1 >= len || n2 < 0 || n1 > n2) {
+ v = NULL;
+ } else {
+ v = xmemdupz(s + n1, (size_t)n2 - (size_t)n1 + 1);
}
- if (tv_list_slice_or_index(rettv->vval.v_list,
- range, n1, n2, rettv, verbose) == FAIL) {
- return FAIL;
+ } else {
+ // The resulting variable is a string of a single
+ // character. If the index is too big or negative the
+ // result is empty.
+ if (n1 >= len || n1 < 0) {
+ v = NULL;
+ } else {
+ v = xmemdupz(s + n1, 1);
}
- break;
- case VAR_DICT: {
- if (range) {
- if (verbose) {
- emsg(_(e_dictrange));
- }
- if (len == -1) {
- tv_clear(&var1);
+ }
+ tv_clear(rettv);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = v;
+ break;
+ }
+ case VAR_BLOB: {
+ int len = tv_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) {
+ n1 = len + n1;
+ if (n1 < 0) {
+ n1 = 0;
}
- return FAIL;
}
-
- if (len == -1) {
- key = tv_get_string_chk(&var1);
- if (key == NULL) {
- tv_clear(&var1);
- return FAIL;
+ if (n2 < 0) {
+ n2 = len + n2;
+ } else if (n2 >= len) {
+ n2 = len - 1;
+ }
+ if (n1 >= len || n2 < 0 || n1 > n2) {
+ tv_clear(rettv);
+ rettv->v_type = VAR_BLOB;
+ rettv->vval.v_blob = NULL;
+ } else {
+ blob_T *const blob = tv_blob_alloc();
+ ga_grow(&blob->bv_ga, n2 - n1 + 1);
+ blob->bv_ga.ga_len = n2 - n1 + 1;
+ for (int i = n1; i <= n2; i++) {
+ tv_blob_set(blob, i - n1, tv_blob_get(rettv->vval.v_blob, i));
}
+ tv_clear(rettv);
+ tv_blob_set_ret(rettv, blob);
}
-
- dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, len);
-
- if (item == NULL && verbose) {
- semsg(_(e_dictkey), key);
+ } 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 (len == -1) {
- tv_clear(&var1);
+ if (n1 < len && n1 >= 0) {
+ const int v = (int)tv_blob_get(rettv->vval.v_blob, n1);
+ tv_clear(rettv);
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = v;
+ } else {
+ semsg(_(e_blobidx), (int64_t)n1);
}
- if (item == NULL || tv_is_luafunc(&item->di_tv)) {
+ }
+ break;
+ }
+ case VAR_LIST:
+ if (var1 == NULL) {
+ n1 = 0;
+ }
+ if (var2 == NULL) {
+ n2 = -1;
+ }
+ if (tv_list_slice_or_index(rettv->vval.v_list,
+ is_range, n1, n2, rettv, verbose) == FAIL) {
+ return FAIL;
+ }
+ break;
+ case VAR_DICT: {
+ if (key == NULL) {
+ key = tv_get_string_chk(var1);
+ if (key == NULL) {
return FAIL;
}
+ }
- tv_copy(&item->di_tv, &var1);
- tv_clear(rettv);
- *rettv = var1;
- break;
+ dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, keylen);
+
+ if (item == NULL && verbose) {
+ semsg(_(e_dictkey), key);
}
- case VAR_BOOL:
- case VAR_SPECIAL:
- case VAR_FUNC:
- case VAR_FLOAT:
- case VAR_PARTIAL:
- case VAR_UNKNOWN:
- break; // Not evaluating, skipping over subscript
+ if (item == NULL || tv_is_luafunc(&item->di_tv)) {
+ return FAIL;
}
- }
+ typval_T tmp;
+ tv_copy(&item->di_tv, &tmp);
+ tv_clear(rettv);
+ *rettv = tmp;
+ break;
+ }
+ }
return OK;
}
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index ba1d60959a..0b2be3074f 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -787,8 +787,8 @@ int tv_list_slice_or_index(list_T *list, bool range, int n1_arg, int n2_arg, typ
n1 = len + n1;
}
if (n1 < 0 || n1 >= len) {
- // For a range we allow invalid values and return an empty
- // list. A list index out of range is an error.
+ // For a range we allow invalid values and return an empty list.
+ // A list index out of range is an error.
if (!range) {
if (verbose) {
semsg(_(e_listidx), (int64_t)n1);
diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim
index 0ff3582da9..11dade18f3 100644
--- a/test/old/testdir/test_listdict.vim
+++ b/test/old/testdir/test_listdict.vim
@@ -1,5 +1,7 @@
" Tests for the List and Dict types
+source vim9.vim
+
func TearDown()
" Run garbage collection after every test
call test_garbagecollect_now()
@@ -37,6 +39,23 @@ func Test_list_slice()
let l[:1] += [1, 2]
let l[2:] -= [1]
call assert_equal([2, 4, 2], l)
+
+ let lines =<< trim END
+ VAR l = [1, 2]
+ call assert_equal([1, 2], l[:])
+ call assert_equal([2], l[-1 : -1])
+ call assert_equal([1, 2], l[-2 : -1])
+ END
+ call CheckLegacyAndVim9Success(lines)
+
+ let l = [1, 2]
+ call assert_equal([], l[-3 : -1])
+
+ let lines =<< trim END
+ var l = [1, 2]
+ assert_equal([1, 2], l[-3 : -1])
+ END
+ call CheckDefAndScriptSuccess(lines)
endfunc
" List identity