Sfoglia il codice sorgente

New syntax for to-be-closed variables

The new syntax is <local *toclose x = f()>. The mark '*' allows other
attributes to be added later without the need of new keywords; it
also allows better error messages.  The API function was also renamed
('lua_tobeclosed' -> 'lua_toclose').
Roberto Ierusalimschy 6 anni fa
parent
commit
b8fed93215
8 ha cambiato i file con 48 aggiunte e 47 eliminazioni
  1. 1 1
      lapi.c
  2. 13 12
      lparser.c
  3. 2 2
      ltests.c
  4. 1 1
      lua.h
  5. 6 6
      testes/api.lua
  6. 3 3
      testes/files.lua
  7. 1 1
      testes/goto.lua
  8. 21 21
      testes/locals.lua

+ 1 - 1
lapi.c

@@ -1207,7 +1207,7 @@ LUA_API int lua_next (lua_State *L, int idx) {
 }
 
 
-LUA_API void lua_tobeclosed (lua_State *L) {
+LUA_API void lua_toclose (lua_State *L) {
   int nresults = L->ci->nresults;
   luaF_newtbcupval(L, L->top - 1);  /* create new to-be-closed upvalue */
   if (!hastocloseCfunc(nresults))  /* function not marked yet? */

+ 13 - 12
lparser.c

@@ -1546,16 +1546,15 @@ static void localfunc (LexState *ls) {
 }
 
 
-static void commonlocalstat (LexState *ls, TString *firstvar) {
+static void commonlocalstat (LexState *ls) {
   /* stat -> LOCAL NAME {',' NAME} ['=' explist] */
-  int nvars = 1;
+  int nvars = 0;
   int nexps;
   expdesc e;
-  new_localvar(ls, firstvar);
-  while (testnext(ls, ',')) {
+  do {
     new_localvar(ls, str_checkname(ls));
     nvars++;
-  }
+  } while (testnext(ls, ','));
   if (testnext(ls, '='))
     nexps = explist(ls, &e);
   else {
@@ -1567,8 +1566,12 @@ static void commonlocalstat (LexState *ls, TString *firstvar) {
 }
 
 
-static void scopedlocalstat (LexState *ls) {
+static void tocloselocalstat (LexState *ls) {
   FuncState *fs = ls->fs;
+  TString *attr = str_checkname(ls);
+  if (strcmp(getstr(attr), "toclose") != 0)
+    luaK_semerror(ls,
+      luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
   new_localvar(ls, str_checkname(ls));
   checknext(ls, '=');
   exp1(ls, 0);
@@ -1580,13 +1583,11 @@ static void scopedlocalstat (LexState *ls) {
 
 static void localstat (LexState *ls) {
   /* stat -> LOCAL NAME {',' NAME} ['=' explist]
-           | LOCAL SCOPED NAME '=' exp */
-  TString *firstvar = str_checkname(ls);
-  if (ls->t.token == TK_NAME &&
-      eqshrstr(firstvar, luaS_newliteral(ls->L, "scoped")))
-    scopedlocalstat(ls);
+           | LOCAL *toclose NAME '=' exp */
+  if (testnext(ls, '*'))
+    tocloselocalstat(ls);
   else
-    commonlocalstat(ls, firstvar);
+    commonlocalstat(ls);
 }
 
 

+ 2 - 2
ltests.c

@@ -1550,8 +1550,8 @@ static struct X { int x; } x;
       int i = getindex;
       return lua_yieldk(L1, nres, i, Cfunck);
     }
-    else if EQ("tobeclosed") {
-      lua_tobeclosed(L);
+    else if EQ("toclose") {
+      lua_toclose(L);
     }
     else luaL_error(L, "unknown instruction %s", buff);
   }

+ 1 - 1
lua.h

@@ -333,7 +333,7 @@ LUA_API size_t   (lua_stringtonumber) (lua_State *L, const char *s);
 LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
 LUA_API void      (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
 
-LUA_API void  (lua_tobeclosed) (lua_State *L);
+LUA_API void  (lua_toclose) (lua_State *L);
 
 
 /*

+ 6 - 6
testes/api.lua

@@ -987,7 +987,7 @@ do
 
   local a = T.testC([[
     call 0 1   # create resource
-    tobeclosed  # mark it to be closed
+    toclose    # mark it to be closed
     return 1
   ]], newresource)
   assert(a[1] == 11)
@@ -996,7 +996,7 @@ do
   -- repeat the test, but calling function in a 'multret' context
   local a = {T.testC([[
     call 0 1   # create resource
-    tobeclosed  # mark it to be closed
+    toclose    # mark it to be closed
     return 2
   ]], newresource)}
   assert(type(a[1]) == "string" and a[2][1] == 11)
@@ -1005,7 +1005,7 @@ do
   -- error
   local a, b = pcall(T.testC, [[
     call 0 1   # create resource
-    tobeclosed  # mark it to be closed
+    toclose    # mark it to be closed
     error       # resource is the error object
   ]], newresource)
   assert(a == false and b[1] == 11)
@@ -1019,10 +1019,10 @@ do
   local a = T.testC([[
     pushvalue 2
     call 0 1   # create resource
-    tobeclosed  # mark it to be closed
+    toclose    # mark it to be closed
     pushvalue 2
     call 0 1   # create another resource
-    tobeclosed  # mark it to be closed
+    toclose    # mark it to be closed
     pushvalue 3
     pushint 2   # there should be two open resources
     call 1 0
@@ -1102,7 +1102,7 @@ end)
 testamem("to-be-closed variables", function()
   local flag
   do
-    local scoped x = function () flag = true end
+    local *toclose x = function () flag = true end
     flag = false
     local x = {}
   end

+ 3 - 3
testes/files.lua

@@ -125,7 +125,7 @@ do
   -- closing file by scope
   local F = nil
   do
-    local scoped f = assert(io.open(file, "w"))
+    local *toclose f = assert(io.open(file, "w"))
     F = f
   end
   assert(tostring(F) == "file (closed)")
@@ -135,7 +135,7 @@ assert(os.remove(file))
 
 do
   -- test writing/reading numbers
-  local scoped f = assert(io.open(file, "w"))
+  local *toclose f = assert(io.open(file, "w"))
   f:write(maxint, '\n')
   f:write(string.format("0X%x\n", maxint))
   f:write("0xABCp-3", '\n')
@@ -158,7 +158,7 @@ assert(os.remove(file))
 
 -- testing multiple arguments to io.read
 do
-  local scoped f = assert(io.open(file, "w"))
+  local *toclose f = assert(io.open(file, "w"))
   f:write[[
 a line
 another line

+ 1 - 1
testes/goto.lua

@@ -258,7 +258,7 @@ do
   ::L2:: goto L3
 
   ::L1:: do
-    local scoped a = function () X = true end
+    local *toclose a = function () X = true end
     assert(X == nil)
     if a then goto L2 end   -- jumping back out of scope of 'a'
   end

+ 21 - 21
testes/locals.lua

@@ -181,9 +181,9 @@ local function stack(n) n = ((n == 0) or stack(n - 1)) end
 do
   local a = {}
   do
-    local scoped x = setmetatable({"x"}, {__close = function (self)
+    local *toclose x = setmetatable({"x"}, {__close = function (self)
                                                    a[#a + 1] = self[1] end})
-    local scoped y = function (x) assert(x == nil); a[#a + 1] = "y" end
+    local *toclose y = function (x) assert(x == nil); a[#a + 1] = "y" end
     a[#a + 1] = "in"
   end
   a[#a + 1] = "out"
@@ -197,7 +197,7 @@ do
 
   -- closing functions do not corrupt returning values
   local function foo (x)
-    local scoped _ = closescope
+    local *toclose _ = closescope
     return x, X, 23
   end
 
@@ -206,7 +206,7 @@ do
 
   X = false
   foo = function (x)
-    local scoped _ = closescope
+    local *toclose _ = closescope
     local y = 15
     return y
   end
@@ -215,7 +215,7 @@ do
 
   X = false
   foo = function ()
-    local scoped x = closescope
+    local *toclose x = closescope
     return x
   end
 
@@ -228,13 +228,13 @@ do
   -- to-be-closed variables must be closed in tail calls
   local X, Y
   local function foo ()
-    local scoped _ = function () Y = 10 end
+    local *toclose _ = function () Y = 10 end
     assert(X == 20 and Y == nil)
     return 1,2,3
   end
 
   local function bar ()
-    local scoped _ = function () X = 20 end
+    local *toclose _ = function () X = 20 end
     return foo()
   end
 
@@ -245,11 +245,11 @@ end
 do   -- errors in __close
   local log = {}
   local function foo (err)
-    local scoped x = function (msg) log[#log + 1] = msg; error(1) end
-    local scoped x1 = function (msg) log[#log + 1] = msg; end
-    local scoped gc = function () collectgarbage() end
-    local scoped y = function (msg) log[#log + 1] = msg; error(2) end
-    local scoped z = function (msg) log[#log + 1] = msg or 10; error(3) end
+    local *toclose x = function (msg) log[#log + 1] = msg; error(1) end
+    local *toclose x1 = function (msg) log[#log + 1] = msg; end
+    local *toclose gc = function () collectgarbage() end
+    local *toclose y = function (msg) log[#log + 1] = msg; error(2) end
+    local *toclose z = function (msg) log[#log + 1] = msg or 10; error(3) end
     if err then error(4) end
   end
   local stat, msg = pcall(foo, false)
@@ -267,8 +267,8 @@ end
 if rawget(_G, "T") then
   -- memory error inside closing function
   local function foo ()
-    local scoped y = function () T.alloccount() end
-    local scoped x = setmetatable({}, {__close = function ()
+    local *toclose y = function () T.alloccount() end
+    local *toclose x = setmetatable({}, {__close = function ()
       T.alloccount(0); local x = {}   -- force a memory error
     end})
     error("a")   -- common error inside the function's body
@@ -294,7 +294,7 @@ if rawget(_G, "T") then
   end
 
   local function test ()
-    local scoped x = enter(0)   -- set a memory limit
+    local *toclose x = enter(0)   -- set a memory limit
     -- creation of previous upvalue will raise a memory error
     os.exit(false)    -- should not run
   end
@@ -309,14 +309,14 @@ if rawget(_G, "T") then
 
   -- repeat test with extra closing upvalues
   local function test ()
-    local scoped xxx = function (msg)
+    local *toclose xxx = function (msg)
       assert(msg == "not enough memory");
       error(1000)   -- raise another error
     end
-    local scoped xx = function (msg)
+    local *toclose xx = function (msg)
       assert(msg == "not enough memory");
     end
-    local scoped x = enter(0)   -- set a memory limit
+    local *toclose x = enter(0)   -- set a memory limit
     -- creation of previous upvalue will raise a memory error
     os.exit(false)    -- should not run
   end
@@ -333,9 +333,9 @@ do
   local x = false
   local y = false
   local co = coroutine.create(function ()
-    local scoped xv = function () x = true end
+    local *toclose xv = function () x = true end
     do
-      local scoped yv = function () y = true end
+      local *toclose yv = function () y = true end
       coroutine.yield(100)   -- yield doesn't close variable
     end
     coroutine.yield(200)   -- yield doesn't close variable
@@ -353,7 +353,7 @@ end
 -- a suspended coroutine should not close its variables when collected
 local co
 co = coroutine.wrap(function()
-  local scoped x = function () os.exit(false) end    -- should not run
+  local *toclose x = function () os.exit(false) end    -- should not run
   co = nil
   coroutine.yield()
 end)