first implementation of 'goto'

This commit is contained in:
Roberto Ierusalimschy 2011-02-04 15:34:43 -02:00
parent a4a8914c20
commit 7cc0e63d8a
4 changed files with 227 additions and 23 deletions

11
ldo.c
View File

@ -1,5 +1,5 @@
/*
** $Id: ldo.c,v 2.89 2010/09/30 17:21:31 roberto Exp roberto $
** $Id: ldo.c,v 2.90 2010/10/25 19:01:37 roberto Exp roberto $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
@ -617,6 +617,8 @@ struct SParser { /* data to `f_parser' */
ZIO *z;
Mbuffer buff; /* buffer to be used by the scanner */
Varlist varl; /* list of local variables (to be used by the parser) */
Gotolist gtl; /* list of pending gotos (") */
Labellist labell; /* list of active labels (") */
const char *name;
};
@ -628,7 +630,8 @@ static void f_parser (lua_State *L, void *ud) {
int c = luaZ_lookahead(p->z);
tf = (c == LUA_SIGNATURE[0])
? luaU_undump(L, p->z, &p->buff, p->name)
: luaY_parser(L, p->z, &p->buff, &p->varl, p->name);
: luaY_parser(L, p->z, &p->buff, &p->varl,
&p->gtl, &p->labell, p->name);
setptvalue2s(L, L->top, tf);
incr_top(L);
cl = luaF_newLclosure(L, tf);
@ -644,10 +647,14 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) {
L->nny++; /* cannot yield during parsing */
p.z = z; p.name = name;
p.varl.actvar = NULL; p.varl.nactvar = p.varl.actvarsize = 0;
p.gtl.gt = NULL; p.gtl.ngt = p.gtl.gtsize = 0;
p.labell.label = NULL; p.labell.nlabel = p.labell.labelsize = 0;
luaZ_initbuffer(L, &p.buff);
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
luaZ_freebuffer(L, &p.buff);
luaM_freearray(L, p.varl.actvar, p.varl.actvarsize);
luaM_freearray(L, p.gtl.gt, p.gtl.gtsize);
luaM_freearray(L, p.labell.label, p.labell.labelsize);
L->nny--;
return status;
}

4
llex.h
View File

@ -1,5 +1,5 @@
/*
** $Id: llex.h,v 1.65 2010/04/05 16:35:37 roberto Exp roberto $
** $Id: llex.h,v 1.66 2011/02/02 14:55:17 roberto Exp roberto $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
@ -59,6 +59,8 @@ typedef struct LexState {
ZIO *z; /* input stream */
Mbuffer *buff; /* buffer for tokens */
struct Varlist *varl; /* list of all active local variables */
struct Gotolist *gtl; /* list of pending gotos */
struct Labellist *labell; /* list of active labels */
TString *source; /* current source name */
TString *envn; /* environment variable name */
char decpoint; /* locale decimal point */

188
lparser.c
View File

@ -1,5 +1,5 @@
/*
** $Id: lparser.c,v 2.95 2011/01/26 16:30:02 roberto Exp roberto $
** $Id: lparser.c,v 2.96 2011/02/01 18:03:10 roberto Exp roberto $
** Lua Parser
** See Copyright Notice in lua.h
*/
@ -42,7 +42,9 @@
typedef struct BlockCnt {
struct BlockCnt *previous; /* chain */
int breaklist; /* list of jumps out of this loop */
lu_byte nactvar; /* # active locals outside the breakable structure */
int firstlabel; /* index (in Labellist) of first label in this block */
int firstgoto; /* index (in Gotolist) of first pending goto in this block */
lu_byte nactvar; /* # active locals outside the block */
lu_byte upval; /* true if some variable in the block is an upvalue */
lu_byte isbreakable; /* true if `block' is a loop */
} BlockCnt;
@ -52,7 +54,7 @@ typedef struct BlockCnt {
/*
** prototypes for recursive non-terminal functions
*/
static void chunk (LexState *ls);
static void statlist (LexState *ls);
static void expr (LexState *ls, expdesc *v);
@ -172,7 +174,7 @@ static void new_localvar (LexState *ls, TString *name) {
checklimit(fs, vl->nactvar + 1 - fs->firstlocal,
MAXVARS, "local variables");
luaM_growvector(ls->L, vl->actvar, vl->nactvar + 1,
vl->actvarsize, vardesc, MAX_INT, "local variables");
vl->actvarsize, Vardesc, MAX_INT, "local variables");
vl->actvar[vl->nactvar++].idx = cast(unsigned short, reg);
}
@ -327,10 +329,93 @@ static void enterlevel (LexState *ls) {
#define leavelevel(ls) (G((ls)->L)->nCcalls--)
static void closegoto (LexState *ls, int g, Labeldesc *label) {
int i;
FuncState *fs = ls->fs;
Gotodesc *gt = &ls->gtl->gt[g];
lua_assert(gt->name == label->name);
if (gt->currlevel < label->nactvar) {
const char *msg = luaO_pushfstring(ls->L,
"<goto> at line %d attemps to jump into the scope of local " LUA_QS,
gt->line, getstr(getlocvar(fs, gt->currlevel)->varname));;
luaX_syntaxerror(ls, msg);
}
luaK_patchlist(fs, gt->pc, label->pc);
/* remove goto from pending list */
for (i = g; i < ls->gtl->ngt - 1; i++)
ls->gtl->gt[i] = ls->gtl->gt[i + 1];
ls->gtl->ngt--;
}
/*
** try to close a goto with existing labels; this solves backward jumps
*/
static int findlabel (LexState *ls, int g) {
int i;
BlockCnt *bl = ls->fs->bl;
Labellist *labell = ls->labell;
Gotodesc *gt = &ls->gtl->gt[g];
/* check labels in current block for a match */
for (i = bl->firstlabel; i < labell->nlabel; i++) {
Labeldesc *lb = &labell->label[i];
if (lb->name == gt->name) {
lua_assert(labell->label[i].pc <= gt->pc);
if (gt->currlevel > lb->nactvar &&
(bl->upval || ls->labell->nlabel > bl->firstlabel))
luaK_patchclose(ls->fs, gt->pc, lb->nactvar);
closegoto(ls, g, lb); /* close it */
return 1;
}
}
return 0; /* label not found; cannot close goto */
}
/*
** check whether new label 'lb' matches any pending goto in current
** block; solves forward jumps
*/
static void findgotos (LexState *ls, Labeldesc *lb) {
int i;
Gotolist *gtl = ls->gtl;
for (i = ls->fs->bl->firstgoto; i < gtl->ngt; i++) {
if (gtl->gt[i].name == lb->name)
closegoto(ls, i, lb);
}
}
/*
** "export" pending gotos to outer level, to check them against
** outer labels; if the block being exited has upvalues, and
** the goto exists the scope of any variable (which can be the
** upvalue), close those variables being exited.
*/
static void movegotosout (FuncState *fs, BlockCnt *bl) {
int i = bl->firstgoto;
LexState *ls = fs->ls;
/* correct pending gotos to current block and try to close it
with visible labels */
while (i < ls->gtl->ngt) {
Gotodesc *gt = &ls->gtl->gt[i];
if (gt->currlevel > bl->nactvar) {
if (bl->upval)
luaK_patchclose(fs, gt->pc, bl->nactvar);
gt->currlevel = bl->nactvar;
}
if (!findlabel(ls, i))
i++; /* move to next one */
}
}
static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) {
bl->breaklist = NO_JUMP;
bl->isbreakable = isbreakable;
bl->nactvar = fs->nactvar;
bl->firstlabel = fs->ls->labell->nlabel;
bl->firstgoto = fs->ls->gtl->ngt;
bl->upval = 0;
bl->previous = fs->bl;
fs->bl = bl;
@ -342,6 +427,8 @@ static void leaveblock (FuncState *fs) {
BlockCnt *bl = fs->bl;
fs->bl = bl->previous;
removevars(fs, bl->nactvar);
fs->ls->labell->nlabel = bl->firstlabel; /* remove local labels */
movegotosout(fs, bl);
if (bl->upval)
luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
/* a block either controls scope or breaks (never both) */
@ -445,8 +532,24 @@ static void open_mainfunc (LexState *ls, FuncState *fs) {
}
static void mainblock (LexState *ls, FuncState *fs) {
BlockCnt bl;
enterblock(fs, &bl, 0);
statlist(ls); /* read main block */
if (bl.firstgoto < ls->gtl->ngt) { /* check pending gotos */
Gotodesc *gt = &ls->gtl->gt[bl.firstgoto];
const char *msg = luaO_pushfstring(ls->L,
"label " LUA_QS " (<goto> at line %d) undefined",
getstr(gt->name), gt->line);
luaX_syntaxerror(ls, msg);
}
bl.upval = 0; /* RETURN will close any pending upvalue */
leaveblock(fs);
}
Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Varlist *varl,
const char *name) {
Gotolist *gtl, Labellist *labell, const char *name) {
LexState lexstate;
FuncState funcstate;
TString *tname = luaS_new(L, name);
@ -454,10 +557,12 @@ Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Varlist *varl,
incr_top(L);
lexstate.buff = buff;
lexstate.varl = varl;
lexstate.gtl = gtl;
lexstate.labell = labell;
luaX_setinput(L, &lexstate, z, tname);
open_mainfunc(&lexstate, &funcstate);
luaX_next(&lexstate); /* read first token */
chunk(&lexstate); /* read main chunk */
mainblock(&lexstate, &funcstate);
check(&lexstate, TK_EOS);
close_func(&lexstate);
L->top--; /* pop name */
@ -645,7 +750,7 @@ static void parlist (LexState *ls) {
static void body (LexState *ls, expdesc *e, int needself, int line) {
/* body -> `(' parlist `)' chunk END */
/* body -> `(' parlist `)' block END */
FuncState new_fs;
open_func(ls, &new_fs);
new_fs.f->linedefined = line;
@ -656,7 +761,7 @@ static void body (LexState *ls, expdesc *e, int needself, int line) {
}
parlist(ls);
checknext(ls, ')');
chunk(ls);
mainblock(ls, &new_fs);
new_fs.f->lastlinedefined = ls->linenumber;
check_match(ls, TK_END, TK_FUNCTION, line);
codeclosure(ls, new_fs.f, e);
@ -949,11 +1054,11 @@ static int block_follow (int token) {
static void block (LexState *ls) {
/* block -> chunk */
/* block -> statlist */
FuncState *fs = ls->fs;
BlockCnt bl;
enterblock(fs, &bl, 0);
chunk(ls);
statlist(ls);
lua_assert(bl.breaklist == NO_JUMP);
leaveblock(fs);
}
@ -1043,6 +1148,13 @@ 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;
@ -1054,11 +1166,49 @@ static void breakstat (LexState *ls) {
if (!bl)
luaX_syntaxerror(ls, "no loop to break");
luaK_concat(fs, &bl->breaklist, luaK_jump(fs));
if (upval)
if (upval ||
(fs->nactvar > bl->nactvar &&
ls->labell->nlabel > bl->firstlabel))
luaK_patchclose(fs, bl->breaklist, bl->nactvar);
}
static void gotostat (LexState *ls, TString *label, int line) {
Gotolist *gtl = ls->gtl;
int g = gtl->ngt; /* index of new goto being created */
/* create new entry for this goto */
luaM_growvector(ls->L, gtl->gt, gtl->ngt, gtl->gtsize,
Gotodesc, MAX_INT, "labels");
gtl->gt[g].name = label;
gtl->gt[g].line = line;
gtl->gt[g].currlevel = ls->fs->nactvar;
gtl->gt[g].pc = luaK_jump(ls->fs); /* create jump instruction */
gtl->ngt++;
findlabel(ls, g);
}
static void labelstat (LexState *ls, TString *label) {
/* label -> '@' NAME ':' */
FuncState *fs = ls->fs;
Labellist *labell = ls->labell;
int l = labell->nlabel; /* index of new label being created */
checknext(ls, ':');
/* create new entry for this label */
luaM_growvector(ls->L, labell->label, labell->nlabel, labell->labelsize,
Labeldesc, MAX_INT, "labels");
labell->label[l].name = label;
labell->label[l].pc = fs->pc;
/* if label is last statement in the block,
assume that local variables are already out of scope */
labell->label[l].nactvar = (ls->t.token == TK_END)
? fs->bl->nactvar
: fs->nactvar;
labell->nlabel++;
findgotos(ls, &labell->label[l]);
}
static void whilestat (LexState *ls, int line) {
/* whilestat -> WHILE cond DO block END */
FuncState *fs = ls->fs;
@ -1087,7 +1237,7 @@ static void repeatstat (LexState *ls, int line) {
enterblock(fs, &bl1, 1); /* loop block */
enterblock(fs, &bl2, 0); /* scope block */
luaX_next(ls); /* skip REPEAT */
chunk(ls);
statlist(ls);
check_match(ls, TK_UNTIL, TK_REPEAT, line);
condexit = cond(ls); /* read condition (inside scope block) */
if (!bl2.upval) { /* no upvalues? */
@ -1382,6 +1532,11 @@ static int statement (LexState *ls) {
localstat(ls);
return 0;
}
case '@': { /* stat -> label */
luaX_next(ls); /* skip '@' */
labelstat(ls, str_checkname(ls));
return 0;
}
case TK_RETURN: { /* stat -> retstat */
luaX_next(ls); /* skip RETURN */
retstat(ls);
@ -1392,6 +1547,11 @@ static int statement (LexState *ls) {
breakstat(ls);
return 1; /* must be last statement */
}
case TK_GOTO: { /* stat -> 'goto' NAME */
luaX_next(ls); /* skip GOTO */
gotostat(ls, str_checkname(ls), line);
return 0;
}
default: { /* stat -> func | assignment */
exprstat(ls);
return 0;
@ -1400,8 +1560,8 @@ static int statement (LexState *ls) {
}
static void chunk (LexState *ls) {
/* chunk -> { stat [`;'] } */
static void statlist (LexState *ls) {
/* statlist -> { stat [`;'] } */
int islast = 0;
enterlevel(ls);
while (!islast && !block_follow(ls->t.token)) {

View File

@ -1,5 +1,5 @@
/*
** $Id: lparser.h,v 1.64 2010/07/02 20:42:40 roberto Exp roberto $
** $Id: lparser.h,v 1.65 2010/07/07 16:27:29 roberto Exp roberto $
** Lua Parser
** See Copyright Notice in lua.h
*/
@ -53,19 +53,53 @@ typedef struct expdesc {
} expdesc;
typedef struct vardesc {
unsigned short idx;
} vardesc;
/* description of active local variable */
typedef struct Vardesc {
unsigned short idx; /* variable index in stack */
} Vardesc;
/* list of all active local variables */
typedef struct Varlist {
vardesc *actvar;
Vardesc *actvar;
int nactvar;
int actvarsize;
} Varlist;
/* description of pending goto statement */
typedef struct Gotodesc {
TString *name;
int pc; /* where it is coded */
int line; /* line where it appeared */
lu_byte currlevel; /* variable level where it appears in current block */
} Gotodesc;
/* list of pending gotos */
typedef struct Gotolist {
Gotodesc *gt;
int ngt;
int gtsize;
} Gotolist;
/* description of active labels */
typedef struct Labeldesc {
TString *name;
int pc; /* label position */
lu_byte nactvar; /* variable level where it appears in current block */
} Labeldesc;
/* list of active labels */
typedef struct Labellist {
Labeldesc *label;
int nlabel;
int labelsize;
} Labellist;
struct BlockCnt; /* defined in lparser.c */
@ -91,7 +125,8 @@ typedef struct FuncState {
LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Varlist *varl, const char *name);
Varlist *varl, Gotolist *gtl,
Labellist *labell, const char *name);
#endif