From c5e6a7179d7dee4315b412b56e172bb1ff092d3e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 4 Dec 2020 19:12:14 +0100 Subject: [PATCH] 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. --- runtime/doc/vim9.txt | 22 +++++++++----- src/dict.c | 54 ++++++++++++++++++++++++++++++++-- src/proto/dict.pro | 1 + src/testdir/test_vim9_expr.vim | 3 +- src/version.c | 2 ++ src/vim9compile.c | 30 ++++++++----------- 6 files changed, 83 insertions(+), 29 deletions(-) diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 085e4453e..5b0fded94 100644 --- 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 index 819f5fa0b..311b0038d 100644 --- 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); @@ -814,6 +814,47 @@ get_literal_key(char_u **arg, typval_T *tv) 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". * "*arg" points to the "{". @@ -864,10 +905,17 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal) { int has_bracket = vim9script && **arg == '['; - if (literal || (vim9script && !has_bracket)) + if (literal) + { + if (get_literal_key_tv(arg, &tvkey) == FAIL) + goto failret; + } + else if (vim9script && !has_bracket) { - if (get_literal_key(arg, &tvkey) == FAIL) + tvkey.vval.v_string = get_literal_key(arg); + if (tvkey.vval.v_string == NULL) goto failret; + tvkey.v_type = VAR_STRING; } else { diff --git a/src/proto/dict.pro b/src/proto/dict.pro index 0ba83657d..dbfdcdcbb 100644 --- a/src/proto/dict.pro +++ b/src/proto/dict.pro @@ -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); 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 index 4d0535130..a0593eadc 100644 --- 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 index 8b8a6c32a..2061ea0a3 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2090, /**/ 2089, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 4b8a8ad71..2cd4b52c0 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3024,26 +3024,11 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) 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, ppconst_T *ppconst) } ++*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) -- 2.40.0