patch 8.2.0695: Vim9: cannot define a function inside a function
Problem: Vim9: cannot define a function inside a function.
Solution: Initial support for :def inside :def.
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 300adfb..7e700d5 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -101,6 +101,7 @@
int lv_from_outer; // when TRUE using ctx_outer scope
int lv_const; // when TRUE cannot be assigned to
int lv_arg; // when TRUE this is an argument
+ int lv_func_idx; // for nested function
} lvar_T;
/*
@@ -2615,6 +2616,7 @@
int error = FCERR_NONE;
ufunc_T *ufunc;
int res = FAIL;
+ lvar_T *lvar;
if (varlen >= sizeof(namebuf))
{
@@ -2641,6 +2643,16 @@
goto theend;
}
+ // Check if the name is a nested function.
+ lvar = lookup_local(namebuf, varlen, cctx);
+ if (lvar != NULL && lvar->lv_func_idx > 0)
+ {
+ dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ + lvar->lv_func_idx;
+ res = generate_CALL(cctx, dfunc->df_ufunc, argcount);
+ goto theend;
+ }
+
// If we can find the function by name generate the right call.
ufunc = find_func(name, FALSE, cctx);
if (ufunc != NULL)
@@ -4049,6 +4061,64 @@
}
/*
+ * Get a line from the compilation context, compatible with exarg_T getline().
+ * Return a pointer to the line in allocated memory.
+ * Return NULL for end-of-file or some error.
+ */
+ static char_u *
+exarg_getline(
+ int c UNUSED,
+ void *cookie,
+ int indent UNUSED,
+ int do_concat UNUSED)
+{
+ cctx_T *cctx = (cctx_T *)cookie;
+
+ if (cctx->ctx_lnum == cctx->ctx_ufunc->uf_lines.ga_len)
+ {
+ iemsg("Heredoc got to end");
+ return NULL;
+ }
+ ++cctx->ctx_lnum;
+ return vim_strsave(((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)
+ [cctx->ctx_lnum]);
+}
+
+/*
+ * Compile a nested :def command.
+ */
+ static char_u *
+compile_nested_function(exarg_T *eap, cctx_T *cctx)
+{
+ char_u *name_start = eap->arg;
+ char_u *name_end = to_name_end(eap->arg, FALSE);
+ char_u *name = get_lambda_name();
+ lvar_T *lvar;
+ ufunc_T *ufunc;
+
+ eap->arg = name_end;
+ eap->getline = exarg_getline;
+ eap->cookie = cctx;
+ eap->skip = cctx->ctx_skip == TRUE;
+ eap->forceit = FALSE;
+ ufunc = def_function(eap, name, cctx);
+
+ if (ufunc == NULL)
+ return NULL;
+
+ // Define a local variable for the function, but change the index to -1 to
+ // mark it as a function name.
+ lvar = reserve_local(cctx, name_start, name_end - name_start,
+ TRUE, &t_func_unknown);
+ lvar->lv_idx = 0;
+ ++cctx->ctx_locals_count; // doesn't count as a local variable
+ lvar->lv_func_idx = ufunc->uf_dfunc_idx;
+
+ // TODO: warning for trailing?
+ return (char_u *)"";
+}
+
+/*
* Return the length of an assignment operator, or zero if there isn't one.
*/
int
@@ -4077,30 +4147,6 @@
NULL
};
-/*
- * Get a line for "=<<".
- * Return a pointer to the line in allocated memory.
- * Return NULL for end-of-file or some error.
- */
- static char_u *
-heredoc_getline(
- int c UNUSED,
- void *cookie,
- int indent UNUSED,
- int do_concat UNUSED)
-{
- cctx_T *cctx = (cctx_T *)cookie;
-
- if (cctx->ctx_lnum == cctx->ctx_ufunc->uf_lines.ga_len)
- {
- iemsg("Heredoc got to end");
- return NULL;
- }
- ++cctx->ctx_lnum;
- return vim_strsave(((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)
- [cctx->ctx_lnum]);
-}
-
typedef enum {
dest_local,
dest_option,
@@ -4394,7 +4440,7 @@
listitem_T *li;
// [let] varname =<< [trim] {end}
- eap->getline = heredoc_getline;
+ eap->getline = exarg_getline;
eap->cookie = cctx;
l = heredoc_get(eap, op + 3, FALSE);
@@ -6299,9 +6345,12 @@
switch (ea.cmdidx)
{
case CMD_def:
+ ea.arg = p;
+ line = compile_nested_function(&ea, &cctx);
+ break;
+
case CMD_function:
- // TODO: Nested function
- emsg("Nested function not implemented yet");
+ emsg(_("E1086: Cannot use :function inside :def"));
goto erret;
case CMD_return: