To: vim_dev@googlegroups.com Subject: Patch 8.2.2015 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2015 Problem: Vim9: literal dict #{} is not like any other language. Solution: Support the JavaScript syntax. Files: runtime/doc/vim9.txt, src/vim9compile.c, src/proto/vim9compile.pro, src/errors.h, src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_builtin.vim, src/testdir/test_vim9_func.vim, src/testdir/test_vim9_script.vim *** ../vim-8.2.2014/runtime/doc/vim9.txt 2020-10-04 16:06:00.509884351 +0200 --- runtime/doc/vim9.txt 2020-11-19 18:50:44.916967440 +0100 *************** *** 112,119 **** 101 number To improve readability there must be a space between a command and the # ! that starts a comment. Note that #{ is the start of a dictionary, therefore ! it does not start a comment. Vim9 functions ~ --- 112,118 ---- 101 number To improve readability there must be a space between a command and the # ! that starts a comment. Vim9 functions ~ *************** *** 303,310 **** myList->add(123) g:myList->add(123) [1, 2, 3]->Process() ! #{a: 1, b: 2}->Process() ! {'a': 1, 'b': 2}->Process() "foobar"->Process() ("foobar")->Process() 'foobar'->Process() --- 302,308 ---- myList->add(123) g:myList->add(123) [1, 2, 3]->Process() ! {a: 1, b: 2}->Process() "foobar"->Process() ("foobar")->Process() 'foobar'->Process() *************** *** 346,352 **** 'two', ] And when a dict spans multiple lines: > ! var mydict = #{ one: 1, two: 2, } --- 344,350 ---- 'two', ] And when a dict spans multiple lines: > ! var mydict = { one: 1, two: 2, } *************** *** 430,435 **** --- 428,454 ---- |curly-braces-names| cannot be used. + 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: > + 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} + + No :xit, :t, :append, :change or :insert ~ These commands are too easily confused with local variable names. *** ../vim-8.2.2014/src/vim9compile.c 2020-11-18 17:38:59.349902386 +0100 --- src/vim9compile.c 2020-11-18 19:54:15.192042721 +0100 *************** *** 2771,2777 **** * Return a pointer to just after the name. Equal to "arg" if there is no * valid name. */ ! static char_u * to_name_end(char_u *arg, int namespace) { char_u *p; --- 2771,2777 ---- * Return a pointer to just after the name. Equal to "arg" if there is no * valid name. */ ! char_u * to_name_end(char_u *arg, int namespace) { char_u *p; *************** *** 2988,2994 **** *arg = skipwhite(*arg + 1); for (;;) { ! char_u *key = NULL; if (may_get_next_line(whitep, arg, cctx) == FAIL) { --- 2988,2995 ---- *arg = skipwhite(*arg + 1); for (;;) { ! char_u *key = NULL; ! char_u *end; if (may_get_next_line(whitep, arg, cctx) == FAIL) { *************** *** 2999,3008 **** if (**arg == '}') break; ! if (literal) { - char_u *end = to_name_end(*arg, !literal); - if (end == *arg) { semsg(_(e_invalid_key_str), *arg); --- 3000,3013 ---- if (**arg == '}') break; ! // Eventually {name: value} will use "name" as a literal key and ! // {[expr]: value} for an evaluated key. ! // Temporarily: if "name" is indeed a valid key, or "[expr]" is ! // used, use the new method, like JavaScript. Otherwise fall back ! // to the old method. ! end = to_name_end(*arg, FALSE); ! if (literal || *end == ':') { if (end == *arg) { semsg(_(e_invalid_key_str), *arg); *************** *** 3015,3022 **** } else { ! isn_T *isn; if (compile_expr0(arg, cctx) == FAIL) return FAIL; isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1; --- 3020,3030 ---- } else { ! isn_T *isn; ! int has_bracket = **arg == '['; + if (has_bracket) + *arg = skipwhite(*arg + 1); if (compile_expr0(arg, cctx) == FAIL) return FAIL; isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1; *************** *** 3025,3034 **** else { type_T *keytype = ((type_T **)stack->ga_data) ! [stack->ga_len - 1]; if (need_type(keytype, &t_string, -1, cctx, ! FALSE, FALSE) == FAIL) return FAIL; } } --- 3033,3052 ---- else { type_T *keytype = ((type_T **)stack->ga_data) ! [stack->ga_len - 1]; if (need_type(keytype, &t_string, -1, cctx, ! FALSE, FALSE) == FAIL) ! return FAIL; ! } ! if (has_bracket) ! { ! *arg = skipwhite(*arg); ! if (**arg != ']') ! { ! emsg(_(e_missing_matching_bracket_after_dict_key)); return FAIL; + } + ++*arg; } } *** ../vim-8.2.2014/src/proto/vim9compile.pro 2020-11-16 20:08:32.395713947 +0100 --- src/proto/vim9compile.pro 2020-11-18 19:53:42.668140849 +0100 *************** *** 8,13 **** --- 8,14 ---- int vim9_comment_start(char_u *p); char_u *peek_next_line_from_context(cctx_T *cctx); char_u *next_line_from_context(cctx_T *cctx, int skip_comment); + char_u *to_name_end(char_u *arg, int namespace); char_u *to_name_const_end(char_u *arg); exptype_T get_compare_type(char_u *p, int *len, int *type_is); void error_white_both(char_u *op, int len); *** ../vim-8.2.2014/src/errors.h 2020-11-18 17:17:11.961928659 +0100 --- src/errors.h 2020-11-18 19:38:17.834785012 +0100 *************** *** 303,305 **** --- 303,307 ---- INIT(= N_("E1137: mapping must not include %s key")); EXTERN char e_using_bool_as_number[] INIT(= N_("E1138: Using a Bool as a Number")); + EXTERN char e_missing_matching_bracket_after_dict_key[] + INIT(= N_("E1139: Missing matching bracket after dict key")); *** ../vim-8.2.2014/src/testdir/test_vim9_expr.vim 2020-11-18 17:38:59.349902386 +0100 --- src/testdir/test_vim9_expr.vim 2020-11-18 22:03:41.843894713 +0100 *************** *** 1883,1888 **** --- 1883,1891 ---- CheckDefAndScriptSuccess(lines) enddef + let g:test_space_dict = {'': 'empty', ' ': 'space'} + let g:test_hash_dict = #{one: 1, two: 2} + def Test_expr7_dict() # dictionary var lines =<< trim END *************** *** 1891,1907 **** assert_equal(g:dict_one, {'one': 1}) var key = 'one' var val = 1 ! assert_equal(g:dict_one, {key: val}) ! var numbers: dict = #{a: 1, b: 2, c: 3} numbers = #{a: 1} numbers = #{} ! var strings: dict = #{a: 'a', b: 'b', c: 'c'} strings = #{a: 'x'} strings = #{} ! var mixed: dict = #{a: 'a', b: 42} mixed = #{a: 'x'} mixed = #{a: 234} mixed = #{} --- 1894,1910 ---- assert_equal(g:dict_one, {'one': 1}) var key = 'one' var val = 1 ! assert_equal(g:dict_one, {[key]: val}) ! var numbers: dict = {a: 1, b: 2, c: 3} numbers = #{a: 1} numbers = #{} ! var strings: dict = {a: 'a', b: 'b', c: 'c'} strings = #{a: 'x'} strings = #{} ! var mixed: dict = {a: 'a', b: 42} mixed = #{a: 'x'} mixed = #{a: 234} mixed = #{} *************** *** 1915,1920 **** --- 1918,1926 ---- dictdict = #{one: #{}, two: #{}} assert_equal({'': 0}, {matchstr('string', 'wont match'): 0}) + + assert_equal(g:test_space_dict, {['']: 'empty', [' ']: 'space'}) + assert_equal(g:test_hash_dict, {one: 1, two: 2}) END CheckDefAndScriptSuccess(lines) *************** *** 1929,1935 **** CheckDefFailure(["var x = #{xxx: 1", "var y = 2"], 'E722:', 2) CheckDefFailure(["var x = #{xxx: 1,"], 'E723:', 2) CheckDefFailure(["var x = {'a': xxx}"], 'E1001:', 1) ! CheckDefFailure(["var x = {xxx: 8}"], 'E1001:', 1) CheckDefFailure(["var x = #{a: 1, a: 2}"], 'E721:', 1) CheckDefFailure(["var x = #"], 'E1015:', 1) CheckDefExecFailure(["var x = g:anint.member"], 'E715:', 1) --- 1935,1941 ---- CheckDefFailure(["var x = #{xxx: 1", "var y = 2"], 'E722:', 2) CheckDefFailure(["var x = #{xxx: 1,"], 'E723:', 2) CheckDefFailure(["var x = {'a': xxx}"], 'E1001:', 1) ! CheckDefFailure(["var x = {xx-x: 8}"], 'E1001:', 1) CheckDefFailure(["var x = #{a: 1, a: 2}"], 'E721:', 1) CheckDefFailure(["var x = #"], 'E1015:', 1) CheckDefExecFailure(["var x = g:anint.member"], 'E715:', 1) *** ../vim-8.2.2014/src/testdir/test_vim9_builtin.vim 2020-11-16 20:08:32.395713947 +0100 --- src/testdir/test_vim9_builtin.vim 2020-11-18 19:41:39.366247106 +0100 *************** *** 226,232 **** def Wrong_dict_key_type(items: list): list ! return filter(items, {_, val -> get({val: 1}, 'x')}) enddef def Test_filter_wrong_dict_key_type() --- 226,232 ---- def Wrong_dict_key_type(items: list): list ! return filter(items, {_, val -> get({[val]: 1}, 'x')}) enddef def Test_filter_wrong_dict_key_type() *** ../vim-8.2.2014/src/testdir/test_vim9_func.vim 2020-11-17 18:50:40.791592326 +0100 --- src/testdir/test_vim9_func.vim 2020-11-18 20:14:06.668096161 +0100 *************** *** 1569,1575 **** def TreeWalk(dir: string): list return readdir(dir)->map({_, val -> fnamemodify(dir .. '/' .. val, ':p')->isdirectory() ! ? {val: TreeWalk(dir .. '/' .. val)} : val }) enddef --- 1569,1575 ---- def TreeWalk(dir: string): list return readdir(dir)->map({_, val -> fnamemodify(dir .. '/' .. val, ':p')->isdirectory() ! ? {[val]: TreeWalk(dir .. '/' .. val)} : val }) enddef *** ../vim-8.2.2014/src/testdir/test_vim9_script.vim 2020-11-04 15:07:13.057780706 +0100 --- src/testdir/test_vim9_script.vim 2020-11-18 20:16:19.795564731 +0100 *************** *** 416,422 **** var nd: dict try ! nd = {g:anumber: 1} catch /E1012:/ n = 266 endtry --- 416,422 ---- var nd: dict try ! nd = {[g:anumber]: 1} catch /E1012:/ n = 266 endtry *************** *** 459,465 **** assert_equal(322, n) try ! d = {'text': 1, g:astring: 2} catch /E721:/ n = 333 endtry --- 459,465 ---- assert_equal(322, n) try ! d = {text: 1, [g:astring]: 2} catch /E721:/ n = 333 endtry *** ../vim-8.2.2014/src/version.c 2020-11-19 18:46:19.549796579 +0100 --- src/version.c 2020-11-19 18:51:01.764914357 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2015, /**/ -- Error:015 - Unable to exit Windows. Try the door. /// 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 ///