mirror of https://github.com/rusefi/lua.git
To-be-closed variable in 'for' loop separated from the state
The variable to be closed in a generic 'for' loop now is the 4th value produced in the loop initialization, instead of being the loop state (the 2nd value produced). That allows a loop to use a state with a '__toclose' metamethod but do not close it. (As an example, 'f:lines()' might use the file 'f' as a state for the loop, but it should not close the file when the loop ends.)
This commit is contained in:
parent
b8fed93215
commit
7f6f70853c
6
liolib.c
6
liolib.c
|
@ -386,8 +386,10 @@ static int io_lines (lua_State *L) {
|
||||||
}
|
}
|
||||||
aux_lines(L, toclose); /* push iteration function */
|
aux_lines(L, toclose); /* push iteration function */
|
||||||
if (toclose) {
|
if (toclose) {
|
||||||
lua_pushvalue(L, 1); /* file will be second result */
|
lua_pushnil(L); /* state */
|
||||||
return 2;
|
lua_pushnil(L); /* control */
|
||||||
|
lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */
|
||||||
|
return 4;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -279,9 +279,9 @@ OP_FORLOOP,/* A Bx R(A)+=R(A+2);
|
||||||
if R(A) <?= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */
|
if R(A) <?= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */
|
||||||
OP_FORPREP,/* A Bx R(A)-=R(A+2); pc+=Bx */
|
OP_FORPREP,/* A Bx R(A)-=R(A+2); pc+=Bx */
|
||||||
|
|
||||||
OP_TFORPREP,/* A Bx create upvalue A; pc+=Bx */
|
OP_TFORPREP,/* A Bx create upvalue for R(A + 3); pc+=Bx */
|
||||||
OP_TFORCALL,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
|
OP_TFORCALL,/* A C R(A+4), ... ,R(A+3+C) := R(A)(R(A+1), R(A+2)); */
|
||||||
OP_TFORLOOP,/* A Bx if R(A+1) ~= nil then { R(A)=R(A+1); pc -= Bx } */
|
OP_TFORLOOP,/* A Bx if R(A+2) ~= nil then { R(A)=R(A+2); pc -= Bx } */
|
||||||
|
|
||||||
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||||
|
|
||||||
|
|
25
lparser.c
25
lparser.c
|
@ -165,6 +165,9 @@ static int registerlocalvar (LexState *ls, TString *varname) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Create a new local variable with the given 'name'.
|
||||||
|
*/
|
||||||
static void new_localvar (LexState *ls, TString *name) {
|
static void new_localvar (LexState *ls, TString *name) {
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
Dyndata *dyd = ls->dyd;
|
Dyndata *dyd = ls->dyd;
|
||||||
|
@ -176,13 +179,8 @@ static void new_localvar (LexState *ls, TString *name) {
|
||||||
dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg);
|
dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void new_localvarliteral_ (LexState *ls, const char *name, size_t sz) {
|
|
||||||
new_localvar(ls, luaX_newstring(ls, name, sz));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define new_localvarliteral(ls,v) \
|
#define new_localvarliteral(ls,v) \
|
||||||
new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1)
|
new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
|
||||||
|
|
||||||
|
|
||||||
static LocVar *getlocvar (FuncState *fs, int i) {
|
static LocVar *getlocvar (FuncState *fs, int i) {
|
||||||
|
@ -192,6 +190,9 @@ static LocVar *getlocvar (FuncState *fs, int i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Start the scope for the last 'nvars' created variables.
|
||||||
|
*/
|
||||||
static void adjustlocalvars (LexState *ls, int nvars) {
|
static void adjustlocalvars (LexState *ls, int nvars) {
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
fs->nactvar = cast_byte(fs->nactvar + nvars);
|
fs->nactvar = cast_byte(fs->nactvar + nvars);
|
||||||
|
@ -1357,7 +1358,6 @@ static void forbody (LexState *ls, int base, int line, int nvars, int kind) {
|
||||||
BlockCnt bl;
|
BlockCnt bl;
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
int prep, endfor;
|
int prep, endfor;
|
||||||
adjustlocalvars(ls, 3); /* control variables */
|
|
||||||
checknext(ls, TK_DO);
|
checknext(ls, TK_DO);
|
||||||
prep = luaK_codeABx(fs, forprep[kind], base, 0);
|
prep = luaK_codeABx(fs, forprep[kind], base, 0);
|
||||||
enterblock(fs, &bl, 0); /* scope for declared variables */
|
enterblock(fs, &bl, 0); /* scope for declared variables */
|
||||||
|
@ -1399,6 +1399,7 @@ static void fornum (LexState *ls, TString *varname, int line) {
|
||||||
luaK_int(fs, fs->freereg, 1);
|
luaK_int(fs, fs->freereg, 1);
|
||||||
luaK_reserveregs(fs, 1);
|
luaK_reserveregs(fs, 1);
|
||||||
}
|
}
|
||||||
|
adjustlocalvars(ls, 3); /* control variables */
|
||||||
forbody(ls, base, line, 1, basicfor);
|
forbody(ls, base, line, 1, basicfor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1407,7 +1408,7 @@ static void forlist (LexState *ls, TString *indexname) {
|
||||||
/* forlist -> NAME {,NAME} IN explist forbody */
|
/* forlist -> NAME {,NAME} IN explist forbody */
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
expdesc e;
|
expdesc e;
|
||||||
int nvars = 4; /* gen, state, control, plus at least one declared var */
|
int nvars = 5; /* gen, state, control, toclose, 'indexname' */
|
||||||
int line;
|
int line;
|
||||||
int base = fs->freereg;
|
int base = fs->freereg;
|
||||||
/* create control variables */
|
/* create control variables */
|
||||||
|
@ -1415,6 +1416,7 @@ static void forlist (LexState *ls, TString *indexname) {
|
||||||
new_localvarliteral(ls, "(for state)");
|
new_localvarliteral(ls, "(for state)");
|
||||||
markupval(fs, fs->nactvar); /* state may create an upvalue */
|
markupval(fs, fs->nactvar); /* state may create an upvalue */
|
||||||
new_localvarliteral(ls, "(for control)");
|
new_localvarliteral(ls, "(for control)");
|
||||||
|
new_localvarliteral(ls, "(for toclose)");
|
||||||
/* create declared variables */
|
/* create declared variables */
|
||||||
new_localvar(ls, indexname);
|
new_localvar(ls, indexname);
|
||||||
while (testnext(ls, ',')) {
|
while (testnext(ls, ',')) {
|
||||||
|
@ -1423,9 +1425,10 @@ static void forlist (LexState *ls, TString *indexname) {
|
||||||
}
|
}
|
||||||
checknext(ls, TK_IN);
|
checknext(ls, TK_IN);
|
||||||
line = ls->linenumber;
|
line = ls->linenumber;
|
||||||
adjust_assign(ls, 3, explist(ls, &e), &e);
|
adjust_assign(ls, 4, explist(ls, &e), &e);
|
||||||
|
adjustlocalvars(ls, 4); /* control variables */
|
||||||
luaK_checkstack(fs, 3); /* extra space to call generator */
|
luaK_checkstack(fs, 3); /* extra space to call generator */
|
||||||
forbody(ls, base, line, nvars - 3, 2);
|
forbody(ls, base, line, nvars - 4, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1575,9 +1578,9 @@ static void tocloselocalstat (LexState *ls) {
|
||||||
new_localvar(ls, str_checkname(ls));
|
new_localvar(ls, str_checkname(ls));
|
||||||
checknext(ls, '=');
|
checknext(ls, '=');
|
||||||
exp1(ls, 0);
|
exp1(ls, 0);
|
||||||
luaK_codeABC(fs, OP_TBC, fs->nactvar, 0, 0);
|
|
||||||
markupval(fs, fs->nactvar);
|
markupval(fs, fs->nactvar);
|
||||||
adjustlocalvars(ls, 1);
|
adjustlocalvars(ls, 1);
|
||||||
|
luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
23
lvm.c
23
lvm.c
|
@ -1654,11 +1654,11 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||||
vmbreak;
|
vmbreak;
|
||||||
}
|
}
|
||||||
vmcase(OP_TFORPREP) {
|
vmcase(OP_TFORPREP) {
|
||||||
/* is 'state' a function or has a '__close' metamethod? */
|
/* is 'toclose' a function or has a '__close' metamethod? */
|
||||||
if (ttisfunction(s2v(ra + 1)) ||
|
if (ttisfunction(s2v(ra + 3)) ||
|
||||||
!ttisnil(luaT_gettmbyobj(L, s2v(ra + 1), TM_CLOSE))) {
|
!ttisnil(luaT_gettmbyobj(L, s2v(ra + 3), TM_CLOSE))) {
|
||||||
/* create to-be-closed upvalue for it */
|
/* create to-be-closed upvalue for it */
|
||||||
halfProtect(luaF_newtbcupval(L, ra + 1));
|
halfProtect(luaF_newtbcupval(L, ra + 3));
|
||||||
}
|
}
|
||||||
pc += GETARG_Bx(i);
|
pc += GETARG_Bx(i);
|
||||||
i = *(pc++); /* go to next instruction */
|
i = *(pc++); /* go to next instruction */
|
||||||
|
@ -1668,13 +1668,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||||
vmcase(OP_TFORCALL) {
|
vmcase(OP_TFORCALL) {
|
||||||
l_tforcall:
|
l_tforcall:
|
||||||
/* 'ra' has the iterator function, 'ra + 1' has the state,
|
/* 'ra' has the iterator function, 'ra + 1' has the state,
|
||||||
and 'ra + 2' has the control variable. The call will use
|
'ra + 2' has the control variable, and 'ra + 3' has the
|
||||||
the stack after these values (starting at 'ra + 3')
|
to-be-closed variable. The call will use the stack after
|
||||||
|
these values (starting at 'ra + 4')
|
||||||
*/
|
*/
|
||||||
/* push function, state, and control variable */
|
/* push function, state, and control variable */
|
||||||
memcpy(ra + 3, ra, 3 * sizeof(*ra));
|
memcpy(ra + 4, ra, 3 * sizeof(*ra));
|
||||||
L->top = ra + 6;
|
L->top = ra + 4 + 3;
|
||||||
Protect(luaD_call(L, ra + 3, GETARG_C(i))); /* do the call */
|
Protect(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
|
||||||
if (trap) { /* stack may have changed? */
|
if (trap) { /* stack may have changed? */
|
||||||
updatebase(ci); /* keep 'base' correct */
|
updatebase(ci); /* keep 'base' correct */
|
||||||
ra = RA(i); /* keep 'ra' correct for next instruction */
|
ra = RA(i); /* keep 'ra' correct for next instruction */
|
||||||
|
@ -1686,8 +1687,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||||
}
|
}
|
||||||
vmcase(OP_TFORLOOP) {
|
vmcase(OP_TFORLOOP) {
|
||||||
l_tforloop:
|
l_tforloop:
|
||||||
if (!ttisnil(s2v(ra + 1))) { /* continue loop? */
|
if (!ttisnil(s2v(ra + 2))) { /* continue loop? */
|
||||||
setobjs2s(L, ra, ra + 1); /* save control variable */
|
setobjs2s(L, ra, ra + 2); /* save control variable */
|
||||||
pc -= GETARG_Bx(i); /* jump back */
|
pc -= GETARG_Bx(i); /* jump back */
|
||||||
}
|
}
|
||||||
vmbreak;
|
vmbreak;
|
||||||
|
|
|
@ -200,7 +200,7 @@ return x + y * z
|
||||||
assert(f:close())
|
assert(f:close())
|
||||||
f = coroutine.wrap(dofile)
|
f = coroutine.wrap(dofile)
|
||||||
assert(f(file) == 10)
|
assert(f(file) == 10)
|
||||||
print(f(100, 101) == 20)
|
assert(f(100, 101) == 20)
|
||||||
assert(f(200) == 100 + 200 * 101)
|
assert(f(200) == 100 + 200 * 101)
|
||||||
assert(os.remove(file))
|
assert(os.remove(file))
|
||||||
|
|
||||||
|
@ -422,6 +422,41 @@ assert(load(io.lines(file, "L"), nil, nil, t))()
|
||||||
assert(t.a == -((10 + 34) * 2))
|
assert(t.a == -((10 + 34) * 2))
|
||||||
|
|
||||||
|
|
||||||
|
do -- testing closing file in line iteration
|
||||||
|
|
||||||
|
-- get the to-be-closed variable from a loop
|
||||||
|
local function gettoclose (lv)
|
||||||
|
lv = lv + 1
|
||||||
|
for i = 1, math.maxinteger do
|
||||||
|
local n, v = debug.getlocal(lv, i)
|
||||||
|
if n == "(for toclose)" then
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local f
|
||||||
|
for l in io.lines(file) do
|
||||||
|
f = gettoclose(1)
|
||||||
|
assert(io.type(f) == "file")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
assert(io.type(f) == "closed file")
|
||||||
|
|
||||||
|
f = nil
|
||||||
|
local function foo (name)
|
||||||
|
for l in io.lines(name) do
|
||||||
|
f = gettoclose(1)
|
||||||
|
assert(io.type(f) == "file")
|
||||||
|
error(f) -- exit loop with an error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local st, msg = pcall(foo, file)
|
||||||
|
assert(st == false and io.type(msg) == "closed file")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- test for multipe arguments in 'lines'
|
-- test for multipe arguments in 'lines'
|
||||||
io.output(file); io.write"0123456789\n":close()
|
io.output(file); io.write"0123456789\n":close()
|
||||||
for a,b in io.lines(file, 1, 1) do
|
for a,b in io.lines(file, 1, 1) do
|
||||||
|
|
|
@ -371,6 +371,8 @@ do
|
||||||
x = x - 1
|
x = x - 1
|
||||||
if x > 0 then return x end
|
if x > 0 then return x end
|
||||||
end,
|
end,
|
||||||
|
nil, -- state
|
||||||
|
nil, -- control variable
|
||||||
function () -- closing function
|
function () -- closing function
|
||||||
numopen = numopen - 1
|
numopen = numopen - 1
|
||||||
end
|
end
|
||||||
|
@ -392,12 +394,16 @@ do
|
||||||
-- repeat test with '__open' metamethod instead of a function
|
-- repeat test with '__open' metamethod instead of a function
|
||||||
local function open (x)
|
local function open (x)
|
||||||
numopen = numopen + 1
|
numopen = numopen + 1
|
||||||
|
local state = setmetatable({x},
|
||||||
|
{__close = function () numopen = numopen - 1 end})
|
||||||
return
|
return
|
||||||
function (t) -- iteraction function
|
function (t) -- iteraction function
|
||||||
t[1] = t[1] - 1
|
t[1] = t[1] - 1
|
||||||
if t[1] > 0 then return t[1] end
|
if t[1] > 0 then return t[1] end
|
||||||
end,
|
end,
|
||||||
setmetatable({x}, {__close = function () numopen = numopen - 1 end})
|
state,
|
||||||
|
nil,
|
||||||
|
state -- to-be-closed
|
||||||
end
|
end
|
||||||
|
|
||||||
local s = 0
|
local s = 0
|
||||||
|
|
Loading…
Reference in New Issue