# HG changeset patch # User Bram Moolenaar # Date 1607105703 -3600 # Node ID 285cde4b8d0efc29c702caa92ea91ecd7f8e93fd # Parent ca8a14cbafb61574131e7dc3bbab267bf4bb4a3b patch 8.2.2090: Vim9: dict does not accept a key in quotes Commit: https://github.com/vim/vim/commit/c5e6a7179d7dee4315b412b56e172bb1ff092d3e Author: Bram Moolenaar Date: Fri Dec 4 19:12:14 2020 +0100 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. diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -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 @@ -436,19 +436,25 @@ Dictionary literals ~ Traditionally Vim has supported dictionary literals with a {} syntax: > let dict = {'key': value} -Later it became clear that using a simple key name is very common, thus -literally dictionaries were introduced in a backwards compatible way: > +Later it became clear that using a simple text key is very common, thus +literal dictionaries were introduced in a backwards compatible way: > let dict = #{key: value} -However, this #{} syntax is unlike any existing language. As it appears that -using a literal key is much more common than using an expression, and +However, this #{} syntax is unlike any existing language. As it turns out +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 -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: > let dict = {key: value} -In case an expression needs to be used for the key, square brackets can be -used, just like in JavaScript: > +This works for alphanumeric characters, underscore and dash. If you want to +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} diff --git a/src/dict.c b/src/dict.c --- a/src/dict.c +++ b/src/dict.c @@ -801,7 +801,7 @@ skip_literal_key(char_u *key) * Return FAIL when there is no valid key. */ 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); @@ -815,6 +815,47 @@ get_literal_key(char_u **arg, typval_T * } /* + * 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". * "*arg" points to the "{". * "literal" is TRUE for #{key: val} @@ -864,11 +905,18 @@ eval_dict(char_u **arg, typval_T *rettv, { 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; } + 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 { if (has_bracket) diff --git a/src/proto/dict.pro b/src/proto/dict.pro --- a/src/proto/dict.pro +++ b/src/proto/dict.pro @@ -34,6 +34,7 @@ varnumber_T dict_get_number_check(dict_T 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 *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); void dict_extend(dict_T *d1, dict_T *d2, char_u *action); dictitem_T *dict_lookup(hashitem_T *hi); diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1930,12 +1930,13 @@ def Test_expr7_dict() assert_equal(g:test_space_dict, {['']: 'empty', [' ']: 'space'}) 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 CheckDefAndScriptSuccess(lines) # legacy syntax doesn't work 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:8}"], 'E1069:', 1) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2090, +/**/ 2089, /**/ 2088, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3024,26 +3024,11 @@ compile_dict(char_u **arg, cctx_T *cctx, if (**arg == '}') break; - // {name: value} uses "name" as a literal key and - // {[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 + if (**arg == '[') { isn_T *isn; + // {[expr]: value} uses an evaluated key. *arg = skipwhite(*arg + 1); if (compile_expr0(arg, cctx) == FAIL) return FAIL; @@ -3066,6 +3051,17 @@ compile_dict(char_u **arg, cctx_T *cctx, } ++*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. if (key != NULL)