mirror of
https://github.com/lua/lua.git
synced 2024-11-23 18:23:43 +08:00
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'.
This commit is contained in:
parent
347d6961ac
commit
d9f40e3f6f
36
lcode.c
36
lcode.c
@ -678,11 +678,12 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
|
|||||||
void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
||||||
switch (e->k) {
|
switch (e->k) {
|
||||||
case VLOCAL: { /* already in a register */
|
case VLOCAL: { /* already in a register */
|
||||||
|
e->u.info = e->u.var.idx;
|
||||||
e->k = VNONRELOC; /* becomes a non-relocatable value */
|
e->k = VNONRELOC; /* becomes a non-relocatable value */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VUPVAL: { /* move value to some (pending) register */
|
case VUPVAL: { /* move value to some (pending) register */
|
||||||
e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);
|
e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.var.idx, 0);
|
||||||
e->k = VRELOC;
|
e->k = VRELOC;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -938,12 +939,12 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
|
|||||||
switch (var->k) {
|
switch (var->k) {
|
||||||
case VLOCAL: {
|
case VLOCAL: {
|
||||||
freeexp(fs, ex);
|
freeexp(fs, ex);
|
||||||
exp2reg(fs, ex, var->u.info); /* compute 'ex' into proper place */
|
exp2reg(fs, ex, var->u.var.idx); /* compute 'ex' into proper place */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case VUPVAL: {
|
case VUPVAL: {
|
||||||
int e = luaK_exp2anyreg(fs, ex);
|
int e = luaK_exp2anyreg(fs, ex);
|
||||||
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
|
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.var.idx, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VINDEXUP: {
|
case VINDEXUP: {
|
||||||
@ -1165,25 +1166,30 @@ static int isSCnumber (expdesc *e, lua_Integer *i, int *isfloat) {
|
|||||||
** values in registers.
|
** values in registers.
|
||||||
*/
|
*/
|
||||||
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
|
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
|
||||||
lua_assert(!hasjumps(t) && (vkisinreg(t->k) || t->k == VUPVAL));
|
lua_assert(!hasjumps(t) &&
|
||||||
|
(t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL));
|
||||||
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */
|
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */
|
||||||
luaK_exp2anyreg(fs, t); /* put it in a register */
|
luaK_exp2anyreg(fs, t); /* put it in a register */
|
||||||
t->u.ind.t = t->u.info; /* register or upvalue index */
|
|
||||||
if (t->k == VUPVAL) {
|
if (t->k == VUPVAL) {
|
||||||
|
t->u.ind.t = t->u.var.idx; /* upvalue index */
|
||||||
t->u.ind.idx = k->u.info; /* literal string */
|
t->u.ind.idx = k->u.info; /* literal string */
|
||||||
t->k = VINDEXUP;
|
t->k = VINDEXUP;
|
||||||
}
|
}
|
||||||
else if (isKstr(fs, k)) {
|
|
||||||
t->u.ind.idx = k->u.info; /* literal string */
|
|
||||||
t->k = VINDEXSTR;
|
|
||||||
}
|
|
||||||
else if (isCint(k)) {
|
|
||||||
t->u.ind.idx = cast_int(k->u.ival); /* integer constant in proper range */
|
|
||||||
t->k = VINDEXI;
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */
|
/* register index of the table */
|
||||||
t->k = VINDEXED;
|
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.idx: t->u.info;
|
||||||
|
if (isKstr(fs, k)) {
|
||||||
|
t->u.ind.idx = k->u.info; /* literal string */
|
||||||
|
t->k = VINDEXSTR;
|
||||||
|
}
|
||||||
|
else if (isCint(k)) {
|
||||||
|
t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */
|
||||||
|
t->k = VINDEXI;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */
|
||||||
|
t->k = VINDEXED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
113
lparser.c
113
lparser.c
@ -156,6 +156,13 @@ static void init_exp (expdesc *e, expkind k, int i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void init_var (expdesc *e, expkind k, int i) {
|
||||||
|
e->f = e->t = NO_JUMP;
|
||||||
|
e->k = k;
|
||||||
|
e->u.var.idx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void codestring (LexState *ls, expdesc *e, TString *s) {
|
static void codestring (LexState *ls, expdesc *e, TString *s) {
|
||||||
init_exp(e, VK, luaK_stringK(ls->fs, s));
|
init_exp(e, VK, luaK_stringK(ls->fs, s));
|
||||||
}
|
}
|
||||||
@ -187,31 +194,82 @@ static int registerlocalvar (LexState *ls, TString *varname) {
|
|||||||
/*
|
/*
|
||||||
** Create a new local variable with the given 'name'.
|
** Create a new local variable with the given 'name'.
|
||||||
*/
|
*/
|
||||||
static void new_localvar (LexState *ls, TString *name) {
|
static Vardesc *new_localvar (LexState *ls, TString *name) {
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
Dyndata *dyd = ls->dyd;
|
Dyndata *dyd = ls->dyd;
|
||||||
|
Vardesc *var;
|
||||||
int reg = registerlocalvar(ls, name);
|
int reg = registerlocalvar(ls, name);
|
||||||
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
|
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
|
||||||
MAXVARS, "local variables");
|
MAXVARS, "local variables");
|
||||||
luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1,
|
luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1,
|
||||||
dyd->actvar.size, Vardesc, MAX_INT, "local variables");
|
dyd->actvar.size, Vardesc, MAX_INT, "local variables");
|
||||||
dyd->actvar.arr[dyd->actvar.n++].idx = cast(short, reg);
|
var = &dyd->actvar.arr[dyd->actvar.n++];
|
||||||
|
var->idx = cast(short, reg);
|
||||||
|
var->name = name;
|
||||||
|
var->ro = 0;
|
||||||
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define new_localvarliteral(ls,v) \
|
#define new_localvarliteral(ls,v) \
|
||||||
new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
|
new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the "variable description" (Vardesc) of a given
|
||||||
|
** variable
|
||||||
|
*/
|
||||||
|
static Vardesc *getlocalvardesc (FuncState *fs, int i) {
|
||||||
|
return &fs->ls->dyd->actvar.arr[fs->firstlocal + i];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Get the debug-information entry for current variable 'i'.
|
** Get the debug-information entry for current variable 'i'.
|
||||||
*/
|
*/
|
||||||
static LocVar *getlocvar (FuncState *fs, int i) {
|
static LocVar *getlocvar (FuncState *fs, int i) {
|
||||||
int idx = fs->ls->dyd->actvar.arr[fs->firstlocal + i].idx;
|
int idx = getlocalvardesc(fs, i)->idx;
|
||||||
lua_assert(idx < fs->nlocvars);
|
lua_assert(idx < fs->nlocvars);
|
||||||
return &fs->f->locvars[idx];
|
return &fs->f->locvars[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the "variable description" (Vardesc) of a given
|
||||||
|
** variable or upvalue
|
||||||
|
*/
|
||||||
|
static Vardesc *getvardesc (FuncState *fs, expdesc *e) {
|
||||||
|
if (e->k == VLOCAL)
|
||||||
|
return getlocalvardesc(fs, e->u.var.idx);
|
||||||
|
else if (e->k != VUPVAL)
|
||||||
|
return NULL; /* not a local variable */
|
||||||
|
else { /* upvalue: must go up all levels up to the original local */
|
||||||
|
int idx = e->u.var.idx;
|
||||||
|
for (;;) {
|
||||||
|
Upvaldesc *up = &fs->f->upvalues[idx];
|
||||||
|
fs = fs->prev; /* must look at the previous level */
|
||||||
|
idx = up->idx; /* at this index */
|
||||||
|
if (fs == NULL) { /* no more levels? (can happen only with _ENV) */
|
||||||
|
lua_assert(strcmp(getstr(up->name), LUA_ENV) == 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if (up->instack) /* got to the original level? */
|
||||||
|
return getlocalvardesc(fs, idx);
|
||||||
|
/* else repeat for previous level */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void check_readonly (LexState *ls, expdesc *e) {
|
||||||
|
Vardesc *vardesc = getvardesc(ls->fs, e);
|
||||||
|
if (vardesc && vardesc->ro) { /* is variable local and const? */
|
||||||
|
const char *msg = luaO_pushfstring(ls->L,
|
||||||
|
"assignment to const variable '%s'", getstr(vardesc->name));
|
||||||
|
luaK_semerror(ls, msg); /* error */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Start the scope for the last 'nvars' created variables.
|
** Start the scope for the last 'nvars' created variables.
|
||||||
** (debug info.)
|
** (debug info.)
|
||||||
@ -259,7 +317,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
|
|||||||
while (oldsize < f->sizeupvalues)
|
while (oldsize < f->sizeupvalues)
|
||||||
f->upvalues[oldsize++].name = NULL;
|
f->upvalues[oldsize++].name = NULL;
|
||||||
f->upvalues[fs->nups].instack = (v->k == VLOCAL);
|
f->upvalues[fs->nups].instack = (v->k == VLOCAL);
|
||||||
f->upvalues[fs->nups].idx = cast_byte(v->u.info);
|
f->upvalues[fs->nups].idx = cast_byte(v->u.var.idx);
|
||||||
f->upvalues[fs->nups].name = name;
|
f->upvalues[fs->nups].name = name;
|
||||||
luaC_objbarrier(fs->ls->L, f, name);
|
luaC_objbarrier(fs->ls->L, f, name);
|
||||||
return fs->nups++;
|
return fs->nups++;
|
||||||
@ -304,7 +362,7 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
|||||||
else {
|
else {
|
||||||
int v = searchvar(fs, n); /* look up locals at current level */
|
int v = searchvar(fs, n); /* look up locals at current level */
|
||||||
if (v >= 0) { /* found? */
|
if (v >= 0) { /* found? */
|
||||||
init_exp(var, VLOCAL, v); /* variable is local */
|
init_var(var, VLOCAL, v); /* variable is local */
|
||||||
if (!base)
|
if (!base)
|
||||||
markupval(fs, v); /* local will be used as an upval */
|
markupval(fs, v); /* local will be used as an upval */
|
||||||
}
|
}
|
||||||
@ -317,7 +375,7 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
|||||||
/* else was LOCAL or UPVAL */
|
/* else was LOCAL or UPVAL */
|
||||||
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
||||||
}
|
}
|
||||||
init_exp(var, VUPVAL, idx); /* new or old upvalue */
|
init_var(var, VUPVAL, idx); /* new or old upvalue */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1199,20 +1257,20 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
|||||||
for (; lh; lh = lh->prev) { /* check all previous assignments */
|
for (; lh; lh = lh->prev) { /* check all previous assignments */
|
||||||
if (vkisindexed(lh->v.k)) { /* assignment to table field? */
|
if (vkisindexed(lh->v.k)) { /* assignment to table field? */
|
||||||
if (lh->v.k == VINDEXUP) { /* is table an upvalue? */
|
if (lh->v.k == VINDEXUP) { /* is table an upvalue? */
|
||||||
if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) {
|
if (v->k == VUPVAL && lh->v.u.ind.t == v->u.var.idx) {
|
||||||
conflict = 1; /* table is the upvalue being assigned now */
|
conflict = 1; /* table is the upvalue being assigned now */
|
||||||
lh->v.k = VINDEXSTR;
|
lh->v.k = VINDEXSTR;
|
||||||
lh->v.u.ind.t = extra; /* assignment will use safe copy */
|
lh->v.u.ind.t = extra; /* assignment will use safe copy */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { /* table is a register */
|
else { /* table is a register */
|
||||||
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.info) {
|
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.idx) {
|
||||||
conflict = 1; /* table is the local being assigned now */
|
conflict = 1; /* table is the local being assigned now */
|
||||||
lh->v.u.ind.t = extra; /* assignment will use safe copy */
|
lh->v.u.ind.t = extra; /* assignment will use safe copy */
|
||||||
}
|
}
|
||||||
/* is index the local being assigned? */
|
/* is index the local being assigned? */
|
||||||
if (lh->v.k == VINDEXED && v->k == VLOCAL &&
|
if (lh->v.k == VINDEXED && v->k == VLOCAL &&
|
||||||
lh->v.u.ind.idx == v->u.info) {
|
lh->v.u.ind.idx == v->u.var.idx) {
|
||||||
conflict = 1;
|
conflict = 1;
|
||||||
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
|
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
|
||||||
}
|
}
|
||||||
@ -1222,7 +1280,7 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
|||||||
if (conflict) {
|
if (conflict) {
|
||||||
/* copy upvalue/local value to a temporary (in position 'extra') */
|
/* copy upvalue/local value to a temporary (in position 'extra') */
|
||||||
OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
|
OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
|
||||||
luaK_codeABC(fs, op, extra, v->u.info, 0);
|
luaK_codeABC(fs, op, extra, v->u.var.idx, 0);
|
||||||
luaK_reserveregs(fs, 1);
|
luaK_reserveregs(fs, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1237,6 +1295,7 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
|||||||
static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
|
static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
|
||||||
expdesc e;
|
expdesc e;
|
||||||
check_condition(ls, vkisvar(lh->v.k), "syntax error");
|
check_condition(ls, vkisvar(lh->v.k), "syntax error");
|
||||||
|
check_readonly(ls, &lh->v);
|
||||||
if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */
|
if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */
|
||||||
struct LHS_assign nv;
|
struct LHS_assign nv;
|
||||||
nv.prev = lh;
|
nv.prev = lh;
|
||||||
@ -1615,28 +1674,38 @@ static void commonlocalstat (LexState *ls) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void tocloselocalstat (LexState *ls) {
|
static void tocloselocalstat (LexState *ls, Vardesc *var) {
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
TString *attr = str_checkname(ls);
|
var->ro = 1; /* to-be-closed variables are always read-only */
|
||||||
if (strcmp(getstr(attr), "toclose") != 0)
|
|
||||||
luaK_semerror(ls,
|
|
||||||
luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
|
|
||||||
testnext(ls, '>');
|
|
||||||
new_localvar(ls, str_checkname(ls));
|
|
||||||
checknext(ls, '=');
|
|
||||||
exp1(ls);
|
|
||||||
markupval(fs, fs->nactvar);
|
markupval(fs, fs->nactvar);
|
||||||
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
||||||
adjustlocalvars(ls, 1);
|
|
||||||
luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0);
|
luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void attriblocalstat (LexState *ls) {
|
||||||
|
Vardesc *var;
|
||||||
|
TString *attr = str_checkname(ls);
|
||||||
|
testnext(ls, '>');
|
||||||
|
var = new_localvar(ls, str_checkname(ls));
|
||||||
|
checknext(ls, '=');
|
||||||
|
exp1(ls);
|
||||||
|
adjustlocalvars(ls, 1);
|
||||||
|
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 localstat (LexState *ls) {
|
static void localstat (LexState *ls) {
|
||||||
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
|
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
|
||||||
| LOCAL *toclose NAME '=' exp */
|
| LOCAL *toclose NAME '=' exp */
|
||||||
if (testnext(ls, '<'))
|
if (testnext(ls, '<'))
|
||||||
tocloselocalstat(ls);
|
attriblocalstat(ls);
|
||||||
else
|
else
|
||||||
commonlocalstat(ls);
|
commonlocalstat(ls);
|
||||||
}
|
}
|
||||||
@ -1801,7 +1870,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
|
|||||||
expdesc v;
|
expdesc v;
|
||||||
open_func(ls, fs, &bl);
|
open_func(ls, fs, &bl);
|
||||||
setvararg(fs, 0); /* main function is always declared vararg */
|
setvararg(fs, 0); /* main function is always declared vararg */
|
||||||
init_exp(&v, VLOCAL, 0); /* create and... */
|
init_var(&v, VLOCAL, 0); /* create and... */
|
||||||
newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */
|
newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */
|
||||||
luaX_next(ls); /* read first token */
|
luaX_next(ls); /* read first token */
|
||||||
statlist(ls); /* parse main body */
|
statlist(ls); /* parse main body */
|
||||||
|
13
lparser.h
13
lparser.h
@ -33,8 +33,8 @@ typedef enum {
|
|||||||
VKINT, /* integer constant; nval = numerical integer value */
|
VKINT, /* integer constant; nval = numerical integer value */
|
||||||
VNONRELOC, /* expression has its value in a fixed register;
|
VNONRELOC, /* expression has its value in a fixed register;
|
||||||
info = result register */
|
info = result register */
|
||||||
VLOCAL, /* local variable; info = local register */
|
VLOCAL, /* local variable; var.idx = local register */
|
||||||
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
|
VUPVAL, /* upvalue variable; var.idx = index of upvalue in 'upvalues' */
|
||||||
VINDEXED, /* indexed variable;
|
VINDEXED, /* indexed variable;
|
||||||
ind.t = table register;
|
ind.t = table register;
|
||||||
ind.idx = key's R index */
|
ind.idx = key's R index */
|
||||||
@ -58,7 +58,7 @@ typedef enum {
|
|||||||
|
|
||||||
#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR)
|
#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR)
|
||||||
#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR)
|
#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR)
|
||||||
#define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL)
|
|
||||||
|
|
||||||
typedef struct expdesc {
|
typedef struct expdesc {
|
||||||
expkind k;
|
expkind k;
|
||||||
@ -70,15 +70,20 @@ typedef struct expdesc {
|
|||||||
short idx; /* index (R or "long" K) */
|
short idx; /* index (R or "long" K) */
|
||||||
lu_byte t; /* table (register or upvalue) */
|
lu_byte t; /* table (register or upvalue) */
|
||||||
} ind;
|
} ind;
|
||||||
|
struct { /* for local variables and upvalues */
|
||||||
|
lu_byte idx; /* index of the variable */
|
||||||
|
} var;
|
||||||
} u;
|
} u;
|
||||||
int t; /* patch list of 'exit when true' */
|
int t; /* patch list of 'exit when true' */
|
||||||
int f; /* patch list of 'exit when false' */
|
int f; /* patch list of 'exit when false' */
|
||||||
} expdesc;
|
} expdesc;
|
||||||
|
|
||||||
|
|
||||||
/* description of active local variable */
|
/* description of an active local variable */
|
||||||
typedef struct Vardesc {
|
typedef struct Vardesc {
|
||||||
|
TString *name;
|
||||||
short idx; /* index of the variable in the Proto's 'locvars' array */
|
short idx; /* index of the variable in the Proto's 'locvars' array */
|
||||||
|
lu_byte ro; /* true if variable is 'const' */
|
||||||
} Vardesc;
|
} Vardesc;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1488,13 +1488,24 @@ Function calls are explained in @See{functioncall}.
|
|||||||
|
|
||||||
@sect3{localvar| @title{Local Declarations}
|
@sect3{localvar| @title{Local Declarations}
|
||||||
@x{Local variables} can be declared anywhere inside a block.
|
@x{Local variables} can be declared anywhere inside a block.
|
||||||
The declaration can include an initial assignment:
|
The declaration can include an initialization:
|
||||||
@Produc{
|
@Produc{
|
||||||
@producname{stat}@producbody{@Rw{local} namelist @bnfopt{@bnfter{=} explist}}
|
@producname{stat}@producbody{@Rw{local} namelist @bnfopt{@bnfter{=} explist}}
|
||||||
}
|
@producname{stat}@producbody{
|
||||||
|
@Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp
|
||||||
|
}}
|
||||||
If present, an initial assignment has the same semantics
|
If present, an initial assignment has the same semantics
|
||||||
of a multiple assignment @see{assignment}.
|
of a multiple assignment @see{assignment}.
|
||||||
Otherwise, all variables are initialized with @nil.
|
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.
|
||||||
|
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}, wich declares a to-be-closed variable @see{to-be-closed}.
|
||||||
|
|
||||||
|
|
||||||
A chunk is also a block @see{chunks},
|
A chunk is also a block @see{chunks},
|
||||||
and so local variables can be declared in a chunk outside any explicit block.
|
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}.
|
|||||||
@sect3{to-be-closed| @title{To-be-closed Variables}
|
@sect3{to-be-closed| @title{To-be-closed Variables}
|
||||||
|
|
||||||
A local variable can be declared as a @def{to-be-closed} variable,
|
A local variable can be declared as a @def{to-be-closed} variable,
|
||||||
with the following syntax:
|
using the identifier @id{toclose} as its attribute:
|
||||||
@Produc{
|
@Produc{
|
||||||
@producname{stat}@producbody{
|
@producname{stat}@producbody{
|
||||||
@Rw{local} @bnfter{<} @bnfter{toclose} @bnfter{>} Name @bnfter{=} exp
|
@Rw{local} @bnfter{<} @id{toclose} @bnfter{>} Name @bnfter{=} exp
|
||||||
}}
|
}}
|
||||||
A to-be-closed variable behaves like a normal local variable,
|
A to-be-closed variable behaves like a constant local variable,
|
||||||
except that its value is @emph{closed} whenever the variable
|
except that its value is @emph{closed} whenever the variable
|
||||||
goes out of scope, including normal block termination,
|
goes out of scope, including normal block termination,
|
||||||
exiting its block by @Rw{break}/@Rw{goto}/@Rw{return},
|
exiting its block by @Rw{break}/@Rw{goto}/@Rw{return},
|
||||||
@ -7603,7 +7614,7 @@ or a float otherwise.
|
|||||||
|
|
||||||
@LibEntry{math.abs (x)|
|
@LibEntry{math.abs (x)|
|
||||||
|
|
||||||
Returns the absolute value of @id{x}. (integer/float)
|
Returns the maximum value between @id{x} and @id{-x}. (integer/float)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8042,7 +8053,8 @@ following the lexical conventions of Lua.
|
|||||||
This format always reads the longest input sequence that
|
This format always reads the longest input sequence that
|
||||||
is a valid prefix for a numeral;
|
is a valid prefix for a numeral;
|
||||||
if that prefix does not form a valid numeral
|
if that prefix does not form a valid numeral
|
||||||
(e.g., an empty string, @St{0x}, or @St{3.4e-}),
|
(e.g., an empty string, @St{0x}, or @St{3.4e-})
|
||||||
|
or it is too long (more than 200 characters),
|
||||||
it is discarded and the format returns @nil.
|
it is discarded and the format returns @nil.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8949,7 +8961,7 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
|
|||||||
@OrNL @Rw{function} funcname funcbody
|
@OrNL @Rw{function} funcname funcbody
|
||||||
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
|
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
|
||||||
@OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist}
|
@OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist}
|
||||||
@OrNL @Rw{local} @bnfter{<} @bnfter{toclose} @bnfter{>} Name @bnfter{=} exp
|
@OrNL @Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp
|
||||||
}
|
}
|
||||||
|
|
||||||
@producname{retstat}@producbody{@Rw{return}
|
@producname{retstat}@producbody{@Rw{return}
|
||||||
|
@ -59,6 +59,41 @@ assert((x>y) and x or y == 2);
|
|||||||
|
|
||||||
assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891)
|
assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891)
|
||||||
|
|
||||||
|
do -- testing operators with diffent kinds of constants
|
||||||
|
-- operands to consider:
|
||||||
|
-- * fit in register
|
||||||
|
-- * constant doesn't fit in register
|
||||||
|
-- * floats with integral values
|
||||||
|
local operand = {3, 100, 5.0, -10, -5.0, 10000, -10000}
|
||||||
|
local operator = {"+", "-", "*", "/", "//", "%", "^",
|
||||||
|
"&", "|", "^", "<<", ">>",
|
||||||
|
"==", "~=", "<", ">", "<=", ">=",}
|
||||||
|
for _, op in ipairs(operator) do
|
||||||
|
local f = assert(load(string.format([[return function (x,y)
|
||||||
|
return x %s y
|
||||||
|
end]], op)))();
|
||||||
|
for _, o1 in ipairs(operand) do
|
||||||
|
for _, o2 in ipairs(operand) do
|
||||||
|
local gab = f(o1, o2)
|
||||||
|
|
||||||
|
_ENV.XX = o1
|
||||||
|
code = string.format("return XX %s %s", op, o2)
|
||||||
|
res = assert(load(code))()
|
||||||
|
assert(res == gab)
|
||||||
|
|
||||||
|
_ENV.XX = o2
|
||||||
|
local code = string.format("return (%s) %s XX", o1, op)
|
||||||
|
local res = assert(load(code))()
|
||||||
|
assert(res == gab)
|
||||||
|
|
||||||
|
code = string.format("return (%s) %s %s", o1, op, o2)
|
||||||
|
res = assert(load(code))()
|
||||||
|
assert(res == gab)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- silly loops
|
-- silly loops
|
||||||
repeat until 1; repeat until true;
|
repeat until 1; repeat until true;
|
||||||
@ -175,6 +210,28 @@ assert(a==1 and b==nil)
|
|||||||
|
|
||||||
print'+';
|
print'+';
|
||||||
|
|
||||||
|
do -- testing constants
|
||||||
|
local <const> prog = [[local <XXX> x = 10]]
|
||||||
|
checkload(prog, "unknown attribute 'XXX'")
|
||||||
|
|
||||||
|
checkload([[local <const> xxx = 20; xxx = 10]],
|
||||||
|
":1: assignment to const variable 'xxx'")
|
||||||
|
|
||||||
|
checkload([[
|
||||||
|
local xx;
|
||||||
|
local <const> xxx = 20;
|
||||||
|
local yyy;
|
||||||
|
local function foo ()
|
||||||
|
local abc = xx + yyy + xxx;
|
||||||
|
return function () return function () xxx = yyy end end
|
||||||
|
end
|
||||||
|
]], ":6: assignment to const variable 'xxx'")
|
||||||
|
|
||||||
|
checkload([[
|
||||||
|
local <toclose> x = nil
|
||||||
|
x = io.open()
|
||||||
|
]], ":2: assignment to const variable 'x'")
|
||||||
|
end
|
||||||
|
|
||||||
f = [[
|
f = [[
|
||||||
return function ( a , b , c , d , e )
|
return function ( a , b , c , d , e )
|
||||||
@ -245,12 +302,12 @@ print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')')
|
|||||||
|
|
||||||
|
|
||||||
-- operators with their respective values
|
-- operators with their respective values
|
||||||
local binops = {
|
local <const> binops = {
|
||||||
{" and ", function (a,b) if not a then return a else return b end end},
|
{" and ", function (a,b) if not a then return a else return b end end},
|
||||||
{" or ", function (a,b) if a then return a else return b end end},
|
{" or ", function (a,b) if a then return a else return b end end},
|
||||||
}
|
}
|
||||||
|
|
||||||
local cases = {}
|
local <const> cases = {}
|
||||||
|
|
||||||
-- creates all combinations of '(cases[i] op cases[n-i])' plus
|
-- creates all combinations of '(cases[i] op cases[n-i])' plus
|
||||||
-- 'not(cases[i] op cases[n-i])' (syntax + value)
|
-- 'not(cases[i] op cases[n-i])' (syntax + value)
|
||||||
|
@ -144,7 +144,7 @@ do
|
|||||||
f:write(string.format("0x%X\n", -maxint))
|
f:write(string.format("0x%X\n", -maxint))
|
||||||
f:write("-0xABCp-3", '\n')
|
f:write("-0xABCp-3", '\n')
|
||||||
assert(f:close())
|
assert(f:close())
|
||||||
f = assert(io.open(file, "r"))
|
local <toclose> f = assert(io.open(file, "r"))
|
||||||
assert(f:read("n") == maxint)
|
assert(f:read("n") == maxint)
|
||||||
assert(f:read("n") == maxint)
|
assert(f:read("n") == maxint)
|
||||||
assert(f:read("n") == 0xABCp-3)
|
assert(f:read("n") == 0xABCp-3)
|
||||||
@ -170,18 +170,18 @@ three
|
|||||||
]]
|
]]
|
||||||
local l1, l2, l3, l4, n1, n2, c, dummy
|
local l1, l2, l3, l4, n1, n2, c, dummy
|
||||||
assert(f:close())
|
assert(f:close())
|
||||||
f = assert(io.open(file, "r"))
|
local <toclose> f = assert(io.open(file, "r"))
|
||||||
l1, l2, n1, n2, dummy = f:read("l", "L", "n", "n")
|
l1, l2, n1, n2, dummy = f:read("l", "L", "n", "n")
|
||||||
assert(l1 == "a line" and l2 == "another line\n" and
|
assert(l1 == "a line" and l2 == "another line\n" and
|
||||||
n1 == 1234 and n2 == 3.45 and dummy == nil)
|
n1 == 1234 and n2 == 3.45 and dummy == nil)
|
||||||
assert(f:close())
|
assert(f:close())
|
||||||
f = assert(io.open(file, "r"))
|
local <toclose> f = assert(io.open(file, "r"))
|
||||||
l1, l2, n1, n2, c, l3, l4, dummy = f:read(7, "l", "n", "n", 1, "l", "l")
|
l1, l2, n1, n2, c, l3, l4, dummy = f:read(7, "l", "n", "n", 1, "l", "l")
|
||||||
assert(l1 == "a line\n" and l2 == "another line" and c == '\n' and
|
assert(l1 == "a line\n" and l2 == "another line" and c == '\n' and
|
||||||
n1 == 1234 and n2 == 3.45 and l3 == "one" and l4 == "two"
|
n1 == 1234 and n2 == 3.45 and l3 == "one" and l4 == "two"
|
||||||
and dummy == nil)
|
and dummy == nil)
|
||||||
assert(f:close())
|
assert(f:close())
|
||||||
f = assert(io.open(file, "r"))
|
local <toclose> f = assert(io.open(file, "r"))
|
||||||
-- second item failing
|
-- second item failing
|
||||||
l1, n1, n2, dummy = f:read("l", "n", "n", "l")
|
l1, n1, n2, dummy = f:read("l", "n", "n", "l")
|
||||||
assert(l1 == "a line" and n1 == nil)
|
assert(l1 == "a line" and n1 == nil)
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
print("testing numbers and math lib")
|
print("testing numbers and math lib")
|
||||||
|
|
||||||
local minint = math.mininteger
|
local <const> minint = math.mininteger
|
||||||
local maxint = math.maxinteger
|
local <const> maxint = math.maxinteger
|
||||||
|
|
||||||
local intbits = math.floor(math.log(maxint, 2) + 0.5) + 1
|
local <const> intbits = math.floor(math.log(maxint, 2) + 0.5) + 1
|
||||||
assert((1 << intbits) == 0)
|
assert((1 << intbits) == 0)
|
||||||
|
|
||||||
assert(minint == 1 << (intbits - 1))
|
assert(minint == 1 << (intbits - 1))
|
||||||
|
Loading…
Reference in New Issue
Block a user