To: vim_dev@googlegroups.com Subject: Patch 8.2.4883 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4883 Problem: String interpolation only works in heredoc. Solution: Support interpolated strings. Use syntax for heredoc consistent with strings, similar to C#. (closes #10327) Files: runtime/doc/eval.txt, src/errors.h, src/eval.c, src/evalvars.c, src/proto/evalvars.pro, src/typval.c, src/proto/typval.pro, src/vim9compile.c, src/proto/vim9compile.pro, src/vim9expr.c, src/testdir/test_debugger.vim, src/testdir/test_expr.vim, src/testdir/test_let.vim, src/testdir/test_vim9_assign.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.4882/runtime/doc/eval.txt 2022-04-21 23:29:58.944561834 +0100 --- runtime/doc/eval.txt 2022-05-06 12:47:38.262557008 +0100 *************** *** 1509,1514 **** --- 1524,1548 ---- if a =~ '\s*' + interpolated-string *interp-string* *E256* + -------------------- + $"string" interpolated string constant *expr-$quote* + $'string' interpolated literal string constant *expr-$'* + + Interpolated strings are an extension of the |string| and |literal-string|, + allowing the inclusion of Vim script expressions (see |expr1|). Any + expression returning a value can be enclosed between curly braces. The value + is converted to a string. All the text and results of the expressions + are concatenated to make a new string. + + To include an opening brace '{' or closing brace '}' in the string content + double it. + + Examples: > + let your_name = input("What's your name? ") + echo $"Hello, {your_name}!" + echo $"The square root of 9 is {sqrt(9)}" + option *expr-option* *E112* *E113* ------ &option option value, local value if possible *** ../vim-8.2.4882/src/errors.h 2022-05-04 16:46:51.349318219 +0100 --- src/errors.h 2022-05-06 12:50:05.054395197 +0100 *************** *** 3268,3271 **** --- 3268,3275 ---- EXTERN char e_channel_job_feature_not_available[] INIT(= N_("E1277: Channel and job feature is not available")); # endif + EXTERN char e_stray_closing_curly_str[] + INIT(= N_("E1278: Stray '}' without a matching '{': %s")); + EXTERN char e_missing_close_curly_str[] + INIT(= N_("E1279: Missing '}': %s")); #endif *** ../vim-8.2.4882/src/eval.c 2022-05-05 12:20:24.355225504 +0100 --- src/eval.c 2022-05-06 12:36:09.571276494 +0100 *************** *** 3769,3776 **** /* * Environment variable: $VAR. */ ! case '$': ret = eval_env_var(arg, rettv, evaluate); break; /* --- 3769,3780 ---- /* * Environment variable: $VAR. + * Interpolated string: $"string" or $'string'. */ ! case '$': if ((*arg)[1] == '"' || (*arg)[1] == '\'') ! ret = eval_interp_string(arg, rettv, evaluate); ! else ! ret = eval_env_var(arg, rettv, evaluate); break; /* *** ../vim-8.2.4882/src/evalvars.c 2022-05-04 18:12:51.217336786 +0100 --- src/evalvars.c 2022-05-06 12:36:09.571276494 +0100 *************** *** 603,661 **** } /* ! * Evaluate all the Vim expressions (`=expr`) in string "str" and return the * resulting string. The caller must free the returned string. */ ! static char_u * eval_all_expr_in_str(char_u *str) { garray_T ga; - char_u *s; char_u *p; char_u save_c; ! char_u *exprval; ! int status; ga_init2(&ga, 1, 80); p = str; - // Look for `=expr`, evaluate the expression and replace `=expr` with the - // result. while (*p != NUL) { ! s = p; ! while (*p != NUL && (*p != '`' || p[1] != '=')) ! p++; ! ga_concat_len(&ga, s, p - s); if (*p == NUL) ! break; // no backtick expression found ! s = p; ! p += 2; // skip `= ! status = *p == NUL ? OK : skip_expr(&p, NULL); ! if (status == FAIL || *p != '`') { ! // invalid expression or missing ending backtick ! if (status != FAIL) ! emsg(_(e_missing_backtick)); ! vim_free(ga.ga_data); return NULL; } ! s += 2; // skip `= ! save_c = *p; ! *p = NUL; ! exprval = eval_to_string(s, TRUE); ! *p = save_c; ! p++; ! if (exprval == NULL) { ! // expression evaluation failed ! vim_free(ga.ga_data); return NULL; } ! ga_concat(&ga, exprval); ! vim_free(exprval); } ga_append(&ga, NUL); --- 603,690 ---- } /* ! * Evaluate all the Vim expressions ({expr}) in string "str" and return the * resulting string. The caller must free the returned string. */ ! char_u * eval_all_expr_in_str(char_u *str) { garray_T ga; char_u *p; char_u save_c; ! char_u *expr_val; ga_init2(&ga, 1, 80); p = str; while (*p != NUL) { ! char_u *lit_start; ! char_u *block_start; ! char_u *block_end; ! int escaped_brace = FALSE; ! ! // Look for a block start. ! lit_start = p; ! while (*p != '{' && *p != '}' && *p != NUL) ! ++p; ! ! if (*p != NUL && *p == p[1]) ! { ! // Escaped brace, unescape and continue. ! // Include the brace in the literal string. ! ++p; ! escaped_brace = TRUE; ! } ! else if (*p == '}') ! { ! semsg(_(e_stray_closing_curly_str), str); ! ga_clear(&ga); ! return NULL; ! } ! ! // Append the literal part. ! ga_concat_len(&ga, lit_start, (size_t)(p - lit_start)); ! if (*p == NUL) ! break; ! if (escaped_brace) ! { ! // Skip the second brace. ! ++p; ! continue; ! } ! // Skip the opening {. ! block_start = ++p; ! block_end = block_start; ! if (*block_start != NUL && skip_expr(&block_end, NULL) == FAIL) { ! ga_clear(&ga); return NULL; } ! block_end = skipwhite(block_end); ! // The block must be closed by a }. ! if (*block_end != '}') { ! semsg(_(e_missing_close_curly_str), str); ! ga_clear(&ga); return NULL; } ! save_c = *block_end; ! *block_end = NUL; ! expr_val = eval_to_string(block_start, TRUE); ! *block_end = save_c; ! if (expr_val == NULL) ! { ! ga_clear(&ga); ! return NULL; ! } ! ga_concat(&ga, expr_val); ! vim_free(expr_val); ! ! p = block_end + 1; } ga_append(&ga, NUL); *************** *** 825,831 **** str = theline + ti; if (vim9compile) { ! if (compile_heredoc_string(str, evalstr, cctx) == FAIL) { vim_free(theline); vim_free(text_indent); --- 854,860 ---- str = theline + ti; if (vim9compile) { ! if (compile_all_expr_in_str(str, evalstr, cctx) == FAIL) { vim_free(theline); vim_free(text_indent); *** ../vim-8.2.4882/src/proto/evalvars.pro 2022-04-21 23:29:58.944561834 +0100 --- src/proto/evalvars.pro 2022-05-06 12:36:09.571276494 +0100 *************** *** 105,108 **** --- 105,110 ---- void copy_callback(callback_T *dest, callback_T *src); void expand_autload_callback(callback_T *cb); void free_callback(callback_T *callback); + char_u *eval_all_expr_in_str(char_u *str); + /* vim: set ft=c : */ *** ../vim-8.2.4882/src/typval.c 2022-05-04 15:40:16.032317666 +0100 --- src/typval.c 2022-05-06 12:36:09.575276491 +0100 *************** *** 2266,2271 **** --- 2266,2297 ---- return OK; } + int + eval_interp_string(char_u **arg, typval_T *rettv, int evaluate) + { + typval_T tv; + int ret; + + // *arg is on the '$' character. + (*arg)++; + + rettv->v_type = VAR_STRING; + + if (**arg == '"') + ret = eval_string(arg, &tv, evaluate); + else + ret = eval_lit_string(arg, &tv, evaluate); + + if (ret == FAIL || !evaluate) + return ret; + + rettv->vval.v_string = eval_all_expr_in_str(tv.vval.v_string); + + clear_tv(&tv); + + return rettv->vval.v_string != NULL ? OK : FAIL; + } + /* * Return a string with the string representation of a variable. * If the memory is allocated "tofree" is set to it, otherwise NULL. *** ../vim-8.2.4882/src/proto/typval.pro 2022-05-04 15:40:16.032317666 +0100 --- src/proto/typval.pro 2022-05-06 12:36:09.571276494 +0100 *************** *** 72,77 **** --- 72,78 ---- int eval_lit_string(char_u **arg, typval_T *rettv, int evaluate); char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); int eval_env_var(char_u **arg, typval_T *rettv, int evaluate); + int eval_interp_string(char_u **arg, typval_T *rettv, int evaluate); linenr_T tv_get_lnum(typval_T *argvars); linenr_T tv_get_lnum_buf(typval_T *argvars, buf_T *buf); buf_T *tv_get_buf(typval_T *tv, int curtab_only); *** ../vim-8.2.4882/src/vim9compile.c 2022-05-05 15:19:59.615770189 +0100 --- src/vim9compile.c 2022-05-06 12:36:09.575276491 +0100 *************** *** 969,1048 **** } /* ! * Compile a heredoc string "str" (either containing a literal string or a mix ! * of literal strings and Vim expressions of the form `=`). This is used ! * when compiling a heredoc assignment to a variable in a Vim9 def function. ! * Vim9 instructions are generated to push strings, evaluate expressions, ! * concatenate them and create a list of lines. When "evalstr" is TRUE, Vim ! * expressions in "str" are evaluated. */ int ! compile_heredoc_string(char_u *str, int evalstr, cctx_T *cctx) { ! char_u *p; char_u *val; if (cctx->ctx_skip == SKIP_YES) return OK; ! if (evalstr && (p = (char_u *)strstr((char *)str, "`=")) != NULL) { ! char_u *start = str; ! int count = 0; ! // Need to evaluate expressions of the form `=` in the string. ! // Split the string into literal strings and Vim expressions and ! // generate instructions to concatenate the literal strings and the ! // result of evaluating the Vim expressions. ! for (;;) { ! if (p > start) ! { ! // literal string before the expression ! val = vim_strnsave(start, p - start); ! generate_PUSHS(cctx, &val); ! count++; ! } ! p += 2; ! // evaluate the Vim expression and convert the result to string. ! if (compile_expr0(&p, cctx) == FAIL) return FAIL; ! may_generate_2STRING(-1, TRUE, cctx); ! count++; ! p = skipwhite(p); ! if (*p != '`') ! { ! emsg(_(e_missing_backtick)); ! return FAIL; ! } ! start = p + 1; ! p = (char_u *)strstr((char *)start, "`="); ! if (p == NULL) ! { ! // no more Vim expressions left to process ! if (*skipwhite(start) != NUL) ! { ! val = vim_strsave(start); ! generate_PUSHS(cctx, &val); ! count++; ! } ! break; ! } } ! if (count > 1) ! generate_CONCAT(cctx, count); ! } ! else ! { ! // literal string ! val = vim_strsave(str); ! generate_PUSHS(cctx, &val); } return OK; } --- 969,1071 ---- } /* ! * Compile a string "str" (either containing a literal string or a mix of ! * literal strings and Vim expressions of the form `{expr}`). This is used ! * when compiling a heredoc assignment to a variable or an interpolated string ! * in a Vim9 def function. Vim9 instructions are generated to push strings, ! * evaluate expressions, concatenate them and create a list of lines. When ! * "evalstr" is TRUE, Vim expressions in "str" are evaluated. */ int ! compile_all_expr_in_str(char_u *str, int evalstr, cctx_T *cctx) { ! char_u *p = str; char_u *val; + char_u save_c; + int count = 0; if (cctx->ctx_skip == SKIP_YES) return OK; ! if (!evalstr || *str == NUL) { ! // Literal string, possibly empty. ! val = *str != NUL ? vim_strsave(str) : NULL; ! return generate_PUSHS(cctx, &val); ! } ! ! // Push all the string pieces to the stack, followed by a ISN_CONCAT. ! while (*p != NUL) ! { ! char_u *lit_start; ! char_u *block_start; ! char_u *block_end; ! int escaped_brace = FALSE; ! ! // Look for a block start. ! lit_start = p; ! while (*p != '{' && *p != '}' && *p != NUL) ! ++p; ! if (*p != NUL && *p == p[1]) ! { ! // Escaped brace, unescape and continue. ! // Include the brace in the literal string. ! ++p; ! escaped_brace = TRUE; ! } ! else if (*p == '}') { ! semsg(_(e_stray_closing_curly_str), str); ! return FAIL; ! } ! // Append the literal part. ! if (p != lit_start) ! { ! val = vim_strnsave(lit_start, (size_t)(p - lit_start)); ! if (generate_PUSHS(cctx, &val) == FAIL) return FAIL; ! ++count; ! } ! if (*p == NUL) ! break; ! if (escaped_brace) ! { ! // Skip the second brace. ! ++p; ! continue; } ! // Skip the opening {. ! block_start = skipwhite(p + 1); ! block_end = block_start; ! if (*block_start != NUL &&skip_expr(&block_end, NULL) == FAIL) ! return FAIL; ! block_end = skipwhite(block_end); ! // The block must be closed by a }. ! if (*block_end != '}') ! { ! semsg(_(e_missing_close_curly_str), str); ! return FAIL; ! } ! save_c = *block_end; ! *block_end = NUL; ! if (compile_expr0(&block_start, cctx) == FAIL) ! return FAIL; ! *block_end = save_c; ! may_generate_2STRING(-1, TRUE, cctx); ! ++count; ! ! p = block_end + 1; } + // Small optimization, if there's only a single piece skip the ISN_CONCAT. + if (count != 1) + return generate_CONCAT(cctx, count); + return OK; } *** ../vim-8.2.4882/src/proto/vim9compile.pro 2022-04-21 23:29:58.948561831 +0100 --- src/proto/vim9compile.pro 2022-05-06 12:36:09.571276494 +0100 *************** *** 16,22 **** int may_get_next_line_error(char_u *whitep, char_u **arg, cctx_T *cctx); void fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx); int func_needs_compiling(ufunc_T *ufunc, compiletype_T compile_type); ! int compile_heredoc_string(char_u *str, int evalstr, cctx_T *cctx); int assignment_len(char_u *p, int *heredoc); void vim9_declare_error(char_u *name); int get_var_dest(char_u *name, assign_dest_T *dest, cmdidx_T cmdidx, int *option_scope, int *vimvaridx, type_T **type, cctx_T *cctx); --- 16,22 ---- int may_get_next_line_error(char_u *whitep, char_u **arg, cctx_T *cctx); void fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx); int func_needs_compiling(ufunc_T *ufunc, compiletype_T compile_type); ! int compile_all_expr_in_str(char_u *str, int evalstr, cctx_T *cctx); int assignment_len(char_u *p, int *heredoc); void vim9_declare_error(char_u *name); int get_var_dest(char_u *name, assign_dest_T *dest, cmdidx_T cmdidx, int *option_scope, int *vimvaridx, type_T **type, cctx_T *cctx); *** ../vim-8.2.4882/src/vim9expr.c 2022-05-05 13:52:59.416192105 +0100 --- src/vim9expr.c 2022-05-06 12:36:09.575276491 +0100 *************** *** 1375,1380 **** --- 1375,1407 ---- } /* + * Compile "$"string"" or "$'string'". + */ + static int + compile_interp_string(char_u **arg, cctx_T *cctx) + { + typval_T tv; + int ret; + int evaluate = cctx->ctx_skip != SKIP_YES; + + // *arg is on the '$' character. + (*arg)++; + + if (**arg == '"') + ret = eval_string(arg, &tv, evaluate); + else + ret = eval_lit_string(arg, &tv, evaluate); + + if (ret == FAIL || !evaluate) + return ret; + + ret = compile_all_expr_in_str(tv.vval.v_string, TRUE, cctx); + clear_tv(&tv); + + return ret; + } + + /* * Compile "@r". */ static int *************** *** 2226,2235 **** /* * Environment variable: $VAR. */ case '$': if (generate_ppconst(cctx, ppconst) == FAIL) return FAIL; ! ret = compile_get_env(arg, cctx); break; /* --- 2253,2266 ---- /* * Environment variable: $VAR. + * Interpolated string: $"string" or $'string'. */ case '$': if (generate_ppconst(cctx, ppconst) == FAIL) return FAIL; ! if ((*arg)[1] == '"' || (*arg)[1] == '\'') ! ret = compile_interp_string(arg, cctx); ! else ! ret = compile_get_env(arg, cctx); break; /* *** ../vim-8.2.4882/src/testdir/test_debugger.vim 2022-04-23 10:50:18.967609291 +0100 --- src/testdir/test_debugger.vim 2022-05-06 12:36:09.571276494 +0100 *************** *** 377,383 **** let expected =<< eval trim END Oldval = "10" Newval = "11" ! `=fnamemodify('Xtest.vim', ':p')` line 1: let g:Xtest_var += 1 END call RunDbgCmd(buf, ':source %', expected) --- 377,383 ---- let expected =<< eval trim END Oldval = "10" Newval = "11" ! {fnamemodify('Xtest.vim', ':p')} line 1: let g:Xtest_var += 1 END call RunDbgCmd(buf, ':source %', expected) *************** *** 385,391 **** let expected =<< eval trim END Oldval = "11" Newval = "12" ! `=fnamemodify('Xtest.vim', ':p')` line 1: let g:Xtest_var += 1 END call RunDbgCmd(buf, ':source %', expected) --- 385,391 ---- let expected =<< eval trim END Oldval = "11" Newval = "12" ! {fnamemodify('Xtest.vim', ':p')} line 1: let g:Xtest_var += 1 END call RunDbgCmd(buf, ':source %', expected) *** ../vim-8.2.4882/src/testdir/test_expr.vim 2022-03-09 11:56:17.873358764 +0000 --- src/testdir/test_expr.vim 2022-05-06 12:55:15.734048448 +0100 *************** *** 890,893 **** --- 890,949 ---- call v9.CheckLegacyAndVim9Success(lines) endfunc + func Test_string_interp() + let lines =<< trim END + call assert_equal('', $"") + call assert_equal('foobar', $"foobar") + #" Escaping rules. + call assert_equal('"foo"{bar}', $"\"foo\"{{bar}}") + call assert_equal('"foo"{bar}', $'"foo"{{bar}}') + call assert_equal('foobar', $"{\"foo\"}" .. $'{''bar''}') + #" Whitespace before/after the expression. + call assert_equal('3', $"{ 1 + 2 }") + #" String conversion. + call assert_equal('hello from ' .. v:version, $"hello from {v:version}") + call assert_equal('hello from ' .. v:version, $'hello from {v:version}') + #" Paper over a small difference between VimScript behaviour. + call assert_equal(string(v:true), $"{v:true}") + call assert_equal('(1+1=2)', $"(1+1={1 + 1})") + #" Hex-escaped opening brace: char2nr('{') == 0x7b + call assert_equal('esc123ape', $"esc\x7b123}ape") + call assert_equal('me{}me', $"me{\x7b}\x7dme") + VAR var1 = "sun" + VAR var2 = "shine" + call assert_equal('sunshine', $"{var1}{var2}") + call assert_equal('sunsunsun', $"{var1->repeat(3)}") + #" Multibyte strings. + call assert_equal('say ハロー・ワールド', $"say {'ハロー・ワールド'}") + #" Nested. + call assert_equal('foobarbaz', $"foo{$\"{'bar'}\"}baz") + #" Do not evaluate blocks when the expr is skipped. + VAR tmp = 0 + if v:false + echo "${ LET tmp += 1 }" + endif + call assert_equal(0, tmp) + + #" Stray closing brace. + call assert_fails('echo $"moo}"', 'E1278:') + #" Undefined variable in expansion. + call assert_fails('echo $"{moo}"', 'E121:') + #" Empty blocks are rejected. + call assert_fails('echo $"{}"', 'E15:') + call assert_fails('echo $"{ }"', 'E15:') + END + call v9.CheckLegacyAndVim9Success(lines) + + let lines =<< trim END + call assert_equal('5', $"{({x -> x + 1})(4)}") + END + call v9.CheckLegacySuccess(lines) + + let lines =<< trim END + call assert_equal('5', $"{((x) => x + 1)(4)}") + call assert_fails('echo $"{ # foo }"', 'E1279:') + END + call v9.CheckDefAndScriptSuccess(lines) + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-8.2.4882/src/testdir/test_let.vim 2022-04-28 16:51:37.508460219 +0100 --- src/testdir/test_let.vim 2022-05-06 13:12:32.909201913 +0100 *************** *** 381,386 **** --- 381,397 ---- call assert_equal(['Text', 'with', 'indent'], text) endfunc + func Test_let_interpolated() + call assert_equal('{text}', $'{{text}}') + call assert_equal('{{text}}', $'{{{{text}}}}') + let text = 'text' + call assert_equal('text{{', $'{text .. "{{"}') + call assert_equal('text{{', $"{text .. '{{'}") + " FIXME: should not need to escape quotes in the expression + call assert_equal('text{{', $'{text .. ''{{''}') + call assert_equal('text{{', $"{text .. \"{{\"}") + endfunc + " Test for the setting a variable using the heredoc syntax. " Keep near the end, this messes up highlighting. func Test_let_heredoc() *************** *** 498,569 **** call assert_equal([' x', ' \y', ' z'], [a, b, c]) endfunc ! " Test for evaluating Vim expressions in a heredoc using `=expr` " Keep near the end, this messes up highlighting. func Test_let_heredoc_eval() let str = '' let code =<< trim eval END ! let a = `=5 + 10` ! let b = `=min([10, 6])` + `=max([4, 6])` ! `=str` ! let c = "abc`=str`d" END call assert_equal(['let a = 15', 'let b = 6 + 6', '', 'let c = "abcd"'], code) let $TESTVAR = "Hello" let code =<< eval trim END ! let s = "`=$TESTVAR`" END call assert_equal(['let s = "Hello"'], code) let code =<< eval END ! let s = "`=$TESTVAR`" END call assert_equal([' let s = "Hello"'], code) let a = 10 let data =<< eval END ! `=a` END call assert_equal(['10'], data) let x = 'X' let code =<< eval trim END ! let a = `abc` ! let b = `=x` ! let c = ` END ! call assert_equal(['let a = `abc`', 'let b = X', 'let c = `'], code) let code = 'xxx' let code =<< eval trim END ! let n = `=5 + ! 6` END call assert_equal('xxx', code) let code =<< eval trim END ! let n = `=min([1, 2]` + `=max([3, 4])` END call assert_equal('xxx', code) let lines =<< trim LINES let text =<< eval trim END ! let b = `= END LINES ! call v9.CheckScriptFailure(lines, 'E1083:') let lines =<< trim LINES let text =<< eval trim END ! let b = `=abc END LINES ! call v9.CheckScriptFailure(lines, 'E1083:') let lines =<< trim LINES let text =<< eval trim END ! let b = `=` END LINES call v9.CheckScriptFailure(lines, 'E15:') --- 509,580 ---- call assert_equal([' x', ' \y', ' z'], [a, b, c]) endfunc ! " Test for evaluating Vim expressions in a heredoc using {expr} " Keep near the end, this messes up highlighting. func Test_let_heredoc_eval() let str = '' let code =<< trim eval END ! let a = {5 + 10} ! let b = {min([10, 6])} + {max([4, 6])} ! {str} ! let c = "abc{str}d" END call assert_equal(['let a = 15', 'let b = 6 + 6', '', 'let c = "abcd"'], code) let $TESTVAR = "Hello" let code =<< eval trim END ! let s = "{$TESTVAR}" END call assert_equal(['let s = "Hello"'], code) let code =<< eval END ! let s = "{$TESTVAR}" END call assert_equal([' let s = "Hello"'], code) let a = 10 let data =<< eval END ! {a} END call assert_equal(['10'], data) let x = 'X' let code =<< eval trim END ! let a = {{abc}} ! let b = {x} ! let c = {{ END ! call assert_equal(['let a = {abc}', 'let b = X', 'let c = {'], code) let code = 'xxx' let code =<< eval trim END ! let n = {5 + ! 6} END call assert_equal('xxx', code) let code =<< eval trim END ! let n = {min([1, 2]} + {max([3, 4])} END call assert_equal('xxx', code) let lines =<< trim LINES let text =<< eval trim END ! let b = { END LINES ! call v9.CheckScriptFailure(lines, 'E1279:') let lines =<< trim LINES let text =<< eval trim END ! let b = {abc END LINES ! call v9.CheckScriptFailure(lines, 'E1279:') let lines =<< trim LINES let text =<< eval trim END ! let b = {} END LINES call v9.CheckScriptFailure(lines, 'E15:') *************** *** 571,577 **** " skipped heredoc if 0 let msg =<< trim eval END ! n is: `=n` END endif --- 582,588 ---- " skipped heredoc if 0 let msg =<< trim eval END ! n is: {n} END endif *************** *** 583,589 **** let lines =<< trim END let Xvar =<< eval CODE let a = 1 ! let b = `=5+` let c = 2 CODE let g:Count += 1 --- 594,600 ---- let lines =<< trim END let Xvar =<< eval CODE let a = 1 ! let b = {5+} let c = 2 CODE let g:Count += 1 *************** *** 592,601 **** let g:Count = 0 call assert_fails('source', 'E15:') call assert_equal(1, g:Count) ! call setline(3, 'let b = `=abc`') call assert_fails('source', 'E121:') call assert_equal(2, g:Count) ! call setline(3, 'let b = `=abc` + `=min([9, 4])` + 2') call assert_fails('source', 'E121:') call assert_equal(3, g:Count) call assert_equal('test', g:Xvar) --- 603,612 ---- let g:Count = 0 call assert_fails('source', 'E15:') call assert_equal(1, g:Count) ! call setline(3, 'let b = {abc}') call assert_fails('source', 'E121:') call assert_equal(2, g:Count) ! call setline(3, 'let b = {abc} + {min([9, 4])} + 2') call assert_fails('source', 'E121:') call assert_equal(3, g:Count) call assert_equal('test', g:Xvar) *** ../vim-8.2.4882/src/testdir/test_vim9_assign.vim 2022-04-21 23:29:58.948561831 +0100 --- src/testdir/test_vim9_assign.vim 2022-05-06 13:04:08.837647084 +0100 *************** *** 2670,2679 **** var a3 = "3" var a4 = "" var code =<< trim eval END ! var a = `=5 + 10` ! var b = `=min([10, 6])` + `=max([4, 6])` ! var c = "`=s`" ! var d = x`=a1`x`=a2`x`=a3`x`=a4` END assert_equal(['var a = 15', 'var b = 6 + 6', 'var c = "local"', 'var d = x1x2x3x'], code) CODE --- 2670,2679 ---- var a3 = "3" var a4 = "" var code =<< trim eval END ! var a = {5 + 10} ! var b = {min([10, 6])} + {max([4, 6])} ! var c = "{s}" ! var d = x{a1}x{a2}x{a3}x{a4} END assert_equal(['var a = 15', 'var b = 6 + 6', 'var c = "local"', 'var d = x1x2x3x'], code) CODE *************** *** 2681,2687 **** lines =<< trim CODE var code =<< eval trim END ! var s = "`=$SOME_ENV_VAR`" END assert_equal(['var s = "somemore"'], code) CODE --- 2681,2687 ---- lines =<< trim CODE var code =<< eval trim END ! var s = "{$SOME_ENV_VAR}" END assert_equal(['var s = "somemore"'], code) CODE *************** *** 2689,2695 **** lines =<< trim CODE var code =<< eval END ! var s = "`=$SOME_ENV_VAR`" END assert_equal([' var s = "somemore"'], code) CODE --- 2689,2695 ---- lines =<< trim CODE var code =<< eval END ! var s = "{$SOME_ENV_VAR}" END assert_equal([' var s = "somemore"'], code) CODE *************** *** 2697,2730 **** lines =<< trim CODE var code =<< eval trim END ! let a = `abc` ! let b = `=g:someVar` ! let c = ` END ! assert_equal(['let a = `abc`', 'let b = X', 'let c = `'], code) CODE v9.CheckDefAndScriptSuccess(lines) lines =<< trim LINES var text =<< eval trim END ! let b = `= END LINES ! v9.CheckDefAndScriptFailure(lines, ['E1143: Empty expression: ""', 'E1083: Missing backtick']) lines =<< trim LINES var text =<< eval trim END ! let b = `=abc END LINES ! v9.CheckDefAndScriptFailure(lines, ['E1001: Variable not found: abc', 'E1083: Missing backtick']) lines =<< trim LINES var text =<< eval trim END ! let b = `=` END LINES ! v9.CheckDefAndScriptFailure(lines, ['E1015: Name expected: `', 'E15: Invalid expression: "`"']) enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker --- 2697,2730 ---- lines =<< trim CODE var code =<< eval trim END ! let a = {{abc}} ! let b = {g:someVar} ! let c = {{ END ! assert_equal(['let a = {abc}', 'let b = X', 'let c = {'], code) CODE v9.CheckDefAndScriptSuccess(lines) lines =<< trim LINES var text =<< eval trim END ! let b = { END LINES ! v9.CheckDefAndScriptFailure(lines, "E1279: Missing '}'") lines =<< trim LINES var text =<< eval trim END ! let b = {abc END LINES ! v9.CheckDefAndScriptFailure(lines, "E1279: Missing '}'") lines =<< trim LINES var text =<< eval trim END ! let b = {} END LINES ! v9.CheckDefAndScriptFailure(lines, 'E15: Invalid expression: "}"') enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker *** ../vim-8.2.4882/src/testdir/test_vim9_disassemble.vim 2022-05-05 13:52:59.416192105 +0100 --- src/testdir/test_vim9_disassemble.vim 2022-05-06 12:36:09.575276491 +0100 *************** *** 2840,2845 **** --- 2840,2864 ---- delfunc g:ThatFunc enddef + def s:MakeString(x: number): string + return $"x={x} x^2={x * x}" + enddef + def Test_disassemble_string_interp() + var instr = execute('disassemble s:MakeString') + assert_match('MakeString\_s*' .. + 'return $"x={x} x^2={x \* x}"\_s*' .. + '0 PUSHS "x="\_s*' .. + '1 LOAD arg\[-1\]\_s*' .. + '2 2STRING stack\[-1\]\_s*' .. + '3 PUSHS " x^2="\_s*' .. + '4 LOAD arg\[-1\]\_s*' .. + '5 LOAD arg\[-1\]\_s*' .. + '6 OPNR \*\_s*' .. + '7 2STRING stack\[-1\]\_s*' .. + '8 CONCAT size 4\_s*' .. + '9 RETURN\_s*', + instr) + enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker *** ../vim-8.2.4882/src/version.c 2022-05-06 12:15:33.864427755 +0100 --- src/version.c 2022-05-06 12:43:03.766853770 +0100 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 4883, /**/ -- Corduroy pillows: They're making headlines! /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///