1
0
forked from aniani/vim

patch 8.2.2090: Vim9: dict does not accept a key in quotes

Problem:    Vim9: dict does not accept a key in quotes.
Solution:   Recognize a key in single or double quotes.
This commit is contained in:
Bram Moolenaar
2020-12-04 19:12:14 +01:00
parent 6cd42db9dc
commit c5e6a7179d
6 changed files with 83 additions and 29 deletions

View File

@@ -1,4 +1,4 @@
*vim9.txt* For Vim version 8.2. Last change: 2020 Nov 25 *vim9.txt* For Vim version 8.2. Last change: 2020 Dec 04
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -436,19 +436,25 @@ Dictionary literals ~
Traditionally Vim has supported dictionary literals with a {} syntax: > Traditionally Vim has supported dictionary literals with a {} syntax: >
let dict = {'key': value} let dict = {'key': value}
Later it became clear that using a simple key name is very common, thus Later it became clear that using a simple text key is very common, thus
literally dictionaries were introduced in a backwards compatible way: > literal dictionaries were introduced in a backwards compatible way: >
let dict = #{key: value} let dict = #{key: value}
However, this #{} syntax is unlike any existing language. As it appears that However, this #{} syntax is unlike any existing language. As it turns out
using a literal key is much more common than using an expression, and that using a literal key is much more common than using an expression, and
considering that JavaScript uses this syntax, using the {} form for dictionary considering that JavaScript uses this syntax, using the {} form for dictionary
literals was considered a much more useful syntax. In Vim9 script the {} form literals is considered a much more useful syntax. In Vim9 script the {} form
uses literal keys: > uses literal keys: >
let dict = {key: value} let dict = {key: value}
In case an expression needs to be used for the key, square brackets can be This works for alphanumeric characters, underscore and dash. If you want to
used, just like in JavaScript: > use another character, use a single or double quoted string: >
let dict = {'key with space': value}
let dict = {"key\twith\ttabs": value}
let dict = {'': value} # empty key
In case the key needs to be an expression, square brackets can be used, just
like in JavaScript: >
let dict = {["key" .. nr]: value} let dict = {["key" .. nr]: value}

View File

@@ -801,7 +801,7 @@ skip_literal_key(char_u *key)
* Return FAIL when there is no valid key. * Return FAIL when there is no valid key.
*/ */
static int static int
get_literal_key(char_u **arg, typval_T *tv) get_literal_key_tv(char_u **arg, typval_T *tv)
{ {
char_u *p = skip_literal_key(*arg); char_u *p = skip_literal_key(*arg);
@@ -814,6 +814,47 @@ get_literal_key(char_u **arg, typval_T *tv)
return OK; return OK;
} }
/*
* Get a literal key for a Vim9 dict:
* {"name": value},
* {'name': value},
* {name: value} use "name" as a literal key
* Return the key in allocated memory or NULL in the case of an error.
* "arg" is advanced to just after the key.
*/
char_u *
get_literal_key(char_u **arg)
{
char_u *key;
char_u *end;
typval_T rettv;
if (**arg == '\'')
{
if (eval_lit_string(arg, &rettv, TRUE) == FAIL)
return NULL;
key = rettv.vval.v_string;
}
else if (**arg == '"')
{
if (eval_string(arg, &rettv, TRUE) == FAIL)
return NULL;
key = rettv.vval.v_string;
}
else
{
end = skip_literal_key(*arg);
if (end == *arg)
{
semsg(_(e_invalid_key_str), *arg);
return NULL;
}
key = vim_strnsave(*arg, end - *arg);
*arg = end;
}
return key;
}
/* /*
* Allocate a variable for a Dictionary and fill it from "*arg". * Allocate a variable for a Dictionary and fill it from "*arg".
* "*arg" points to the "{". * "*arg" points to the "{".
@@ -864,11 +905,18 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
{ {
int has_bracket = vim9script && **arg == '['; int has_bracket = vim9script && **arg == '[';
if (literal || (vim9script && !has_bracket)) if (literal)
{ {
if (get_literal_key(arg, &tvkey) == FAIL) if (get_literal_key_tv(arg, &tvkey) == FAIL)
goto failret; goto failret;
} }
else if (vim9script && !has_bracket)
{
tvkey.vval.v_string = get_literal_key(arg);
if (tvkey.vval.v_string == NULL)
goto failret;
tvkey.v_type = VAR_STRING;
}
else else
{ {
if (has_bracket) if (has_bracket)

View File

@@ -34,6 +34,7 @@ varnumber_T dict_get_number_check(dict_T *d, char_u *key);
varnumber_T dict_get_bool(dict_T *d, char_u *key, int def); varnumber_T dict_get_bool(dict_T *d, char_u *key, int def);
char_u *dict2string(typval_T *tv, int copyID, int restore_copyID); char_u *dict2string(typval_T *tv, int copyID, int restore_copyID);
char_u *skip_literal_key(char_u *key); char_u *skip_literal_key(char_u *key);
char_u *get_literal_key(char_u **arg);
int eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal); int eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal);
void dict_extend(dict_T *d1, dict_T *d2, char_u *action); void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
dictitem_T *dict_lookup(hashitem_T *hi); dictitem_T *dict_lookup(hashitem_T *hi);

View File

@@ -1930,12 +1930,13 @@ def Test_expr7_dict()
assert_equal(g:test_space_dict, {['']: 'empty', [' ']: 'space'}) assert_equal(g:test_space_dict, {['']: 'empty', [' ']: 'space'})
assert_equal(g:test_hash_dict, {one: 1, two: 2}) assert_equal(g:test_hash_dict, {one: 1, two: 2})
assert_equal({['a a']: 1, ['b/c']: 2}, {'a a': 1, "b/c": 2})
END END
CheckDefAndScriptSuccess(lines) CheckDefAndScriptSuccess(lines)
# legacy syntax doesn't work # legacy syntax doesn't work
CheckDefFailure(["var x = #{key: 8}"], 'E1097:', 2) CheckDefFailure(["var x = #{key: 8}"], 'E1097:', 2)
CheckDefFailure(["var x = {'key': 8}"], 'E1014:', 1)
CheckDefFailure(["var x = 'a' .. #{a: 1}"], 'E1097:', 2) CheckDefFailure(["var x = 'a' .. #{a: 1}"], 'E1097:', 2)
CheckDefFailure(["var x = {a:8}"], 'E1069:', 1) CheckDefFailure(["var x = {a:8}"], 'E1069:', 1)

View File

@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
2090,
/**/ /**/
2089, 2089,
/**/ /**/

View File

@@ -3024,26 +3024,11 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
if (**arg == '}') if (**arg == '}')
break; break;
// {name: value} uses "name" as a literal key and if (**arg == '[')
// {[expr]: value} uses an evaluated key.
if (**arg != '[')
{
char_u *end = skip_literal_key(*arg);
if (end == *arg)
{
semsg(_(e_invalid_key_str), *arg);
return FAIL;
}
key = vim_strnsave(*arg, end - *arg);
if (generate_PUSHS(cctx, key) == FAIL)
return FAIL;
*arg = end;
}
else
{ {
isn_T *isn; isn_T *isn;
// {[expr]: value} uses an evaluated key.
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
if (compile_expr0(arg, cctx) == FAIL) if (compile_expr0(arg, cctx) == FAIL)
return FAIL; return FAIL;
@@ -3066,6 +3051,17 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
} }
++*arg; ++*arg;
} }
else
{
// {"name": value},
// {'name': value},
// {name: value} use "name" as a literal key
key = get_literal_key(arg);
if (key == NULL)
return FAIL;
if (generate_PUSHS(cctx, key) == FAIL)
return FAIL;
}
// Check for duplicate keys, if using string keys. // Check for duplicate keys, if using string keys.
if (key != NULL) if (key != NULL)