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