Skip to content

Commit c0e38ba

Browse files
committed
Initial implementation of the "continue" keyword.
1 parent 247b362 commit c0e38ba

File tree

3 files changed

+61
-9
lines changed

3 files changed

+61
-9
lines changed

src/lj_errmsg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ ERRDEF(XDOTS, "cannot use " LUA_QL("...") " outside a vararg function")
145145
ERRDEF(XSYNTAX, "syntax error")
146146
ERRDEF(XFOR, LUA_QL("=") " or " LUA_QL("in") " expected")
147147
ERRDEF(XBREAK, "no loop to break")
148+
ERRDEF(XCONTINUE, "no loop to continue")
148149
ERRDEF(XLUNDEF, "undefined label " LUA_QS)
149150
ERRDEF(XLDUP, "duplicate label " LUA_QS)
150151
ERRDEF(XGSCOPE, "<goto %s> jumps into the scope of local " LUA_QS)

src/lj_lex.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
/* Lua lexer tokens. */
1515
#define TKDEF(_, __) \
16-
_(and) _(break) _(do) _(else) _(elseif) _(end) _(false) \
16+
_(and) _(break) _(continue) _(do) _(else) _(elseif) _(end) _(false) \
1717
_(for) _(function) _(goto) _(if) _(in) _(local) _(nil) _(not) _(or) \
1818
_(repeat) _(return) _(then) _(true) _(until) _(while) \
1919
__(concat, ..) __(dots, ...) __(eq, ==) __(ge, >=) __(le, <=) __(ne, ~=) \

src/lj_parse.c

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,10 @@ typedef struct FuncScope {
105105
#define FSCOPE_GOLA 0x04 /* Goto or label used in scope. */
106106
#define FSCOPE_UPVAL 0x08 /* Upvalue in scope. */
107107
#define FSCOPE_NOCLOSE 0x10 /* Do not close upvalues. */
108+
#define FSCOPE_CONTINUE 0x20 /* Continue used in scope (FSCOPE_GOLA must also be set). */
108109

109110
#define NAME_BREAK ((GCstr *)(uintptr_t)1)
111+
#define NAME_CONTINUE ((GCstr *)(uintptr_t)2)
110112

111113
/* Index into variable stack. */
112114
typedef uint16_t VarIndex;
@@ -1145,7 +1147,7 @@ static MSize gola_new(LexState *ls, GCstr *name, uint8_t info, BCPos pc)
11451147
lj_lex_error(ls, 0, LJ_ERR_XLIMC, LJ_MAX_VSTACK);
11461148
lj_mem_growvec(ls->L, ls->vstack, ls->sizevstack, LJ_MAX_VSTACK, VarInfo);
11471149
}
1148-
lua_assert(name == NAME_BREAK || lj_tab_getstr(fs->kt, name) != NULL);
1150+
lua_assert(name == NAME_BREAK || name == NAME_CONTINUE || lj_tab_getstr(fs->kt, name) != NULL);
11491151
/* NOBARRIER: name is anchored in fs->kt and ls->vstack is not a GCobj. */
11501152
setgcref(ls->vstack[vtop].name, obj2gco(name));
11511153
ls->vstack[vtop].startpc = pc;
@@ -1197,7 +1199,7 @@ static void gola_resolve(LexState *ls, FuncScope *bl, MSize idx)
11971199
GCstr *name = strref(var_get(ls, ls->fs, vg->slot).name);
11981200
lua_assert((uintptr_t)name >= VARNAME__MAX);
11991201
ls->linenumber = ls->fs->bcbase[vg->startpc].line;
1200-
lua_assert(strref(vg->name) != NAME_BREAK);
1202+
lua_assert(strref(vg->name) != NAME_BREAK && strref(vg->name) != NAME_CONTINUE);
12011203
lj_lex_error(ls, 0, LJ_ERR_XGSCOPE,
12021204
strdata(strref(vg->name)), strdata(name));
12031205
}
@@ -1223,15 +1225,25 @@ static void gola_fixup(LexState *ls, FuncScope *bl)
12231225
gola_patch(ls, vg, v);
12241226
}
12251227
} else if (gola_isgoto(v)) {
1226-
if (bl->prev) { /* Propagate goto or break to outer scope. */
1227-
bl->prev->flags |= name == NAME_BREAK ? FSCOPE_BREAK : FSCOPE_GOLA;
1228+
if (bl->prev) { /* Propagate goto, continue or break to outer scope. */
1229+
/* If this is a loop, we should never see unresolved NAME_CONTINUEs.
1230+
* If this happens, a loop failed to call fscope_loop_continue before
1231+
* closing the scope. */
1232+
if(bl->flags & FSCOPE_LOOP)
1233+
lua_assert(name != NAME_CONTINUE);
1234+
bl->prev->flags |=
1235+
name == NAME_BREAK ? FSCOPE_BREAK :
1236+
name == NAME_CONTINUE ? FSCOPE_CONTINUE :
1237+
FSCOPE_GOLA;
12281238
v->slot = bl->nactvar;
12291239
if ((bl->flags & FSCOPE_UPVAL))
12301240
gola_close(ls, v);
12311241
} else { /* No outer scope: undefined goto label or no loop. */
12321242
ls->linenumber = ls->fs->bcbase[v->startpc].line;
12331243
if (name == NAME_BREAK)
12341244
lj_lex_error(ls, 0, LJ_ERR_XBREAK);
1245+
else if (name == NAME_CONTINUE)
1246+
lj_lex_error(ls, 0, LJ_ERR_XCONTINUE);
12351247
else
12361248
lj_lex_error(ls, 0, LJ_ERR_XLUNDEF, strdata(name));
12371249
}
@@ -1264,6 +1276,29 @@ static void fscope_begin(FuncState *fs, FuncScope *bl, int flags)
12641276
lua_assert(fs->freereg == fs->nactvar);
12651277
}
12661278

1279+
/* When an FSCOPE_LOOP is ending, this is called to set the instruction continue statements
1280+
* should jump to. */
1281+
static void fscope_loop_continue(FuncState *fs, BCPos pos)
1282+
{
1283+
FuncScope *bl = fs->bl;
1284+
LexState *ls = fs->ls;
1285+
1286+
/* This must be called before the loop is closed, so we don't propagate FSCOPE_CONTINUE
1287+
* out to the enclosing scope. */
1288+
lua_assert((bl->flags & FSCOPE_LOOP));
1289+
1290+
/* If continue wasn't used in this scope, we have nothing to do. */
1291+
if (!(bl->flags & FSCOPE_CONTINUE))
1292+
return;
1293+
1294+
bl->flags &= ~FSCOPE_CONTINUE;
1295+
1296+
/* Generate a CONTINUE label, and resolve continues inside this scope to it. */
1297+
MSize idx = gola_new(ls, NAME_CONTINUE, VSTACK_LABEL, pos);
1298+
ls->vtop = idx; /* Drop continue label immediately. */
1299+
gola_resolve(ls, bl, idx);
1300+
}
1301+
12671302
/* End a scope. */
12681303
static void fscope_end(FuncState *fs)
12691304
{
@@ -1285,7 +1320,7 @@ static void fscope_end(FuncState *fs)
12851320
return;
12861321
}
12871322
}
1288-
if ((bl->flags & FSCOPE_GOLA)) {
1323+
if ((bl->flags & FSCOPE_GOLA) || (bl->flags & FSCOPE_CONTINUE)) {
12891324
gola_fixup(ls, bl);
12901325
}
12911326
}
@@ -2389,6 +2424,13 @@ static void parse_goto(LexState *ls)
23892424
gola_new(ls, name, VSTACK_GOTO, bcemit_jmp(fs));
23902425
}
23912426

2427+
static void parse_continue(LexState *ls)
2428+
{
2429+
FuncState *fs = ls->fs;
2430+
fs->bl->flags |= FSCOPE_CONTINUE;
2431+
gola_new(ls, NAME_CONTINUE, VSTACK_GOTO, bcemit_jmp(fs));
2432+
}
2433+
23922434
/* Parse label. */
23932435
static void parse_label(LexState *ls)
23942436
{
@@ -2448,6 +2490,7 @@ static void parse_while(LexState *ls, BCLine line)
24482490
parse_block(ls);
24492491
jmp_patch(fs, bcemit_jmp(fs), start);
24502492
lex_match(ls, TK_end, TK_while, line);
2493+
fscope_loop_continue(fs, start); /* continue statements jump to start */
24512494
fscope_end(fs);
24522495
jmp_tohere(fs, condexit);
24532496
jmp_patchins(fs, loop, fs->pc);
@@ -2458,14 +2501,15 @@ static void parse_repeat(LexState *ls, BCLine line)
24582501
{
24592502
FuncState *fs = ls->fs;
24602503
BCPos loop = fs->lasttarget = fs->pc;
2461-
BCPos condexit;
2504+
BCPos condexit, iter;
24622505
FuncScope bl1, bl2;
24632506
fscope_begin(fs, &bl1, FSCOPE_LOOP); /* Breakable loop scope. */
24642507
fscope_begin(fs, &bl2, 0); /* Inner scope. */
24652508
lj_lex_next(ls); /* Skip 'repeat'. */
24662509
bcemit_AD(fs, BC_LOOP, fs->nactvar, 0);
24672510
parse_chunk(ls);
24682511
lex_match(ls, TK_until, TK_repeat, line);
2512+
iter = fs->pc; // continue jumps here
24692513
condexit = expr_cond(ls); /* Parse condition (still inside inner scope). */
24702514
if (!(bl2.flags & FSCOPE_UPVAL)) { /* No upvalues? Just end inner scope. */
24712515
fscope_end(fs);
@@ -2477,6 +2521,7 @@ static void parse_repeat(LexState *ls, BCLine line)
24772521
}
24782522
jmp_patch(fs, condexit, loop); /* Jump backwards if !cond. */
24792523
jmp_patchins(fs, loop, fs->pc);
2524+
fscope_loop_continue(fs, iter); /* continue statements jump to condexit. */
24802525
fscope_end(fs); /* End loop scope. */
24812526
}
24822527

@@ -2516,6 +2561,7 @@ static void parse_for_num(LexState *ls, GCstr *varname, BCLine line)
25162561
fs->bcbase[loopend].line = line; /* Fix line for control ins. */
25172562
jmp_patchins(fs, loopend, loop+1);
25182563
jmp_patchins(fs, loop, fs->pc);
2564+
fscope_loop_continue(fs, loopend); /* continue statements jump to loopend. */
25192565
}
25202566

25212567
/* Try to predict whether the iterator is next() and specialize the bytecode.
@@ -2558,7 +2604,7 @@ static void parse_for_iter(LexState *ls, GCstr *indexname)
25582604
BCReg nvars = 0;
25592605
BCLine line;
25602606
BCReg base = fs->freereg + 3;
2561-
BCPos loop, loopend, exprpc = fs->pc;
2607+
BCPos loop, loopend, iter, exprpc = fs->pc;
25622608
FuncScope bl;
25632609
int isnext;
25642610
/* Hidden control variables. */
@@ -2584,11 +2630,12 @@ static void parse_for_iter(LexState *ls, GCstr *indexname)
25842630
fscope_end(fs);
25852631
/* Perform loop inversion. Loop control instructions are at the end. */
25862632
jmp_patchins(fs, loop, fs->pc);
2587-
bcemit_ABC(fs, isnext ? BC_ITERN : BC_ITERC, base, nvars-3+1, 2+1);
2633+
iter = bcemit_ABC(fs, isnext ? BC_ITERN : BC_ITERC, base, nvars-3+1, 2+1);
25882634
loopend = bcemit_AJ(fs, BC_ITERL, base, NO_JMP);
25892635
fs->bcbase[loopend-1].line = line; /* Fix line for control ins. */
25902636
fs->bcbase[loopend].line = line;
25912637
jmp_patchins(fs, loopend, loop+1);
2638+
fscope_loop_continue(fs, iter); /* continue statements jump to iter. */
25922639
}
25932640

25942641
/* Parse 'for' statement. */
@@ -2691,6 +2738,10 @@ static int parse_stmt(LexState *ls)
26912738
case TK_label:
26922739
parse_label(ls);
26932740
break;
2741+
case TK_continue:
2742+
lj_lex_next(ls);
2743+
parse_continue(ls);
2744+
break;
26942745
case TK_goto:
26952746
if (LJ_52 || lj_lex_lookahead(ls) == TK_name) {
26962747
lj_lex_next(ls);

0 commit comments

Comments
 (0)