mirror of
https://github.com/lua/lua.git
synced 2025-01-14 05:43:00 +08:00
New semantics for the integer 'for' loop
The numerical 'for' loop over integers now uses a precomputed counter to control its number of iteractions. This change eliminates several weird cases caused by overflows (wrap-around) in the control variable. (It also ensures that every integer loop halts.) Also, the special opcodes for the usual case of step==1 were removed. (The new code is already somewhat complex for the usual case, but efficient.)
This commit is contained in:
parent
1e0c73d5b6
commit
9b37a4695e
@ -99,8 +99,6 @@ static void *disptab[NUM_OPCODES] = {
|
||||
&&L_OP_RETURN,
|
||||
&&L_OP_RETURN0,
|
||||
&&L_OP_RETURN1,
|
||||
&&L_OP_FORLOOP1,
|
||||
&&L_OP_FORPREP1,
|
||||
&&L_OP_FORLOOP,
|
||||
&&L_OP_FORPREP,
|
||||
&&L_OP_TFORPREP,
|
||||
|
@ -93,8 +93,6 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
||||
,opmode(0, 1, 0, 0, iABC) /* OP_RETURN */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_RETURN0 */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_RETURN1 */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_FORLOOP1 */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_FORPREP1 */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_FORLOOP */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_FORPREP */
|
||||
,opmode(0, 0, 0, 0, iABx) /* OP_TFORPREP */
|
||||
|
@ -280,10 +280,6 @@ OP_RETURN,/* A B C return R(A), ... ,R(A+B-2) (see note) */
|
||||
OP_RETURN0,/* return */
|
||||
OP_RETURN1,/* A return R(A) */
|
||||
|
||||
OP_FORLOOP1,/* A Bx R(A)++;
|
||||
if R(A) <= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */
|
||||
OP_FORPREP1,/* A Bx R(A)--; pc+=Bx */
|
||||
|
||||
OP_FORLOOP,/* A Bx R(A)+=R(A+2);
|
||||
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 */
|
||||
|
@ -84,8 +84,6 @@ static const char *const opnames[] = {
|
||||
"RETURN",
|
||||
"RETURN0",
|
||||
"RETURN1",
|
||||
"FORLOOP1",
|
||||
"FORPREP1",
|
||||
"FORLOOP",
|
||||
"FORPREP",
|
||||
"TFORPREP",
|
||||
|
42
lparser.c
42
lparser.c
@ -1371,18 +1371,14 @@ static void repeatstat (LexState *ls, int line) {
|
||||
|
||||
/*
|
||||
** Read an expression and generate code to put its results in next
|
||||
** stack slot. Return true if expression is a constant integer and,
|
||||
** if 'i' is not-zero, its value is equal to 'i'.
|
||||
** stack slot.
|
||||
**
|
||||
*/
|
||||
static int exp1 (LexState *ls, int i) {
|
||||
static void exp1 (LexState *ls) {
|
||||
expdesc e;
|
||||
int res;
|
||||
expr(ls, &e);
|
||||
res = luaK_isKint(&e) && (i == 0 || i == e.u.ival);
|
||||
luaK_exp2nextreg(ls->fs, &e);
|
||||
lua_assert(e.k == VNONRELOC);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -1403,31 +1399,29 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) {
|
||||
|
||||
|
||||
/*
|
||||
** Generate code for a 'for' loop. 'kind' can be zero (a common for
|
||||
** loop), one (a basic for loop, with integer values and increment of
|
||||
** 1), or two (a generic for loop).
|
||||
** Generate code for a 'for' loop.
|
||||
*/
|
||||
static void forbody (LexState *ls, int base, int line, int nvars, int kind) {
|
||||
static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
|
||||
/* forbody -> DO block */
|
||||
static OpCode forprep[3] = {OP_FORPREP, OP_FORPREP1, OP_TFORPREP};
|
||||
static OpCode forloop[3] = {OP_FORLOOP, OP_FORLOOP1, OP_TFORLOOP};
|
||||
static OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP};
|
||||
static OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP};
|
||||
BlockCnt bl;
|
||||
FuncState *fs = ls->fs;
|
||||
int prep, endfor;
|
||||
checknext(ls, TK_DO);
|
||||
prep = luaK_codeABx(fs, forprep[kind], base, 0);
|
||||
prep = luaK_codeABx(fs, forprep[isgen], base, 0);
|
||||
enterblock(fs, &bl, 0); /* scope for declared variables */
|
||||
adjustlocalvars(ls, nvars);
|
||||
luaK_reserveregs(fs, nvars);
|
||||
block(ls);
|
||||
leaveblock(fs); /* end of scope for declared variables */
|
||||
fixforjump(fs, prep, luaK_getlabel(fs), 0);
|
||||
if (kind == 2) { /* generic for? */
|
||||
if (isgen) { /* generic for? */
|
||||
luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars);
|
||||
luaK_fixline(fs, line);
|
||||
base += 2; /* base for 'OP_TFORLOOP' (skips function and state) */
|
||||
}
|
||||
endfor = luaK_codeABx(fs, forloop[kind], base, 0);
|
||||
endfor = luaK_codeABx(fs, forloop[isgen], base, 0);
|
||||
fixforjump(fs, endfor, prep + 1, 1);
|
||||
luaK_fixline(fs, line);
|
||||
}
|
||||
@ -1437,26 +1431,22 @@ static void fornum (LexState *ls, TString *varname, int line) {
|
||||
/* fornum -> NAME = exp,exp[,exp] forbody */
|
||||
FuncState *fs = ls->fs;
|
||||
int base = fs->freereg;
|
||||
int basicfor = 1; /* true if it is a "basic" 'for' (integer + 1) */
|
||||
new_localvarliteral(ls, "(for index)");
|
||||
new_localvarliteral(ls, "(for limit)");
|
||||
new_localvarliteral(ls, "(for step)");
|
||||
new_localvar(ls, varname);
|
||||
checknext(ls, '=');
|
||||
if (!exp1(ls, 0)) /* initial value not an integer? */
|
||||
basicfor = 0; /* not a basic 'for' */
|
||||
exp1(ls); /* initial value */
|
||||
checknext(ls, ',');
|
||||
exp1(ls, 0); /* limit */
|
||||
if (testnext(ls, ',')) {
|
||||
if (!exp1(ls, 1)) /* optional step not 1? */
|
||||
basicfor = 0; /* not a basic 'for' */
|
||||
}
|
||||
exp1(ls); /* limit */
|
||||
if (testnext(ls, ','))
|
||||
exp1(ls); /* optional step */
|
||||
else { /* default step = 1 */
|
||||
luaK_int(fs, fs->freereg, 1);
|
||||
luaK_reserveregs(fs, 1);
|
||||
}
|
||||
adjustlocalvars(ls, 3); /* control variables */
|
||||
forbody(ls, base, line, 1, basicfor);
|
||||
forbody(ls, base, line, 1, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -1484,7 +1474,7 @@ static void forlist (LexState *ls, TString *indexname) {
|
||||
adjust_assign(ls, 4, explist(ls, &e), &e);
|
||||
adjustlocalvars(ls, 4); /* control variables */
|
||||
luaK_checkstack(fs, 3); /* extra space to call generator */
|
||||
forbody(ls, base, line, nvars - 4, 2);
|
||||
forbody(ls, base, line, nvars - 4, 1);
|
||||
}
|
||||
|
||||
|
||||
@ -1633,7 +1623,7 @@ static void tocloselocalstat (LexState *ls) {
|
||||
luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
|
||||
new_localvar(ls, str_checkname(ls));
|
||||
checknext(ls, '=');
|
||||
exp1(ls, 0);
|
||||
exp1(ls);
|
||||
markupval(fs, fs->nactvar);
|
||||
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
||||
adjustlocalvars(ls, 1);
|
||||
|
143
lvm.c
143
lvm.c
@ -148,35 +148,34 @@ int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) {
|
||||
|
||||
/*
|
||||
** Try to convert a 'for' limit to an integer, preserving the semantics
|
||||
** of the loop. (The following explanation assumes a non-negative step;
|
||||
** of the loop. (The following explanation assumes a positive step;
|
||||
** it is valid for negative steps mutatis mutandis.)
|
||||
** If the limit is an integer or can be converted to an integer,
|
||||
** rounding down, that is it.
|
||||
** Otherwise, check whether the limit can be converted to a float. If
|
||||
** the number is too large, it is OK to set the limit as LUA_MAXINTEGER,
|
||||
** which means no limit. If the number is too negative, the loop
|
||||
** should not run, because any initial integer value is larger than the
|
||||
** limit. So, it sets the limit to LUA_MININTEGER. 'stopnow' corrects
|
||||
** the extreme case when the initial value is LUA_MININTEGER, in which
|
||||
** case the LUA_MININTEGER limit would still run the loop once.
|
||||
** the float is too large, clip it to LUA_MAXINTEGER. If the float
|
||||
** is too negative, the loop should not run, because any initial
|
||||
** integer value is greater than such limit; so, it sets 'stopnow'.
|
||||
** (For this latter case, no integer limit would be correct; even a
|
||||
** limit of LUA_MININTEGER would run the loop once for an initial
|
||||
** value equal to LUA_MININTEGER.)
|
||||
*/
|
||||
static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step,
|
||||
static int forlimit (const TValue *lim, lua_Integer *p, lua_Integer step,
|
||||
int *stopnow) {
|
||||
*stopnow = 0; /* usually, let loops run */
|
||||
if (ttisinteger(obj))
|
||||
*p = ivalue(obj);
|
||||
else if (!luaV_tointeger(obj, p, (step < 0 ? 2 : 1))) {
|
||||
if (!luaV_tointeger(lim, p, (step < 0 ? 2 : 1))) {
|
||||
/* not coercible to in integer */
|
||||
lua_Number n; /* try to convert to float */
|
||||
if (!tonumber(obj, &n)) /* cannot convert to float? */
|
||||
lua_Number flim; /* try to convert to float */
|
||||
if (!tonumber(lim, &flim)) /* cannot convert to float? */
|
||||
return 0; /* not a number */
|
||||
if (luai_numlt(0, n)) { /* if true, float is larger than max integer */
|
||||
*p = LUA_MAXINTEGER;
|
||||
if (step < 0) *stopnow = 1;
|
||||
/* 'flim' is a float out of integer bounds */
|
||||
if (luai_numlt(0, flim)) { /* if it is positive, it is too large */
|
||||
*p = LUA_MAXINTEGER; /* truncate */
|
||||
if (step < 0) *stopnow = 1; /* initial value must be less than it */
|
||||
}
|
||||
else { /* float is less than min integer */
|
||||
*p = LUA_MININTEGER;
|
||||
if (step >= 0) *stopnow = 1;
|
||||
else { /* it is less than min integer */
|
||||
*p = LUA_MININTEGER; /* truncate */
|
||||
if (step > 0) *stopnow = 1; /* initial value must be greater than it */
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@ -1636,85 +1635,87 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
vmcase(OP_FORLOOP1) {
|
||||
lua_Integer idx = intop(+, ivalue(s2v(ra)), 1); /* increment index */
|
||||
lua_Integer limit = ivalue(s2v(ra + 1));
|
||||
if (idx <= limit) {
|
||||
pc -= GETARG_Bx(i); /* jump back */
|
||||
chgivalue(s2v(ra), idx); /* update internal index... */
|
||||
setivalue(s2v(ra + 3), idx); /* ...and external index */
|
||||
}
|
||||
updatetrap(ci);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_FORPREP1) {
|
||||
TValue *init = s2v(ra);
|
||||
TValue *plimit = s2v(ra + 1);
|
||||
lua_Integer ilimit, initv;
|
||||
int stopnow;
|
||||
if (unlikely(!forlimit(plimit, &ilimit, 1, &stopnow))) {
|
||||
savestate(L, ci); /* for the error message */
|
||||
luaG_forerror(L, plimit, "limit");
|
||||
}
|
||||
initv = (stopnow ? 0 : ivalue(init));
|
||||
setivalue(plimit, ilimit);
|
||||
setivalue(init, intop(-, initv, 1));
|
||||
pc += GETARG_Bx(i);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_FORLOOP) {
|
||||
if (ttisinteger(s2v(ra))) { /* integer loop? */
|
||||
if (ttisinteger(s2v(ra + 2))) { /* integer loop? */
|
||||
lua_Unsigned count = l_castS2U(ivalue(s2v(ra)));
|
||||
if (count > 0) { /* still more iterations? */
|
||||
lua_Integer step = ivalue(s2v(ra + 2));
|
||||
lua_Integer idx = intop(+, ivalue(s2v(ra)), step); /* new index */
|
||||
lua_Integer limit = ivalue(s2v(ra + 1));
|
||||
if ((0 < step) ? (idx <= limit) : (limit <= idx)) {
|
||||
lua_Integer idx = ivalue(s2v(ra + 3));
|
||||
idx = intop(+, idx, step); /* add step to index */
|
||||
chgivalue(s2v(ra), count - 1); /* update counter... */
|
||||
setivalue(s2v(ra + 3), idx); /* ...and index */
|
||||
pc -= GETARG_Bx(i); /* jump back */
|
||||
chgivalue(s2v(ra), idx); /* update internal index... */
|
||||
setivalue(s2v(ra + 3), idx); /* ...and external index */
|
||||
}
|
||||
}
|
||||
else { /* floating loop */
|
||||
lua_Number step = fltvalue(s2v(ra + 2));
|
||||
lua_Number limit = fltvalue(s2v(ra + 1));
|
||||
lua_Number idx = fltvalue(s2v(ra));
|
||||
lua_Number idx = fltvalue(s2v(ra + 3));
|
||||
idx = luai_numadd(L, idx, step); /* inc. index */
|
||||
if (luai_numlt(0, step) ? luai_numle(idx, limit)
|
||||
: luai_numle(limit, idx)) {
|
||||
setfltvalue(s2v(ra + 3), idx); /* update index */
|
||||
pc -= GETARG_Bx(i); /* jump back */
|
||||
chgfltvalue(s2v(ra), idx); /* update internal index... */
|
||||
setfltvalue(s2v(ra + 3), idx); /* ...and external index */
|
||||
}
|
||||
}
|
||||
updatetrap(ci);
|
||||
updatetrap(ci); /* allows a signal to break the loop */
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_FORPREP) {
|
||||
TValue *init = s2v(ra);
|
||||
TValue *pinit = s2v(ra);
|
||||
TValue *plimit = s2v(ra + 1);
|
||||
TValue *pstep = s2v(ra + 2);
|
||||
lua_Integer ilimit;
|
||||
int stopnow;
|
||||
if (ttisinteger(init) && ttisinteger(pstep) &&
|
||||
savestate(L, ci); /* in case of errors */
|
||||
if (ttisinteger(pinit) && ttisinteger(pstep) &&
|
||||
forlimit(plimit, &ilimit, ivalue(pstep), &stopnow)) {
|
||||
/* all values are integer */
|
||||
lua_Integer initv = (stopnow ? 0 : ivalue(init));
|
||||
setivalue(plimit, ilimit);
|
||||
setivalue(init, intop(-, initv, ivalue(pstep)));
|
||||
/* integer loop */
|
||||
lua_Integer init = ivalue(pinit);
|
||||
lua_Integer step = ivalue(pstep);
|
||||
setivalue(s2v(ra + 3), init); /* control variable */
|
||||
if (step == 0)
|
||||
luaG_runerror(L, "'for' step is zero");
|
||||
else if (stopnow)
|
||||
pc += GETARG_Bx(i) + 1; /* skip the loop */
|
||||
else if (step > 0) { /* ascending loop? */
|
||||
if (init > ilimit)
|
||||
pc += GETARG_Bx(i) + 1; /* skip the loop */
|
||||
else {
|
||||
lua_Unsigned count = l_castS2U(ilimit) - l_castS2U(init);
|
||||
if (step != 1) /* avoid division in the too common case */
|
||||
count /= l_castS2U(step);
|
||||
setivalue(s2v(ra), count);
|
||||
}
|
||||
}
|
||||
else { /* descending loop */
|
||||
if (init < ilimit)
|
||||
pc += GETARG_Bx(i) + 1; /* skip the loop */
|
||||
else {
|
||||
lua_Unsigned count = l_castS2U(init) - l_castS2U(ilimit);
|
||||
count /= -l_castS2U(step);
|
||||
setivalue(s2v(ra), count);
|
||||
}
|
||||
}
|
||||
}
|
||||
else { /* try making all values floats */
|
||||
lua_Number ninit; lua_Number nlimit; lua_Number nstep;
|
||||
savestate(L, ci); /* in case of errors */
|
||||
if (unlikely(!tonumber(plimit, &nlimit)))
|
||||
lua_Number init; lua_Number flimit; lua_Number step;
|
||||
if (unlikely(!tonumber(plimit, &flimit)))
|
||||
luaG_forerror(L, plimit, "limit");
|
||||
setfltvalue(plimit, nlimit);
|
||||
if (unlikely(!tonumber(pstep, &nstep)))
|
||||
setfltvalue(plimit, flimit);
|
||||
if (unlikely(!tonumber(pstep, &step)))
|
||||
luaG_forerror(L, pstep, "step");
|
||||
setfltvalue(pstep, nstep);
|
||||
if (unlikely(!tonumber(init, &ninit)))
|
||||
luaG_forerror(L, init, "initial value");
|
||||
setfltvalue(init, luai_numsub(L, ninit, nstep));
|
||||
setfltvalue(pstep, step);
|
||||
if (unlikely(!tonumber(pinit, &init)))
|
||||
luaG_forerror(L, pinit, "initial value");
|
||||
if (step == 0)
|
||||
luaG_runerror(L, "'for' step is zero");
|
||||
if (luai_numlt(0, step) ? luai_numlt(flimit, init)
|
||||
: luai_numlt(init, flimit))
|
||||
pc += GETARG_Bx(i) + 1; /* skip the loop */
|
||||
else
|
||||
setfltvalue(s2v(ra + 3), init); /* control variable */
|
||||
}
|
||||
pc += GETARG_Bx(i);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_TFORPREP) {
|
||||
|
120
manual/manual.of
120
manual/manual.of
@ -594,7 +594,7 @@ controls how long the collector waits before starting a new cycle.
|
||||
The collector starts a new cycle when the use of memory
|
||||
hits @M{n%} of the use after the previous collection.
|
||||
Larger values make the collector less aggressive.
|
||||
Values smaller than 100 mean the collector will not wait to
|
||||
Values less than 100 mean the collector will not wait to
|
||||
start a new cycle.
|
||||
A value of 200 means that the collector waits for the total memory in use
|
||||
to double before starting a new cycle.
|
||||
@ -608,7 +608,7 @@ how many elements it marks or sweeps for each
|
||||
kilobyte of memory allocated.
|
||||
Larger values make the collector more aggressive but also increase
|
||||
the size of each incremental step.
|
||||
You should not use values smaller than 100,
|
||||
You should not use values less than 100,
|
||||
because they make the collector too slow and
|
||||
can result in the collector never finishing a cycle.
|
||||
The default value is 100; the maximum value is 1000.
|
||||
@ -1004,7 +1004,7 @@ the escape sequence @T{\u{@rep{XXX}}}
|
||||
(note the mandatory enclosing brackets),
|
||||
where @rep{XXX} is a sequence of one or more hexadecimal digits
|
||||
representing the character code point.
|
||||
This code point can be any value smaller than @M{2@sp{31}}.
|
||||
This code point can be any value less than @M{2@sp{31}}.
|
||||
(Lua uses the original UTF-8 specification here.)
|
||||
|
||||
Literal strings can also be defined using a long format
|
||||
@ -1370,74 +1370,50 @@ because now @Rw{return} is the last statement in its (inner) block.
|
||||
The @Rw{for} statement has two forms:
|
||||
one numerical and one generic.
|
||||
|
||||
@sect4{@title{The numerical @Rw{for} loop}
|
||||
|
||||
The numerical @Rw{for} loop repeats a block of code while a
|
||||
control variable runs through an arithmetic progression.
|
||||
control variable goes through an arithmetic progression.
|
||||
It has the following syntax:
|
||||
@Produc{
|
||||
@producname{stat}@producbody{@Rw{for} @bnfNter{Name} @bnfter{=}
|
||||
exp @bnfter{,} exp @bnfopt{@bnfter{,} exp} @Rw{do} block @Rw{end}}
|
||||
}
|
||||
The @emph{block} is repeated for @emph{name} starting at the value of
|
||||
the first @emph{exp}, until it passes the second @emph{exp} by steps of the
|
||||
third @emph{exp}.
|
||||
More precisely, a @Rw{for} statement like
|
||||
@verbatim{
|
||||
for v = @rep{e1}, @rep{e2}, @rep{e3} do @rep{block} end
|
||||
}
|
||||
is equivalent to the code:
|
||||
@verbatim{
|
||||
do
|
||||
local @rep{var}, @rep{limit}, @rep{step} = tonumber(@rep{e1}), tonumber(@rep{e2}), tonumber(@rep{e3})
|
||||
if not (@rep{var} and @rep{limit} and @rep{step}) then error() end
|
||||
@rep{var} = @rep{var} - @rep{step}
|
||||
while true do
|
||||
@rep{var} = @rep{var} + @rep{step}
|
||||
if (@rep{step} >= 0 and @rep{var} > @rep{limit}) or (@rep{step} < 0 and @rep{var} < @rep{limit}) then
|
||||
break
|
||||
end
|
||||
local v = @rep{var}
|
||||
@rep{block}
|
||||
end
|
||||
end
|
||||
}
|
||||
The given identifier (@bnfNter{Name}) defines the control variable,
|
||||
which is local to the loop body (@emph{block}).
|
||||
|
||||
Note the following:
|
||||
@itemize{
|
||||
The loop starts by evaluating once the three control expressions;
|
||||
they must all result in numbers.
|
||||
Their values are called respectively
|
||||
the @emph{initial value}, the @emph{limit}, and the @emph{step}.
|
||||
If the step is absent, it defaults @N{to 1}.
|
||||
Then the loop body is repeated with the value of the control variable
|
||||
going through an arithmetic progression,
|
||||
starting at the initial value,
|
||||
with a common difference given by the step,
|
||||
until that value passes the limit.
|
||||
A negative step makes a decreasing sequence;
|
||||
a step equal to zero raises an error.
|
||||
If the initial value is already greater than the limit
|
||||
(or less than, if the step is negative), the body is not executed.
|
||||
|
||||
@item{
|
||||
All three control expressions are evaluated only once,
|
||||
before the loop starts.
|
||||
They must all result in numbers.
|
||||
}
|
||||
If both the initial value and the step are integers,
|
||||
the loop is done with integers;
|
||||
in this case, the range of the control variable is limited
|
||||
by the range of integers.
|
||||
Otherwise, the loop is done with floats.
|
||||
(Beware of floating-point accuracy in this case.)
|
||||
|
||||
@item{
|
||||
@T{@rep{var}}, @T{@rep{limit}}, and @T{@rep{step}} are invisible variables.
|
||||
The names shown here are for explanatory purposes only.
|
||||
}
|
||||
|
||||
@item{
|
||||
If the third expression (the step) is absent,
|
||||
then a step @N{of 1} is used.
|
||||
}
|
||||
|
||||
@item{
|
||||
You can use @Rw{break} and @Rw{goto} to exit a @Rw{for} loop.
|
||||
}
|
||||
|
||||
@item{
|
||||
The loop variable @T{v} is local to the loop body.
|
||||
You should not change the value of the control variable
|
||||
during the loop.
|
||||
If you need its value after the loop,
|
||||
assign it to another variable before exiting the loop.
|
||||
}
|
||||
|
||||
@item{
|
||||
The values in @rep{var}, @rep{limit}, and @rep{step}
|
||||
can be integers or floats.
|
||||
All operations on them respect the usual rules in Lua.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@sect4{@title{The generic @Rw{for} loop}
|
||||
|
||||
|
||||
The generic @Rw{for} statement works over functions,
|
||||
called @def{iterators}.
|
||||
On each iteration, the iterator function is called to produce a new value,
|
||||
@ -1499,6 +1475,8 @@ then assign them to other variables before breaking or exiting the loop.
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@sect3{funcstat| @title{Function Calls as Statements}
|
||||
To allow possible side-effects,
|
||||
function calls can be executed as statements:
|
||||
@ -1819,7 +1797,7 @@ A comparison @T{a > b} is translated to @T{b < a}
|
||||
and @T{a >= b} is translated to @T{b <= a}.
|
||||
|
||||
Following the @x{IEEE 754} standard,
|
||||
@x{NaN} is considered neither smaller than,
|
||||
@x{NaN} is considered neither less than,
|
||||
nor equal to, nor greater than any value (including itself).
|
||||
|
||||
}
|
||||
@ -2171,7 +2149,7 @@ then the function returns with no results.
|
||||
@index{multiple return}
|
||||
There is a system-dependent limit on the number of values
|
||||
that a function may return.
|
||||
This limit is guaranteed to be larger than 1000.
|
||||
This limit is guaranteed to be greater than 1000.
|
||||
|
||||
The @emphx{colon} syntax
|
||||
is used for defining @def{methods},
|
||||
@ -2367,7 +2345,7 @@ but it also can be any positive index after the stack top
|
||||
within the space allocated for the stack,
|
||||
that is, indices up to the stack size.
|
||||
(Note that 0 is never an acceptable index.)
|
||||
Indices to upvalues @see{c-closure} larger than the real number
|
||||
Indices to upvalues @see{c-closure} greater than the real number
|
||||
of upvalues in the current @N{C function} are also acceptable (but invalid).
|
||||
Except when noted otherwise,
|
||||
functions in the API work with acceptable indices.
|
||||
@ -2879,7 +2857,7 @@ Ensures that the stack has space for at least @id{n} extra slots
|
||||
(that is, that you can safely push up to @id{n} values into it).
|
||||
It returns false if it cannot fulfill the request,
|
||||
either because it would cause the stack
|
||||
to be larger than a fixed maximum size
|
||||
to be greater than a fixed maximum size
|
||||
(typically at least several thousand elements) or
|
||||
because it cannot allocate memory for the extra space.
|
||||
This function never shrinks the stack;
|
||||
@ -4053,7 +4031,7 @@ for the @Q{newindex} event @see{metatable}.
|
||||
|
||||
Accepts any index, @N{or 0},
|
||||
and sets the stack top to this index.
|
||||
If the new top is larger than the old one,
|
||||
If the new top is greater than the old one,
|
||||
then the new elements are filled with @nil.
|
||||
If @id{index} @N{is 0}, then all stack elements are removed.
|
||||
|
||||
@ -5056,7 +5034,7 @@ size @id{sz} with a call @T{luaL_buffinitsize(L, &b, sz)}.}
|
||||
@item{
|
||||
Finish by calling @T{luaL_pushresultsize(&b, sz)},
|
||||
where @id{sz} is the total size of the resulting string
|
||||
copied into that space (which may be smaller than or
|
||||
copied into that space (which may be less than or
|
||||
equal to the preallocated size).
|
||||
}
|
||||
|
||||
@ -7336,7 +7314,7 @@ Functions that interpret byte sequences only accept
|
||||
valid sequences (well formed and not overlong).
|
||||
By default, they only accept byte sequences
|
||||
that result in valid Unicode code points,
|
||||
rejecting values larger than @T{10FFFF} and surrogates.
|
||||
rejecting values greater than @T{10FFFF} and surrogates.
|
||||
A boolean argument @id{nonstrict}, when available,
|
||||
lifts these checks,
|
||||
so that all values up to @T{0x7FFFFFFF} are accepted.
|
||||
@ -7572,7 +7550,7 @@ returns the arc tangent of @id{y}.
|
||||
|
||||
@LibEntry{math.ceil (x)|
|
||||
|
||||
Returns the smallest integral value larger than or equal to @id{x}.
|
||||
Returns the smallest integral value greater than or equal to @id{x}.
|
||||
|
||||
}
|
||||
|
||||
@ -7597,7 +7575,7 @@ Returns the value @M{e@sp{x}}
|
||||
|
||||
@LibEntry{math.floor (x)|
|
||||
|
||||
Returns the largest integral value smaller than or equal to @id{x}.
|
||||
Returns the largest integral value less than or equal to @id{x}.
|
||||
|
||||
}
|
||||
|
||||
@ -7611,7 +7589,7 @@ that rounds the quotient towards zero. (integer/float)
|
||||
@LibEntry{math.huge|
|
||||
|
||||
The float value @idx{HUGE_VAL},
|
||||
a value larger than any other numeric value.
|
||||
a value greater than any other numeric value.
|
||||
|
||||
}
|
||||
|
||||
@ -8352,7 +8330,7 @@ of the given thread:
|
||||
@N{level 1} is the function that called @id{getinfo}
|
||||
(except for tail calls, which do not count on the stack);
|
||||
and so on.
|
||||
If @id{f} is a number larger than the number of active functions,
|
||||
If @id{f} is a number greater than the number of active functions,
|
||||
then @id{getinfo} returns @nil.
|
||||
|
||||
The returned table can contain all the fields returned by @Lid{lua_getinfo},
|
||||
@ -8745,6 +8723,12 @@ has been removed.
|
||||
When needed, this metamethod must be explicitly defined.
|
||||
}
|
||||
|
||||
@item{
|
||||
The semantics of the numerical @Rw{for} loop
|
||||
over integers changed in some details.
|
||||
In particular, the control variable never wraps around.
|
||||
}
|
||||
|
||||
@item{
|
||||
When a coroutine finishes with an error,
|
||||
its stack is unwound (to run any pending closing methods).
|
||||
|
@ -303,9 +303,9 @@ check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'RETURN1')
|
||||
|
||||
-- basic 'for' loops
|
||||
check(function () for i = -10, 10.5 do end end,
|
||||
'LOADI', 'LOADK', 'LOADI', 'FORPREP1', 'FORLOOP1', 'RETURN0')
|
||||
'LOADI', 'LOADK', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
|
||||
check(function () for i = 0xfffffff, 10.0, 1 do end end,
|
||||
'LOADK', 'LOADF', 'LOADI', 'FORPREP1', 'FORLOOP1', 'RETURN0')
|
||||
'LOADK', 'LOADF', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
|
||||
|
||||
-- bug in constant folding for 5.1
|
||||
check(function () return -nil end, 'LOADNIL', 'UNM', 'RETURN1')
|
||||
|
@ -162,7 +162,7 @@ test([[for i,v in pairs{'a','b'} do
|
||||
end
|
||||
]], {1,2,1,2,1,3})
|
||||
|
||||
test([[for i=1,4 do a=1 end]], {1,1,1,1,1})
|
||||
test([[for i=1,4 do a=1 end]], {1,1,1,1}, true)
|
||||
|
||||
|
||||
do -- testing line info/trace with large gaps in source
|
||||
|
@ -601,6 +601,69 @@ do -- checking types
|
||||
|
||||
end
|
||||
|
||||
|
||||
do -- testing other strange cases for numeric 'for'
|
||||
|
||||
local function checkfor (from, to, step, t)
|
||||
local c = 0
|
||||
for i = from, to, step do
|
||||
c = c + 1
|
||||
assert(i == t[c])
|
||||
end
|
||||
assert(c == #t)
|
||||
end
|
||||
|
||||
local maxi = math.maxinteger
|
||||
local mini = math.mininteger
|
||||
|
||||
checkfor(mini, maxi, maxi, {mini, -1, maxi - 1})
|
||||
|
||||
checkfor(mini, math.huge, maxi, {mini, -1, maxi - 1})
|
||||
|
||||
checkfor(maxi, mini, mini, {maxi, -1})
|
||||
|
||||
checkfor(maxi, mini, -maxi, {maxi, 0, -maxi})
|
||||
|
||||
checkfor(maxi, -math.huge, mini, {maxi, -1})
|
||||
|
||||
checkfor(maxi, mini, 1, {})
|
||||
checkfor(mini, maxi, -1, {})
|
||||
|
||||
checkfor(maxi - 6, maxi, 3, {maxi - 6, maxi - 3, maxi})
|
||||
checkfor(mini + 4, mini, -2, {mini + 4, mini + 2, mini})
|
||||
|
||||
local step = maxi // 10
|
||||
local c = mini
|
||||
for i = mini, maxi, step do
|
||||
assert(i == c)
|
||||
c = c + step
|
||||
end
|
||||
|
||||
c = maxi
|
||||
for i = maxi, mini, -step do
|
||||
assert(i == c)
|
||||
c = c - step
|
||||
end
|
||||
|
||||
checkfor(maxi, maxi, maxi, {maxi})
|
||||
checkfor(maxi, maxi, mini, {maxi})
|
||||
checkfor(mini, mini, maxi, {mini})
|
||||
checkfor(mini, mini, mini, {mini})
|
||||
end
|
||||
|
||||
|
||||
checkerror("'for' step is zero", function ()
|
||||
for i = 1, 10, 0 do end
|
||||
end)
|
||||
|
||||
checkerror("'for' step is zero", function ()
|
||||
for i = 1, -10, 0 do end
|
||||
end)
|
||||
|
||||
checkerror("'for' step is zero", function ()
|
||||
for i = 1.0, -10, 0.0 do end
|
||||
end)
|
||||
|
||||
collectgarbage()
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user