diff src/vim9script.c @ 20339:7587d892c00c v8.2.0725

patch 8.2.0725: Vim9: cannot call a function declared later in Vim9 script Commit: https://github.com/vim/vim/commit/09689a02840be40fa7bb10b1921fb5bc5b2908f1 Author: Bram Moolenaar <Bram@vim.org> Date: Sat May 9 22:50:08 2020 +0200 patch 8.2.0725: Vim9: cannot call a function declared later in Vim9 script Problem: Vim9: cannot call a function declared later in Vim9 script. Solution: Make two passes through the script file.
author Bram Moolenaar <Bram@vim.org>
date Sat, 09 May 2020 23:00:04 +0200
parents 63cc54100ae4
children 680296770464
line wrap: on
line diff
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -32,7 +32,11 @@ in_vim9script(void)
     void
 ex_vim9script(exarg_T *eap)
 {
-    scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
+    scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
+    garray_T	    *gap;
+    garray_T	    func_ga;
+    int		    idx;
+    ufunc_T	    *ufunc;
 
     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
     {
@@ -47,12 +51,97 @@ ex_vim9script(exarg_T *eap)
     current_sctx.sc_version = SCRIPT_VERSION_VIM9;
     si->sn_version = SCRIPT_VERSION_VIM9;
     si->sn_had_command = TRUE;
+    ga_init2(&func_ga, sizeof(ufunc_T *), 20);
 
     if (STRCMP(p_cpo, CPO_VIM) != 0)
     {
 	si->sn_save_cpo = p_cpo;
 	p_cpo = vim_strsave((char_u *)CPO_VIM);
     }
+
+    // Make a pass through the script to find:
+    // - function declarations
+    // - variable and constant declarations
+    // - imports
+    // The types are recognized, so that they can be used when compiling a
+    // function.
+    gap = source_get_line_ga(eap->cookie);
+    for (;;)
+    {
+	char_u	    *line;
+	char_u	    *p;
+
+	if (ga_grow(gap, 1) == FAIL)
+	    return;
+	line = eap->getline(':', eap->cookie, 0, TRUE);
+	if (line == NULL)
+	    break;
+	((char_u **)(gap->ga_data))[gap->ga_len++] = line;
+	line = skipwhite(line);
+	p = line;
+	if (checkforcmd(&p, "function", 2) || checkforcmd(&p, "def", 3))
+	{
+	    int		    lnum_start = SOURCING_LNUM - 1;
+
+	    // Handle :function and :def by calling def_function().
+	    // It will read upto the matching :endded or :endfunction.
+	    eap->cmdidx = *line == 'f' ? CMD_function : CMD_def;
+	    eap->cmd = line;
+	    eap->arg = p;
+	    eap->forceit = FALSE;
+	    ufunc = def_function(eap, NULL, NULL, FALSE);
+
+	    if (ufunc != NULL && *line == 'd' && ga_grow(&func_ga, 1) == OK)
+	    {
+		// Add the function to the list of :def functions, so that it
+		// can be referenced by index.  It's compiled below.
+		add_def_function(ufunc);
+		((ufunc_T **)(func_ga.ga_data))[func_ga.ga_len++] = ufunc;
+	    }
+
+	    // Store empty lines in place of the function, we don't need to
+	    // process it again.
+	    vim_free(((char_u **)(gap->ga_data))[--gap->ga_len]);
+	    if (ga_grow(gap, SOURCING_LNUM - lnum_start) == OK)
+		while (lnum_start < SOURCING_LNUM)
+		{
+		    // getsourceline() will skip over NULL lines.
+		    ((char_u **)(gap->ga_data))[gap->ga_len++] = NULL;
+		    ++lnum_start;
+		}
+	}
+	else if (checkforcmd(&p, "let", 3) || checkforcmd(&p, "const", 4))
+	{
+	    eap->cmd = line;
+	    eap->arg = p;
+	    eap->forceit = FALSE;
+	    eap->cmdidx = *line == 'l' ? CMD_let: CMD_const;
+
+	    // The command will be executed again, it's OK to redefine the
+	    // variable then.
+	    ex_let_const(eap, TRUE);
+	}
+	else if (checkforcmd(&p, "import", 3))
+	{
+	    eap->arg = p;
+	    ex_import(eap);
+
+	    // Store empty line, we don't need to process the command again.
+	    vim_free(((char_u **)(gap->ga_data))[--gap->ga_len]);
+	    ((char_u **)(gap->ga_data))[gap->ga_len++] = NULL;
+	}
+    }
+
+    // Compile the :def functions.
+    for (idx = 0; idx < func_ga.ga_len; ++idx)
+    {
+	ufunc = ((ufunc_T **)(func_ga.ga_data))[idx];
+	compile_def_function(ufunc, FALSE, NULL);
+    }
+    ga_clear(&func_ga);
+
+    // Return to process the commands at the script level.
+    source_use_line_ga(eap->cookie);
 }
 
 /*
@@ -64,7 +153,7 @@ ex_vim9script(exarg_T *eap)
  * ":export {Name, ...}"
  */
     void
-ex_export(exarg_T *eap UNUSED)
+ex_export(exarg_T *eap)
 {
     if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
     {