summaryrefslogtreecommitdiff
path: root/src/vim9cmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vim9cmds.c')
-rw-r--r--src/vim9cmds.c130
1 files changed, 95 insertions, 35 deletions
diff --git a/src/vim9cmds.c b/src/vim9cmds.c
index 08f11a688..c9c222c47 100644
--- a/src/vim9cmds.c
+++ b/src/vim9cmds.c
@@ -347,6 +347,8 @@ new_scope(cctx_T *cctx, scopetype_T type)
cctx->ctx_scope = scope;
scope->se_type = type;
scope->se_local_count = cctx->ctx_locals.ga_len;
+ if (scope->se_outer != NULL)
+ scope->se_loop_depth = scope->se_outer->se_loop_depth;
return scope;
}
@@ -823,7 +825,9 @@ compile_for(char_u *arg_start, cctx_T *cctx)
scope_T *scope;
forscope_T *forscope;
lvar_T *loop_lvar; // loop iteration variable
+ int loop_lvar_idx;
lvar_T *funcref_lvar;
+ int funcref_lvar_idx;
lvar_T *var_lvar; // variable for "var"
type_T *vartype;
type_T *item_type = &t_any;
@@ -867,6 +871,12 @@ compile_for(char_u *arg_start, cctx_T *cctx)
scope = new_scope(cctx, FOR_SCOPE);
if (scope == NULL)
return NULL;
+ if (scope->se_loop_depth == MAX_LOOP_DEPTH)
+ {
+ emsg(_(e_loop_nesting_too_deep));
+ return NULL;
+ }
+ ++scope->se_loop_depth;
forscope = &scope->se_u.se_for;
// Reserve a variable to store the loop iteration counter and initialize it
@@ -877,7 +887,9 @@ compile_for(char_u *arg_start, cctx_T *cctx)
drop_scope(cctx);
return NULL; // out of memory
}
- generate_STORENR(cctx, loop_lvar->lv_idx, -1);
+ // get the index before a following reserve_local() makes the lval invalid
+ loop_lvar_idx = loop_lvar->lv_idx;
+ generate_STORENR(cctx, loop_lvar_idx, -1);
// Reserve a variable to store ec_funcrefs.ga_len, used in ISN_ENDLOOP.
// The variable index is always the loop var index plus one.
@@ -888,6 +900,8 @@ compile_for(char_u *arg_start, cctx_T *cctx)
drop_scope(cctx);
return NULL; // out of memory
}
+ // get the index before a following reserve_local() makes the lval invalid
+ funcref_lvar_idx = funcref_lvar->lv_idx;
// compile "expr", it remains on the stack until "endfor"
arg = p;
@@ -951,7 +965,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
cctx->ctx_prev_lnum = save_prev_lnum;
}
- generate_FOR(cctx, loop_lvar->lv_idx);
+ generate_FOR(cctx, loop_lvar_idx);
arg = arg_start;
if (var_list)
@@ -1053,8 +1067,8 @@ compile_for(char_u *arg_start, cctx_T *cctx)
}
// remember the number of variables and closures, used for ENDLOOP
- compile_fill_loop_info(&forscope->fs_loop_info,
- funcref_lvar->lv_idx, cctx);
+ compile_fill_loop_info(&forscope->fs_loop_info, funcref_lvar_idx, cctx);
+ forscope->fs_loop_info.li_depth = scope->se_loop_depth - 1;
}
return arg_end;
@@ -1075,8 +1089,7 @@ compile_loop_end(loop_info_T *loop_info, cctx_T *cctx)
{
if (cctx->ctx_locals.ga_len > loop_info->li_local_count
&& cctx->ctx_closure_count > loop_info->li_closure_count)
- return generate_ENDLOOP(cctx, loop_info->li_funcref_idx,
- loop_info->li_local_count);
+ return generate_ENDLOOP(cctx, loop_info);
return OK;
}
@@ -1151,10 +1164,17 @@ compile_while(char_u *arg, cctx_T *cctx)
scope_T *scope;
whilescope_T *whilescope;
lvar_T *funcref_lvar;
+ int funcref_lvar_idx;
scope = new_scope(cctx, WHILE_SCOPE);
if (scope == NULL)
return NULL;
+ if (scope->se_loop_depth == MAX_LOOP_DEPTH)
+ {
+ emsg(_(e_loop_nesting_too_deep));
+ return NULL;
+ }
+ ++scope->se_loop_depth;
whilescope = &scope->se_u.se_while;
// "endwhile" jumps back here, one before when profiling or using cmdmods
@@ -1168,10 +1188,12 @@ compile_while(char_u *arg, cctx_T *cctx)
drop_scope(cctx);
return NULL; // out of memory
}
+ // get the index before a following reserve_local() makes the lval invalid
+ funcref_lvar_idx = funcref_lvar->lv_idx;
// remember the number of variables and closures, used for ENDLOOP
- compile_fill_loop_info(&whilescope->ws_loop_info,
- funcref_lvar->lv_idx, cctx);
+ compile_fill_loop_info(&whilescope->ws_loop_info, funcref_lvar_idx, cctx);
+ whilescope->ws_loop_info.li_depth = scope->se_loop_depth - 1;
// compile "expr"
if (compile_expr0(&p, cctx) == FAIL)
@@ -1193,7 +1215,7 @@ compile_while(char_u *arg, cctx_T *cctx)
// "while_end" is set when ":endwhile" is found
if (compile_jump_to_end(&whilescope->ws_end_label,
- JUMP_WHILE_FALSE, funcref_lvar->lv_idx, cctx) == FAIL)
+ JUMP_WHILE_FALSE, funcref_lvar_idx, cctx) == FAIL)
return FAIL;
}
@@ -1249,45 +1271,83 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
/*
* Get the current information about variables declared inside a loop.
- * Returns zero if there are none, otherwise the count.
- * "loop_var_idx" is then set to the index of the first variable.
+ * Returns TRUE if there are any and fills "lvi".
*/
- short
-get_loop_var_info(cctx_T *cctx, short *loop_var_idx)
+ int
+get_loop_var_info(cctx_T *cctx, loopvarinfo_T *lvi)
{
scope_T *scope = cctx->ctx_scope;
- int start_local_count;
+ int prev_local_count = 0;
- while (scope != NULL && scope->se_type != WHILE_SCOPE
+ CLEAR_POINTER(lvi);
+ for (;;)
+ {
+ loop_info_T *loopinfo;
+ int cur_local_last;
+ int start_local_count;
+
+ while (scope != NULL && scope->se_type != WHILE_SCOPE
&& scope->se_type != FOR_SCOPE)
- scope = scope->se_outer;
- if (scope == NULL)
- return 0;
+ scope = scope->se_outer;
+ if (scope == NULL)
+ break;
- if (scope->se_type == WHILE_SCOPE)
- start_local_count = scope->se_u.se_while.ws_loop_info.li_local_count;
- else
- start_local_count = scope->se_u.se_for.fs_loop_info.li_local_count;
- if (cctx->ctx_locals.ga_len > start_local_count)
- {
- *loop_var_idx = (short)start_local_count;
- return (short)(cctx->ctx_locals.ga_len - start_local_count);
+ if (scope->se_type == WHILE_SCOPE)
+ {
+ loopinfo = &scope->se_u.se_while.ws_loop_info;
+ // :while reserves one variable for funcref count
+ cur_local_last = loopinfo->li_local_count - 1;
+ }
+ else
+ {
+ loopinfo = &scope->se_u.se_for.fs_loop_info;
+ // :for reserves three variable: loop count, funcref count and loop
+ // var
+ cur_local_last = loopinfo->li_local_count - 3;
+ }
+
+ start_local_count = loopinfo->li_local_count;
+ if (cctx->ctx_locals.ga_len > start_local_count)
+ {
+ lvi->lvi_loop[loopinfo->li_depth].var_idx =
+ (short)start_local_count;
+ lvi->lvi_loop[loopinfo->li_depth].var_count =
+ (short)(cctx->ctx_locals.ga_len - start_local_count
+ - prev_local_count);
+ if (lvi->lvi_depth == 0)
+ lvi->lvi_depth = loopinfo->li_depth + 1;
+ }
+
+ scope = scope->se_outer;
+ prev_local_count = cctx->ctx_locals.ga_len - cur_local_last;
}
- return 0;
+ return lvi->lvi_depth > 0;
}
/*
- * Get the index of the first variable in a loop, if any.
- * Returns -1 if none.
+ * Get the index of the variable "idx" in a loop, if any.
*/
- int
-get_loop_var_idx(cctx_T *cctx)
+ void
+get_loop_var_idx(cctx_T *cctx, int idx, lvar_T *lvar)
{
- short loop_var_idx;
+ loopvarinfo_T lvi;
+
+ lvar->lv_loop_depth = -1;
+ lvar->lv_loop_idx = -1;
+ if (get_loop_var_info(cctx, &lvi))
+ {
+ int depth;
- if (get_loop_var_info(cctx, &loop_var_idx) > 0)
- return loop_var_idx;
- return -1;
+ for (depth = lvi.lvi_depth - 1; depth >= 0; --depth)
+ if (idx >= lvi.lvi_loop[depth].var_idx
+ && idx < lvi.lvi_loop[depth].var_idx
+ + lvi.lvi_loop[depth].var_count)
+ {
+ lvar->lv_loop_depth = depth;
+ lvar->lv_loop_idx = lvi.lvi_loop[depth].var_idx;
+ return;
+ }
+ }
}
/*