Sfoglia il codice sorgente

Variable attributes can prefix name list

In this format, the attribute applies to all names in the list;
e.g. "global<const> print, require, math".
Roberto Ierusalimschy 2 mesi fa
parent
commit
abbae57c78
15 ha cambiato i file con 84 aggiunte e 60 eliminazioni
  1. 31 22
      lparser.c
  2. 17 13
      manual/manual.of
  3. 1 1
      testes/all.lua
  4. 1 1
      testes/calls.lua
  5. 1 1
      testes/closure.lua
  6. 1 1
      testes/code.lua
  7. 1 1
      testes/files.lua
  8. 6 6
      testes/goto.lua
  9. 1 1
      testes/literals.lua
  10. 17 5
      testes/locals.lua
  11. 3 4
      testes/math.lua
  12. 1 1
      testes/nextvar.lua
  13. 1 1
      testes/pm.lua
  14. 1 1
      testes/strings.lua
  15. 1 1
      testes/utf8.lua

+ 31 - 22
lparser.c

@@ -1733,7 +1733,7 @@ static void localfunc (LexState *ls) {
 }
 
 
-static lu_byte getvarattribute (LexState *ls) {
+static lu_byte getvarattribute (LexState *ls, lu_byte df) {
   /* attrib -> ['<' NAME '>'] */
   if (testnext(ls, '<')) {
     TString *ts = str_checkname(ls);
@@ -1746,7 +1746,7 @@ static lu_byte getvarattribute (LexState *ls) {
     else
       luaK_semerror(ls, "unknown attribute '%s'", attr);
   }
-  return VDKREG;  /* regular variable */
+  return df;  /* return default value */
 }
 
 
@@ -1767,10 +1767,12 @@ static void localstat (LexState *ls) {
   int nvars = 0;
   int nexps;
   expdesc e;
-  do {
-    TString *vname = str_checkname(ls);
-    lu_byte kind = getvarattribute(ls);
-    vidx = new_varkind(ls, vname, kind);
+  /* get prefixed attribute (if any); default is regular local variable */
+  lu_byte defkind = getvarattribute(ls, VDKREG);
+  do {  /* for each variable */
+    TString *vname = str_checkname(ls);  /* get its name */
+    lu_byte kind = getvarattribute(ls, defkind);  /* postfixed attribute */
+    vidx = new_varkind(ls, vname, kind);  /* predeclare it */
     if (kind == RDKTOCLOSE) {  /* to-be-closed? */
       if (toclose != -1)  /* one already present? */
         luaK_semerror(ls, "multiple to-be-closed variables in local list");
@@ -1778,13 +1780,13 @@ static void localstat (LexState *ls) {
     }
     nvars++;
   } while (testnext(ls, ','));
-  if (testnext(ls, '='))
+  if (testnext(ls, '='))  /* initialization? */
     nexps = explist(ls, &e);
   else {
     e.k = VVOID;
     nexps = 0;
   }
-  var = getlocalvardesc(fs, vidx);  /* get last variable */
+  var = getlocalvardesc(fs, vidx);  /* retrieve last variable */
   if (nvars == nexps &&  /* no adjustments? */
       var->vd.kind == RDKCONST &&  /* last variable is const? */
       luaK_exp2const(fs, &e, &var->k)) {  /* compile-time constant? */
@@ -1800,29 +1802,35 @@ static void localstat (LexState *ls) {
 }
 
 
-static lu_byte getglobalattribute (LexState *ls) {
-  lu_byte kind = getvarattribute(ls);
-  if (kind == RDKTOCLOSE)
-    luaK_semerror(ls, "global variables cannot be to-be-closed");
-  /* adjust kind for global variable */
-  return (kind == VDKREG) ? GDKREG : GDKCONST;
+static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
+  lu_byte kind = getvarattribute(ls, df);
+  switch (kind) {
+    case RDKTOCLOSE:
+      luaK_semerror(ls, "global variables cannot be to-be-closed");
+      break;  /* to avoid warnings */
+    case RDKCONST:
+      return GDKCONST;  /* adjust kind for global variable */
+    default:
+      return kind;
+  }
 }
 
 
 static void globalstat (LexState *ls) {
-  /* globalstat -> (GLOBAL) '*' attrib
-     globalstat -> (GLOBAL) NAME attrib {',' NAME attrib} */
+  /* 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, '*')) {
-    lu_byte kind = getglobalattribute(ls);
     /* use NULL as name to represent '*' entries */
-    new_varkind(ls, NULL, kind);
+    new_varkind(ls, NULL, defkind);
     fs->nactvar++;  /* activate declaration */
   }
   else {
-    do {
+    do {  /* list of names */
       TString *vname = str_checkname(ls);
-      lu_byte kind = getglobalattribute(ls);
+      lu_byte kind = getglobalattribute(ls, defkind);
       new_varkind(ls, vname, kind);
       fs->nactvar++;  /* activate declaration */
     } while (testnext(ls, ','));
@@ -2003,8 +2011,9 @@ static void statement (LexState *ls) {
          is not reserved */
       if (ls->t.seminfo.ts == ls->glbn) {  /* current = "global"? */
         int lk = luaX_lookahead(ls);
-        if (lk == TK_NAME || lk == '*' || lk == TK_FUNCTION) {
-          /* 'global name' or 'global *' or 'global function' */
+        if (lk == '<' || lk == TK_NAME || lk == '*' || lk == TK_FUNCTION) {
+          /* 'global <attrib>' or 'global name' or 'global *' or
+             'global function' */
           globalstatfunc(ls, line);
           break;
         }

+ 17 - 13
manual/manual.of

@@ -1651,43 +1651,47 @@ Function calls are explained in @See{functioncall}.
 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{local}
+  attnamelist @bnfopt{@bnfter{=} explist}}
 @producname{stat}@producbody{@Rw{global} attnamelist}
-@producname{attnamelist}@producbody{
-  @bnfNter{Name} @bnfopt{attrib}
-     @bnfrep{@bnfter{,} @bnfNter{Name} @bnfopt{attrib}}}
 }
 If present, an initial assignment has the same semantics
 of a multiple assignment @see{assignment}.
 Otherwise, all local variables are initialized with @nil.
 
-Each variable name may be postfixed by an attribute
-(a name between angle brackets):
+The list of names may be prefixed by an attribute
+(a name between angle brackets)
+and each variable name may be postfixed by an attribute:
 @Produc{
+@producname{attnamelist}@producbody{
+  @bnfopt{attrib} @bnfNter{Name} @bnfopt{attrib}
+     @bnfrep{@bnfter{,} @bnfNter{Name} @bnfopt{attrib}}}
 @producname{attrib}@producbody{@bnfter{<} @bnfNter{Name} @bnfter{>}}
 }
+A prefixed attribute applies to all names in the list;
+a postfixed attribute applies to its particular name.
 There are two possible attributes:
 @id{const}, which declares a @emph{constant} or @emph{read-only} variable,
 @index{constant variable}
 that is, a variable that cannot be used as the left-hand side of an
 assignment,
 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.
+A list of variables can contain at most one to-be-closed variable.
 
 Lua offers also a collective declaration for global variables:
 @Produc{
-@producname{stat}@producbody{@Rw{global} @bnfter{*} @bnfopt{attrib}}
+@producname{stat}@producbody{@Rw{global} @bnfopt{attrib} @bnfter{*}}
 }
 This special form implicitly declares
 as globals all names not explicitly declared previously.
 In particular,
-@T{global * <const>} implicitly declares
+@T{global<const> *} implicitly declares
 as read-only globals all names not explicitly declared previously;
 see the following example:
 @verbatim{
 global X
-global * <const>
+global<const> *
 print(math.pi)   -- Ok, 'print' and 'math' are read-only
 X = 1            -- Ok, declared as read-write
 Y = 1            -- Error, Y is read-only
@@ -1700,7 +1704,7 @@ the scope of any other @Rw{global} declaration.
 Therefore, a program that does not use global declarations
 or start with @T{global *}
 has free read-write access to any global;
-a program that starts with @T{global * <const>}
+a program that starts with @T{global<const> *}
 has free read-only access to any global;
 and a program that starts with any other global declaration
 (e.g., @T{global none}) can only refer to declared variables.
@@ -9620,11 +9624,11 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
 @OrNL	@Rw{global} @Rw{function} @bnfNter{Name} funcbody
 @OrNL	@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
 @OrNL	@Rw{global} attnamelist
-@OrNL	@Rw{global} @bnfter{*} @bnfopt{attrib}
+@OrNL	@Rw{global} @bnfopt{attrib} @bnfter{*}
 }
 
 @producname{attnamelist}@producbody{
-  @bnfNter{Name} @bnfopt{attrib}
+  @bnfopt{attrib} @bnfNter{Name} @bnfopt{attrib}
         @bnfrep{@bnfter{,} @bnfNter{Name} @bnfopt{attrib}}}
 
 @producname{attrib}@producbody{@bnfter{<} @bnfNter{Name} @bnfter{>}}

+ 1 - 1
testes/all.lua

@@ -2,7 +2,7 @@
 -- $Id: testes/all.lua $
 -- See Copyright Notice in file lua.h
 
-global * <const>
+global <const> *
 
 global _soft, _port, _nomsg
 global T

+ 1 - 1
testes/calls.lua

@@ -1,7 +1,7 @@
 -- $Id: testes/calls.lua $
 -- See Copyright Notice in file lua.h
 
-global * <const>
+global <const> *
 
 print("testing functions and calls")
 

+ 1 - 1
testes/closure.lua

@@ -1,7 +1,7 @@
 -- $Id: testes/closure.lua $
 -- See Copyright Notice in file lua.h
 
-global * <const>
+global <const> *
 
 print "testing closures"
 

+ 1 - 1
testes/code.lua

@@ -1,7 +1,7 @@
 -- $Id: testes/code.lua $
 -- See Copyright Notice in file lua.h
 
-global * <const>
+global <const> *
 
 if T==nil then
   (Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')

+ 1 - 1
testes/files.lua

@@ -1,7 +1,7 @@
 -- $Id: testes/files.lua $
 -- See Copyright Notice in file lua.h
 
-global * <const>
+global <const> *
 
 local debug = require "debug"
 

+ 6 - 6
testes/goto.lua

@@ -1,9 +1,9 @@
 -- $Id: testes/goto.lua $
 -- See Copyright Notice in file lua.h
 
-global require
-global print, load, assert, string, setmetatable
-global collectgarbage, error
+global<const> require
+global<const> print, load, assert, string, setmetatable
+global<const> collectgarbage, error
 
 print("testing goto and global declarations")
 
@@ -304,7 +304,7 @@ do
 
   -- global variables cannot be to-be-closed
   checkerr("global X<close>", "cannot be")
-  checkerr("global * <close>", "cannot be")
+  checkerr("global <close> *", "cannot be")
 
   do
     local X = 10
@@ -345,7 +345,7 @@ do
   end
 
   checkerr([[
-    global foo <const>;
+    global<const> foo;
     function foo (x) return end   -- ERROR: foo is read-only
   ]], "assign to const variable 'foo'")
 
@@ -357,7 +357,7 @@ do
   ]], "%:2%:")   -- correct line in error message
 
   checkerr([[
-    global * <const>;
+    global<const> *;
     print(X)    -- Ok to use
     Y = 1   -- ERROR
   ]], "assign to const variable 'Y'")

+ 1 - 1
testes/literals.lua

@@ -3,7 +3,7 @@
 
 print('testing scanner')
 
-global * <const>
+global <const> *
 
 local debug = require "debug"
 

+ 17 - 5
testes/locals.lua

@@ -1,7 +1,7 @@
 -- $Id: testes/locals.lua $
 -- See Copyright Notice in file lua.h
 
-global * <const>
+global <const> *
 
 print('testing local variables and environments')
 
@@ -181,23 +181,25 @@ assert(x==20)
 A = nil
 
 
-do   -- constants
+do   print("testing local constants")
   global assert<const>, load, string, X
   X = 1   -- not a constant
   local a<const>, b, c<const> = 10, 20, 30
   b = a + c + b    -- 'b' is not constant
   assert(a == 10 and b == 60 and c == 30)
+
   local function checkro (name, code)
     local st, msg = load(code)
     local gab = string.format("attempt to assign to const variable '%s'", name)
     assert(not st and string.find(msg, gab))
   end
+
   checkro("y", "local x, y <const>, z = 10, 20, 30; x = 11; y = 12")
   checkro("x", "local x <const>, y, z <const> = 10, 20, 30; x = 11")
   checkro("z", "local x <const>, y, z <const> = 10, 20, 30; y = 10; z = 11")
-  checkro("foo", "local foo <const> = 10; function foo() end")
-  checkro("foo", "local foo <const> = {}; function foo() end")
-  checkro("foo", "global foo <const>; function foo() end")
+  checkro("foo", "local<const> foo = 10; function foo() end")
+  checkro("foo", "local<const> foo <const> = {}; function foo() end")
+  checkro("foo", "global<const> foo <const>; function foo() end")
   checkro("XX", "global XX <const>; XX = 10")
   checkro("XX", "local _ENV; global XX <const>; XX = 10")
 
@@ -218,8 +220,18 @@ do   -- constants
 end
 
 
+
 print"testing to-be-closed variables"
 
+
+do
+  local st, msg = load("local <close> a, b")
+  assert(not st and string.find(msg, "multiple"))
+
+  local st, msg = load("local a<close>, b<close>")
+  assert(not st and string.find(msg, "multiple"))
+end
+
 local function stack(n) n = ((n == 0) or stack(n - 1)) end
 
 local function func2close (f, x, y)

+ 3 - 4
testes/math.lua

@@ -8,11 +8,10 @@ local string = require "string"
 
 global none
 
-global print, assert, pcall, type, pairs, load
-global tonumber, tostring, select
+global<const> print, assert, pcall, type, pairs, load
+global<const> tonumber, tostring, select
 
-local minint <const> = math.mininteger
-local maxint <const> = math.maxinteger
+local<const> minint, maxint = math.mininteger, math.maxinteger
 
 local intbits <const> = math.floor(math.log(maxint, 2) + 0.5) + 1
 assert((1 << intbits) == 0)

+ 1 - 1
testes/nextvar.lua

@@ -1,7 +1,7 @@
 -- $Id: testes/nextvar.lua $
 -- See Copyright Notice in file lua.h
 
-global * <const>
+global <const> *
 
 print('testing tables, next, and for')
 

+ 1 - 1
testes/pm.lua

@@ -6,7 +6,7 @@
 
 print('testing pattern matching')
 
-global * <const>
+global <const> *
 
 local function checkerror (msg, f, ...)
   local s, err = pcall(f, ...)

+ 1 - 1
testes/strings.lua

@@ -3,7 +3,7 @@
 
 -- ISO Latin encoding
 
-global * <const>
+global <const> *
 
 print('testing strings and string library')
 

+ 1 - 1
testes/utf8.lua

@@ -3,7 +3,7 @@
 
 -- UTF-8 file
 
-global * <const>
+global <const> *
 
 print "testing UTF-8 library"