mirror of https://github.com/rusefi/lua.git
Local attributes can be used in list of local variables
The syntax for local attributes ('const'/'toclose') was unified with the regular syntax for local variables, so that we can have variables with attributes in local definitions with multiple names; for instance: local <toclose> f, <const> err = io.open(fname) This new syntax does not implement constant propagation, yet. This commit also has some small improvements to the manual.
This commit is contained in:
parent
8eca21c2e8
commit
4d46289331
90
lparser.c
90
lparser.c
|
@ -1656,13 +1656,50 @@ static void localfunc (LexState *ls) {
|
|||
}
|
||||
|
||||
|
||||
static void commonlocalstat (LexState *ls) {
|
||||
/* stat -> LOCAL NAME {',' NAME} ['=' explist] */
|
||||
static int getlocalattribute (LexState *ls) {
|
||||
/* ATTRIB -> ['<' Name '>'] */
|
||||
if (testnext(ls, '<')) {
|
||||
const char *attr = getstr(str_checkname(ls));
|
||||
checknext(ls, '>');
|
||||
if (strcmp(attr, "const") == 0)
|
||||
return 1; /* read-only variable */
|
||||
else if (strcmp(attr, "toclose") == 0)
|
||||
return 2; /* to-be-closed variable */
|
||||
else
|
||||
luaK_semerror(ls,
|
||||
luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void checktoclose (LexState *ls, int toclose) {
|
||||
if (toclose != -1) { /* is there a to-be-closed variable? */
|
||||
FuncState *fs = ls->fs;
|
||||
markupval(fs, fs->nactvar + toclose + 1);
|
||||
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
||||
luaK_codeABC(fs, OP_TBC, fs->nactvar + toclose, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void localstat (LexState *ls) {
|
||||
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
|
||||
int toclose = -1; /* index of to-be-closed variable (if any) */
|
||||
int nvars = 0;
|
||||
int nexps;
|
||||
expdesc e;
|
||||
do {
|
||||
new_localvar(ls, str_checkname(ls));
|
||||
int kind = getlocalattribute(ls);
|
||||
Vardesc *var = new_localvar(ls, str_checkname(ls));
|
||||
if (kind != 0) { /* is there an attribute? */
|
||||
var->ro = 1; /* all attributes make variable read-only */
|
||||
if (kind == 2) { /* to-be-closed? */
|
||||
if (toclose != -1) /* one already present? */
|
||||
luaK_semerror(ls, "multiple to-be-closed variables in local list");
|
||||
toclose = nvars;
|
||||
}
|
||||
}
|
||||
nvars++;
|
||||
} while (testnext(ls, ','));
|
||||
if (testnext(ls, '='))
|
||||
|
@ -1672,56 +1709,11 @@ static void commonlocalstat (LexState *ls) {
|
|||
nexps = 0;
|
||||
}
|
||||
adjust_assign(ls, nvars, nexps, &e);
|
||||
checktoclose(ls, toclose);
|
||||
adjustlocalvars(ls, nvars);
|
||||
}
|
||||
|
||||
|
||||
static void tocloselocalstat (LexState *ls, Vardesc *var) {
|
||||
FuncState *fs = ls->fs;
|
||||
var->ro = 1; /* to-be-closed variables are always read-only */
|
||||
markupval(fs, fs->nactvar + 1);
|
||||
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
||||
luaK_codeABC(fs, OP_TBC, fs->nactvar, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void checkattrib (LexState *ls, TString *attr, Vardesc *var) {
|
||||
if (strcmp(getstr(attr), "const") == 0)
|
||||
var->ro = 1; /* set variable as read-only */
|
||||
else if (strcmp(getstr(attr), "toclose") == 0)
|
||||
tocloselocalstat(ls, var);
|
||||
else
|
||||
luaK_semerror(ls,
|
||||
luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
|
||||
}
|
||||
|
||||
|
||||
static void attriblocalstat (LexState *ls) {
|
||||
FuncState *fs = ls->fs;
|
||||
Vardesc *var;
|
||||
expdesc e;
|
||||
TString *attr = str_checkname(ls);
|
||||
testnext(ls, '>');
|
||||
var = new_localvar(ls, str_checkname(ls));
|
||||
checknext(ls, '=');
|
||||
expr(ls, &e);
|
||||
checkattrib(ls, attr, var);
|
||||
luaK_tonumeral(fs, &e, &var->val);
|
||||
luaK_exp2nextreg(fs, &e);
|
||||
adjustlocalvars(ls, 1);
|
||||
}
|
||||
|
||||
|
||||
static void localstat (LexState *ls) {
|
||||
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
|
||||
| LOCAL *toclose NAME '=' exp */
|
||||
if (testnext(ls, '<'))
|
||||
attriblocalstat(ls);
|
||||
else
|
||||
commonlocalstat(ls);
|
||||
}
|
||||
|
||||
|
||||
static int funcname (LexState *ls, expdesc *v) {
|
||||
/* funcname -> NAME {fieldsel} [':' NAME] */
|
||||
int ismethod = 0;
|
||||
|
|
|
@ -1399,23 +1399,30 @@ 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
|
||||
|
||||
If both the initial value and the step are integers,
|
||||
the loop is done with integers;
|
||||
note that the limit may not be an integer.
|
||||
Otherwise, the loop is done with floats.
|
||||
(Beware of floating-point accuracy in this case.)
|
||||
|
||||
After that initialization,
|
||||
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.
|
||||
with a common difference given by the step.
|
||||
A negative step makes a decreasing sequence;
|
||||
a step equal to zero raises an error.
|
||||
The loop continues while the value is less than
|
||||
or equal to the limit
|
||||
(greater than or equal to for a negative step).
|
||||
If the initial value is already greater than the limit
|
||||
(or less than, if the step is negative),
|
||||
the body is not executed.
|
||||
|
||||
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 clipped
|
||||
by the range of integers.
|
||||
Otherwise, the loop is done with floats.
|
||||
(Beware of floating-point accuracy in this case.)
|
||||
For integer loops,
|
||||
the control variable never wraps around;
|
||||
instead, the loop ends in case of an overflow.
|
||||
|
||||
You should not change the value of the control variable
|
||||
during the loop.
|
||||
|
@ -1490,22 +1497,25 @@ Function calls are explained in @See{functioncall}.
|
|||
@x{Local variables} can be declared anywhere inside a block.
|
||||
The declaration can include an initialization:
|
||||
@Produc{
|
||||
@producname{stat}@producbody{@Rw{local} namelist @bnfopt{@bnfter{=} explist}}
|
||||
@producname{stat}@producbody{
|
||||
@Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp
|
||||
}}
|
||||
@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}}
|
||||
@producname{attnamelist}@producbody{
|
||||
attrib @bnfNter{Name} @bnfrep{@bnfter{,} attrib @bnfNter{Name}}}
|
||||
}
|
||||
If present, an initial assignment has the same semantics
|
||||
of a multiple assignment @see{assignment}.
|
||||
Otherwise, all variables are initialized with @nil.
|
||||
The second syntax declares a local with a given attribute,
|
||||
which is the name between the angle brackets.
|
||||
In this case, there must be an initialization.
|
||||
|
||||
Each variable name may be preceded by an attribute
|
||||
(a name between angle brackets):
|
||||
@Produc{
|
||||
@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
|
||||
}
|
||||
There are two possible attributes:
|
||||
@id{const}, which declares a @x{constant variable},
|
||||
that is, a variable that cannot be assigned to
|
||||
after its initialization;
|
||||
and @id{toclose}, which declares a to-be-closed variable @see{to-be-closed}.
|
||||
|
||||
A list of variables can contain at most one to-be-closed variable.
|
||||
|
||||
A chunk is also a block @see{chunks},
|
||||
and so local variables can be declared in a chunk outside any explicit block.
|
||||
|
@ -1516,12 +1526,6 @@ The visibility rules for local variables are explained in @See{visibility}.
|
|||
|
||||
@sect3{to-be-closed| @title{To-be-closed Variables}
|
||||
|
||||
A local variable can be declared as a @def{to-be-closed} variable,
|
||||
using the identifier @id{toclose} as its attribute:
|
||||
@Produc{
|
||||
@producname{stat}@producbody{
|
||||
@Rw{local} @bnfter{<} @id{toclose} @bnfter{>} Name @bnfter{=} exp
|
||||
}}
|
||||
A to-be-closed variable behaves like a constant local variable,
|
||||
except that its value is @emph{closed} whenever the variable
|
||||
goes out of scope, including normal block termination,
|
||||
|
@ -8215,7 +8219,7 @@ then @id{date} returns the date as a string,
|
|||
formatted according to the same rules as the @ANSI{strftime}.
|
||||
|
||||
If @id{format} is absent, it defaults to @St{%c},
|
||||
which gives a reasonable date and time representation
|
||||
which gives a human-readable date and time representation
|
||||
using the current locale.
|
||||
|
||||
On non-POSIX systems,
|
||||
|
@ -9022,10 +9026,14 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
|
|||
@OrNL @Rw{for} namelist @Rw{in} explist @Rw{do} block @Rw{end}
|
||||
@OrNL @Rw{function} funcname funcbody
|
||||
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
|
||||
@OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist}
|
||||
@OrNL @Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp
|
||||
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
|
||||
}
|
||||
|
||||
@producname{attnamelist}@producbody{
|
||||
attrib @bnfNter{Name} @bnfrep{@bnfter{,} attrib @bnfNter{Name}}}
|
||||
|
||||
@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
|
||||
|
||||
@producname{retstat}@producbody{@Rw{return}
|
||||
@bnfopt{explist} @bnfopt{@bnfter{;}}}
|
||||
|
||||
|
|
|
@ -173,12 +173,32 @@ end
|
|||
assert(x==20)
|
||||
|
||||
|
||||
do -- constants
|
||||
local <const> a, b, <const> c = 10, 20, 30
|
||||
b = a + c + b -- 'b' is not constant
|
||||
assert(a == 10 and b == 60 and c == 30)
|
||||
local function checkro (code, name)
|
||||
local st, msg = load(code)
|
||||
local gab = string.format("attempt to assign to const variable '%s'", name)
|
||||
assert(not st and string.find(msg, gab))
|
||||
end
|
||||
checkro("local x, <const> y, z = 10, 20, 30; x = 11; y = 12", "y")
|
||||
checkro("local <const> x, y, <const> z = 10, 20, 30; x = 11", "x")
|
||||
checkro("local <const> x, y, <const> z = 10, 20, 30; y = 10; z = 11", "z")
|
||||
end
|
||||
|
||||
|
||||
print"testing to-be-closed variables"
|
||||
|
||||
local function stack(n) n = ((n == 0) or stack(n - 1)) end
|
||||
|
||||
local function func2close (f)
|
||||
return setmetatable({}, {__close = f})
|
||||
local function func2close (f, x, y)
|
||||
local obj = setmetatable({}, {__close = f})
|
||||
if x then
|
||||
return x, obj, y
|
||||
else
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -187,10 +207,11 @@ do
|
|||
do
|
||||
local <toclose> x = setmetatable({"x"}, {__close = function (self)
|
||||
a[#a + 1] = self[1] end})
|
||||
local <toclose> y = func2close(function (self, err)
|
||||
assert(err == nil); a[#a + 1] = "y"
|
||||
end)
|
||||
local w, <toclose> y, z = func2close(function (self, err)
|
||||
assert(err == nil); a[#a + 1] = "y"
|
||||
end, 10, 20)
|
||||
a[#a + 1] = "in"
|
||||
assert(w == 10 and z == 20)
|
||||
end
|
||||
a[#a + 1] = "out"
|
||||
assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out")
|
||||
|
@ -199,7 +220,8 @@ end
|
|||
do
|
||||
local X = false
|
||||
|
||||
local closescope = func2close(function () stack(10); X = true end)
|
||||
local x, closescope = func2close(function () stack(10); X = true end, 100)
|
||||
assert(x == 100); x = 101; -- 'x' is not read-only
|
||||
|
||||
-- closing functions do not corrupt returning values
|
||||
local function foo (x)
|
||||
|
|
Loading…
Reference in New Issue