mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
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)
This commit is contained in:
@@ -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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (count > 1)
|
||||
generate_CONCAT(cctx, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
// literal string
|
||||
val = vim_strsave(str);
|
||||
generate_PUSHS(cctx, &val);
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user