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')
|
||||
|
||||
#if defined(LUA_COMPAT_GLOBAL)
|
||||
#define GLOBALLEX ".g" /* not recognizable by the scanner */
|
||||
#else
|
||||
#define GLOBALLEX "global"
|
||||
#endif
|
||||
|
||||
/* ORDER RESERVED */
|
||||
static const char *const luaX_tokens [] = {
|
||||
"and", "break", "do", "else", "elseif",
|
||||
"end", "false", "for", "function", "goto", "if",
|
||||
"end", "false", "for", "function", GLOBALLEX, "goto", "if",
|
||||
"in", "local", "nil", "not", "or", "repeat",
|
||||
"return", "then", "true", "until", "while",
|
||||
"//", "..", "...", "==", ">=", "<=", "~=",
|
||||
|
||||
4
llex.h
4
llex.h
@@ -33,8 +33,8 @@ enum RESERVED {
|
||||
/* terminal symbols denoted by reserved words */
|
||||
TK_AND = FIRST_RESERVED, TK_BREAK,
|
||||
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_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
|
||||
TK_GLOBAL, TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR,
|
||||
TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
|
||||
/* other terminal symbols */
|
||||
TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
|
||||
TK_SHL, TK_SHR,
|
||||
|
||||
111
lparser.c
111
lparser.c
@@ -30,8 +30,8 @@
|
||||
|
||||
|
||||
|
||||
/* maximum number of local variables per function (must be smaller
|
||||
than 250, due to the bytecode format) */
|
||||
/* maximum number of variable declarationss per function (must be
|
||||
smaller than 250, due to the bytecode format) */
|
||||
#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 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 globdec; /* true if inside the scope of any global declaration */
|
||||
} 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.
|
||||
*/
|
||||
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;
|
||||
FuncState *fs = ls->fs;
|
||||
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.
|
||||
*/
|
||||
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) \
|
||||
@@ -238,7 +239,7 @@ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
|
||||
static lu_byte reglevel (FuncState *fs, int nvar) {
|
||||
while (nvar-- > 0) {
|
||||
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 0; /* no variables in registers */
|
||||
@@ -259,7 +260,7 @@ lu_byte luaY_nvarstack (FuncState *fs) {
|
||||
*/
|
||||
static LocVar *localdebuginfo (FuncState *fs, int vidx) {
|
||||
Vardesc *vd = getlocalvardesc(fs, vidx);
|
||||
if (vd->vd.kind == RDKCTC)
|
||||
if (!varinreg(vd))
|
||||
return NULL; /* no debug info. for constants */
|
||||
else {
|
||||
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 (vd->vd.kind == RDKCTC) /* compile-time constant? */
|
||||
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);
|
||||
return cast_int(var->k);
|
||||
}
|
||||
@@ -440,25 +443,24 @@ static void marktobeclosed (FuncState *fs) {
|
||||
** 'var' as 'void' as a flag.
|
||||
*/
|
||||
static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
||||
if (fs == NULL) /* no more levels? */
|
||||
init_exp(var, VVOID, 0); /* default is global */
|
||||
else {
|
||||
int v = searchvar(fs, n, var); /* look up locals at current level */
|
||||
if (v >= 0) { /* found? */
|
||||
if (v == VLOCAL && !base)
|
||||
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
|
||||
}
|
||||
else { /* not found as local at current level; try upvalues */
|
||||
int idx = searchupvalue(fs, n); /* try existing upvalues */
|
||||
if (idx < 0) { /* not found? */
|
||||
int v = searchvar(fs, n, var); /* look up locals at current level */
|
||||
if (v >= 0) { /* found? */
|
||||
if (v == VLOCAL && !base)
|
||||
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
|
||||
}
|
||||
else { /* not found as local at current level; try upvalues */
|
||||
int idx = searchupvalue(fs, n); /* try existing upvalues */
|
||||
if (idx < 0) { /* not found? */
|
||||
if (fs->prev != NULL) /* more levels? */
|
||||
singlevaraux(fs->prev, n, var, 0); /* try upper levels */
|
||||
if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
|
||||
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
||||
else /* it is a global or a constant */
|
||||
return; /* don't need to do anything at this level */
|
||||
}
|
||||
init_exp(var, VUPVAL, idx); /* new or old upvalue */
|
||||
else /* no more levels */
|
||||
init_exp(var, VGLOBAL, -1); /* global by default */
|
||||
if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
|
||||
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
||||
else /* it is a global or a constant */
|
||||
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);
|
||||
FuncState *fs = ls->fs;
|
||||
singlevaraux(fs, varname, var, 1);
|
||||
if (var->k == VVOID) { /* global name? */
|
||||
if (var->k == VGLOBAL) { /* global name? */
|
||||
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 */
|
||||
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 */
|
||||
codestring(&key, varname); /* key is variable name */
|
||||
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->firstgoto = fs->ls->dyd->gt.n;
|
||||
bl->upval = 0;
|
||||
/* inherit 'insidetbc' from enclosing block */
|
||||
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;
|
||||
lua_assert(fs->freereg == luaY_nvarstack(fs));
|
||||
}
|
||||
@@ -1600,7 +1612,7 @@ static void fornum (LexState *ls, TString *varname, int line) {
|
||||
int base = fs->freereg;
|
||||
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, '=');
|
||||
exp1(ls); /* initial value */
|
||||
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)"); /* state */
|
||||
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 */
|
||||
while (testnext(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 '>'] */
|
||||
if (testnext(ls, '<')) {
|
||||
TString *ts = str_checkname(ls);
|
||||
@@ -1738,8 +1750,8 @@ static void localstat (LexState *ls) {
|
||||
expdesc e;
|
||||
do {
|
||||
TString *vname = str_checkname(ls);
|
||||
lu_byte kind = getlocalattribute(ls);
|
||||
vidx = new_localvarkind(ls, vname, kind);
|
||||
lu_byte kind = getvarattribute(ls);
|
||||
vidx = new_varkind(ls, vname, kind);
|
||||
if (kind == RDKTOCLOSE) { /* to-be-closed? */
|
||||
if (toclose != -1) /* one already present? */
|
||||
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) {
|
||||
/* funcname -> NAME {fieldsel} [':' NAME] */
|
||||
int ismethod = 0;
|
||||
@@ -1888,6 +1918,10 @@ static void statement (LexState *ls) {
|
||||
localstat(ls);
|
||||
break;
|
||||
}
|
||||
case TK_GLOBAL: { /* stat -> globalstat */
|
||||
globalstat(ls);
|
||||
break;
|
||||
}
|
||||
case TK_DBCOLON: { /* stat -> label */
|
||||
luaX_next(ls); /* skip double colon */
|
||||
labelstat(ls, str_checkname(ls), line);
|
||||
@@ -1907,6 +1941,17 @@ static void statement (LexState *ls) {
|
||||
gotostat(ls, line);
|
||||
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 */
|
||||
exprstat(ls);
|
||||
break;
|
||||
|
||||
15
lparser.h
15
lparser.h
@@ -37,6 +37,9 @@ typedef enum {
|
||||
info = result register */
|
||||
VLOCAL, /* local variable; var.ridx = register index;
|
||||
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' */
|
||||
VCONST, /* compile-time <const> variable;
|
||||
info = absolute index in 'actvar.arr' */
|
||||
@@ -87,10 +90,16 @@ typedef struct expdesc {
|
||||
|
||||
|
||||
/* kinds of variables */
|
||||
#define VDKREG 0 /* regular */
|
||||
#define RDKCONST 1 /* constant */
|
||||
#define VDKREG 0 /* regular local */
|
||||
#define RDKCONST 1 /* local constant */
|
||||
#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 */
|
||||
typedef union Vardesc {
|
||||
|
||||
1
ltests.h
1
ltests.h
@@ -14,6 +14,7 @@
|
||||
/* test Lua with compatibility code */
|
||||
#define LUA_COMPAT_MATHLIB
|
||||
#define LUA_COMPAT_LT_LE
|
||||
#undef LUA_COMPAT_GLOBAL
|
||||
|
||||
|
||||
#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.
|
||||
** 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},
|
||||
any reference to a free name
|
||||
(that is, a name not bound to any declaration) @id{var}
|
||||
any reference to a global variable @id{var}
|
||||
is syntactically translated to @T{_ENV.var}.
|
||||
Moreover, every chunk is compiled in the scope of
|
||||
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
|
||||
the translation of free names,
|
||||
@id{_ENV} is a completely regular name.
|
||||
@id{_ENV} is a regular name.
|
||||
In particular,
|
||||
you can define new variables and parameters with that name.
|
||||
Each reference to a free name uses the @id{_ENV} that is
|
||||
visible at that point in the program,
|
||||
following the usual visibility rules of Lua @see{visibility}.
|
||||
(However, you should not define @id{_ENV} as a global variable,
|
||||
otherwise @T{_ENV.var} would translate to
|
||||
@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}.
|
||||
|
||||
@@ -244,8 +323,8 @@ When Lua loads a chunk,
|
||||
the default value for its @id{_ENV} variable
|
||||
is the global environment @seeF{load}.
|
||||
Therefore, by default,
|
||||
free names in Lua code refer to entries in the global environment
|
||||
and, therefore, they are also called @def{global variables}.
|
||||
global variables in Lua code refer to entries in the global environment
|
||||
and, therefore, they act as conventional global variables.
|
||||
Moreover, all standard libraries are loaded in the global environment
|
||||
and some functions there operate on that environment.
|
||||
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
|
||||
(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{
|
||||
@producname{var}@producbody{@bnfNter{Name}}
|
||||
}
|
||||
@bnfNter{Name} denotes identifiers @see{lexical}.
|
||||
|
||||
Any variable name is assumed to be global unless explicitly declared
|
||||
as a local @see{localvar}.
|
||||
@x{Local variables} are @emph{lexically scoped}:
|
||||
Because variables are @emph{lexically scoped},
|
||||
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.
|
||||
|
||||
@@ -1227,8 +1304,6 @@ The syntax @id{var.Name} is just syntactic sugar for
|
||||
|
||||
An access to a global variable @id{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}
|
||||
@x{Local variables} can be declared anywhere inside a block.
|
||||
The declaration can include an initialization:
|
||||
@sect3{localvar| @title{Variable Declarations}
|
||||
Local and global variables can be declared anywhere inside a block.
|
||||
The declaration for locals can include an initialization:
|
||||
@Produc{
|
||||
@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}}
|
||||
@producname{stat}@producbody{@Rw{global} attnamelist}
|
||||
@producname{attnamelist}@producbody{
|
||||
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
|
||||
}
|
||||
If present, an initial assignment has the same semantics
|
||||
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
|
||||
(a name between angle brackets):
|
||||
@@ -1595,11 +1671,22 @@ that is, a variable that cannot be assigned to
|
||||
after its initialization;
|
||||
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.
|
||||
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},
|
||||
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{local} @Rw{function} @bnfNter{Name} funcbody
|
||||
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
|
||||
@OrNL @Rw{global} attnamelist
|
||||
}
|
||||
|
||||
@producname{attnamelist}@producbody{
|
||||
|
||||
@@ -349,6 +349,7 @@ end, "crl")
|
||||
|
||||
|
||||
function f(a,b)
|
||||
global collectgarbage, assert, g, string
|
||||
collectgarbage()
|
||||
local _, x = debug.getlocal(1, 1)
|
||||
local _, y = debug.getlocal(1, 2)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
-- $Id: testes/goto.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
print("testing goto and global declarations")
|
||||
|
||||
collectgarbage()
|
||||
|
||||
local function errmsg (code, m)
|
||||
@@ -280,7 +282,47 @@ end
|
||||
|
||||
|
||||
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'
|
||||
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
|
||||
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 maxint <const> = math.maxinteger
|
||||
|
||||
@@ -184,7 +192,7 @@ do
|
||||
for i = -3, 3 do -- variables avoid constant folding
|
||||
for j = -3, 3 do
|
||||
-- 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)))
|
||||
end
|
||||
end
|
||||
@@ -430,7 +438,7 @@ for i = 2,36 do
|
||||
assert(tonumber('\t10000000000\t', i) == i10)
|
||||
end
|
||||
|
||||
if not _soft then
|
||||
if not _ENV._soft then
|
||||
-- tests with very long numerals
|
||||
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)
|
||||
@@ -632,7 +640,7 @@ assert(maxint % -2 == -1)
|
||||
|
||||
-- non-portable tests because Windows C library cannot compute
|
||||
-- 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
|
||||
anan(0.0 % 0)
|
||||
anan(1.3 % 0)
|
||||
@@ -779,6 +787,7 @@ assert(a == '10' and b == '20')
|
||||
|
||||
do
|
||||
print("testing -0 and NaN")
|
||||
global rawset, undef
|
||||
local mz <const> = -0.0
|
||||
local z <const> = 0.0
|
||||
assert(mz == z)
|
||||
@@ -1074,6 +1083,7 @@ do
|
||||
-- different numbers should print differently.
|
||||
-- check pairs of floats with minimum detectable difference
|
||||
local p = floatbits - 1
|
||||
global ipairs
|
||||
for i = 1, maxexp - 1 do
|
||||
for _, i in ipairs{-i, i} do
|
||||
local x = 2^i
|
||||
|
||||
Reference in New Issue
Block a user