diff options
author | LemonBoy <thatlemon@gmail.com> | 2022-05-06 13:14:50 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2022-05-06 13:14:50 +0100 |
commit | 2eaef106e4a7fc9dc74a7e672b5f550ec1f9786e (patch) | |
tree | 543832333e5ef90b9e51477457a7e65572274227 /src/vim9compile.c | |
parent | e7d6dbc5721342e3d6b04cf285e4510b5569e707 (diff) | |
download | vim-git-2eaef106e4a7fc9dc74a7e672b5f550ec1f9786e.tar.gz |
patch 8.2.4883: string interpolation only works in heredocv8.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)
Diffstat (limited to 'src/vim9compile.c')
-rw-r--r-- | src/vim9compile.c | 133 |
1 files changed, 78 insertions, 55 deletions
diff --git a/src/vim9compile.c b/src/vim9compile.c index b0cf6a787..9b7108b83 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -969,80 +969,103 @@ 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) { - char_u *start = str; - int count = 0; + // Literal string, possibly empty. + val = *str != NUL ? vim_strsave(str) : NULL; + return generate_PUSHS(cctx, &val); + } - // 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 (;;) + // 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]) { - if (p > start) - { - // literal string before the expression - val = vim_strnsave(start, p - start); - generate_PUSHS(cctx, &val); - count++; - } - p += 2; + // 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; + } - // evaluate the Vim expression and convert the result to string. - if (compile_expr0(&p, cctx) == 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++; + ++count; + } - p = skipwhite(p); - if (*p != '`') - { - emsg(_(e_missing_backtick)); - return FAIL; - } - start = p + 1; + if (*p == NUL) + break; - 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 (escaped_brace) + { + // Skip the second brace. + ++p; + continue; } - if (count > 1) - generate_CONCAT(cctx, count); - } - else - { - // literal string - val = vim_strsave(str); - generate_PUSHS(cctx, &val); + // 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; } |