Patch 8.2.4804
Problem:    Expression in heredoc doesn't work for compiled function.
Solution:   Implement compiling the heredoc expressions. (Yegappan Lakshmanan,
            closes #10232)
Files:      runtime/doc/eval.txt, src/evalvars.c, src/proto/evalvars.pro,
            src/ex_getln.c, src/vim9compile.c, src/proto/vim9compile.pro,
            src/testdir/test_vim9_assign.vim


*** ../vim-8.2.4803/runtime/doc/eval.txt        2022-04-17 12:46:50.101294003 
+0100
--- runtime/doc/eval.txt        2022-04-21 23:19:41.837086804 +0100
***************
*** 3210,3217 ****
                        expression evaluation fails, then the assignment fails.
                        once the "`=" has been found {expr} and a backtick
                        must follow.  {expr} cannot be empty.
-                       Currenty, in a compiled function {expr} is evaluated
-                       when compiling the function, THIS WILL CHANGE.
  
                        {endmarker} must not contain white space.
                        {endmarker} cannot start with a lower case character.
--- 3247,3252 ----
*** ../vim-8.2.4803/src/evalvars.c      2022-04-18 15:45:19.704436521 +0100
--- src/evalvars.c      2022-04-21 23:25:57.708784208 +0100
***************
*** 673,688 ****
   *
   * The {marker} is a string. If the optional 'trim' word is supplied before 
the
   * marker, then the leading indentation before the lines (matching the
!  * indentation in the 'cmd' line) is stripped.
   *
   * When getting lines for an embedded script (e.g. python, lua, perl, ruby,
!  * tcl, mzscheme), script_get is set to TRUE. In this case, if the marker is
   * missing, then '.' is accepted as a marker.
   *
   * Returns a List with {lines} or NULL on failure.
   */
      list_T *
! heredoc_get(exarg_T *eap, char_u *cmd, int script_get)
  {
      char_u    *theline = NULL;
      char_u    *marker;
--- 673,693 ----
   *
   * The {marker} is a string. If the optional 'trim' word is supplied before 
the
   * marker, then the leading indentation before the lines (matching the
!  * indentation in the "cmd" line) is stripped.
   *
   * When getting lines for an embedded script (e.g. python, lua, perl, ruby,
!  * tcl, mzscheme), "script_get" is set to TRUE. In this case, if the marker is
   * missing, then '.' is accepted as a marker.
   *
+  * When compiling a heredoc assignment to a variable in a Vim9 def function,
+  * "vim9compile" is set to TRUE. In this case, instead of generating a list of
+  * string values from the heredoc, vim9 instructions are generated.  On 
success
+  * the returned list will be empty.
+  *
   * Returns a List with {lines} or NULL on failure.
   */
      list_T *
! heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile)
  {
      char_u    *theline = NULL;
      char_u    *marker;
***************
*** 696,701 ****
--- 701,708 ----
      int               comment_char = in_vim9script() ? '#' : '"';
      int               evalstr = FALSE;
      int               eval_failed = FALSE;
+     cctx_T    *cctx = vim9compile ? eap->cookie : NULL;
+     int               count = 0;
  
      if (eap->getline == NULL)
      {
***************
*** 816,840 ****
                    break;
  
        str = theline + ti;
!       if (evalstr)
        {
!           str = eval_all_expr_in_str(str);
!           if (str == NULL)
            {
!               // expression evaluation failed
!               eval_failed = TRUE;
!               continue;
            }
!           vim_free(theline);
!           theline = str;
        }
  
!       if (list_append_string(l, str, -1) == FAIL)
!           break;
      }
      vim_free(theline);
      vim_free(text_indent);
  
      if (eval_failed)
      {
        // expression evaluation in the heredoc failed
--- 823,863 ----
                    break;
  
        str = theline + ti;
!       if (vim9compile)
        {
!           if (compile_heredoc_string(str, evalstr, cctx) == FAIL)
            {
!               vim_free(theline);
!               vim_free(text_indent);
!               return FAIL;
            }
!           count++;
        }
+       else
+       {
+           if (evalstr)
+           {
+               str = eval_all_expr_in_str(str);
+               if (str == NULL)
+               {
+                   // expression evaluation failed
+                   eval_failed = TRUE;
+                   continue;
+               }
+               vim_free(theline);
+               theline = str;
+           }
  
!           if (list_append_string(l, str, -1) == FAIL)
!               break;
!       }
      }
      vim_free(theline);
      vim_free(text_indent);
  
+     if (vim9compile && cctx->ctx_skip != SKIP_YES && !eval_failed)
+       generate_NEWLIST(cctx, count, FALSE);
+ 
      if (eval_failed)
      {
        // expression evaluation in the heredoc failed
***************
*** 986,992 ****
        long    cur_lnum = SOURCING_LNUM;
  
        // HERE document
!       l = heredoc_get(eap, expr + 3, FALSE);
        if (l != NULL)
        {
            rettv_list_set(&rettv, l);
--- 1009,1015 ----
        long    cur_lnum = SOURCING_LNUM;
  
        // HERE document
!       l = heredoc_get(eap, expr + 3, FALSE, FALSE);
        if (l != NULL)
        {
            rettv_list_set(&rettv, l);
*** ../vim-8.2.4803/src/proto/evalvars.pro      2022-03-20 17:46:01.797053490 
+0000
--- src/proto/evalvars.pro      2022-04-21 23:19:41.837086804 +0100
***************
*** 13,19 ****
  int get_spellword(list_T *list, char_u **pp);
  void prepare_vimvar(int idx, typval_T *save_tv);
  void restore_vimvar(int idx, typval_T *save_tv);
! list_T *heredoc_get(exarg_T *eap, char_u *cmd, int script_get);
  void ex_var(exarg_T *eap);
  void ex_let(exarg_T *eap);
  int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, int 
var_count, int flags, char_u *op);
--- 13,19 ----
  int get_spellword(list_T *list, char_u **pp);
  void prepare_vimvar(int idx, typval_T *save_tv);
  void restore_vimvar(int idx, typval_T *save_tv);
! list_T *heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int 
vim9compile);
  void ex_var(exarg_T *eap);
  void ex_let(exarg_T *eap);
  int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, int 
var_count, int flags, char_u *op);
*** ../vim-8.2.4803/src/ex_getln.c      2022-04-15 13:53:30.048708690 +0100
--- src/ex_getln.c      2022-04-21 23:19:41.837086804 +0100
***************
*** 4605,4611 ****
        return NULL;
      cmd += 2;
  
!     l = heredoc_get(eap, cmd, TRUE);
      if (l == NULL)
        return NULL;
  
--- 4605,4611 ----
        return NULL;
      cmd += 2;
  
!     l = heredoc_get(eap, cmd, TRUE, FALSE);
      if (l == NULL)
        return NULL;
  
*** ../vim-8.2.4803/src/vim9compile.c   2022-04-02 19:43:53.927491819 +0100
--- src/vim9compile.c   2022-04-21 23:28:38.244637908 +0100
***************
*** 595,600 ****
--- 595,601 ----
  
  /*
   * 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,973 ****
--- 969,1051 ----
  }
  
  /*
+  * 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,1970 ****
      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);
        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;
--- 2024,2037 ----
      if (heredoc)
      {
        list_T     *l;
  
        // [let] varname =<< [trim] {end}
        eap->getline = exarg_getline;
        eap->cookie = cctx;
!       l = heredoc_get(eap, op + 3, FALSE, TRUE);
        if (l == NULL)
            return NULL;
  
        list_free(l);
        p += STRLEN(p);
        end = p;
*** ../vim-8.2.4803/src/proto/vim9compile.pro   2022-04-02 19:43:53.927491819 
+0100
--- src/proto/vim9compile.pro   2022-04-21 23:19:41.837086804 +0100
***************
*** 16,21 ****
--- 16,22 ----
  int may_get_next_line_error(char_u *whitep, char_u **arg, cctx_T *cctx);
  void fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx);
  int func_needs_compiling(ufunc_T *ufunc, compiletype_T compile_type);
+ int compile_heredoc_string(char_u *str, int evalstr, cctx_T *cctx);
  int assignment_len(char_u *p, int *heredoc);
  void vim9_declare_error(char_u *name);
  int get_var_dest(char_u *name, assign_dest_T *dest, cmdidx_T cmdidx, int 
*option_scope, int *vimvaridx, type_T **type, cctx_T *cctx);
*** ../vim-8.2.4803/src/testdir/test_vim9_assign.vim    2022-04-17 
12:46:50.101294003 +0100
--- src/testdir/test_vim9_assign.vim    2022-04-21 23:19:41.841086802 +0100
***************
*** 1821,1830 ****
  enddef
  
  def Test_heredoc()
!   var lines =<< trim END # comment
!     text
    END
!   assert_equal(['text'], lines)
  
    v9.CheckDefFailure(['var lines =<< trim END X', 'END'], 'E488:')
    v9.CheckDefFailure(['var lines =<< trim END " comment', 'END'], 'E488:')
--- 1821,1851 ----
  enddef
  
  def Test_heredoc()
!   # simple heredoc
!   var lines =<< trim END
!     var text =<< trim TEXT # comment
!       abc
!     TEXT
!     assert_equal(['abc'], text)
!   END
!   v9.CheckDefAndScriptSuccess(lines)
! 
!   # empty heredoc
!   lines =<< trim END
!      var text =<< trim TEXT
!      TEXT
!      assert_equal([], text)
    END
!   v9.CheckDefAndScriptSuccess(lines)
! 
!   # heredoc with a single empty line
!   lines =<< trim END
!      var text =<< trim TEXT
! 
!      TEXT
!      assert_equal([''], text)
!   END
!   v9.CheckDefAndScriptSuccess(lines)
  
    v9.CheckDefFailure(['var lines =<< trim END X', 'END'], 'E488:')
    v9.CheckDefFailure(['var lines =<< trim END " comment', 'END'], 'E488:')
***************
*** 2642,2692 ****
  " Test for heredoc with Vim expressions.
  " This messes up highlighting, keep it near the end.
  def Test_heredoc_expr()
!   var code =<< trim eval END
!     var a = `=5 + 10`
!     var b = `=min([10, 6])` + `=max([4, 6])`
!   END
!   assert_equal(['var a = 15', 'var b = 6 + 6'], code)
  
!   code =<< eval trim END
!     var s = "`=$SOME_ENV_VAR`"
!   END
!   assert_equal(['var s = "somemore"'], code)
  
!   code =<< eval END
!     var s = "`=$SOME_ENV_VAR`"
! END
!   assert_equal(['    var s = "somemore"'], code)
  
!   code =<< eval trim END
!     let a = `abc`
!     let b = `=g:someVar`
!     let c = `
!   END
!   assert_equal(['let a = `abc`', 'let b = X', 'let c = `'], code)
  
!   var lines =<< trim LINES
        var text =<< eval trim END
          let b = `=
        END
    LINES
!   v9.CheckDefAndScriptFailure(lines, 'E1083:')
  
    lines =<< trim LINES
        var text =<< eval trim END
          let b = `=abc
        END
    LINES
!   v9.CheckDefAndScriptFailure(lines, 'E1083:')
  
    lines =<< trim LINES
        var text =<< eval trim END
          let b = `=`
        END
    LINES
!   v9.CheckDefAndScriptFailure(lines, 'E15:')
  enddef
  
- 
- 
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
--- 2663,2730 ----
  " Test for heredoc with Vim expressions.
  " This messes up highlighting, keep it near the end.
  def Test_heredoc_expr()
!   var lines =<< trim CODE
!     var s = "local"
!     var a1 = "1"
!     var a2 = "2"
!     var a3 = "3"
!     var a4 = ""
!     var code =<< trim eval END
!       var a = `=5 + 10`
!       var b = `=min([10, 6])` + `=max([4, 6])`
!       var c = "`=s`"
!       var d = x`=a1`x`=a2`x`=a3`x`=a4`
!     END
!     assert_equal(['var a = 15', 'var b = 6 + 6', 'var c = "local"', 'var d = 
x1x2x3x'], code)
!   CODE
!   v9.CheckDefAndScriptSuccess(lines)
  
!   lines =<< trim CODE
!     var code =<< eval trim END
!       var s = "`=$SOME_ENV_VAR`"
!     END
!     assert_equal(['var s = "somemore"'], code)
!   CODE
!   v9.CheckDefAndScriptSuccess(lines)
  
!   lines =<< trim CODE
!     var code =<< eval END
!       var s = "`=$SOME_ENV_VAR`"
!     END
!     assert_equal(['  var s = "somemore"'], code)
!   CODE
!   v9.CheckDefAndScriptSuccess(lines)
  
!   lines =<< trim CODE
!     var code =<< eval trim END
!       let a = `abc`
!       let b = `=g:someVar`
!       let c = `
!     END
!     assert_equal(['let a = `abc`', 'let b = X', 'let c = `'], code)
!   CODE
!   v9.CheckDefAndScriptSuccess(lines)
  
!   lines =<< trim LINES
        var text =<< eval trim END
          let b = `=
        END
    LINES
!   v9.CheckDefAndScriptFailure(lines, ['E1143: Empty expression: ""', 'E1083: 
Missing backtick'])
  
    lines =<< trim LINES
        var text =<< eval trim END
          let b = `=abc
        END
    LINES
!   v9.CheckDefAndScriptFailure(lines, ['E1001: Variable not found: abc', 
'E1083: Missing backtick'])
  
    lines =<< trim LINES
        var text =<< eval trim END
          let b = `=`
        END
    LINES
!   v9.CheckDefAndScriptFailure(lines, ['E1015: Name expected: `', 'E15: 
Invalid expression: "`"'])
  enddef
  
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-8.2.4803/src/version.c       2022-04-21 22:52:07.062317208 +0100
--- src/version.c       2022-04-21 23:22:38.984952863 +0100
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     4804,
  /**/

-- 
To define recursion, we must first define recursion.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220421223056.84C431C40C6%40moolenaar.net.

Raspunde prin e-mail lui