commit 7ff21273d66541ac19ad817f90e8bcea5790f355
parent 207dad86065acf4b6168bea1f13e88b7306a7440
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Thu, 14 Mar 2002 15:01:30 -0300
implementation of `global' statement
Diffstat:
M | llimits.h | | | 8 | ++++---- |
M | lparser.c | | | 269 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
M | lparser.h | | | 36 | ++++++++++++++++++------------------ |
M | ltests.c | | | 4 | ++-- |
4 files changed, 211 insertions(+), 106 deletions(-)
diff --git a/llimits.h b/llimits.h
@@ -1,5 +1,5 @@
/*
-** $Id: llimits.h,v 1.38 2002/03/05 16:22:54 roberto Exp roberto $
+** $Id: llimits.h,v 1.39 2002/03/07 18:11:51 roberto Exp roberto $
** Limits, basic types, and some other `installation-dependent' definitions
** See Copyright Notice in lua.h
*/
@@ -96,9 +96,9 @@ typedef unsigned long Instruction;
#define MAXSTACK 250
-/* maximum number of local variables */
-#ifndef MAXLOCALS
-#define MAXLOCALS 200 /* arbitrary limit (<MAXSTACK) */
+/* maximum number of variables declared in a function */
+#ifndef MAXVARS
+#define MAXVARS 200 /* arbitrary limit (<MAXSTACK) */
#endif
diff --git a/lparser.c b/lparser.c
@@ -1,5 +1,5 @@
/*
-** $Id: lparser.c,v 1.167 2002/02/14 21:46:58 roberto Exp $
+** $Id: lparser.c,v 1.168 2002/03/08 19:10:32 roberto Exp roberto $
** Lua Parser
** See Copyright Notice in lua.h
*/
@@ -24,14 +24,17 @@
/*
-** nodes for break list (list of active breakable loops)
+** nodes for block list (list of active blocks)
*/
-typedef struct Breaklabel {
- struct Breaklabel *previous; /* chain */
+typedef struct BlockCnt {
+ struct BlockCnt *previous; /* chain */
int breaklist; /* list of jumps out of this loop */
- int nactloc; /* # of active local variables outside the breakable structure */
-} Breaklabel;
-
+ int nactloc; /* # active local variables outside the breakable structure */
+ int nactvar;
+ int defaultglob;
+ int upval; /* true if some variable in the block is an upvalue */
+ int isbreakable; /* true if `block' is a loop */
+} BlockCnt;
@@ -77,9 +80,7 @@ static void check (LexState *ls, int c) {
}
-static void check_condition (LexState *ls, int c, const char *msg) {
- if (!c) luaK_error(ls, msg);
-}
+#define check_condition(ls,c,msg) { if (!(c)) luaK_error(ls, msg); }
static int optional (LexState *ls, int c) {
@@ -143,41 +144,51 @@ static int luaI_registerlocalvar (LexState *ls, TString *varname) {
}
-static void new_localvar (LexState *ls, TString *name, int n) {
+static vardesc *new_var (LexState *ls, int n) {
FuncState *fs = ls->fs;
- luaX_checklimit(ls, fs->nactloc+n+1, MAXLOCALS, "local variables");
- fs->actloc[fs->nactloc+n] = luaI_registerlocalvar(ls, name);
+ luaX_checklimit(ls, fs->nactvar+n+1, MAXVARS, "variables");
+ return &fs->actvar[fs->nactvar+n];
+}
+
+
+static void new_localvar (LexState *ls, TString *name, int n) {
+ vardesc *v = new_var(ls, n);
+ v->k = VLOCAL;
+ v->i = luaI_registerlocalvar(ls, name);
+ v->level = ls->fs->nactloc + n;
}
static void adjustlocalvars (LexState *ls, int nvars) {
FuncState *fs = ls->fs;
while (nvars--) {
- fs->f->locvars[fs->actloc[fs->nactloc]].startpc = fs->pc;
- resetbit(fs->wasup, fs->nactloc);
+ lua_assert(fs->actvar[fs->nactvar].k == VLOCAL);
+ fs->f->locvars[fs->actvar[fs->nactvar].i].startpc = fs->pc;
+ fs->nactvar++;
fs->nactloc++;
}
}
-static void closelevel (LexState *ls, int level) {
+static void adjustglobalvars (LexState *ls, int nvars, int level) {
FuncState *fs = ls->fs;
- int i;
- for (i=level; i<fs->nactloc; i++)
- if (testbit(fs->wasup, i)) {
- luaK_codeABC(fs, OP_CLOSE, level, 0, 0);
- return;
- }
- return; /* nothing to close */
+ while (nvars--) {
+ fs->actvar[fs->nactvar].k = VGLOBAL;
+ fs->actvar[fs->nactvar].level = level;
+ fs->nactvar++;
+ }
}
-static void removelocalvars (LexState *ls, int nvars, int toclose) {
+static void removevars (LexState *ls, int tolevel) {
FuncState *fs = ls->fs;
- if (toclose)
- closelevel(ls, fs->nactloc - nvars);
- while (nvars--)
- fs->f->locvars[fs->actloc[--fs->nactloc]].endpc = fs->pc;
+ while (fs->nactvar > tolevel) {
+ fs->nactvar--;
+ if (fs->actvar[fs->nactvar].k == VLOCAL) {
+ fs->nactloc--;
+ fs->f->locvars[fs->actvar[fs->nactvar].i].endpc = fs->pc;
+ }
+ }
}
@@ -186,6 +197,12 @@ static void new_localvarstr (LexState *ls, const char *name, int n) {
}
+static void create_local (LexState *ls, const char *name) {
+ new_localvarstr(ls, name, 0);
+ adjustlocalvars(ls, 1);
+}
+
+
static int indexupvalue (FuncState *fs, expdesc *v) {
int i;
for (i=0; i<fs->f->nupvalues; i++) {
@@ -199,28 +216,72 @@ static int indexupvalue (FuncState *fs, expdesc *v) {
}
-static void singlevar (FuncState *fs, TString *n, expdesc *var, int baselevel) {
- if (fs == NULL)
- init_exp(var, VGLOBAL, 0); /* not local in any level; global variable */
- else { /* look up at current level */
- int i;
- for (i=fs->nactloc-1; i >= 0; i--) {
- if (n == fs->f->locvars[fs->actloc[i]].varname) {
- if (!baselevel)
- setbit(fs->wasup, i); /* will be upvalue in some other level */
- init_exp(var, VLOCAL, i);
- return;
+static vardesc *searchvar (FuncState *fs, TString *n) {
+ int i;
+ for (i=fs->nactvar-1; i >= 0; i--) {
+ vardesc *v = &fs->actvar[i];
+ if (v->k == VLOCAL ? n == fs->f->locvars[v->i].varname
+ : n == tsvalue(&fs->f->k[v->i]))
+ return v;
+ }
+ return NULL; /* not found */
+}
+
+
+static void markupval (FuncState *fs, int level) {
+ BlockCnt *bl = fs->bl;
+ while (bl && bl->nactloc > level) bl = bl->previous;
+ if (bl) bl->upval = 1;
+}
+
+
+static int singlevar_aux (FuncState *fs, TString *n, expdesc *var, int nd) {
+ if (fs == NULL) { /* no more levels? */
+ init_exp(var, VGLOBAL, NO_REG); /* default is free global */
+ return VNIL; /* not found */
+ }
+ else {
+ vardesc *v = searchvar(fs, n); /* look up at current level */
+ if (v) {
+ if (v->level == NO_REG) { /* free global? */
+ lua_assert(v->k == VGLOBAL);
+ init_exp(var, VGLOBAL, NO_REG);
}
+ else
+ init_exp(var, VLOCAL, v->level);
+ return v->k;
}
- /* not found at current level; try upper one */
- singlevar(fs->prev, n, var, 0);
- if (var->k == VGLOBAL) {
- if (baselevel)
- var->info = luaK_stringK(fs, n); /* info points to global name */
+ else { /* not found at current level; try upper one */
+ int k = singlevar_aux(fs->prev, n, var, nd && fs->defaultglob == NO_REG);
+ if (var->k == VGLOBAL) {
+ if (k == VNIL && nd && fs->defaultglob != NO_REG) {
+ init_exp(var, VLOCAL, fs->defaultglob);
+ k = VGLOBAL; /* now there is a declaration */
+ }
+ }
+ else { /* LOCAL or UPVAL */
+ if (var->k == VLOCAL)
+ markupval(fs->prev, var->info); /* local will be used as an upval */
+ var->info = indexupvalue(fs, var);
+ var->k = VUPVAL; /* upvalue in this level */
+ }
+ return k;
}
- else { /* local variable in some upper level? */
- var->info = indexupvalue(fs, var);
- var->k = VUPVAL; /* upvalue in this level */
+ }
+}
+
+
+static void singlevar (FuncState *fs, TString *n, expdesc *var) {
+ int k = singlevar_aux(fs, n, var, 1);
+ if (k == VNIL || k == VGLOBAL) { /* global? */
+ if (var->k == VGLOBAL) /* free global? */
+ var->info = luaK_stringK(fs, n);
+ else { /* `indexed' global */
+ expdesc e;
+ codestring(fs->ls, &e, n);
+ luaK_exp2anyreg(fs, var);
+ var->aux = luaK_exp2RK(fs, &e);
+ var->k = VINDEXED;
}
}
}
@@ -250,29 +311,38 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
static void code_params (LexState *ls, int nparams, int dots) {
FuncState *fs = ls->fs;
adjustlocalvars(ls, nparams);
- luaX_checklimit(ls, fs->nactloc, MAXPARAMS, "parameters");
+ luaX_checklimit(ls, fs->nactvar, MAXPARAMS, "parameters");
fs->f->numparams = cast(lu_byte, fs->nactloc);
fs->f->is_vararg = cast(lu_byte, dots);
- if (dots) {
- new_localvarstr(ls, "arg", 0);
- adjustlocalvars(ls, 1);
- }
+ if (dots)
+ create_local(ls, "arg");
luaK_reserveregs(fs, fs->nactloc); /* reserve register for parameters */
}
-static void enterbreak (FuncState *fs, Breaklabel *bl) {
+static void enterblock (FuncState *fs, BlockCnt *bl, int isbreakable) {
bl->breaklist = NO_JUMP;
+ bl->isbreakable = isbreakable;
bl->nactloc = fs->nactloc;
+ bl->nactvar = fs->nactvar;
+ bl->defaultglob = fs->defaultglob;
+ bl->upval = 0;
bl->previous = fs->bl;
fs->bl = bl;
}
-static void leavebreak (FuncState *fs, Breaklabel *bl) {
+static void leaveblock (FuncState *fs) {
+ BlockCnt *bl = fs->bl;
fs->bl = bl->previous;
- luaK_patchtohere(fs, bl->breaklist);
+ removevars(fs->ls, bl->nactvar);
+ if (bl->upval)
+ luaK_codeABC(fs, OP_CLOSE, bl->nactloc, 0, 0);
lua_assert(bl->nactloc == fs->nactloc);
+ lua_assert(bl->nactvar == fs->nactvar);
+ fs->defaultglob = bl->defaultglob;
+ fs->freereg = bl->nactloc; /* free registers used by locals */
+ luaK_patchtohere(fs, bl->breaklist);
}
@@ -308,7 +378,9 @@ static void open_func (LexState *ls, FuncState *fs) {
fs->nlineinfo = 0;
fs->nlocvars = 0;
fs->nactloc = 0;
+ fs->nactvar = 0;
fs->lastline = 0;
+ fs->defaultglob = NO_REG; /* default is free globals */
fs->bl = NULL;
f->code = NULL;
f->source = ls->source;
@@ -322,7 +394,7 @@ static void close_func (LexState *ls) {
lua_State *L = ls->L;
FuncState *fs = ls->fs;
Proto *f = fs->f;
- removelocalvars(ls, fs->nactloc, 0);
+ removevars(ls, 0);
luaK_codeABC(fs, OP_RETURN, 0, 1, 0); /* final return */
luaK_getlabel(fs); /* close eventual list of pending jumps */
lua_assert(G(L)->roottable == fs->h);
@@ -352,8 +424,7 @@ Proto *luaY_parser (lua_State *L, ZIO *z) {
open_func(&lexstate, &funcstate);
next(&lexstate); /* read first token */
chunk(&lexstate);
- check_condition(&lexstate, (lexstate.t.token == TK_EOS),
- "<eof> expected");
+ check_condition(&lexstate, (lexstate.t.token == TK_EOS), "<eof> expected");
close_func(&lexstate);
lua_assert(funcstate.prev == NULL);
lua_assert(funcstate.f->nupvalues == 0);
@@ -593,13 +664,13 @@ static void prefixexp (LexState *ls, expdesc *v) {
return;
}
case TK_NAME: {
- singlevar(ls->fs, str_checkname(ls), v, 1);
+ singlevar(ls->fs, str_checkname(ls), v);
next(ls);
return;
}
case '%': { /* for compatibility only */
next(ls); /* skip `%' */
- singlevar(ls->fs, str_checkname(ls), v, 1);
+ singlevar(ls->fs, str_checkname(ls), v);
check_condition(ls, v->k == VUPVAL, "global upvalues are obsolete");
next(ls);
return;
@@ -797,10 +868,11 @@ static int block_follow (int token) {
static void block (LexState *ls) {
/* block -> chunk */
FuncState *fs = ls->fs;
- int nactloc = fs->nactloc;
+ BlockCnt bl;
+ enterblock(fs, &bl, 0);
chunk(ls);
- removelocalvars(ls, fs->nactloc - nactloc, 1);
- fs->freereg = nactloc; /* free registers used by locals */
+ lua_assert(bl.breaklist == NO_JUMP);
+ leaveblock(fs);
}
@@ -887,8 +959,8 @@ static void whilestat (LexState *ls, int line) {
FuncState *fs = ls->fs;
int while_init = luaK_getlabel(fs);
expdesc v;
- Breaklabel bl;
- enterbreak(fs, &bl);
+ BlockCnt bl;
+ enterblock(fs, &bl, 1);
next(ls);
cond(ls, &v);
check(ls, TK_DO);
@@ -896,7 +968,7 @@ static void whilestat (LexState *ls, int line) {
luaK_patchlist(fs, luaK_jump(fs), while_init);
luaK_patchtohere(fs, v.f);
check_match(ls, TK_END, TK_WHILE, line);
- leavebreak(fs, &bl);
+ leaveblock(fs);
}
@@ -905,14 +977,14 @@ static void repeatstat (LexState *ls, int line) {
FuncState *fs = ls->fs;
int repeat_init = luaK_getlabel(fs);
expdesc v;
- Breaklabel bl;
- enterbreak(fs, &bl);
+ BlockCnt bl;
+ enterblock(fs, &bl, 1);
next(ls);
block(ls);
check_match(ls, TK_UNTIL, TK_REPEAT, line);
cond(ls, &v);
luaK_patchlist(fs, v.f, repeat_init);
- leavebreak(fs, &bl);
+ leaveblock(fs);
}
@@ -949,7 +1021,6 @@ static void fornum (LexState *ls, TString *varname) {
block(ls);
luaK_patchtohere(fs, prep-1);
luaK_patchlist(fs, luaK_codeAsBc(fs, OP_FORLOOP, base, NO_JUMP), prep);
- removelocalvars(ls, 3, 1);
}
@@ -977,7 +1048,6 @@ static void forlist (LexState *ls, TString *indexname) {
block(ls);
luaK_patchlist(fs, luaK_jump(fs), prep-1);
luaK_patchtohere(fs, prep);
- removelocalvars(ls, nvars+1, 1);
}
@@ -985,8 +1055,8 @@ static void forstat (LexState *ls, int line) {
/* forstat -> fornum | forlist */
FuncState *fs = ls->fs;
TString *varname;
- Breaklabel bl;
- enterbreak(fs, &bl);
+ BlockCnt bl;
+ enterblock(fs, &bl, 1);
next(ls); /* skip `for' */
varname = str_checkname(ls); /* first variable name */
next(ls); /* skip var name */
@@ -996,7 +1066,7 @@ static void forstat (LexState *ls, int line) {
default: luaK_error(ls, "`=' or `in' expected");
}
check_match(ls, TK_END, TK_FOR, line);
- leavebreak(fs, &bl);
+ leaveblock(fs);
}
@@ -1054,10 +1124,37 @@ static void localstat (LexState *ls) {
}
+static void globalstat (LexState *ls) {
+ /* stat -> GLOBAL NAME {`,' NAME} [IN exp] | GLOBAL IN exp */
+ FuncState *fs = ls->fs;
+ next(ls); /* skip GLOBAL */
+ if (optional(ls, TK_IN)) { /* default declaration? */
+ exp1(ls);
+ fs->defaultglob = fs->freereg - 1;
+ create_local(ls, "(global table)");
+ }
+ else {
+ int nvars = 0;
+ do {
+ vardesc *v = new_var(ls, nvars++);
+ v->i = luaK_stringK(ls->fs, str_checkname(ls));
+ next(ls); /* skip name */
+ } while (optional(ls, ','));
+ if (!optional(ls, TK_IN))
+ adjustglobalvars(ls, nvars, NO_REG); /* free globals */
+ else {
+ exp1(ls);
+ adjustglobalvars(ls, nvars, ls->fs->freereg - 1);
+ create_local(ls, "(global table)");
+ }
+ }
+}
+
+
static int funcname (LexState *ls, expdesc *v) {
/* funcname -> NAME {field} [`:' NAME] */
int needself = 0;
- singlevar(ls->fs, str_checkname(ls), v, 1);
+ singlevar(ls->fs, str_checkname(ls), v);
next(ls); /* skip var name */
while (ls->t.token == '.') {
luaY_field(ls, v);
@@ -1125,11 +1222,17 @@ static void retstat (LexState *ls) {
static void breakstat (LexState *ls) {
/* stat -> BREAK [NAME] */
FuncState *fs = ls->fs;
- Breaklabel *bl = fs->bl;
+ BlockCnt *bl = fs->bl;
+ int upval = 0;
+ next(ls); /* skip BREAK */
+ while (bl && !bl->isbreakable) {
+ upval |= bl->upval;
+ bl = bl->previous;
+ }
if (!bl)
luaK_error(ls, "no loop to break");
- next(ls); /* skip BREAK */
- closelevel(ls, bl->nactloc);
+ if (upval)
+ luaK_codeABC(fs, OP_CLOSE, bl->nactloc, 0, 0);
luaK_concat(fs, &bl->breaklist, luaK_jump(fs));
}
@@ -1167,6 +1270,10 @@ static int statement (LexState *ls) {
localstat(ls);
return 0;
}
+ case TK_GLOBAL: { /* stat -> globalstat */
+ globalstat(ls);
+ return 0;
+ }
case TK_RETURN: { /* stat -> retstat */
retstat(ls);
return 1; /* must be last statement */
@@ -1207,10 +1314,8 @@ static void body (LexState *ls, expdesc *e, int needself, int line) {
open_func(ls, &new_fs);
new_fs.f->lineDefined = line;
check(ls, '(');
- if (needself) {
- new_localvarstr(ls, "self", 0);
- adjustlocalvars(ls, 1);
- }
+ if (needself)
+ create_local(ls, "self");
parlist(ls);
check(ls, ')');
chunk(ls);
diff --git a/lparser.h b/lparser.h
@@ -1,5 +1,5 @@
/*
-** $Id: lparser.h,v 1.1 2001/11/29 22:14:34 rieru Exp rieru $
+** $Id: lparser.h,v 1.39 2002/02/08 22:42:41 roberto Exp roberto $
** Lua Parser
** See Copyright Notice in lua.h
*/
@@ -13,18 +13,6 @@
#include "lzio.h"
-
-/* small implementation of bit arrays */
-
-#define BPW (CHAR_BIT*sizeof(unsigned int)) /* bits per word */
-
-#define words2bits(b) (((b)-1)/BPW + 1)
-
-#define setbit(a, b) ((a)[(b)/BPW] |= (1 << (b)%BPW))
-#define resetbit(a, b) ((a)[(b)/BPW] &= ~((1 << (b)%BPW)))
-#define testbit(a, b) ((a)[(b)/BPW] & (1 << (b)%BPW))
-
-
/*
** Expression descriptor
*/
@@ -37,7 +25,7 @@ typedef enum {
VK, /* info = index of constant in `k' */
VLOCAL, /* info = local register */
VUPVAL, /* info = index of upvalue in `upvalues' */
- VGLOBAL, /* info = index of global name in `k' */
+ VGLOBAL, /* info = index of table; aux = index of global name in `k' */
VINDEXED, /* info = table register; aux = index register (or `k') */
VRELOCABLE, /* info = instruction pc */
VNONRELOC, /* info = result register */
@@ -53,6 +41,18 @@ typedef struct expdesc {
} expdesc;
+/* describe declared variables */
+typedef struct vardesc {
+ int i; /* if local, its index in `locvars';
+ if global, its name index in `k' */
+ lu_byte k;
+ lu_byte level; /* if local, stack level;
+ if global, corresponding local (NO_REG for free globals) */
+} vardesc;
+
+
+struct BlockCnt; /* defined in lparser.c */
+
/* state needed to generate code for a given function */
typedef struct FuncState {
Proto *f; /* current function header */
@@ -60,21 +60,21 @@ typedef struct FuncState {
struct FuncState *prev; /* enclosing function */
struct LexState *ls; /* lexical state */
struct lua_State *L; /* copy of the Lua state */
- struct Breaklabel *bl; /* chain of breakable blocks */
+ struct BlockCnt *bl; /* chain of current blocks */
int pc; /* next position to code (equivalent to `ncode') */
int lasttarget; /* `pc' of last `jump target' */
int jlt; /* list of jumps to `lasttarget' */
int freereg; /* first free register */
+ int defaultglob; /* where to look for non-declared globals */
int nk; /* number of elements in `k' */
int np; /* number of elements in `p' */
int nlineinfo; /* number of elements in `lineinfo' */
int nlocvars; /* number of elements in `locvars' */
int nactloc; /* number of active local variables */
+ int nactvar; /* number of elements in array `actvar' */
int lastline; /* line where last `lineinfo' was generated */
expdesc upvalues[MAXUPVALUES]; /* upvalues */
- int actloc[MAXLOCALS]; /* local-variable stack (indices to locvars) */
- unsigned int wasup[words2bits(MAXLOCALS)]; /* bit array to mark whether a
- local variable was used as upvalue at some level */
+ vardesc actvar[MAXVARS]; /* declared-variable stack */
} FuncState;
diff --git a/ltests.c b/ltests.c
@@ -1,5 +1,5 @@
/*
-** $Id: ltests.c,v 1.110 2002/03/04 15:26:56 roberto Exp roberto $
+** $Id: ltests.c,v 1.111 2002/03/04 21:29:41 roberto Exp roberto $
** Internal Module for Debugging of the Lua Implementation
** See Copyright Notice in lua.h
*/
@@ -219,7 +219,7 @@ static int get_limits (lua_State *L) {
lua_newtable(L);
setnameval(L, "BITS_INT", BITS_INT);
setnameval(L, "LFPF", LFIELDS_PER_FLUSH);
- setnameval(L, "MAXLOCALS", MAXLOCALS);
+ setnameval(L, "MAXVARS", MAXVARS);
setnameval(L, "MAXPARAMS", MAXPARAMS);
setnameval(L, "MAXSTACK", MAXSTACK);
setnameval(L, "MAXUPVALUES", MAXUPVALUES);