First implementation of global declarations

This commit is contained in:
Roberto Ierusalimschy
2025-05-05 16:24:59 -03:00
parent e055905914
commit be81209063
10 changed files with 272 additions and 117 deletions

7
llex.c
View File

@@ -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
View File

@@ -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
View File

@@ -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;

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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{

View File

@@ -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)

View File

@@ -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'

View File

@@ -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