From 7ff21273d66541ac19ad817f90e8bcea5790f355 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 14 Mar 2002 15:01:52 -0300 Subject: [PATCH] implementation of `global' statement --- llimits.h | 8 +- lparser.c | 269 +++++++++++++++++++++++++++++++++++++----------------- lparser.h | 36 ++++---- ltests.c | 4 +- 4 files changed, 211 insertions(+), 106 deletions(-) diff --git a/llimits.h b/llimits.h index 22cfe305..9f6d6520 100644 --- 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 (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; inactloc; 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; if->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), - " expected"); + check_condition(&lexstate, (lexstate.t.token == TK_EOS), " 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 index 5df59891..44f1759e 100644 --- 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 index 50836dfc..6dbc2bb1 100644 --- 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);