State in generic 'for' acts as a to-be-closed variable

The implicit variable 'state' in a generic 'for' is marked as a
to-be-closed variable, so that the state will be closed as soon
as the loop ends, no matter how.

Taking advantage of this new facility, the call 'io.lines(filename)'
now returns the open file as a second result. Therefore,
an iteraction like 'for l in io.lines(name)...' will close the
file even when the loop ends with a break or an error.
This commit is contained in:
Roberto Ierusalimschy
2018-10-31 14:54:45 -03:00
parent e073cbc2e5
commit 947a372f58
5 changed files with 111 additions and 24 deletions

View File

@@ -462,13 +462,13 @@ X
- y;
]]:close()
_G.X = 1
assert(not load(io.lines(file)))
assert(not load((io.lines(file))))
collectgarbage() -- to close file in previous iteration
load(io.lines(file, "L"))()
load((io.lines(file, "L")))()
assert(_G.X == 2)
load(io.lines(file, 1))()
load((io.lines(file, 1)))()
assert(_G.X == 4)
load(io.lines(file, 3))()
load((io.lines(file, 3)))()
assert(_G.X == 8)
print('+')

View File

@@ -108,7 +108,7 @@ print'+'
if rawget(_G, "T") then
-- testing clearing of dead elements from tables
collectgarbage("stop") -- stop GC
local a = {[{}] = 4, [3] = 0, alo = 1,
local a = {[{}] = 4, [3] = 0, alo = 1,
a1234567890123456789012345678901234567890 = 10}
local t = T.querytab(a)
@@ -360,6 +360,54 @@ end)
co() -- start coroutine
assert(co == nil) -- eventually it will be collected
-- to-be-closed variables in generic for loops
do
local numopen = 0
local function open (x)
numopen = numopen + 1
return
function () -- iteraction function
x = x - 1
if x > 0 then return x end
end,
function () -- closing function
numopen = numopen - 1
end
end
local s = 0
for i in open(10) do
s = s + i
end
assert(s == 45 and numopen == 0)
local s = 0
for i in open(10) do
if i < 5 then break end
s = s + i
end
assert(s == 35 and numopen == 0)
-- repeat test with '__open' metamethod instead of a function
local function open (x)
numopen = numopen + 1
return
function (t) -- iteraction function
t[1] = t[1] - 1
if t[1] > 0 then return t[1] end
end,
setmetatable({x}, {__close = function () numopen = numopen - 1 end})
end
local s = 0
for i in open(10) do
if (i < 5) then break end
s = s + i
end
assert(s == 35 and numopen == 0)
end
print('OK')
return 5,f