Pārlūkot izejas kodu

Optional initialization for global declarations

Roberto Ierusalimschy 3 nedēļas atpakaļ
vecāks
revīzija
942c10a5e3
7 mainītis faili ar 96 papildinājumiem un 38 dzēšanām
  1. 58 23
      lparser.c
  2. 10 8
      manual/manual.of
  3. 1 1
      testes/bwcoercion.lua
  4. 2 2
      testes/calls.lua
  5. 2 2
      testes/files.lua
  6. 22 1
      testes/goto.lua
  7. 1 1
      testes/tracegc.lua

+ 58 - 23
lparser.c

@@ -30,7 +30,7 @@
 
 
 
-/* maximum number of variable declarationss per function (must be
+/* maximum number of variable declarations per function (must be
    smaller than 250, due to the bytecode format) */
 #define MAXVARS		200
 
@@ -197,7 +197,7 @@ static int new_varkind (LexState *ls, TString *name, lu_byte kind) {
   Dyndata *dyd = ls->dyd;
   Vardesc *var;
   luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
-             dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarationss");
+             dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarations");
   var = &dyd->actvar.arr[dyd->actvar.n++];
   var->vd.kind = kind;  /* default */
   var->vd.name = name;
@@ -485,6 +485,20 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
 }
 
 
+static void buildglobal (LexState *ls, TString *varname, expdesc *var) {
+  FuncState *fs = ls->fs;
+  expdesc key;
+  init_exp(var, VGLOBAL, -1);  /* global by default */
+  singlevaraux(fs, ls->envn, var, 1);  /* get environment variable */
+  if (var->k == VGLOBAL)
+    luaK_semerror(ls, "_ENV is global when accessing variable '%s'",
+                      getstr(varname));
+  luaK_exp2anyregup(fs, var);  /* _ENV could be a constant */
+  codestring(&key, varname);  /* key is variable name */
+  luaK_indexed(fs, var, &key);  /* 'var' represents _ENV[varname] */
+}
+
+
 /*
 ** Find a variable with the given name 'n', handling global variables
 ** too.
@@ -494,18 +508,11 @@ static void buildvar (LexState *ls, TString *varname, expdesc *var) {
   init_exp(var, VGLOBAL, -1);  /* global by default */
   singlevaraux(fs, varname, var, 1);
   if (var->k == VGLOBAL) {  /* global name? */
-    expdesc key;
     int info = var->u.info;
     /* global by default in the scope of a global declaration? */
     if (info == -2)
       luaK_semerror(ls, "variable '%s' not declared", getstr(varname));
-    singlevaraux(fs, ls->envn, var, 1);  /* get environment variable */
-    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] */
+    buildglobal(ls, varname, var);
     if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST)
       var->u.ind.ro = 1;  /* mark variable as read-only */
     else  /* anyway must be a global */
@@ -665,7 +672,7 @@ static void createlabel (LexState *ls, TString *name, int line, int last) {
 
 
 /*
-** Traverse the pending goto's of the finishing block checking whether
+** Traverse the pending gotos of the finishing block checking whether
 ** each match some label of that block. Those that do not match are
 ** "exported" to the outer block, to be solved there. In particular,
 ** its 'nactvar' is updated with the level of the inner block,
@@ -1435,6 +1442,15 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
   }
 }
 
+
+/* Create code to store the "top" register in 'var' */
+static void storevartop (FuncState *fs, expdesc *var) {
+  expdesc e;
+  init_exp(&e, VNONRELOC, fs->freereg - 1);
+  luaK_storevar(fs, var, &e);  /* will also free the top register */
+}
+
+
 /*
 ** Parse and compile a multiple assignment. The first "variable"
 ** (a 'suffixedexp') was already read by the caller.
@@ -1468,8 +1484,7 @@ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
       return;  /* avoid default */
     }
   }
-  init_exp(&e, VNONRELOC, ls->fs->freereg-1);  /* default assignment */
-  luaK_storevar(ls->fs, &lh->v, &e);
+  storevartop(ls->fs, &lh->v);  /* default assignment */
 }
 
 
@@ -1821,25 +1836,45 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
 }
 
 
+static void globalnames (LexState *ls, lu_byte defkind) {
+  FuncState *fs = ls->fs;
+  int nvars = 0;
+  int lastidx;  /* index of last registered variable */
+  do {  /* for each name */
+    TString *vname = str_checkname(ls);
+    lu_byte kind = getglobalattribute(ls, defkind);
+    lastidx = new_varkind(ls, vname, kind);
+    nvars++;
+  } while (testnext(ls, ','));
+  if (testnext(ls, '=')) {  /* initialization? */
+    expdesc e;
+    int i;
+    int nexps = explist(ls, &e);  /* read list of expressions */
+    adjust_assign(ls, nvars, nexps, &e);
+    for (i = 0; i < nvars; i++) {  /* for each variable */
+      expdesc var;
+      TString *varname = getlocalvardesc(fs, lastidx - i)->vd.name;
+      buildglobal(ls, varname, &var);  /* create global variable in 'var' */
+      storevartop(fs, &var);
+    }
+  }
+  fs->nactvar = cast_short(fs->nactvar + nvars);  /* activate declaration */
+}
+
+
 static void globalstat (LexState *ls) {
   /* globalstat -> (GLOBAL) attrib '*'
      globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */
   FuncState *fs = ls->fs;
   /* get prefixed attribute (if any); default is regular global variable */
   lu_byte defkind = getglobalattribute(ls, GDKREG);
-  if (testnext(ls, '*')) {
+  if (!testnext(ls, '*'))
+    globalnames(ls, defkind);
+  else {
     /* use NULL as name to represent '*' entries */
     new_varkind(ls, NULL, defkind);
     fs->nactvar++;  /* activate declaration */
   }
-  else {
-    do {  /* list of names */
-      TString *vname = str_checkname(ls);
-      lu_byte kind = getglobalattribute(ls, defkind);
-      new_varkind(ls, vname, kind);
-      fs->nactvar++;  /* activate declaration */
-    } while (testnext(ls, ','));
-  }
 }
 
 
@@ -1850,7 +1885,7 @@ static void globalfunc (LexState *ls, int line) {
   TString *fname = str_checkname(ls);
   new_varkind(ls, fname, GDKREG);  /* declare global variable */
   fs->nactvar++;  /* enter its scope */
-  buildvar(ls, fname, &var);
+  buildglobal(ls, fname, &var);
   body(ls, &b, 0, ls->linenumber);  /* compile and return closure in 'b' */
   luaK_storevar(fs, &var, &b);
   luaK_fixline(fs, line);  /* definition "happens" in the first line */

+ 10 - 8
manual/manual.of

@@ -229,7 +229,7 @@ as the following example illustrates:
 @verbatim{
 X = 1       -- Ok, global by default
 do
-  global Y  -- voids implicit initial declaration
+  global Y  -- voids the implicit initial declaration
   Y = 1     -- Ok, Y declared as global
   X = 1     -- ERROR, X not declared
 end
@@ -269,7 +269,7 @@ 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.
+and so the @id{x} in the right-hand side refers to the outside variable.
 
 Because of the @x{lexical scoping} rules,
 local variables can be freely accessed by functions
@@ -1651,11 +1651,12 @@ Function calls are explained in @See{functioncall}.
 
 @sect3{localvar| @title{Variable Declarations}
 Local and global variables can be declared anywhere inside a block.
-The declaration for locals can include an initialization:
+The declaration can include an initialization:
 @Produc{
 @producname{stat}@producbody{@Rw{local}
   attnamelist @bnfopt{@bnfter{=} explist}}
-@producname{stat}@producbody{@Rw{global} attnamelist}
+@producname{stat}@producbody{@Rw{global}
+  attnamelist @bnfopt{@bnfter{=} explist}}
 }
 If present, an initial assignment has the same semantics
 of a multiple assignment @see{assignment}.
@@ -1712,7 +1713,8 @@ and a program that starts with any other global declaration
 (e.g., @T{global none}) can only refer to declared variables.
 
 Note that, for global variables,
-the effect of any declaration is only syntactical:
+the effect of any declaration is only syntactical
+(except for the optional assignment):
 @verbatim{
 global X <const>, _G
 X = 1           -- ERROR
@@ -3924,8 +3926,8 @@ This macro may evaluate its arguments more than once.
 
 }
 
-@APIEntry{unsigned (lua_numbertocstring) (lua_State *L, int idx,
-                                          char *buff);|
+@APIEntry{unsigned lua_numbertocstring (lua_State *L, int idx,
+                                        char *buff);|
 @apii{0,0,-}
 
 Converts the number at acceptable index @id{idx} to a string
@@ -4050,7 +4052,7 @@ This function is equivalent to @Lid{lua_pushcclosure} with no upvalues.
 
 }
 
-@APIEntry{const char *(lua_pushexternalstring) (lua_State *L,
+@APIEntry{const char *lua_pushexternalstring (lua_State *L,
                 const char *s, size_t len, lua_Alloc falloc, void *ud);|
 @apii{0,1,m}
 

+ 1 - 1
testes/bwcoercion.lua

@@ -4,7 +4,7 @@ local strsub = string.sub
 
 local print = print
 
-_ENV = nil
+global none
 
 -- Try to convert a value to an integer, without assuming any coercion.
 local function toint (x)

+ 2 - 2
testes/calls.lua

@@ -24,7 +24,7 @@ assert(not pcall(type))
 
 
 -- testing local-function recursion
-global fact; fact = false
+global fact = false
 do
   local res = 1
   local function fact (n)
@@ -65,7 +65,7 @@ a.b.c:f2('k', 12); assert(a.b.c.k == 12)
 
 print('+')
 
-global t; t = nil   -- 'declare' t
+global t = nil   -- 'declare' t
 function f(a,b,c) local d = 'a'; t={a,b,c,d} end
 
 f(      -- this line change must be valid

+ 2 - 2
testes/files.lua

@@ -715,7 +715,7 @@ do
 end
 
 
-if T and T.nonblock then
+if T and T.nonblock and not _port then
   print("testing failed write")
 
   -- unable to write anything to /dev/full
@@ -840,7 +840,7 @@ assert(os.date("!\0\0") == "\0\0")
 local x = string.rep("a", 10000)
 assert(os.date(x) == x)
 local t = os.time()
-global D; D = os.date("*t", t)
+global D = os.date("*t", t)
 assert(os.date(string.rep("%d", 1000), t) ==
        string.rep(os.date("%d", t), 1000))
 assert(os.date(string.rep("%", 200)) == string.rep("%", 100))

+ 22 - 1
testes/goto.lua

@@ -380,7 +380,7 @@ do
   global *
   Y = x + Y
   assert(_ENV.Y == 20)
-
+  Y = nil
 end
 
 
@@ -411,5 +411,26 @@ do   -- mixing lots of global/local declarations
   _ENV.x200 = nil
 end
 
+do  print "testing initialization in global declarations"
+  global<const> a, b, c = 10, 20, 30
+  assert(_ENV.a == 10 and b == 20 and c == 30)
+
+  global<const> a, b, c = 10
+  assert(_ENV.a == 10 and b == nil and c == nil)
+
+  global table
+  global a, b, c, d = table.unpack{1, 2, 3, 6, 5}
+  assert(_ENV.a == 1 and b == 2 and c == 3 and d == 6)
+
+  local a, b = 100, 200
+  do
+    global a, b = a, b
+  end
+  assert(_ENV.a == 100 and _ENV.b == 200)
+
+
+  _ENV.a, _ENV.b, _ENV.c, _ENV.d = nil   -- erase these globals
+end
+
 print'OK'
 

+ 1 - 1
testes/tracegc.lua

@@ -6,7 +6,7 @@ local M = {}
 local setmetatable, stderr, collectgarbage =
          setmetatable, io.stderr, collectgarbage
 
-_ENV = nil
+global none
 
 local active = false