Browse Source

First implementation of global declarations

Roberto Ierusalimschy 3 months ago
parent
commit
be81209063
10 changed files with 272 additions and 117 deletions
  1. 6 1
      llex.c
  2. 2 2
      llex.h
  3. 78 33
      lparser.c
  4. 12 3
      lparser.h
  5. 1 0
      ltests.h
  6. 6 0
      luaconf.h
  7. 110 74
      manual/manual.of
  8. 1 0
      testes/db.lua
  9. 43 1
      testes/goto.lua
  10. 13 3
      testes/math.lua

+ 6 - 1
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",
     "//", "..", "...", "==", ">=", "<=", "~=",

+ 2 - 2
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,

+ 78 - 33
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;

+ 12 - 3
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 - 0
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

+ 6 - 0
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

+ 110 - 74
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{

+ 1 - 0
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)

+ 43 - 1
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<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'
+

+ 13 - 3
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 <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