Mercurial > vim
diff src/vim9compile.c @ 28718:723c7d940cba
patch 8.2.4883: string interpolation only works in heredoc
Commit: https://github.com/vim/vim/commit/2eaef106e4a7fc9dc74a7e672b5f550ec1f9786e
Author: LemonBoy <thatlemon@gmail.com>
Date: Fri May 6 13:14:50 2022 +0100
patch 8.2.4883: string interpolation only works in heredoc
Problem: String interpolation only works in heredoc.
Solution: Support interpolated strings. Use syntax for heredoc consistent
with strings, similar to C#. (closes #10327)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Fri, 06 May 2022 14:15:03 +0200 |
parents | 7f12fe85ed8b |
children | fb86364889a4 |
line wrap: on
line diff
--- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -969,79 +969,102 @@ theend: } /* - * Compile a heredoc 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 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. + * 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_heredoc_string(char_u *str, int evalstr, cctx_T *cctx) +compile_all_expr_in_str(char_u *str, int evalstr, cctx_T *cctx) { - char_u *p; + char_u *p = str; char_u *val; + char_u save_c; + int count = 0; if (cctx->ctx_skip == SKIP_YES) return OK; - if (evalstr && (p = (char_u *)strstr((char *)str, "`=")) != NULL) + 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 *start = str; - int count = 0; - - // Need to evaluate expressions of the form `=<expr>` 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 (;;) + 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]) { - 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) + // 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; - 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; - } + ++count; + } + + if (*p == NUL) + break; + + if (escaped_brace) + { + // Skip the second brace. + ++p; + continue; } - if (count > 1) - generate_CONCAT(cctx, count); + // 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; } - else - { - // literal string - val = vim_strsave(str); - generate_PUSHS(cctx, &val); - } + + // Small optimization, if there's only a single piece skip the ISN_CONCAT. + if (count != 1) + return generate_CONCAT(cctx, count); return OK; }