diff src/vim9compile.c @ 28560:060fc3b69697 v8.2.4804

patch 8.2.4804: expression in heredoc doesn't work for compiled function Commit: https://github.com/vim/vim/commit/1fc6ea9bf3548b578676331f5eac1f7e0611c268 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Thu Apr 21 23:30:15 2022 +0100 patch 8.2.4804: expression in heredoc doesn't work for compiled function Problem: Expression in heredoc doesn't work for compiled function. Solution: Implement compiling the heredoc expressions. (Yegappan Lakshmanan, closes #10232)
author Bram Moolenaar <Bram@vim.org>
date Fri, 22 Apr 2022 00:45:04 +0200
parents bd1dcc605e58
children d550054e1328
line wrap: on
line diff
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -595,6 +595,7 @@ find_imported_in_script(char_u *name, si
 
 /*
  * Find "name" in imported items of the current script.
+ * If "len" is 0 use any length that works.
  * If "load" is TRUE and the script was not loaded yet, load it now.
  */
     imported_T *
@@ -968,6 +969,83 @@ 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.
+ */
+    int
+compile_heredoc_string(char_u *str, int evalstr, cctx_T *cctx)
+{
+    char_u	*p;
+    char_u	*val;
+
+    if (cctx->ctx_skip == SKIP_YES)
+	return OK;
+
+    if (evalstr && (p = (char_u *)strstr((char *)str, "`=")) != NULL)
+    {
+	char_u	*start = str;
+
+	// 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.
+	val = vim_strsave((char_u *)"");
+	generate_PUSHS(cctx, &val);
+
+	for (;;)
+	{
+	    if (p > start)
+	    {
+		// literal string before the expression
+		val = vim_strnsave(start, p - start);
+		generate_PUSHS(cctx, &val);
+		generate_instr_drop(cctx, ISN_CONCAT, 1);
+	    }
+	    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);
+	    generate_instr_drop(cctx, ISN_CONCAT, 1);
+
+	    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);
+		    generate_instr_drop(cctx, ISN_CONCAT, 1);
+		}
+		break;
+	    }
+	}
+    }
+    else
+    {
+	// literal string
+	val = vim_strsave(str);
+	generate_PUSHS(cctx, &val);
+    }
+
+    return OK;
+}
+
+/*
  * Return the length of an assignment operator, or zero if there isn't one.
  */
     int
@@ -1946,25 +2024,14 @@ compile_assignment(char_u *arg, exarg_T 
     if (heredoc)
     {
 	list_T	   *l;
-	listitem_T *li;
 
 	// [let] varname =<< [trim] {end}
 	eap->getline = exarg_getline;
 	eap->cookie = cctx;
-	l = heredoc_get(eap, op + 3, FALSE);
+	l = heredoc_get(eap, op + 3, FALSE, TRUE);
 	if (l == NULL)
 	    return NULL;
 
-	if (cctx->ctx_skip != SKIP_YES)
-	{
-	    // Push each line and the create the list.
-	    FOR_ALL_LIST_ITEMS(l, li)
-	    {
-		generate_PUSHS(cctx, &li->li_tv.vval.v_string);
-		li->li_tv.vval.v_string = NULL;
-	    }
-	    generate_NEWLIST(cctx, l->lv_len, FALSE);
-	}
 	list_free(l);
 	p += STRLEN(p);
 	end = p;