aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lparser.c90
-rw-r--r--manual/manual.of60
-rw-r--r--testes/locals.lua34
3 files changed, 103 insertions, 81 deletions
diff --git a/lparser.c b/lparser.c
index 875f7d04..52486e08 100644
--- a/lparser.c
+++ b/lparser.c
@@ -1656,13 +1656,50 @@ static void localfunc (LexState *ls) {
1656} 1656}
1657 1657
1658 1658
1659static void commonlocalstat (LexState *ls) { 1659static int getlocalattribute (LexState *ls) {
1660 /* stat -> LOCAL NAME {',' NAME} ['=' explist] */ 1660 /* ATTRIB -> ['<' Name '>'] */
1661 if (testnext(ls, '<')) {
1662 const char *attr = getstr(str_checkname(ls));
1663 checknext(ls, '>');
1664 if (strcmp(attr, "const") == 0)
1665 return 1; /* read-only variable */
1666 else if (strcmp(attr, "toclose") == 0)
1667 return 2; /* to-be-closed variable */
1668 else
1669 luaK_semerror(ls,
1670 luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
1671 }
1672 return 0;
1673}
1674
1675
1676static void checktoclose (LexState *ls, int toclose) {
1677 if (toclose != -1) { /* is there a to-be-closed variable? */
1678 FuncState *fs = ls->fs;
1679 markupval(fs, fs->nactvar + toclose + 1);
1680 fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
1681 luaK_codeABC(fs, OP_TBC, fs->nactvar + toclose, 0, 0);
1682 }
1683}
1684
1685
1686static void localstat (LexState *ls) {
1687 /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
1688 int toclose = -1; /* index of to-be-closed variable (if any) */
1661 int nvars = 0; 1689 int nvars = 0;
1662 int nexps; 1690 int nexps;
1663 expdesc e; 1691 expdesc e;
1664 do { 1692 do {
1665 new_localvar(ls, str_checkname(ls)); 1693 int kind = getlocalattribute(ls);
1694 Vardesc *var = new_localvar(ls, str_checkname(ls));
1695 if (kind != 0) { /* is there an attribute? */
1696 var->ro = 1; /* all attributes make variable read-only */
1697 if (kind == 2) { /* to-be-closed? */
1698 if (toclose != -1) /* one already present? */
1699 luaK_semerror(ls, "multiple to-be-closed variables in local list");
1700 toclose = nvars;
1701 }
1702 }
1666 nvars++; 1703 nvars++;
1667 } while (testnext(ls, ',')); 1704 } while (testnext(ls, ','));
1668 if (testnext(ls, '=')) 1705 if (testnext(ls, '='))
@@ -1672,56 +1709,11 @@ static void commonlocalstat (LexState *ls) {
1672 nexps = 0; 1709 nexps = 0;
1673 } 1710 }
1674 adjust_assign(ls, nvars, nexps, &e); 1711 adjust_assign(ls, nvars, nexps, &e);
1712 checktoclose(ls, toclose);
1675 adjustlocalvars(ls, nvars); 1713 adjustlocalvars(ls, nvars);
1676} 1714}
1677 1715
1678 1716
1679static void tocloselocalstat (LexState *ls, Vardesc *var) {
1680 FuncState *fs = ls->fs;
1681 var->ro = 1; /* to-be-closed variables are always read-only */
1682 markupval(fs, fs->nactvar + 1);
1683 fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
1684 luaK_codeABC(fs, OP_TBC, fs->nactvar, 0, 0);
1685}
1686
1687
1688static void checkattrib (LexState *ls, TString *attr, Vardesc *var) {
1689 if (strcmp(getstr(attr), "const") == 0)
1690 var->ro = 1; /* set variable as read-only */
1691 else if (strcmp(getstr(attr), "toclose") == 0)
1692 tocloselocalstat(ls, var);
1693 else
1694 luaK_semerror(ls,
1695 luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
1696}
1697
1698
1699static void attriblocalstat (LexState *ls) {
1700 FuncState *fs = ls->fs;
1701 Vardesc *var;
1702 expdesc e;
1703 TString *attr = str_checkname(ls);
1704 testnext(ls, '>');
1705 var = new_localvar(ls, str_checkname(ls));
1706 checknext(ls, '=');
1707 expr(ls, &e);
1708 checkattrib(ls, attr, var);
1709 luaK_tonumeral(fs, &e, &var->val);
1710 luaK_exp2nextreg(fs, &e);
1711 adjustlocalvars(ls, 1);
1712}
1713
1714
1715static void localstat (LexState *ls) {
1716 /* stat -> LOCAL NAME {',' NAME} ['=' explist]
1717 | LOCAL *toclose NAME '=' exp */
1718 if (testnext(ls, '<'))
1719 attriblocalstat(ls);
1720 else
1721 commonlocalstat(ls);
1722}
1723
1724
1725static int funcname (LexState *ls, expdesc *v) { 1717static int funcname (LexState *ls, expdesc *v) {
1726 /* funcname -> NAME {fieldsel} [':' NAME] */ 1718 /* funcname -> NAME {fieldsel} [':' NAME] */
1727 int ismethod = 0; 1719 int ismethod = 0;
diff --git a/manual/manual.of b/manual/manual.of
index e9416956..136e9022 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -1399,23 +1399,30 @@ they must all result in numbers.
1399Their values are called respectively 1399Their values are called respectively
1400the @emph{initial value}, the @emph{limit}, and the @emph{step}. 1400the @emph{initial value}, the @emph{limit}, and the @emph{step}.
1401If the step is absent, it defaults @N{to 1}. 1401If the step is absent, it defaults @N{to 1}.
1402Then the loop body is repeated with the value of the control variable 1402
1403If both the initial value and the step are integers,
1404the loop is done with integers;
1405note that the limit may not be an integer.
1406Otherwise, the loop is done with floats.
1407(Beware of floating-point accuracy in this case.)
1408
1409After that initialization,
1410the loop body is repeated with the value of the control variable
1403going through an arithmetic progression, 1411going through an arithmetic progression,
1404starting at the initial value, 1412starting at the initial value,
1405with a common difference given by the step, 1413with a common difference given by the step.
1406until that value passes the limit.
1407A negative step makes a decreasing sequence; 1414A negative step makes a decreasing sequence;
1408a step equal to zero raises an error. 1415a step equal to zero raises an error.
1416The loop continues while the value is less than
1417or equal to the limit
1418(greater than or equal to for a negative step).
1409If the initial value is already greater than the limit 1419If the initial value is already greater than the limit
1410(or less than, if the step is negative), 1420(or less than, if the step is negative),
1411the body is not executed. 1421the body is not executed.
1412 1422
1413If both the initial value and the step are integers, 1423For integer loops,
1414the loop is done with integers; 1424the control variable never wraps around;
1415in this case, the range of the control variable is clipped 1425instead, the loop ends in case of an overflow.
1416by the range of integers.
1417Otherwise, the loop is done with floats.
1418(Beware of floating-point accuracy in this case.)
1419 1426
1420You should not change the value of the control variable 1427You should not change the value of the control variable
1421during the loop. 1428during the loop.
@@ -1490,22 +1497,25 @@ Function calls are explained in @See{functioncall}.
1490@x{Local variables} can be declared anywhere inside a block. 1497@x{Local variables} can be declared anywhere inside a block.
1491The declaration can include an initialization: 1498The declaration can include an initialization:
1492@Produc{ 1499@Produc{
1493@producname{stat}@producbody{@Rw{local} namelist @bnfopt{@bnfter{=} explist}} 1500@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}}
1494@producname{stat}@producbody{ 1501@producname{attnamelist}@producbody{
1495 @Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp 1502 attrib @bnfNter{Name} @bnfrep{@bnfter{,} attrib @bnfNter{Name}}}
1496}} 1503}
1497If present, an initial assignment has the same semantics 1504If present, an initial assignment has the same semantics
1498of a multiple assignment @see{assignment}. 1505of a multiple assignment @see{assignment}.
1499Otherwise, all variables are initialized with @nil. 1506Otherwise, all variables are initialized with @nil.
1500The second syntax declares a local with a given attribute, 1507
1501which is the name between the angle brackets. 1508Each variable name may be preceded by an attribute
1502In this case, there must be an initialization. 1509(a name between angle brackets):
1510@Produc{
1511@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
1512}
1503There are two possible attributes: 1513There are two possible attributes:
1504@id{const}, which declares a @x{constant variable}, 1514@id{const}, which declares a @x{constant variable},
1505that is, a variable that cannot be assigned to 1515that is, a variable that cannot be assigned to
1506after its initialization; 1516after its initialization;
1507and @id{toclose}, which declares a to-be-closed variable @see{to-be-closed}. 1517and @id{toclose}, which declares a to-be-closed variable @see{to-be-closed}.
1508 1518A list of variables can contain at most one to-be-closed variable.
1509 1519
1510A chunk is also a block @see{chunks}, 1520A chunk is also a block @see{chunks},
1511and so local variables can be declared in a chunk outside any explicit block. 1521and 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}.
1516 1526
1517@sect3{to-be-closed| @title{To-be-closed Variables} 1527@sect3{to-be-closed| @title{To-be-closed Variables}
1518 1528
1519A local variable can be declared as a @def{to-be-closed} variable,
1520using the identifier @id{toclose} as its attribute:
1521@Produc{
1522@producname{stat}@producbody{
1523 @Rw{local} @bnfter{<} @id{toclose} @bnfter{>} Name @bnfter{=} exp
1524}}
1525A to-be-closed variable behaves like a constant local variable, 1529A to-be-closed variable behaves like a constant local variable,
1526except that its value is @emph{closed} whenever the variable 1530except that its value is @emph{closed} whenever the variable
1527goes out of scope, including normal block termination, 1531goes out of scope, including normal block termination,
@@ -8215,7 +8219,7 @@ then @id{date} returns the date as a string,
8215formatted according to the same rules as the @ANSI{strftime}. 8219formatted according to the same rules as the @ANSI{strftime}.
8216 8220
8217If @id{format} is absent, it defaults to @St{%c}, 8221If @id{format} is absent, it defaults to @St{%c},
8218which gives a reasonable date and time representation 8222which gives a human-readable date and time representation
8219using the current locale. 8223using the current locale.
8220 8224
8221On non-POSIX systems, 8225On non-POSIX systems,
@@ -9022,10 +9026,14 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
9022@OrNL @Rw{for} namelist @Rw{in} explist @Rw{do} block @Rw{end} 9026@OrNL @Rw{for} namelist @Rw{in} explist @Rw{do} block @Rw{end}
9023@OrNL @Rw{function} funcname funcbody 9027@OrNL @Rw{function} funcname funcbody
9024@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody 9028@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
9025@OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist} 9029@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
9026@OrNL @Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp
9027} 9030}
9028 9031
9032@producname{attnamelist}@producbody{
9033 attrib @bnfNter{Name} @bnfrep{@bnfter{,} attrib @bnfNter{Name}}}
9034
9035@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
9036
9029@producname{retstat}@producbody{@Rw{return} 9037@producname{retstat}@producbody{@Rw{return}
9030 @bnfopt{explist} @bnfopt{@bnfter{;}}} 9038 @bnfopt{explist} @bnfopt{@bnfter{;}}}
9031 9039
diff --git a/testes/locals.lua b/testes/locals.lua
index a41b6f0e..50230a27 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -173,12 +173,32 @@ end
173assert(x==20) 173assert(x==20)
174 174
175 175
176do -- constants
177 local <const> a, b, <const> c = 10, 20, 30
178 b = a + c + b -- 'b' is not constant
179 assert(a == 10 and b == 60 and c == 30)
180 local function checkro (code, name)
181 local st, msg = load(code)
182 local gab = string.format("attempt to assign to const variable '%s'", name)
183 assert(not st and string.find(msg, gab))
184 end
185 checkro("local x, <const> y, z = 10, 20, 30; x = 11; y = 12", "y")
186 checkro("local <const> x, y, <const> z = 10, 20, 30; x = 11", "x")
187 checkro("local <const> x, y, <const> z = 10, 20, 30; y = 10; z = 11", "z")
188end
189
190
176print"testing to-be-closed variables" 191print"testing to-be-closed variables"
177 192
178local function stack(n) n = ((n == 0) or stack(n - 1)) end 193local function stack(n) n = ((n == 0) or stack(n - 1)) end
179 194
180local function func2close (f) 195local function func2close (f, x, y)
181 return setmetatable({}, {__close = f}) 196 local obj = setmetatable({}, {__close = f})
197 if x then
198 return x, obj, y
199 else
200 return obj
201 end
182end 202end
183 203
184 204
@@ -187,10 +207,11 @@ do
187 do 207 do
188 local <toclose> x = setmetatable({"x"}, {__close = function (self) 208 local <toclose> x = setmetatable({"x"}, {__close = function (self)
189 a[#a + 1] = self[1] end}) 209 a[#a + 1] = self[1] end})
190 local <toclose> y = func2close(function (self, err) 210 local w, <toclose> y, z = func2close(function (self, err)
191 assert(err == nil); a[#a + 1] = "y" 211 assert(err == nil); a[#a + 1] = "y"
192 end) 212 end, 10, 20)
193 a[#a + 1] = "in" 213 a[#a + 1] = "in"
214 assert(w == 10 and z == 20)
194 end 215 end
195 a[#a + 1] = "out" 216 a[#a + 1] = "out"
196 assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out") 217 assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out")
@@ -199,7 +220,8 @@ end
199do 220do
200 local X = false 221 local X = false
201 222
202 local closescope = func2close(function () stack(10); X = true end) 223 local x, closescope = func2close(function () stack(10); X = true end, 100)
224 assert(x == 100); x = 101; -- 'x' is not read-only
203 225
204 -- closing functions do not corrupt returning values 226 -- closing functions do not corrupt returning values
205 local function foo (x) 227 local function foo (x)