diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-05-17 11:11:44 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-05-17 11:11:44 -0300 |
| commit | d9f40e3f6fb61650240c47d548bee69b24b07859 (patch) | |
| tree | ab01022b3e3bc6bdb800423c97095a9423e0a798 | |
| parent | 347d6961ac14213264c7176e3d125c9ba8475b01 (diff) | |
| download | lua-d9f40e3f6fb61650240c47d548bee69b24b07859.tar.gz lua-d9f40e3f6fb61650240c47d548bee69b24b07859.tar.bz2 lua-d9f40e3f6fb61650240c47d548bee69b24b07859.zip | |
First implementation for 'const' variables
A variable can be declared const, which means it cannot be assigned to,
with the syntax 'local <const> name = exp'.
| -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)) |
