From be8120906304a8658fab998587b969e0e42f5650 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 5 May 2025 16:24:59 -0300 Subject: First implementation of global declarations --- llex.c | 7 ++- llex.h | 4 +- lparser.c | 111 +++++++++++++++++++++++---------- lparser.h | 15 ++++- ltests.h | 1 + luaconf.h | 6 ++ manual/manual.of | 184 +++++++++++++++++++++++++++++++++---------------------- testes/db.lua | 1 + testes/goto.lua | 44 ++++++++++++- testes/math.lua | 16 ++++- 10 files changed, 272 insertions(+), 117 deletions(-) diff --git a/llex.c b/llex.c index 4b5a1f75..9d93224f 100644 --- a/llex.c +++ b/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", "//", "..", "...", "==", ">=", "<=", "~=", diff --git a/llex.h b/llex.h index c3500ef6..078e4d31 100644 --- a/llex.h +++ b/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, diff --git a/lparser.c b/lparser.c index e7e05f48..1c5fdca6 100644 --- a/lparser.c +++ b/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; diff --git a/lparser.h b/lparser.h index a3063569..3cd0ba77 100644 --- a/lparser.h +++ b/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 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 { diff --git a/ltests.h b/ltests.h index 7f0ce404..43f08162 100644 --- a/ltests.h +++ b/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 diff --git a/luaconf.h b/luaconf.h index bd394650..51e77547 100644 --- a/luaconf.h +++ b/luaconf.h @@ -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 diff --git a/manual/manual.of b/manual/manual.of index 7cd0d4db..ace5d375 100644 --- a/manual/manual.of +++ b/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 +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{ diff --git a/testes/db.lua b/testes/db.lua index e4982c20..ae204c41 100644 --- a/testes/db.lua +++ b/testes/db.lua @@ -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) diff --git a/testes/goto.lua b/testes/goto.lua index eca68516..fdfddb85 100644 --- a/testes/goto.lua +++ b/testes/goto.lua @@ -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; global assert + 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", "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' + diff --git a/testes/math.lua b/testes/math.lua index 88a57ce7..242579b1 100644 --- a/testes/math.lua +++ b/testes/math.lua @@ -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 = math.mininteger local maxint = 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 = -0.0 local z = 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 -- cgit v1.2.3-55-g6feb