mirror of https://github.com/rusefi/lua.git
first implementation of 'goto'
This commit is contained in:
parent
a4a8914c20
commit
7cc0e63d8a
11
ldo.c
11
ldo.c
|
@ -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
4
llex.h
|
@ -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
188
lparser.c
|
@ -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)) {
|
||||
|
|
47
lparser.h
47
lparser.h
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue