diff options
-rw-r--r-- | lcode.c | 36 | ||||
-rw-r--r-- | lparser.c | 109 | ||||
-rw-r--r-- | lparser.h | 13 | ||||
-rw-r--r-- | manual/manual.of | 28 | ||||
-rw-r--r-- | testes/constructs.lua | 61 | ||||
-rw-r--r-- | testes/files.lua | 8 | ||||
-rw-r--r-- | testes/math.lua | 6 |
7 files changed, 205 insertions, 56 deletions
@@ -678,11 +678,12 @@ void luaK_setoneret (FuncState *fs, expdesc *e) { | |||
678 | void luaK_dischargevars (FuncState *fs, expdesc *e) { | 678 | void luaK_dischargevars (FuncState *fs, expdesc *e) { |
679 | switch (e->k) { | 679 | switch (e->k) { |
680 | case VLOCAL: { /* already in a register */ | 680 | case VLOCAL: { /* already in a register */ |
681 | e->u.info = e->u.var.idx; | ||
681 | e->k = VNONRELOC; /* becomes a non-relocatable value */ | 682 | e->k = VNONRELOC; /* becomes a non-relocatable value */ |
682 | break; | 683 | break; |
683 | } | 684 | } |
684 | case VUPVAL: { /* move value to some (pending) register */ | 685 | case VUPVAL: { /* move value to some (pending) register */ |
685 | e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); | 686 | e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.var.idx, 0); |
686 | e->k = VRELOC; | 687 | e->k = VRELOC; |
687 | break; | 688 | break; |
688 | } | 689 | } |
@@ -938,12 +939,12 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { | |||
938 | switch (var->k) { | 939 | switch (var->k) { |
939 | case VLOCAL: { | 940 | case VLOCAL: { |
940 | freeexp(fs, ex); | 941 | freeexp(fs, ex); |
941 | exp2reg(fs, ex, var->u.info); /* compute 'ex' into proper place */ | 942 | exp2reg(fs, ex, var->u.var.idx); /* compute 'ex' into proper place */ |
942 | return; | 943 | return; |
943 | } | 944 | } |
944 | case VUPVAL: { | 945 | case VUPVAL: { |
945 | int e = luaK_exp2anyreg(fs, ex); | 946 | int e = luaK_exp2anyreg(fs, ex); |
946 | luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); | 947 | luaK_codeABC(fs, OP_SETUPVAL, e, var->u.var.idx, 0); |
947 | break; | 948 | break; |
948 | } | 949 | } |
949 | case VINDEXUP: { | 950 | case VINDEXUP: { |
@@ -1165,25 +1166,30 @@ static int isSCnumber (expdesc *e, lua_Integer *i, int *isfloat) { | |||
1165 | ** values in registers. | 1166 | ** values in registers. |
1166 | */ | 1167 | */ |
1167 | void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { | 1168 | void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { |
1168 | lua_assert(!hasjumps(t) && (vkisinreg(t->k) || t->k == VUPVAL)); | 1169 | lua_assert(!hasjumps(t) && |
1170 | (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL)); | ||
1169 | if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */ | 1171 | if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */ |
1170 | luaK_exp2anyreg(fs, t); /* put it in a register */ | 1172 | luaK_exp2anyreg(fs, t); /* put it in a register */ |
1171 | t->u.ind.t = t->u.info; /* register or upvalue index */ | ||
1172 | if (t->k == VUPVAL) { | 1173 | if (t->k == VUPVAL) { |
1174 | t->u.ind.t = t->u.var.idx; /* upvalue index */ | ||
1173 | t->u.ind.idx = k->u.info; /* literal string */ | 1175 | t->u.ind.idx = k->u.info; /* literal string */ |
1174 | t->k = VINDEXUP; | 1176 | t->k = VINDEXUP; |
1175 | } | 1177 | } |
1176 | else if (isKstr(fs, k)) { | ||
1177 | t->u.ind.idx = k->u.info; /* literal string */ | ||
1178 | t->k = VINDEXSTR; | ||
1179 | } | ||
1180 | else if (isCint(k)) { | ||
1181 | t->u.ind.idx = cast_int(k->u.ival); /* integer constant in proper range */ | ||
1182 | t->k = VINDEXI; | ||
1183 | } | ||
1184 | else { | 1178 | else { |
1185 | t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */ | 1179 | /* register index of the table */ |
1186 | t->k = VINDEXED; | 1180 | t->u.ind.t = (t->k == VLOCAL) ? t->u.var.idx: t->u.info; |
1181 | if (isKstr(fs, k)) { | ||
1182 | t->u.ind.idx = k->u.info; /* literal string */ | ||
1183 | t->k = VINDEXSTR; | ||
1184 | } | ||
1185 | else if (isCint(k)) { | ||
1186 | t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */ | ||
1187 | t->k = VINDEXI; | ||
1188 | } | ||
1189 | else { | ||
1190 | t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */ | ||
1191 | t->k = VINDEXED; | ||
1192 | } | ||
1187 | } | 1193 | } |
1188 | } | 1194 | } |
1189 | 1195 | ||
@@ -156,6 +156,13 @@ static void init_exp (expdesc *e, expkind k, int i) { | |||
156 | } | 156 | } |
157 | 157 | ||
158 | 158 | ||
159 | static void init_var (expdesc *e, expkind k, int i) { | ||
160 | e->f = e->t = NO_JUMP; | ||
161 | e->k = k; | ||
162 | e->u.var.idx = i; | ||
163 | } | ||
164 | |||
165 | |||
159 | static void codestring (LexState *ls, expdesc *e, TString *s) { | 166 | static void codestring (LexState *ls, expdesc *e, TString *s) { |
160 | init_exp(e, VK, luaK_stringK(ls->fs, s)); | 167 | init_exp(e, VK, luaK_stringK(ls->fs, s)); |
161 | } | 168 | } |
@@ -187,32 +194,83 @@ static int registerlocalvar (LexState *ls, TString *varname) { | |||
187 | /* | 194 | /* |
188 | ** Create a new local variable with the given 'name'. | 195 | ** Create a new local variable with the given 'name'. |
189 | */ | 196 | */ |
190 | static void new_localvar (LexState *ls, TString *name) { | 197 | static Vardesc *new_localvar (LexState *ls, TString *name) { |
191 | FuncState *fs = ls->fs; | 198 | FuncState *fs = ls->fs; |
192 | Dyndata *dyd = ls->dyd; | 199 | Dyndata *dyd = ls->dyd; |
200 | Vardesc *var; | ||
193 | int reg = registerlocalvar(ls, name); | 201 | int reg = registerlocalvar(ls, name); |
194 | checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, | 202 | checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, |
195 | MAXVARS, "local variables"); | 203 | MAXVARS, "local variables"); |
196 | luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1, | 204 | luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1, |
197 | dyd->actvar.size, Vardesc, MAX_INT, "local variables"); | 205 | dyd->actvar.size, Vardesc, MAX_INT, "local variables"); |
198 | dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg); | 206 | var = &dyd->actvar.arr[dyd->actvar.n++]; |
207 | var->idx = cast(short, reg); | ||
208 | var->name = name; | ||
209 | var->ro = 0; | ||
210 | return var; | ||
199 | } | 211 | } |
200 | 212 | ||
201 | #define new_localvarliteral(ls,v) \ | 213 | #define new_localvarliteral(ls,v) \ |
202 | new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1)); | 214 | new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1)); |
203 | 215 | ||
204 | 216 | ||
217 | |||
218 | /* | ||
219 | ** Return the "variable description" (Vardesc) of a given | ||
220 | ** variable | ||
221 | */ | ||
222 | static Vardesc *getlocalvardesc (FuncState *fs, int i) { | ||
223 | return &fs->ls->dyd->actvar.arr[fs->firstlocal + i]; | ||
224 | } | ||
225 | |||
205 | /* | 226 | /* |
206 | ** Get the debug-information entry for current variable 'i'. | 227 | ** Get the debug-information entry for current variable 'i'. |
207 | */ | 228 | */ |
208 | static LocVar *getlocvar (FuncState *fs, int i) { | 229 | static LocVar *getlocvar (FuncState *fs, int i) { |
209 | int idx = fs->ls->dyd->actvar.arr[fs->firstlocal + i].idx; | 230 | int idx = getlocalvardesc(fs, i)->idx; |
210 | lua_assert(idx < fs->nlocvars); | 231 | lua_assert(idx < fs->nlocvars); |
211 | return &fs->f->locvars[idx]; | 232 | return &fs->f->locvars[idx]; |
212 | } | 233 | } |
213 | 234 | ||
214 | 235 | ||
215 | /* | 236 | /* |
237 | ** Return the "variable description" (Vardesc) of a given | ||
238 | ** variable or upvalue | ||
239 | */ | ||
240 | static Vardesc *getvardesc (FuncState *fs, expdesc *e) { | ||
241 | if (e->k == VLOCAL) | ||
242 | return getlocalvardesc(fs, e->u.var.idx); | ||
243 | else if (e->k != VUPVAL) | ||
244 | return NULL; /* not a local variable */ | ||
245 | else { /* upvalue: must go up all levels up to the original local */ | ||
246 | int idx = e->u.var.idx; | ||
247 | for (;;) { | ||
248 | Upvaldesc *up = &fs->f->upvalues[idx]; | ||
249 | fs = fs->prev; /* must look at the previous level */ | ||
250 | idx = up->idx; /* at this index */ | ||
251 | if (fs == NULL) { /* no more levels? (can happen only with _ENV) */ | ||
252 | lua_assert(strcmp(getstr(up->name), LUA_ENV) == 0); | ||
253 | return NULL; | ||
254 | } | ||
255 | else if (up->instack) /* got to the original level? */ | ||
256 | return getlocalvardesc(fs, idx); | ||
257 | /* else repeat for previous level */ | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | |||
262 | |||
263 | static void check_readonly (LexState *ls, expdesc *e) { | ||
264 | Vardesc *vardesc = getvardesc(ls->fs, e); | ||
265 | if (vardesc && vardesc->ro) { /* is variable local and const? */ | ||
266 | const char *msg = luaO_pushfstring(ls->L, | ||
267 | "assignment to const variable '%s'", getstr(vardesc->name)); | ||
268 | luaK_semerror(ls, msg); /* error */ | ||
269 | } | ||
270 | } | ||
271 | |||
272 | |||
273 | /* | ||
216 | ** Start the scope for the last 'nvars' created variables. | 274 | ** Start the scope for the last 'nvars' created variables. |
217 | ** (debug info.) | 275 | ** (debug info.) |
218 | */ | 276 | */ |
@@ -259,7 +317,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { | |||
259 | while (oldsize < f->sizeupvalues) | 317 | while (oldsize < f->sizeupvalues) |
260 | f->upvalues[oldsize++].name = NULL; | 318 | f->upvalues[oldsize++].name = NULL; |
261 | f->upvalues[fs->nups].instack = (v->k == VLOCAL); | 319 | f->upvalues[fs->nups].instack = (v->k == VLOCAL); |
262 | f->upvalues[fs->nups].idx = cast_byte(v->u.info); | 320 | f->upvalues[fs->nups].idx = cast_byte(v->u.var.idx); |
263 | f->upvalues[fs->nups].name = name; | 321 | f->upvalues[fs->nups].name = name; |
264 | luaC_objbarrier(fs->ls->L, f, name); | 322 | luaC_objbarrier(fs->ls->L, f, name); |
265 | return fs->nups++; | 323 | return fs->nups++; |
@@ -304,7 +362,7 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { | |||
304 | else { | 362 | else { |
305 | int v = searchvar(fs, n); /* look up locals at current level */ | 363 | int v = searchvar(fs, n); /* look up locals at current level */ |
306 | if (v >= 0) { /* found? */ | 364 | if (v >= 0) { /* found? */ |
307 | init_exp(var, VLOCAL, v); /* variable is local */ | 365 | init_var(var, VLOCAL, v); /* variable is local */ |
308 | if (!base) | 366 | if (!base) |
309 | markupval(fs, v); /* local will be used as an upval */ | 367 | markupval(fs, v); /* local will be used as an upval */ |
310 | } | 368 | } |
@@ -317,7 +375,7 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { | |||
317 | /* else was LOCAL or UPVAL */ | 375 | /* else was LOCAL or UPVAL */ |
318 | idx = newupvalue(fs, n, var); /* will be a new upvalue */ | 376 | idx = newupvalue(fs, n, var); /* will be a new upvalue */ |
319 | } | 377 | } |
320 | init_exp(var, VUPVAL, idx); /* new or old upvalue */ | 378 | init_var(var, VUPVAL, idx); /* new or old upvalue */ |
321 | } | 379 | } |
322 | } | 380 | } |
323 | } | 381 | } |
@@ -1199,20 +1257,20 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { | |||
1199 | for (; lh; lh = lh->prev) { /* check all previous assignments */ | 1257 | for (; lh; lh = lh->prev) { /* check all previous assignments */ |
1200 | if (vkisindexed(lh->v.k)) { /* assignment to table field? */ | 1258 | if (vkisindexed(lh->v.k)) { /* assignment to table field? */ |
1201 | if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ | 1259 | if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ |
1202 | if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) { | 1260 | if (v->k == VUPVAL && lh->v.u.ind.t == v->u.var.idx) { |
1203 | conflict = 1; /* table is the upvalue being assigned now */ | 1261 | conflict = 1; /* table is the upvalue being assigned now */ |
1204 | lh->v.k = VINDEXSTR; | 1262 | lh->v.k = VINDEXSTR; |
1205 | lh->v.u.ind.t = extra; /* assignment will use safe copy */ | 1263 | lh->v.u.ind.t = extra; /* assignment will use safe copy */ |
1206 | } | 1264 | } |
1207 | } | 1265 | } |
1208 | else { /* table is a register */ | 1266 | else { /* table is a register */ |
1209 | if (v->k == VLOCAL && lh->v.u.ind.t == v->u.info) { | 1267 | if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.idx) { |
1210 | conflict = 1; /* table is the local being assigned now */ | 1268 | conflict = 1; /* table is the local being assigned now */ |
1211 | lh->v.u.ind.t = extra; /* assignment will use safe copy */ | 1269 | lh->v.u.ind.t = extra; /* assignment will use safe copy */ |
1212 | } | 1270 | } |
1213 | /* is index the local being assigned? */ | 1271 | /* is index the local being assigned? */ |
1214 | if (lh->v.k == VINDEXED && v->k == VLOCAL && | 1272 | if (lh->v.k == VINDEXED && v->k == VLOCAL && |
1215 | lh->v.u.ind.idx == v->u.info) { | 1273 | lh->v.u.ind.idx == v->u.var.idx) { |
1216 | conflict = 1; | 1274 | conflict = 1; |
1217 | lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ | 1275 | lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ |
1218 | } | 1276 | } |
@@ -1222,7 +1280,7 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { | |||
1222 | if (conflict) { | 1280 | if (conflict) { |
1223 | /* copy upvalue/local value to a temporary (in position 'extra') */ | 1281 | /* copy upvalue/local value to a temporary (in position 'extra') */ |
1224 | OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; | 1282 | OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; |
1225 | luaK_codeABC(fs, op, extra, v->u.info, 0); | 1283 | luaK_codeABC(fs, op, extra, v->u.var.idx, 0); |
1226 | luaK_reserveregs(fs, 1); | 1284 | luaK_reserveregs(fs, 1); |
1227 | } | 1285 | } |
1228 | } | 1286 | } |
@@ -1237,6 +1295,7 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { | |||
1237 | static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) { | 1295 | static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) { |
1238 | expdesc e; | 1296 | expdesc e; |
1239 | check_condition(ls, vkisvar(lh->v.k), "syntax error"); | 1297 | check_condition(ls, vkisvar(lh->v.k), "syntax error"); |
1298 | check_readonly(ls, &lh->v); | ||
1240 | if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */ | 1299 | if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */ |
1241 | struct LHS_assign nv; | 1300 | struct LHS_assign nv; |
1242 | nv.prev = lh; | 1301 | nv.prev = lh; |
@@ -1615,20 +1674,30 @@ static void commonlocalstat (LexState *ls) { | |||
1615 | } | 1674 | } |
1616 | 1675 | ||
1617 | 1676 | ||
1618 | static void tocloselocalstat (LexState *ls) { | 1677 | static void tocloselocalstat (LexState *ls, Vardesc *var) { |
1619 | FuncState *fs = ls->fs; | 1678 | FuncState *fs = ls->fs; |
1679 | var->ro = 1; /* to-be-closed variables are always read-only */ | ||
1680 | markupval(fs, fs->nactvar); | ||
1681 | fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */ | ||
1682 | luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0); | ||
1683 | } | ||
1684 | |||
1685 | |||
1686 | static void attriblocalstat (LexState *ls) { | ||
1687 | Vardesc *var; | ||
1620 | TString *attr = str_checkname(ls); | 1688 | TString *attr = str_checkname(ls); |
1621 | if (strcmp(getstr(attr), "toclose") != 0) | ||
1622 | luaK_semerror(ls, | ||
1623 | luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr))); | ||
1624 | testnext(ls, '>'); | 1689 | testnext(ls, '>'); |
1625 | new_localvar(ls, str_checkname(ls)); | 1690 | var = new_localvar(ls, str_checkname(ls)); |
1626 | checknext(ls, '='); | 1691 | checknext(ls, '='); |
1627 | exp1(ls); | 1692 | exp1(ls); |
1628 | markupval(fs, fs->nactvar); | ||
1629 | fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */ | ||
1630 | adjustlocalvars(ls, 1); | 1693 | adjustlocalvars(ls, 1); |
1631 | luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0); | 1694 | if (strcmp(getstr(attr), "const") == 0) |
1695 | var->ro = 1; /* set variable as read-only */ | ||
1696 | else if (strcmp(getstr(attr), "toclose") == 0) | ||
1697 | tocloselocalstat(ls, var); | ||
1698 | else | ||
1699 | luaK_semerror(ls, | ||
1700 | luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr))); | ||
1632 | } | 1701 | } |
1633 | 1702 | ||
1634 | 1703 | ||
@@ -1636,7 +1705,7 @@ static void localstat (LexState *ls) { | |||
1636 | /* stat -> LOCAL NAME {',' NAME} ['=' explist] | 1705 | /* stat -> LOCAL NAME {',' NAME} ['=' explist] |
1637 | | LOCAL *toclose NAME '=' exp */ | 1706 | | LOCAL *toclose NAME '=' exp */ |
1638 | if (testnext(ls, '<')) | 1707 | if (testnext(ls, '<')) |
1639 | tocloselocalstat(ls); | 1708 | attriblocalstat(ls); |
1640 | else | 1709 | else |
1641 | commonlocalstat(ls); | 1710 | commonlocalstat(ls); |
1642 | } | 1711 | } |
@@ -1801,7 +1870,7 @@ static void mainfunc (LexState *ls, FuncState *fs) { | |||
1801 | expdesc v; | 1870 | expdesc v; |
1802 | open_func(ls, fs, &bl); | 1871 | open_func(ls, fs, &bl); |
1803 | setvararg(fs, 0); /* main function is always declared vararg */ | 1872 | setvararg(fs, 0); /* main function is always declared vararg */ |
1804 | init_exp(&v, VLOCAL, 0); /* create and... */ | 1873 | init_var(&v, VLOCAL, 0); /* create and... */ |
1805 | newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ | 1874 | newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ |
1806 | luaX_next(ls); /* read first token */ | 1875 | luaX_next(ls); /* read first token */ |
1807 | statlist(ls); /* parse main body */ | 1876 | statlist(ls); /* parse main body */ |
@@ -33,8 +33,8 @@ typedef enum { | |||
33 | VKINT, /* integer constant; nval = numerical integer value */ | 33 | VKINT, /* integer constant; nval = numerical integer value */ |
34 | VNONRELOC, /* expression has its value in a fixed register; | 34 | VNONRELOC, /* expression has its value in a fixed register; |
35 | info = result register */ | 35 | info = result register */ |
36 | VLOCAL, /* local variable; info = local register */ | 36 | VLOCAL, /* local variable; var.idx = local register */ |
37 | VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ | 37 | VUPVAL, /* upvalue variable; var.idx = index of upvalue in 'upvalues' */ |
38 | VINDEXED, /* indexed variable; | 38 | VINDEXED, /* indexed variable; |
39 | ind.t = table register; | 39 | ind.t = table register; |
40 | ind.idx = key's R index */ | 40 | ind.idx = key's R index */ |
@@ -58,7 +58,7 @@ typedef enum { | |||
58 | 58 | ||
59 | #define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR) | 59 | #define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR) |
60 | #define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR) | 60 | #define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR) |
61 | #define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL) | 61 | |
62 | 62 | ||
63 | typedef struct expdesc { | 63 | typedef struct expdesc { |
64 | expkind k; | 64 | expkind k; |
@@ -70,15 +70,20 @@ typedef struct expdesc { | |||
70 | short idx; /* index (R or "long" K) */ | 70 | short idx; /* index (R or "long" K) */ |
71 | lu_byte t; /* table (register or upvalue) */ | 71 | lu_byte t; /* table (register or upvalue) */ |
72 | } ind; | 72 | } ind; |
73 | struct { /* for local variables and upvalues */ | ||
74 | lu_byte idx; /* index of the variable */ | ||
75 | } var; | ||
73 | } u; | 76 | } u; |
74 | int t; /* patch list of 'exit when true' */ | 77 | int t; /* patch list of 'exit when true' */ |
75 | int f; /* patch list of 'exit when false' */ | 78 | int f; /* patch list of 'exit when false' */ |
76 | } expdesc; | 79 | } expdesc; |
77 | 80 | ||
78 | 81 | ||
79 | /* description of active local variable */ | 82 | /* description of an active local variable */ |
80 | typedef struct Vardesc { | 83 | typedef struct Vardesc { |
84 | TString *name; | ||
81 | short idx; /* index of the variable in the Proto's 'locvars' array */ | 85 | short idx; /* index of the variable in the Proto's 'locvars' array */ |
86 | lu_byte ro; /* true if variable is 'const' */ | ||
82 | } Vardesc; | 87 | } Vardesc; |
83 | 88 | ||
84 | 89 | ||
diff --git a/manual/manual.of b/manual/manual.of index 54a07879..6cac8c6c 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
@@ -1488,13 +1488,24 @@ Function calls are explained in @See{functioncall}. | |||
1488 | 1488 | ||
1489 | @sect3{localvar| @title{Local Declarations} | 1489 | @sect3{localvar| @title{Local Declarations} |
1490 | @x{Local variables} can be declared anywhere inside a block. | 1490 | @x{Local variables} can be declared anywhere inside a block. |
1491 | The declaration can include an initial assignment: | 1491 | The declaration can include an initialization: |
1492 | @Produc{ | 1492 | @Produc{ |
1493 | @producname{stat}@producbody{@Rw{local} namelist @bnfopt{@bnfter{=} explist}} | 1493 | @producname{stat}@producbody{@Rw{local} namelist @bnfopt{@bnfter{=} explist}} |
1494 | } | 1494 | @producname{stat}@producbody{ |
1495 | @Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp | ||
1496 | }} | ||
1495 | If present, an initial assignment has the same semantics | 1497 | If present, an initial assignment has the same semantics |
1496 | of a multiple assignment @see{assignment}. | 1498 | of a multiple assignment @see{assignment}. |
1497 | Otherwise, all variables are initialized with @nil. | 1499 | Otherwise, all variables are initialized with @nil. |
1500 | The second syntax declares a local with a given attribute, | ||
1501 | which is the name between the angle brackets. | ||
1502 | In this case, there must be an initialization. | ||
1503 | There are two possible attributes: | ||
1504 | @id{const}, which declares a @x{constant variable}, | ||
1505 | that is, a variable that cannot be assigned to | ||
1506 | after its initialization; | ||
1507 | and @id{toclose}, wich declares a to-be-closed variable @see{to-be-closed}. | ||
1508 | |||
1498 | 1509 | ||
1499 | A chunk is also a block @see{chunks}, | 1510 | A chunk is also a block @see{chunks}, |
1500 | and so local variables can be declared in a chunk outside any explicit block. | 1511 | and so local variables can be declared in a chunk outside any explicit block. |
@@ -1506,12 +1517,12 @@ The visibility rules for local variables are explained in @See{visibility}. | |||
1506 | @sect3{to-be-closed| @title{To-be-closed Variables} | 1517 | @sect3{to-be-closed| @title{To-be-closed Variables} |
1507 | 1518 | ||
1508 | A local variable can be declared as a @def{to-be-closed} variable, | 1519 | A local variable can be declared as a @def{to-be-closed} variable, |
1509 | with the following syntax: | 1520 | using the identifier @id{toclose} as its attribute: |
1510 | @Produc{ | 1521 | @Produc{ |
1511 | @producname{stat}@producbody{ | 1522 | @producname{stat}@producbody{ |
1512 | @Rw{local} @bnfter{<} @bnfter{toclose} @bnfter{>} Name @bnfter{=} exp | 1523 | @Rw{local} @bnfter{<} @id{toclose} @bnfter{>} Name @bnfter{=} exp |
1513 | }} | 1524 | }} |
1514 | A to-be-closed variable behaves like a normal local variable, | 1525 | A to-be-closed variable behaves like a constant local variable, |
1515 | except that its value is @emph{closed} whenever the variable | 1526 | except that its value is @emph{closed} whenever the variable |
1516 | goes out of scope, including normal block termination, | 1527 | goes out of scope, including normal block termination, |
1517 | exiting its block by @Rw{break}/@Rw{goto}/@Rw{return}, | 1528 | exiting its block by @Rw{break}/@Rw{goto}/@Rw{return}, |
@@ -7603,7 +7614,7 @@ or a float otherwise. | |||
7603 | 7614 | ||
7604 | @LibEntry{math.abs (x)| | 7615 | @LibEntry{math.abs (x)| |
7605 | 7616 | ||
7606 | Returns the absolute value of @id{x}. (integer/float) | 7617 | Returns the maximum value between @id{x} and @id{-x}. (integer/float) |
7607 | 7618 | ||
7608 | } | 7619 | } |
7609 | 7620 | ||
@@ -8042,7 +8053,8 @@ following the lexical conventions of Lua. | |||
8042 | This format always reads the longest input sequence that | 8053 | This format always reads the longest input sequence that |
8043 | is a valid prefix for a numeral; | 8054 | is a valid prefix for a numeral; |
8044 | if that prefix does not form a valid numeral | 8055 | if that prefix does not form a valid numeral |
8045 | (e.g., an empty string, @St{0x}, or @St{3.4e-}), | 8056 | (e.g., an empty string, @St{0x}, or @St{3.4e-}) |
8057 | or it is too long (more than 200 characters), | ||
8046 | it is discarded and the format returns @nil. | 8058 | it is discarded and the format returns @nil. |
8047 | } | 8059 | } |
8048 | 8060 | ||
@@ -8949,7 +8961,7 @@ and @bnfNter{LiteralString}, see @See{lexical}.) | |||
8949 | @OrNL @Rw{function} funcname funcbody | 8961 | @OrNL @Rw{function} funcname funcbody |
8950 | @OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody | 8962 | @OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody |
8951 | @OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist} | 8963 | @OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist} |
8952 | @OrNL @Rw{local} @bnfter{<} @bnfter{toclose} @bnfter{>} Name @bnfter{=} exp | 8964 | @OrNL @Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp |
8953 | } | 8965 | } |
8954 | 8966 | ||
8955 | @producname{retstat}@producbody{@Rw{return} | 8967 | @producname{retstat}@producbody{@Rw{return} |
diff --git a/testes/constructs.lua b/testes/constructs.lua index a83df79e..b91e0979 100644 --- a/testes/constructs.lua +++ b/testes/constructs.lua | |||
@@ -59,6 +59,41 @@ assert((x>y) and x or y == 2); | |||
59 | 59 | ||
60 | assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891) | 60 | assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891) |
61 | 61 | ||
62 | do -- testing operators with diffent kinds of constants | ||
63 | -- operands to consider: | ||
64 | -- * fit in register | ||
65 | -- * constant doesn't fit in register | ||
66 | -- * floats with integral values | ||
67 | local operand = {3, 100, 5.0, -10, -5.0, 10000, -10000} | ||
68 | local operator = {"+", "-", "*", "/", "//", "%", "^", | ||
69 | "&", "|", "^", "<<", ">>", | ||
70 | "==", "~=", "<", ">", "<=", ">=",} | ||
71 | for _, op in ipairs(operator) do | ||
72 | local f = assert(load(string.format([[return function (x,y) | ||
73 | return x %s y | ||
74 | end]], op)))(); | ||
75 | for _, o1 in ipairs(operand) do | ||
76 | for _, o2 in ipairs(operand) do | ||
77 | local gab = f(o1, o2) | ||
78 | |||
79 | _ENV.XX = o1 | ||
80 | code = string.format("return XX %s %s", op, o2) | ||
81 | res = assert(load(code))() | ||
82 | assert(res == gab) | ||
83 | |||
84 | _ENV.XX = o2 | ||
85 | local code = string.format("return (%s) %s XX", o1, op) | ||
86 | local res = assert(load(code))() | ||
87 | assert(res == gab) | ||
88 | |||
89 | code = string.format("return (%s) %s %s", o1, op, o2) | ||
90 | res = assert(load(code))() | ||
91 | assert(res == gab) | ||
92 | end | ||
93 | end | ||
94 | end | ||
95 | end | ||
96 | |||
62 | 97 | ||
63 | -- silly loops | 98 | -- silly loops |
64 | repeat until 1; repeat until true; | 99 | repeat until 1; repeat until true; |
@@ -175,6 +210,28 @@ assert(a==1 and b==nil) | |||
175 | 210 | ||
176 | print'+'; | 211 | print'+'; |
177 | 212 | ||
213 | do -- testing constants | ||
214 | local <const> prog = [[local <XXX> x = 10]] | ||
215 | checkload(prog, "unknown attribute 'XXX'") | ||
216 | |||
217 | checkload([[local <const> xxx = 20; xxx = 10]], | ||
218 | ":1: assignment to const variable 'xxx'") | ||
219 | |||
220 | checkload([[ | ||
221 | local xx; | ||
222 | local <const> xxx = 20; | ||
223 | local yyy; | ||
224 | local function foo () | ||
225 | local abc = xx + yyy + xxx; | ||
226 | return function () return function () xxx = yyy end end | ||
227 | end | ||
228 | ]], ":6: assignment to const variable 'xxx'") | ||
229 | |||
230 | checkload([[ | ||
231 | local <toclose> x = nil | ||
232 | x = io.open() | ||
233 | ]], ":2: assignment to const variable 'x'") | ||
234 | end | ||
178 | 235 | ||
179 | f = [[ | 236 | f = [[ |
180 | return function ( a , b , c , d , e ) | 237 | return function ( a , b , c , d , e ) |
@@ -245,12 +302,12 @@ print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')') | |||
245 | 302 | ||
246 | 303 | ||
247 | -- operators with their respective values | 304 | -- operators with their respective values |
248 | local binops = { | 305 | local <const> binops = { |
249 | {" and ", function (a,b) if not a then return a else return b end end}, | 306 | {" and ", function (a,b) if not a then return a else return b end end}, |
250 | {" or ", function (a,b) if a then return a else return b end end}, | 307 | {" or ", function (a,b) if a then return a else return b end end}, |
251 | } | 308 | } |
252 | 309 | ||
253 | local cases = {} | 310 | local <const> cases = {} |
254 | 311 | ||
255 | -- creates all combinations of '(cases[i] op cases[n-i])' plus | 312 | -- creates all combinations of '(cases[i] op cases[n-i])' plus |
256 | -- 'not(cases[i] op cases[n-i])' (syntax + value) | 313 | -- 'not(cases[i] op cases[n-i])' (syntax + value) |
diff --git a/testes/files.lua b/testes/files.lua index eb100fe1..54931c14 100644 --- a/testes/files.lua +++ b/testes/files.lua | |||
@@ -144,7 +144,7 @@ do | |||
144 | f:write(string.format("0x%X\n", -maxint)) | 144 | f:write(string.format("0x%X\n", -maxint)) |
145 | f:write("-0xABCp-3", '\n') | 145 | f:write("-0xABCp-3", '\n') |
146 | assert(f:close()) | 146 | assert(f:close()) |
147 | f = assert(io.open(file, "r")) | 147 | local <toclose> f = assert(io.open(file, "r")) |
148 | assert(f:read("n") == maxint) | 148 | assert(f:read("n") == maxint) |
149 | assert(f:read("n") == maxint) | 149 | assert(f:read("n") == maxint) |
150 | assert(f:read("n") == 0xABCp-3) | 150 | assert(f:read("n") == 0xABCp-3) |
@@ -170,18 +170,18 @@ three | |||
170 | ]] | 170 | ]] |
171 | local l1, l2, l3, l4, n1, n2, c, dummy | 171 | local l1, l2, l3, l4, n1, n2, c, dummy |
172 | assert(f:close()) | 172 | assert(f:close()) |
173 | f = assert(io.open(file, "r")) | 173 | local <toclose> f = assert(io.open(file, "r")) |
174 | l1, l2, n1, n2, dummy = f:read("l", "L", "n", "n") | 174 | l1, l2, n1, n2, dummy = f:read("l", "L", "n", "n") |
175 | assert(l1 == "a line" and l2 == "another line\n" and | 175 | assert(l1 == "a line" and l2 == "another line\n" and |
176 | n1 == 1234 and n2 == 3.45 and dummy == nil) | 176 | n1 == 1234 and n2 == 3.45 and dummy == nil) |
177 | assert(f:close()) | 177 | assert(f:close()) |
178 | f = assert(io.open(file, "r")) | 178 | local <toclose> f = assert(io.open(file, "r")) |
179 | l1, l2, n1, n2, c, l3, l4, dummy = f:read(7, "l", "n", "n", 1, "l", "l") | 179 | l1, l2, n1, n2, c, l3, l4, dummy = f:read(7, "l", "n", "n", 1, "l", "l") |
180 | assert(l1 == "a line\n" and l2 == "another line" and c == '\n' and | 180 | assert(l1 == "a line\n" and l2 == "another line" and c == '\n' and |
181 | n1 == 1234 and n2 == 3.45 and l3 == "one" and l4 == "two" | 181 | n1 == 1234 and n2 == 3.45 and l3 == "one" and l4 == "two" |
182 | and dummy == nil) | 182 | and dummy == nil) |
183 | assert(f:close()) | 183 | assert(f:close()) |
184 | f = assert(io.open(file, "r")) | 184 | local <toclose> f = assert(io.open(file, "r")) |
185 | -- second item failing | 185 | -- second item failing |
186 | l1, n1, n2, dummy = f:read("l", "n", "n", "l") | 186 | l1, n1, n2, dummy = f:read("l", "n", "n", "l") |
187 | assert(l1 == "a line" and n1 == nil) | 187 | assert(l1 == "a line" and n1 == nil) |
diff --git a/testes/math.lua b/testes/math.lua index b010ff6c..c45a91ad 100644 --- a/testes/math.lua +++ b/testes/math.lua | |||
@@ -3,10 +3,10 @@ | |||
3 | 3 | ||
4 | print("testing numbers and math lib") | 4 | print("testing numbers and math lib") |
5 | 5 | ||
6 | local minint = math.mininteger | 6 | local <const> minint = math.mininteger |
7 | local maxint = math.maxinteger | 7 | local <const> maxint = math.maxinteger |
8 | 8 | ||
9 | local intbits = math.floor(math.log(maxint, 2) + 0.5) + 1 | 9 | local <const> intbits = math.floor(math.log(maxint, 2) + 0.5) + 1 |
10 | assert((1 << intbits) == 0) | 10 | assert((1 << intbits) == 0) |
11 | 11 | ||
12 | assert(minint == 1 << (intbits - 1)) | 12 | assert(minint == 1 << (intbits - 1)) |