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')
 #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",
     "//", "..", "...", "==", ">=", "<=", "~=",
     "//", "..", "...", "==", ">=", "<=", "~=",

+ 2 - 2
llex.h

@@ -33,8 +33,8 @@ enum RESERVED {
   /* terminal symbols denoted by reserved words */
   /* terminal symbols denoted by reserved words */
   TK_AND = FIRST_RESERVED, TK_BREAK,
   TK_AND = FIRST_RESERVED, TK_BREAK,
   TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
   TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
-  TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
-  TK_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 */
   /* 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,

+ 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
 #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? */
-    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 */
         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);
   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);
-    vidx = new_localvarkind(ls, vname, kind);
+    lu_byte kind = getvarattribute(ls);
+    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;

+ 12 - 3
lparser.h

@@ -37,6 +37,9 @@ typedef enum {
                  info = result register */
                  info = result register */
   VLOCAL,  /* local variable; var.ridx = register index;
   VLOCAL,  /* local variable; var.ridx = register index;
               var.vidx = relative index in 'actvar.arr'  */
               var.vidx = relative index in 'actvar.arr'  */
+  VGLOBAL,  /* global variable;
+               info = relative index in 'actvar.arr' (or -1 for
+                      implicit declaration) */
   VUPVAL,  /* upvalue variable; info = index of upvalue in 'upvalues' */
   VUPVAL,  /* upvalue variable; info = index of upvalue in 'upvalues' */
   VCONST,  /* compile-time <const> variable;
   VCONST,  /* compile-time <const> variable;
               info = absolute index in 'actvar.arr'  */
               info = absolute index in 'actvar.arr'  */
@@ -87,10 +90,16 @@ typedef struct expdesc {
 
 
 
 
 /* kinds of variables */
 /* kinds of variables */
-#define VDKREG		0   /* regular */
-#define RDKCONST	1   /* constant */
+#define VDKREG		0   /* regular local */
+#define RDKCONST	1   /* local constant */
 #define RDKTOCLOSE	2   /* to-be-closed */
 #define RDKTOCLOSE	2   /* to-be-closed */
-#define RDKCTC		3   /* compile-time constant */
+#define RDKCTC		3   /* local compile-time constant */
+#define GDKREG		4   /* regular global */
+#define GDKCONST	5   /* global constant */
+
+/* variables that live in registers */
+#define varinreg(v)	((v)->vd.kind <= RDKTOCLOSE)
+
 
 
 /* description of an active local variable */
 /* description of an active local variable */
 typedef union Vardesc {
 typedef union Vardesc {

+ 1 - 0
ltests.h

@@ -14,6 +14,7 @@
 /* test Lua with compatibility code */
 /* test Lua with compatibility code */
 #define LUA_COMPAT_MATHLIB
 #define LUA_COMPAT_MATHLIB
 #define LUA_COMPAT_LT_LE
 #define LUA_COMPAT_LT_LE
+#undef LUA_COMPAT_GLOBAL
 
 
 
 
 #define LUA_DEBUG
 #define LUA_DEBUG

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

+ 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},
 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}.
 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
-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}.
 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
-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
 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
-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
 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}
-@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{
 @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{

+ 1 - 0
testes/db.lua

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

+ 43 - 1
testes/goto.lua

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

+ 13 - 3
testes/math.lua

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