diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-07 10:03:05 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-07 10:03:05 -0200 |
commit | b8fed93215a23a3f443c5b0126f0de1725771b44 (patch) | |
tree | 1d7d29388b8a20fb7d0920d94b02a8040612314d | |
parent | 5e76a4fd313a8690d300085c4e8fcb9dca50c01a (diff) | |
download | lua-github-b8fed93215a23a3f443c5b0126f0de1725771b44.tar.gz |
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').
-rw-r--r-- | lapi.c | 2 | ||||
-rw-r--r-- | lparser.c | 25 | ||||
-rw-r--r-- | ltests.c | 4 | ||||
-rw-r--r-- | lua.h | 2 | ||||
-rw-r--r-- | testes/api.lua | 12 | ||||
-rw-r--r-- | testes/files.lua | 6 | ||||
-rw-r--r-- | testes/goto.lua | 2 | ||||
-rw-r--r-- | testes/locals.lua | 42 |
8 files changed, 48 insertions, 47 deletions
@@ -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? */ @@ -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); } @@ -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); } @@ -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); /* diff --git a/testes/api.lua b/testes/api.lua index 988250f7..a6ddca8e 100644 --- a/testes/api.lua +++ b/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 diff --git a/testes/files.lua b/testes/files.lua index a11c5e61..e68eb9b8 100644 --- a/testes/files.lua +++ b/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 diff --git a/testes/goto.lua b/testes/goto.lua index 92f048fb..5d863a42 100644 --- a/testes/goto.lua +++ b/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 diff --git a/testes/locals.lua b/testes/locals.lua index 1e0f525b..869ac1ff 100644 --- a/testes/locals.lua +++ b/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) |