1
0
forked from aniani/vim

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:
LemonBoy
2022-05-06 13:14:50 +01:00
committed by Bram Moolenaar
parent e7d6dbc572
commit 2eaef106e4
16 changed files with 364 additions and 137 deletions

View File

@@ -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;
}