To: vim_dev@googlegroups.com Subject: Patch 8.2.2090 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2090 Problem: Vim9: dict does not accept a key in quotes. Solution: Recognize a key in single or double quotes. Files: runtime/doc/vim9.txt, src/dict.c, src/proto/dict.pro, src/vim9compile.c, src/testdir/test_vim9_expr.vim *** ../vim-8.2.2089/runtime/doc/vim9.txt 2020-11-19 18:53:15.184492586 +0100 --- runtime/doc/vim9.txt 2020-12-04 19:03:01.619017804 +0100 *************** *** 433,451 **** 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: > let dict = #{key: value} ! However, this #{} syntax is unlike any existing language. As it appears that ! using a literaly 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 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: > let dict = {["key" .. nr]: value} --- 436,460 ---- Traditionally Vim has supported dictionary literals with a {} syntax: > let dict = {'key': value} ! 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 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 is considered a much more useful syntax. In Vim9 script the {} form uses literal keys: > let dict = {key: value} ! 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} *** ../vim-8.2.2089/src/dict.c 2020-12-02 17:36:49.169409752 +0100 --- src/dict.c 2020-12-04 18:55:50.744441533 +0100 *************** *** 801,807 **** * Return FAIL when there is no valid key. */ static int ! get_literal_key(char_u **arg, typval_T *tv) { char_u *p = skip_literal_key(*arg); --- 801,807 ---- * Return FAIL when there is no valid key. */ static int ! get_literal_key_tv(char_u **arg, typval_T *tv) { char_u *p = skip_literal_key(*arg); *************** *** 815,820 **** --- 815,861 ---- } /* + * 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,873 **** { int has_bracket = vim9script && **arg == '['; ! if (literal || (vim9script && !has_bracket)) { ! if (get_literal_key(arg, &tvkey) == FAIL) goto failret; } else { --- 905,921 ---- { int has_bracket = vim9script && **arg == '['; ! if (literal) ! { ! 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 { *** ../vim-8.2.2089/src/proto/dict.pro 2020-12-02 17:36:49.169409752 +0100 --- src/proto/dict.pro 2020-12-04 18:52:02.145226267 +0100 *************** *** 34,39 **** --- 34,40 ---- 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); *** ../vim-8.2.2089/src/vim9compile.c 2020-12-04 12:43:23.786385152 +0100 --- src/vim9compile.c 2020-12-04 18:56:02.592401563 +0100 *************** *** 3024,3049 **** 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 { isn_T *isn; *arg = skipwhite(*arg + 1); if (compile_expr0(arg, cctx) == FAIL) return FAIL; --- 3024,3034 ---- if (**arg == '}') break; ! if (**arg == '[') { isn_T *isn; + // {[expr]: value} uses an evaluated key. *arg = skipwhite(*arg + 1); if (compile_expr0(arg, cctx) == FAIL) return FAIL; *************** *** 3066,3071 **** --- 3051,3067 ---- } ++*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) *** ../vim-8.2.2089/src/testdir/test_vim9_expr.vim 2020-12-02 17:36:49.173409740 +0100 --- src/testdir/test_vim9_expr.vim 2020-12-04 18:58:28.299914463 +0100 *************** *** 1930,1941 **** assert_equal(g:test_space_dict, {['']: 'empty', [' ']: 'space'}) assert_equal(g:test_hash_dict, {one: 1, two: 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) --- 1930,1942 ---- 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 = 'a' .. #{a: 1}"], 'E1097:', 2) CheckDefFailure(["var x = {a:8}"], 'E1069:', 1) *** ../vim-8.2.2089/src/version.c 2020-12-04 18:09:50.648966590 +0100 --- src/version.c 2020-12-04 19:03:27.970932225 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2090, /**/ -- ARTHUR: Go on, Bors, chop its head off. BORS: Right. Silly little bleeder. One rabbit stew coming up. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///