From bf8b08295aa2f4b66aeb7d8313d464ccd80c0b0c Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 10 Feb 2011 12:50:41 -0200 Subject: [PATCH] 'break' coded as 'goto' + small bug when closing multiple gotos to the same label --- lparser.c | 111 +++++++++++++++++++++++++----------------------------- 1 file changed, 52 insertions(+), 59 deletions(-) diff --git a/lparser.c b/lparser.c index cf0882f8..9da2e2c1 100644 --- a/lparser.c +++ b/lparser.c @@ -1,5 +1,5 @@ /* -** $Id: lparser.c,v 2.102 2011/02/09 16:51:28 roberto Exp roberto $ +** $Id: lparser.c,v 2.103 2011/02/09 17:03:18 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ @@ -41,7 +41,6 @@ */ typedef struct BlockCnt { struct BlockCnt *previous; /* chain */ - int breaklist; /* list of jumps out of this loop */ int firstlabel; /* index of first label in this block */ int firstgoto; /* index of first pending goto in this block */ lu_byte nactvar; /* # active locals outside the block */ @@ -380,16 +379,31 @@ static int findlabel (LexState *ls, int g) { } +static int newlabelentry (LexState *ls, Labellist *l, TString *name, + int line, int pc) { + int n = l->n; + luaM_growvector(ls->L, l->arr, n, l->size, Labeldesc, MAX_INT, "labels"); + l->arr[n].name = name; + l->arr[n].line = line; + l->arr[n].nactvar = ls->fs->nactvar; + l->arr[n].pc = pc; + l->n++; + return n; +} + + /* -** check whether new label 'lb' matches any pending goto in current +** check whether new label 'lb' matches any pending gotos in current ** block; solves forward jumps */ static void findgotos (LexState *ls, Labeldesc *lb) { - int i; Dyndata *dyd = ls->dyd; - for (i = ls->fs->bl->firstgoto; i < dyd->gt.n; i++) { + int i = ls->fs->bl->firstgoto; + while (i < dyd->gt.n) { if (eqstr(dyd->gt.arr[i].name, lb->name)) closegoto(ls, i, lb); + else + i++; } } @@ -419,7 +433,6 @@ static void movegotosout (FuncState *fs, BlockCnt *bl) { static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { - bl->breaklist = NO_JUMP; bl->isloop = isloop; bl->nactvar = fs->nactvar; bl->firstlabel = fs->ls->dyd->label.n; @@ -431,30 +444,48 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { } +/* +** create a label named "break" to resolve break statements +*/ +static void breaklabel (LexState *ls) { + TString *n = luaS_new(ls->L, "break"); + int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc); + findgotos(ls, &ls->dyd->label.arr[l]); +} + +/* +** generates an error for an undefined 'goto'; choose appropriate +** message when label name is a resserved word (which can only be 'break') +*/ +static void undefgoto (LexState *ls, Labeldesc *gt) { + const char *msg = (gt->name->tsv.reserved > 0) + ? "<%s> at line %d not inside a loop" + : "label " LUA_QS " ( at line %d) undefined"; + msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); + semerror(ls, msg); +} + + static void leaveblock (FuncState *fs) { BlockCnt *bl = fs->bl; LexState *ls = fs->ls; - fs->bl = bl->previous; - removevars(fs, bl->nactvar); - ls->dyd->label.n = bl->firstlabel; /* remove local labels */ - if (bl->previous) /* inner block? */ - movegotosout(fs, bl); /* update pending gotos to outer block */ - else if (bl->firstgoto < ls->dyd->gt.n) { /* pending gotos in outer block? */ - Labeldesc *gt = &ls->dyd->gt.arr[bl->firstgoto]; - const char *msg = luaO_pushfstring(ls->L, - "label " LUA_QS " ( at line %d) undefined", - getstr(gt->name), gt->line); - semerror(ls, msg); - } if (bl->previous && bl->upval) { /* create a 'jump to here' to close upvalues */ int j = luaK_jump(fs); luaK_patchclose(fs, j, bl->nactvar); luaK_patchtohere(fs, j); } + if (bl->isloop) + breaklabel(ls); /* close pending breaks */ + fs->bl = bl->previous; + removevars(fs, bl->nactvar); lua_assert(bl->nactvar == fs->nactvar); fs->freereg = fs->nactvar; /* free registers */ - luaK_patchtohere(fs, bl->breaklist); + ls->dyd->label.n = bl->firstlabel; /* remove local labels */ + if (bl->previous) /* inner block? */ + movegotosout(fs, bl); /* update pending gotos to outer block */ + else if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ + undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ } @@ -1070,7 +1101,6 @@ static void block (LexState *ls) { BlockCnt bl; enterblock(fs, &bl, 0); statlist(ls); - lua_assert(bl.breaklist == NO_JUMP); leaveblock(fs); } @@ -1159,44 +1189,6 @@ static int cond (LexState *ls) { } -/* code a break statement. The last 'if' decides the need to close - upvalues when leaving the block. If the block has upvalues, it - must be closed. If it has local variables and any label - before the break, those variables must be closed too, as they - may be used as upvalues after the break and through a goto - be exited through this break. -*/ -static void breakstat (LexState *ls) { - FuncState *fs = ls->fs; - BlockCnt *bl = fs->bl; - int upval = 0; - while (bl && !bl->isloop) { - upval |= bl->upval; - bl = bl->previous; - } - if (!bl) - semerror(ls, "no loop to break"); - luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); - if (upval || - (fs->nactvar > bl->nactvar && - ls->dyd->label.n > bl->firstlabel)) - luaK_patchclose(fs, bl->breaklist, bl->nactvar); -} - - -static int newlabelentry (LexState *ls, Labellist *l, TString *name, - int line, int pc) { - int n = l->n; - luaM_growvector(ls->L, l->arr, l->n, l->size, Labeldesc, MAX_INT, "labels"); - l->arr[n].name = name; - l->arr[n].line = line; - l->arr[n].nactvar = ls->fs->nactvar; - l->arr[n].pc = pc; - l->n++; - return n; -} - - static void gotostat (LexState *ls, TString *label, int line) { /* create new entry for this goto */ int g = newlabelentry(ls, &ls->dyd->gt, label, line, luaK_jump(ls->fs)); @@ -1551,7 +1543,8 @@ static int statement (LexState *ls) { } case TK_BREAK: { /* stat -> breakstat */ luaX_next(ls); /* skip BREAK */ - breakstat(ls); + /* code it as "goto 'break'" */ + gotostat(ls, luaS_new(ls->L, "break"), line); return 1; /* must be last statement */ } case TK_GOTO: { /* stat -> 'goto' NAME */