First implementation of global declarations
This commit is contained in:
7
llex.c
7
llex.c
@@ -40,11 +40,16 @@
|
|||||||
|
|
||||||
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
|
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
|
||||||
|
|
||||||
|
#if defined(LUA_COMPAT_GLOBAL)
|
||||||
|
#define GLOBALLEX ".g" /* not recognizable by the scanner */
|
||||||
|
#else
|
||||||
|
#define GLOBALLEX "global"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ORDER RESERVED */
|
/* ORDER RESERVED */
|
||||||
static const char *const luaX_tokens [] = {
|
static const char *const luaX_tokens [] = {
|
||||||
"and", "break", "do", "else", "elseif",
|
"and", "break", "do", "else", "elseif",
|
||||||
"end", "false", "for", "function", "goto", "if",
|
"end", "false", "for", "function", GLOBALLEX, "goto", "if",
|
||||||
"in", "local", "nil", "not", "or", "repeat",
|
"in", "local", "nil", "not", "or", "repeat",
|
||||||
"return", "then", "true", "until", "while",
|
"return", "then", "true", "until", "while",
|
||||||
"//", "..", "...", "==", ">=", "<=", "~=",
|
"//", "..", "...", "==", ">=", "<=", "~=",
|
||||||
|
|||||||
4
llex.h
4
llex.h
@@ -33,8 +33,8 @@ enum RESERVED {
|
|||||||
/* terminal symbols denoted by reserved words */
|
/* terminal symbols denoted by reserved words */
|
||||||
TK_AND = FIRST_RESERVED, TK_BREAK,
|
TK_AND = FIRST_RESERVED, TK_BREAK,
|
||||||
TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
|
TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
|
||||||
TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
|
TK_GLOBAL, TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR,
|
||||||
TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
|
TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
|
||||||
/* other terminal symbols */
|
/* other terminal symbols */
|
||||||
TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
|
TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
|
||||||
TK_SHL, TK_SHR,
|
TK_SHL, TK_SHR,
|
||||||
|
|||||||
111
lparser.c
111
lparser.c
@@ -30,8 +30,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* maximum number of local variables per function (must be smaller
|
/* maximum number of variable declarationss per function (must be
|
||||||
than 250, due to the bytecode format) */
|
smaller than 250, due to the bytecode format) */
|
||||||
#define MAXVARS 200
|
#define MAXVARS 200
|
||||||
|
|
||||||
|
|
||||||
@@ -54,6 +54,7 @@ typedef struct BlockCnt {
|
|||||||
lu_byte upval; /* true if some variable in the block is an upvalue */
|
lu_byte upval; /* true if some variable in the block is an upvalue */
|
||||||
lu_byte isloop; /* 1 if 'block' is a loop; 2 if it has pending breaks */
|
lu_byte isloop; /* 1 if 'block' is a loop; 2 if it has pending breaks */
|
||||||
lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */
|
lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */
|
||||||
|
lu_byte globdec; /* true if inside the scope of any global declaration */
|
||||||
} BlockCnt;
|
} BlockCnt;
|
||||||
|
|
||||||
|
|
||||||
@@ -188,10 +189,10 @@ static short registerlocalvar (LexState *ls, FuncState *fs,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Create a new local variable with the given 'name' and given 'kind'.
|
** Create a new variable with the given 'name' and given 'kind'.
|
||||||
** Return its index in the function.
|
** Return its index in the function.
|
||||||
*/
|
*/
|
||||||
static int new_localvarkind (LexState *ls, TString *name, lu_byte kind) {
|
static int new_varkind (LexState *ls, TString *name, lu_byte kind) {
|
||||||
lua_State *L = ls->L;
|
lua_State *L = ls->L;
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
Dyndata *dyd = ls->dyd;
|
Dyndata *dyd = ls->dyd;
|
||||||
@@ -211,7 +212,7 @@ static int new_localvarkind (LexState *ls, TString *name, lu_byte kind) {
|
|||||||
** Create a new local variable with the given 'name' and regular kind.
|
** Create a new local variable with the given 'name' and regular kind.
|
||||||
*/
|
*/
|
||||||
static int new_localvar (LexState *ls, TString *name) {
|
static int new_localvar (LexState *ls, TString *name) {
|
||||||
return new_localvarkind(ls, name, VDKREG);
|
return new_varkind(ls, name, VDKREG);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define new_localvarliteral(ls,v) \
|
#define new_localvarliteral(ls,v) \
|
||||||
@@ -238,7 +239,7 @@ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
|
|||||||
static lu_byte reglevel (FuncState *fs, int nvar) {
|
static lu_byte reglevel (FuncState *fs, int nvar) {
|
||||||
while (nvar-- > 0) {
|
while (nvar-- > 0) {
|
||||||
Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */
|
Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */
|
||||||
if (vd->vd.kind != RDKCTC) /* is in a register? */
|
if (varinreg(vd)) /* is in a register? */
|
||||||
return cast_byte(vd->vd.ridx + 1);
|
return cast_byte(vd->vd.ridx + 1);
|
||||||
}
|
}
|
||||||
return 0; /* no variables in registers */
|
return 0; /* no variables in registers */
|
||||||
@@ -259,7 +260,7 @@ lu_byte luaY_nvarstack (FuncState *fs) {
|
|||||||
*/
|
*/
|
||||||
static LocVar *localdebuginfo (FuncState *fs, int vidx) {
|
static LocVar *localdebuginfo (FuncState *fs, int vidx) {
|
||||||
Vardesc *vd = getlocalvardesc(fs, vidx);
|
Vardesc *vd = getlocalvardesc(fs, vidx);
|
||||||
if (vd->vd.kind == RDKCTC)
|
if (!varinreg(vd))
|
||||||
return NULL; /* no debug info. for constants */
|
return NULL; /* no debug info. for constants */
|
||||||
else {
|
else {
|
||||||
int idx = vd->vd.pidx;
|
int idx = vd->vd.pidx;
|
||||||
@@ -401,7 +402,9 @@ static int searchvar (FuncState *fs, TString *n, expdesc *var) {
|
|||||||
if (eqstr(n, vd->vd.name)) { /* found? */
|
if (eqstr(n, vd->vd.name)) { /* found? */
|
||||||
if (vd->vd.kind == RDKCTC) /* compile-time constant? */
|
if (vd->vd.kind == RDKCTC) /* compile-time constant? */
|
||||||
init_exp(var, VCONST, fs->firstlocal + i);
|
init_exp(var, VCONST, fs->firstlocal + i);
|
||||||
else /* real variable */
|
else if (vd->vd.kind == GDKREG || vd->vd.kind == GDKCONST)
|
||||||
|
init_exp(var, VGLOBAL, i);
|
||||||
|
else /* local variable */
|
||||||
init_var(fs, var, i);
|
init_var(fs, var, i);
|
||||||
return cast_int(var->k);
|
return cast_int(var->k);
|
||||||
}
|
}
|
||||||
@@ -440,25 +443,24 @@ static void marktobeclosed (FuncState *fs) {
|
|||||||
** 'var' as 'void' as a flag.
|
** 'var' as 'void' as a flag.
|
||||||
*/
|
*/
|
||||||
static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
||||||
if (fs == NULL) /* no more levels? */
|
int v = searchvar(fs, n, var); /* look up locals at current level */
|
||||||
init_exp(var, VVOID, 0); /* default is global */
|
if (v >= 0) { /* found? */
|
||||||
else {
|
if (v == VLOCAL && !base)
|
||||||
int v = searchvar(fs, n, var); /* look up locals at current level */
|
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
|
||||||
if (v >= 0) { /* found? */
|
}
|
||||||
if (v == VLOCAL && !base)
|
else { /* not found as local at current level; try upvalues */
|
||||||
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
|
int idx = searchupvalue(fs, n); /* try existing upvalues */
|
||||||
}
|
if (idx < 0) { /* not found? */
|
||||||
else { /* not found as local at current level; try upvalues */
|
if (fs->prev != NULL) /* more levels? */
|
||||||
int idx = searchupvalue(fs, n); /* try existing upvalues */
|
|
||||||
if (idx < 0) { /* not found? */
|
|
||||||
singlevaraux(fs->prev, n, var, 0); /* try upper levels */
|
singlevaraux(fs->prev, n, var, 0); /* try upper levels */
|
||||||
if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
|
else /* no more levels */
|
||||||
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
init_exp(var, VGLOBAL, -1); /* global by default */
|
||||||
else /* it is a global or a constant */
|
if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
|
||||||
return; /* don't need to do anything at this level */
|
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
||||||
}
|
else /* it is a global or a constant */
|
||||||
init_exp(var, VUPVAL, idx); /* new or old upvalue */
|
return; /* don't need to do anything at this level */
|
||||||
}
|
}
|
||||||
|
init_exp(var, VUPVAL, idx); /* new or old upvalue */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,10 +473,15 @@ static void singlevar (LexState *ls, expdesc *var) {
|
|||||||
TString *varname = str_checkname(ls);
|
TString *varname = str_checkname(ls);
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
singlevaraux(fs, varname, var, 1);
|
singlevaraux(fs, varname, var, 1);
|
||||||
if (var->k == VVOID) { /* global name? */
|
if (var->k == VGLOBAL) { /* global name? */
|
||||||
expdesc key;
|
expdesc key;
|
||||||
|
/* global by default in the scope of a global declaration? */
|
||||||
|
if (var->u.info == -1 && fs->bl->globdec)
|
||||||
|
luaK_semerror(ls, "variable '%s' not declared", getstr(varname));
|
||||||
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
|
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
|
||||||
lua_assert(var->k != VVOID); /* this one must exist */
|
if (var->k == VGLOBAL)
|
||||||
|
luaK_semerror(ls, "_ENV is global when accessing variable '%s'",
|
||||||
|
getstr(varname));
|
||||||
luaK_exp2anyregup(fs, var); /* but could be a constant */
|
luaK_exp2anyregup(fs, var); /* but could be a constant */
|
||||||
codestring(&key, varname); /* key is variable name */
|
codestring(&key, varname); /* key is variable name */
|
||||||
luaK_indexed(fs, var, &key); /* env[varname] */
|
luaK_indexed(fs, var, &key); /* env[varname] */
|
||||||
@@ -664,8 +671,13 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
|
|||||||
bl->firstlabel = fs->ls->dyd->label.n;
|
bl->firstlabel = fs->ls->dyd->label.n;
|
||||||
bl->firstgoto = fs->ls->dyd->gt.n;
|
bl->firstgoto = fs->ls->dyd->gt.n;
|
||||||
bl->upval = 0;
|
bl->upval = 0;
|
||||||
|
/* inherit 'insidetbc' from enclosing block */
|
||||||
bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
|
bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
|
||||||
bl->previous = fs->bl;
|
/* inherit 'globdec' from enclosing block or enclosing function */
|
||||||
|
bl->globdec = fs->bl != NULL ? fs->bl->globdec
|
||||||
|
: fs->prev != NULL ? fs->prev->bl->globdec
|
||||||
|
: 0; /* chunk's first block */
|
||||||
|
bl->previous = fs->bl; /* link block in function's block list */
|
||||||
fs->bl = bl;
|
fs->bl = bl;
|
||||||
lua_assert(fs->freereg == luaY_nvarstack(fs));
|
lua_assert(fs->freereg == luaY_nvarstack(fs));
|
||||||
}
|
}
|
||||||
@@ -1600,7 +1612,7 @@ static void fornum (LexState *ls, TString *varname, int line) {
|
|||||||
int base = fs->freereg;
|
int base = fs->freereg;
|
||||||
new_localvarliteral(ls, "(for state)");
|
new_localvarliteral(ls, "(for state)");
|
||||||
new_localvarliteral(ls, "(for state)");
|
new_localvarliteral(ls, "(for state)");
|
||||||
new_localvarkind(ls, varname, RDKCONST); /* control variable */
|
new_varkind(ls, varname, RDKCONST); /* control variable */
|
||||||
checknext(ls, '=');
|
checknext(ls, '=');
|
||||||
exp1(ls); /* initial value */
|
exp1(ls); /* initial value */
|
||||||
checknext(ls, ',');
|
checknext(ls, ',');
|
||||||
@@ -1627,7 +1639,7 @@ static void forlist (LexState *ls, TString *indexname) {
|
|||||||
new_localvarliteral(ls, "(for state)"); /* iterator function */
|
new_localvarliteral(ls, "(for state)"); /* iterator function */
|
||||||
new_localvarliteral(ls, "(for state)"); /* state */
|
new_localvarliteral(ls, "(for state)"); /* state */
|
||||||
new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */
|
new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */
|
||||||
new_localvarkind(ls, indexname, RDKCONST); /* control variable */
|
new_varkind(ls, indexname, RDKCONST); /* control variable */
|
||||||
/* other declared variables */
|
/* other declared variables */
|
||||||
while (testnext(ls, ',')) {
|
while (testnext(ls, ',')) {
|
||||||
new_localvar(ls, str_checkname(ls));
|
new_localvar(ls, str_checkname(ls));
|
||||||
@@ -1702,7 +1714,7 @@ static void localfunc (LexState *ls) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static lu_byte getlocalattribute (LexState *ls) {
|
static lu_byte getvarattribute (LexState *ls) {
|
||||||
/* ATTRIB -> ['<' Name '>'] */
|
/* ATTRIB -> ['<' Name '>'] */
|
||||||
if (testnext(ls, '<')) {
|
if (testnext(ls, '<')) {
|
||||||
TString *ts = str_checkname(ls);
|
TString *ts = str_checkname(ls);
|
||||||
@@ -1738,8 +1750,8 @@ static void localstat (LexState *ls) {
|
|||||||
expdesc e;
|
expdesc e;
|
||||||
do {
|
do {
|
||||||
TString *vname = str_checkname(ls);
|
TString *vname = str_checkname(ls);
|
||||||
lu_byte kind = getlocalattribute(ls);
|
lu_byte kind = getvarattribute(ls);
|
||||||
vidx = new_localvarkind(ls, vname, kind);
|
vidx = new_varkind(ls, vname, kind);
|
||||||
if (kind == RDKTOCLOSE) { /* to-be-closed? */
|
if (kind == RDKTOCLOSE) { /* to-be-closed? */
|
||||||
if (toclose != -1) /* one already present? */
|
if (toclose != -1) /* one already present? */
|
||||||
luaK_semerror(ls, "multiple to-be-closed variables in local list");
|
luaK_semerror(ls, "multiple to-be-closed variables in local list");
|
||||||
@@ -1769,6 +1781,24 @@ static void localstat (LexState *ls) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void globalstat (LexState *ls) {
|
||||||
|
FuncState *fs = ls->fs;
|
||||||
|
luaX_next(ls); /* skip 'global' */
|
||||||
|
do {
|
||||||
|
TString *vname = str_checkname(ls);
|
||||||
|
lu_byte kind = getvarattribute(ls);
|
||||||
|
if (kind == RDKTOCLOSE)
|
||||||
|
luaK_semerror(ls, "global variable ('%s') cannot be to-be-closed",
|
||||||
|
getstr(vname));
|
||||||
|
/* adjust kind for global variable */
|
||||||
|
kind = (kind == VDKREG) ? GDKREG : GDKCONST;
|
||||||
|
new_varkind(ls, vname, kind);
|
||||||
|
fs->nactvar++; /* activate declaration */
|
||||||
|
} while (testnext(ls, ','));
|
||||||
|
fs->bl->globdec = 1; /* code is in the scope of a global declaration */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int funcname (LexState *ls, expdesc *v) {
|
static int funcname (LexState *ls, expdesc *v) {
|
||||||
/* funcname -> NAME {fieldsel} [':' NAME] */
|
/* funcname -> NAME {fieldsel} [':' NAME] */
|
||||||
int ismethod = 0;
|
int ismethod = 0;
|
||||||
@@ -1888,6 +1918,10 @@ static void statement (LexState *ls) {
|
|||||||
localstat(ls);
|
localstat(ls);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TK_GLOBAL: { /* stat -> globalstat */
|
||||||
|
globalstat(ls);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case TK_DBCOLON: { /* stat -> label */
|
case TK_DBCOLON: { /* stat -> label */
|
||||||
luaX_next(ls); /* skip double colon */
|
luaX_next(ls); /* skip double colon */
|
||||||
labelstat(ls, str_checkname(ls), line);
|
labelstat(ls, str_checkname(ls), line);
|
||||||
@@ -1907,6 +1941,17 @@ static void statement (LexState *ls) {
|
|||||||
gotostat(ls, line);
|
gotostat(ls, line);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TK_NAME: {
|
||||||
|
/* compatibility code to parse global keyword when "global"
|
||||||
|
is not reserved */
|
||||||
|
if (eqstr(ls->t.seminfo.ts, luaS_newliteral(ls->L, "global"))) {
|
||||||
|
int lk = luaX_lookahead(ls);
|
||||||
|
if (lk == TK_NAME) { /* 'global name'? */
|
||||||
|
globalstat(ls);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} /* else... */
|
||||||
|
} /* FALLTHROUGH */
|
||||||
default: { /* stat -> func | assignment */
|
default: { /* stat -> func | assignment */
|
||||||
exprstat(ls);
|
exprstat(ls);
|
||||||
break;
|
break;
|
||||||
|
|||||||
15
lparser.h
15
lparser.h
@@ -37,6 +37,9 @@ typedef enum {
|
|||||||
info = result register */
|
info = result register */
|
||||||
VLOCAL, /* local variable; var.ridx = register index;
|
VLOCAL, /* local variable; var.ridx = register index;
|
||||||
var.vidx = relative index in 'actvar.arr' */
|
var.vidx = relative index in 'actvar.arr' */
|
||||||
|
VGLOBAL, /* global variable;
|
||||||
|
info = relative index in 'actvar.arr' (or -1 for
|
||||||
|
implicit declaration) */
|
||||||
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
|
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
|
||||||
VCONST, /* compile-time <const> variable;
|
VCONST, /* compile-time <const> variable;
|
||||||
info = absolute index in 'actvar.arr' */
|
info = absolute index in 'actvar.arr' */
|
||||||
@@ -87,10 +90,16 @@ typedef struct expdesc {
|
|||||||
|
|
||||||
|
|
||||||
/* kinds of variables */
|
/* kinds of variables */
|
||||||
#define VDKREG 0 /* regular */
|
#define VDKREG 0 /* regular local */
|
||||||
#define RDKCONST 1 /* constant */
|
#define RDKCONST 1 /* local constant */
|
||||||
#define RDKTOCLOSE 2 /* to-be-closed */
|
#define RDKTOCLOSE 2 /* to-be-closed */
|
||||||
#define RDKCTC 3 /* compile-time constant */
|
#define RDKCTC 3 /* local compile-time constant */
|
||||||
|
#define GDKREG 4 /* regular global */
|
||||||
|
#define GDKCONST 5 /* global constant */
|
||||||
|
|
||||||
|
/* variables that live in registers */
|
||||||
|
#define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE)
|
||||||
|
|
||||||
|
|
||||||
/* description of an active local variable */
|
/* description of an active local variable */
|
||||||
typedef union Vardesc {
|
typedef union Vardesc {
|
||||||
|
|||||||
1
ltests.h
1
ltests.h
@@ -14,6 +14,7 @@
|
|||||||
/* test Lua with compatibility code */
|
/* test Lua with compatibility code */
|
||||||
#define LUA_COMPAT_MATHLIB
|
#define LUA_COMPAT_MATHLIB
|
||||||
#define LUA_COMPAT_LT_LE
|
#define LUA_COMPAT_LT_LE
|
||||||
|
#undef LUA_COMPAT_GLOBAL
|
||||||
|
|
||||||
|
|
||||||
#define LUA_DEBUG
|
#define LUA_DEBUG
|
||||||
|
|||||||
@@ -355,6 +355,12 @@
|
|||||||
** ===================================================================
|
** ===================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word
|
||||||
|
*/
|
||||||
|
#define LUA_COMPAT_GLOBAL
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
|
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
|
||||||
** You can define it to get all options, or change specific options
|
** You can define it to get all options, or change specific options
|
||||||
|
|||||||
184
manual/manual.of
184
manual/manual.of
@@ -213,11 +213,88 @@ of a given value @seeF{type}.
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@sect2{globalenv| @title{Environments and the Global Environment}
|
@sect2{globalenv| @title{Scopes, Variables, and Environments}
|
||||||
|
@index{visibility}
|
||||||
|
|
||||||
|
A variable name refers to a global or a local variable according
|
||||||
|
to the declaration that is in context at that point of the code.
|
||||||
|
(For the purposes of this discussion,
|
||||||
|
a function's formal parameter is equivalent to a local variable.)
|
||||||
|
|
||||||
|
All chunks start with an implicit declaration @T{global *},
|
||||||
|
which declares all free names as global variables;
|
||||||
|
this implicit declaration becomes void inside the scope of any other
|
||||||
|
@Rw{global} declaration, regardless of the names being declared.
|
||||||
|
@verbatim{
|
||||||
|
X = 1 -- Ok, global by default
|
||||||
|
do
|
||||||
|
global Y -- voids implicit initial declaration
|
||||||
|
X = 1 -- ERROR, X not declared
|
||||||
|
Y = 1 -- Ok, Y declared as global
|
||||||
|
end
|
||||||
|
X = 2 -- Ok, global by default again
|
||||||
|
}
|
||||||
|
So, outside any global declaration,
|
||||||
|
Lua works as @x{global-by-default}.
|
||||||
|
Inside any global declaration,
|
||||||
|
Lua works without a default:
|
||||||
|
All variables must be declared.
|
||||||
|
|
||||||
|
Lua is a lexically scoped language.
|
||||||
|
The scope of a variable declaration begins at the first statement after
|
||||||
|
the declaration and lasts until the last non-void statement
|
||||||
|
of the innermost block that includes the declaration.
|
||||||
|
(@emph{Void statements} are labels and empty statements.)
|
||||||
|
|
||||||
|
A declaration shadows any declaration for the same name that
|
||||||
|
is in context at the point of the declaration. Inside this
|
||||||
|
shadow, any outer declaration for that name is void.
|
||||||
|
See the next example:
|
||||||
|
@verbatim{
|
||||||
|
global print, x
|
||||||
|
x = 10 -- global variable
|
||||||
|
do -- new block
|
||||||
|
local x = x -- new 'x', with value 10
|
||||||
|
print(x) --> 10
|
||||||
|
x = x+1
|
||||||
|
do -- another block
|
||||||
|
local x = x+1 -- another 'x'
|
||||||
|
print(x) --> 12
|
||||||
|
end
|
||||||
|
print(x) --> 11
|
||||||
|
end
|
||||||
|
print(x) --> 10 (the global one)
|
||||||
|
}
|
||||||
|
|
||||||
|
Notice that, in a declaration like @T{local x = x},
|
||||||
|
the new @id{x} being declared is not in scope yet,
|
||||||
|
and so the @id{x} in the left-hand side refers to the outside variable.
|
||||||
|
|
||||||
|
Because of the @x{lexical scoping} rules,
|
||||||
|
local variables can be freely accessed by functions
|
||||||
|
defined inside their scope.
|
||||||
|
A local variable used by an inner function is called an @def{upvalue}
|
||||||
|
(or @emphx{external local variable}, or simply @emphx{external variable})
|
||||||
|
inside the inner function.
|
||||||
|
|
||||||
|
Notice that each execution of a @Rw{local} statement
|
||||||
|
defines new local variables.
|
||||||
|
Consider the following example:
|
||||||
|
@verbatim{
|
||||||
|
a = {}
|
||||||
|
local x = 20
|
||||||
|
for i = 1, 10 do
|
||||||
|
local y = 0
|
||||||
|
a[i] = function () y = y + 1; return x + y end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
The loop creates ten closures
|
||||||
|
(that is, ten instances of the anonymous function).
|
||||||
|
Each of these closures uses a different @id{y} variable,
|
||||||
|
while all of them share the same @id{x}.
|
||||||
|
|
||||||
As we will discuss further in @refsec{variables} and @refsec{assignment},
|
As we will discuss further in @refsec{variables} and @refsec{assignment},
|
||||||
any reference to a free name
|
any reference to a global variable @id{var}
|
||||||
(that is, a name not bound to any declaration) @id{var}
|
|
||||||
is syntactically translated to @T{_ENV.var}.
|
is syntactically translated to @T{_ENV.var}.
|
||||||
Moreover, every chunk is compiled in the scope of
|
Moreover, every chunk is compiled in the scope of
|
||||||
an external local variable named @id{_ENV} @see{chunks},
|
an external local variable named @id{_ENV} @see{chunks},
|
||||||
@@ -225,12 +302,14 @@ so @id{_ENV} itself is never a free name in a chunk.
|
|||||||
|
|
||||||
Despite the existence of this external @id{_ENV} variable and
|
Despite the existence of this external @id{_ENV} variable and
|
||||||
the translation of free names,
|
the translation of free names,
|
||||||
@id{_ENV} is a completely regular name.
|
@id{_ENV} is a regular name.
|
||||||
In particular,
|
In particular,
|
||||||
you can define new variables and parameters with that name.
|
you can define new variables and parameters with that name.
|
||||||
Each reference to a free name uses the @id{_ENV} that is
|
(However, you should not define @id{_ENV} as a global variable,
|
||||||
visible at that point in the program,
|
otherwise @T{_ENV.var} would translate to
|
||||||
following the usual visibility rules of Lua @see{visibility}.
|
@T{_ENV._ENV.var} and so on, in an infinite loop.)
|
||||||
|
Each reference to a global variable name uses the @id{_ENV} that is
|
||||||
|
visible at that point in the program.
|
||||||
|
|
||||||
Any table used as the value of @id{_ENV} is called an @def{environment}.
|
Any table used as the value of @id{_ENV} is called an @def{environment}.
|
||||||
|
|
||||||
@@ -244,8 +323,8 @@ When Lua loads a chunk,
|
|||||||
the default value for its @id{_ENV} variable
|
the default value for its @id{_ENV} variable
|
||||||
is the global environment @seeF{load}.
|
is the global environment @seeF{load}.
|
||||||
Therefore, by default,
|
Therefore, by default,
|
||||||
free names in Lua code refer to entries in the global environment
|
global variables in Lua code refer to entries in the global environment
|
||||||
and, therefore, they are also called @def{global variables}.
|
and, therefore, they act as conventional global variables.
|
||||||
Moreover, all standard libraries are loaded in the global environment
|
Moreover, all standard libraries are loaded in the global environment
|
||||||
and some functions there operate on that environment.
|
and some functions there operate on that environment.
|
||||||
You can use @Lid{load} (or @Lid{loadfile})
|
You can use @Lid{load} (or @Lid{loadfile})
|
||||||
@@ -1198,17 +1277,15 @@ global variables, local variables, and table fields.
|
|||||||
|
|
||||||
A single name can denote a global variable or a local variable
|
A single name can denote a global variable or a local variable
|
||||||
(or a function's formal parameter,
|
(or a function's formal parameter,
|
||||||
which is a particular kind of local variable):
|
which is a particular kind of local variable) @see{globalenv}:
|
||||||
@Produc{
|
@Produc{
|
||||||
@producname{var}@producbody{@bnfNter{Name}}
|
@producname{var}@producbody{@bnfNter{Name}}
|
||||||
}
|
}
|
||||||
@bnfNter{Name} denotes identifiers @see{lexical}.
|
@bnfNter{Name} denotes identifiers @see{lexical}.
|
||||||
|
|
||||||
Any variable name is assumed to be global unless explicitly declared
|
Because variables are @emph{lexically scoped},
|
||||||
as a local @see{localvar}.
|
|
||||||
@x{Local variables} are @emph{lexically scoped}:
|
|
||||||
local variables can be freely accessed by functions
|
local variables can be freely accessed by functions
|
||||||
defined inside their scope @see{visibility}.
|
defined inside their scope @see{globalenv}.
|
||||||
|
|
||||||
Before the first assignment to a variable, its value is @nil.
|
Before the first assignment to a variable, its value is @nil.
|
||||||
|
|
||||||
@@ -1227,8 +1304,6 @@ The syntax @id{var.Name} is just syntactic sugar for
|
|||||||
|
|
||||||
An access to a global variable @id{x}
|
An access to a global variable @id{x}
|
||||||
is equivalent to @id{_ENV.x}.
|
is equivalent to @id{_ENV.x}.
|
||||||
Due to the way that chunks are compiled,
|
|
||||||
the variable @id{_ENV} itself is never global @see{globalenv}.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1571,17 +1646,18 @@ Function calls are explained in @See{functioncall}.
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@sect3{localvar| @title{Local Declarations}
|
@sect3{localvar| @title{Variable Declarations}
|
||||||
@x{Local variables} can be declared anywhere inside a block.
|
Local and global variables can be declared anywhere inside a block.
|
||||||
The declaration can include an initialization:
|
The declaration for locals can include an initialization:
|
||||||
@Produc{
|
@Produc{
|
||||||
@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}}
|
@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}}
|
||||||
|
@producname{stat}@producbody{@Rw{global} attnamelist}
|
||||||
@producname{attnamelist}@producbody{
|
@producname{attnamelist}@producbody{
|
||||||
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
|
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
|
||||||
}
|
}
|
||||||
If present, an initial assignment has the same semantics
|
If present, an initial assignment has the same semantics
|
||||||
of a multiple assignment @see{assignment}.
|
of a multiple assignment @see{assignment}.
|
||||||
Otherwise, all variables are initialized with @nil.
|
Otherwise, all local variables are initialized with @nil.
|
||||||
|
|
||||||
Each variable name may be postfixed by an attribute
|
Each variable name may be postfixed by an attribute
|
||||||
(a name between angle brackets):
|
(a name between angle brackets):
|
||||||
@@ -1595,11 +1671,22 @@ that is, a variable that cannot be assigned to
|
|||||||
after its initialization;
|
after its initialization;
|
||||||
and @id{close}, which declares a to-be-closed variable @see{to-be-closed}.
|
and @id{close}, which declares a to-be-closed variable @see{to-be-closed}.
|
||||||
A list of variables can contain at most one to-be-closed variable.
|
A list of variables can contain at most one to-be-closed variable.
|
||||||
|
Only local variables can have the @id{close} attribute.
|
||||||
|
|
||||||
|
Note that, for global variables,
|
||||||
|
the @emph{read-only} atribute is only a syntactical restriction:
|
||||||
|
@verbatim{
|
||||||
|
global X <const>
|
||||||
|
X = 1 -- ERROR
|
||||||
|
_ENV.X = 1 -- Ok
|
||||||
|
foo() -- 'foo' can freely change the global X
|
||||||
|
}
|
||||||
|
|
||||||
A chunk is also a block @see{chunks},
|
A chunk is also a block @see{chunks},
|
||||||
and so local variables can be declared in a chunk outside any explicit block.
|
and so variables can be declared in a chunk outside any explicit block.
|
||||||
|
|
||||||
The visibility rules for local variables are explained in @See{visibility}.
|
The visibility rules for variable declarations
|
||||||
|
are explained in @See{globalenv}.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2356,58 +2443,6 @@ return x,y,f() -- returns x, y, and all results from f().
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@sect2{visibility| @title{Visibility Rules}
|
|
||||||
|
|
||||||
@index{visibility}
|
|
||||||
Lua is a lexically scoped language.
|
|
||||||
The scope of a local variable begins at the first statement after
|
|
||||||
its declaration and lasts until the last non-void statement
|
|
||||||
of the innermost block that includes the declaration.
|
|
||||||
(@emph{Void statements} are labels and empty statements.)
|
|
||||||
Consider the following example:
|
|
||||||
@verbatim{
|
|
||||||
x = 10 -- global variable
|
|
||||||
do -- new block
|
|
||||||
local x = x -- new 'x', with value 10
|
|
||||||
print(x) --> 10
|
|
||||||
x = x+1
|
|
||||||
do -- another block
|
|
||||||
local x = x+1 -- another 'x'
|
|
||||||
print(x) --> 12
|
|
||||||
end
|
|
||||||
print(x) --> 11
|
|
||||||
end
|
|
||||||
print(x) --> 10 (the global one)
|
|
||||||
}
|
|
||||||
|
|
||||||
Notice that, in a declaration like @T{local x = x},
|
|
||||||
the new @id{x} being declared is not in scope yet,
|
|
||||||
and so the second @id{x} refers to the outside variable.
|
|
||||||
|
|
||||||
Because of the @x{lexical scoping} rules,
|
|
||||||
local variables can be freely accessed by functions
|
|
||||||
defined inside their scope.
|
|
||||||
A local variable used by an inner function is called an @def{upvalue}
|
|
||||||
(or @emphx{external local variable}, or simply @emphx{external variable})
|
|
||||||
inside the inner function.
|
|
||||||
|
|
||||||
Notice that each execution of a @Rw{local} statement
|
|
||||||
defines new local variables.
|
|
||||||
Consider the following example:
|
|
||||||
@verbatim{
|
|
||||||
a = {}
|
|
||||||
local x = 20
|
|
||||||
for i = 1, 10 do
|
|
||||||
local y = 0
|
|
||||||
a[i] = function () y = y + 1; return x + y end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
The loop creates ten closures
|
|
||||||
(that is, ten instances of the anonymous function).
|
|
||||||
Each of these closures uses a different @id{y} variable,
|
|
||||||
while all of them share the same @id{x}.
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9535,6 +9570,7 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
|
|||||||
@OrNL @Rw{function} funcname funcbody
|
@OrNL @Rw{function} funcname funcbody
|
||||||
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
|
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
|
||||||
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
|
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
|
||||||
|
@OrNL @Rw{global} attnamelist
|
||||||
}
|
}
|
||||||
|
|
||||||
@producname{attnamelist}@producbody{
|
@producname{attnamelist}@producbody{
|
||||||
|
|||||||
@@ -349,6 +349,7 @@ end, "crl")
|
|||||||
|
|
||||||
|
|
||||||
function f(a,b)
|
function f(a,b)
|
||||||
|
global collectgarbage, assert, g, string
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
local _, x = debug.getlocal(1, 1)
|
local _, x = debug.getlocal(1, 1)
|
||||||
local _, y = debug.getlocal(1, 2)
|
local _, y = debug.getlocal(1, 2)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
-- $Id: testes/goto.lua $
|
-- $Id: testes/goto.lua $
|
||||||
-- See Copyright Notice in file lua.h
|
-- See Copyright Notice in file lua.h
|
||||||
|
|
||||||
|
print("testing goto and global declarations")
|
||||||
|
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
|
|
||||||
local function errmsg (code, m)
|
local function errmsg (code, m)
|
||||||
@@ -280,7 +282,47 @@ end
|
|||||||
|
|
||||||
|
|
||||||
foo()
|
foo()
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
do
|
||||||
|
global print, load, T<const>; global assert<const>
|
||||||
|
global string
|
||||||
|
|
||||||
|
local function checkerr (code, err)
|
||||||
|
local st, msg = load(code)
|
||||||
|
assert(not st and string.find(msg, err))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- globals must be declared after a global declaration
|
||||||
|
checkerr("global none; X = 1", "variable 'X'")
|
||||||
|
|
||||||
|
-- global variables cannot be to-be-closed
|
||||||
|
checkerr("global X<close>", "cannot be")
|
||||||
|
|
||||||
|
do
|
||||||
|
local X = 10
|
||||||
|
do global X; X = 20 end
|
||||||
|
assert(X == 10) -- local X
|
||||||
|
end
|
||||||
|
assert(_ENV.X == 20) -- global X
|
||||||
|
|
||||||
|
-- '_ENV' cannot be global
|
||||||
|
checkerr("global _ENV, a; a = 10", "variable 'a'")
|
||||||
|
|
||||||
|
-- global declarations inside functions
|
||||||
|
checkerr([[
|
||||||
|
global none
|
||||||
|
local function foo () XXX = 1 end --< ERROR]], "variable 'XXX'")
|
||||||
|
|
||||||
|
if not T then -- when not in "test mode", "global" isn't reserved
|
||||||
|
assert(load("global = 1; return global")() == 1)
|
||||||
|
print " ('global' is not a reserved word)"
|
||||||
|
else
|
||||||
|
-- "global" reserved, cannot be used as a variable
|
||||||
|
assert(not load("global = 1; return global"))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
print'OK'
|
print'OK'
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
|
|
||||||
print("testing numbers and math lib")
|
print("testing numbers and math lib")
|
||||||
|
|
||||||
|
local math = require "math"
|
||||||
|
local string = require "string"
|
||||||
|
|
||||||
|
global none
|
||||||
|
|
||||||
|
global print, assert, pcall, type, pairs, load
|
||||||
|
global tonumber, tostring, select
|
||||||
|
|
||||||
local minint <const> = math.mininteger
|
local minint <const> = math.mininteger
|
||||||
local maxint <const> = math.maxinteger
|
local maxint <const> = math.maxinteger
|
||||||
|
|
||||||
@@ -184,7 +192,7 @@ do
|
|||||||
for i = -3, 3 do -- variables avoid constant folding
|
for i = -3, 3 do -- variables avoid constant folding
|
||||||
for j = -3, 3 do
|
for j = -3, 3 do
|
||||||
-- domain errors (0^(-n)) are not portable
|
-- domain errors (0^(-n)) are not portable
|
||||||
if not _port or i ~= 0 or j > 0 then
|
if not _ENV._port or i ~= 0 or j > 0 then
|
||||||
assert(eq(i^j, 1 / i^(-j)))
|
assert(eq(i^j, 1 / i^(-j)))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -430,7 +438,7 @@ for i = 2,36 do
|
|||||||
assert(tonumber('\t10000000000\t', i) == i10)
|
assert(tonumber('\t10000000000\t', i) == i10)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not _soft then
|
if not _ENV._soft then
|
||||||
-- tests with very long numerals
|
-- tests with very long numerals
|
||||||
assert(tonumber("0x"..string.rep("f", 13)..".0") == 2.0^(4*13) - 1)
|
assert(tonumber("0x"..string.rep("f", 13)..".0") == 2.0^(4*13) - 1)
|
||||||
assert(tonumber("0x"..string.rep("f", 150)..".0") == 2.0^(4*150) - 1)
|
assert(tonumber("0x"..string.rep("f", 150)..".0") == 2.0^(4*150) - 1)
|
||||||
@@ -632,7 +640,7 @@ assert(maxint % -2 == -1)
|
|||||||
|
|
||||||
-- non-portable tests because Windows C library cannot compute
|
-- non-portable tests because Windows C library cannot compute
|
||||||
-- fmod(1, huge) correctly
|
-- fmod(1, huge) correctly
|
||||||
if not _port then
|
if not _ENV._port then
|
||||||
local function anan (x) assert(isNaN(x)) end -- assert Not a Number
|
local function anan (x) assert(isNaN(x)) end -- assert Not a Number
|
||||||
anan(0.0 % 0)
|
anan(0.0 % 0)
|
||||||
anan(1.3 % 0)
|
anan(1.3 % 0)
|
||||||
@@ -779,6 +787,7 @@ assert(a == '10' and b == '20')
|
|||||||
|
|
||||||
do
|
do
|
||||||
print("testing -0 and NaN")
|
print("testing -0 and NaN")
|
||||||
|
global rawset, undef
|
||||||
local mz <const> = -0.0
|
local mz <const> = -0.0
|
||||||
local z <const> = 0.0
|
local z <const> = 0.0
|
||||||
assert(mz == z)
|
assert(mz == z)
|
||||||
@@ -1074,6 +1083,7 @@ do
|
|||||||
-- different numbers should print differently.
|
-- different numbers should print differently.
|
||||||
-- check pairs of floats with minimum detectable difference
|
-- check pairs of floats with minimum detectable difference
|
||||||
local p = floatbits - 1
|
local p = floatbits - 1
|
||||||
|
global ipairs
|
||||||
for i = 1, maxexp - 1 do
|
for i = 1, maxexp - 1 do
|
||||||
for _, i in ipairs{-i, i} do
|
for _, i in ipairs{-i, i} do
|
||||||
local x = 2^i
|
local x = 2^i
|
||||||
|
|||||||
Reference in New Issue
Block a user