mirror of
https://github.com/lua/lua.git
synced 2026-01-27 01:44:31 +00:00
Optional initialization for global declarations
This commit is contained in:
parent
8485687908
commit
942c10a5e3
81
lparser.c
81
lparser.c
@ -30,7 +30,7 @@
|
||||
|
||||
|
||||
|
||||
/* maximum number of variable declarationss per function (must be
|
||||
/* maximum number of variable declarations per function (must be
|
||||
smaller than 250, due to the bytecode format) */
|
||||
#define MAXVARS 200
|
||||
|
||||
@ -197,7 +197,7 @@ static int new_varkind (LexState *ls, TString *name, lu_byte kind) {
|
||||
Dyndata *dyd = ls->dyd;
|
||||
Vardesc *var;
|
||||
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
|
||||
dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarationss");
|
||||
dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarations");
|
||||
var = &dyd->actvar.arr[dyd->actvar.n++];
|
||||
var->vd.kind = kind; /* default */
|
||||
var->vd.name = name;
|
||||
@ -485,6 +485,20 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
||||
}
|
||||
|
||||
|
||||
static void buildglobal (LexState *ls, TString *varname, expdesc *var) {
|
||||
FuncState *fs = ls->fs;
|
||||
expdesc key;
|
||||
init_exp(var, VGLOBAL, -1); /* global by default */
|
||||
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
|
||||
if (var->k == VGLOBAL)
|
||||
luaK_semerror(ls, "_ENV is global when accessing variable '%s'",
|
||||
getstr(varname));
|
||||
luaK_exp2anyregup(fs, var); /* _ENV could be a constant */
|
||||
codestring(&key, varname); /* key is variable name */
|
||||
luaK_indexed(fs, var, &key); /* 'var' represents _ENV[varname] */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find a variable with the given name 'n', handling global variables
|
||||
** too.
|
||||
@ -494,18 +508,11 @@ static void buildvar (LexState *ls, TString *varname, expdesc *var) {
|
||||
init_exp(var, VGLOBAL, -1); /* global by default */
|
||||
singlevaraux(fs, varname, var, 1);
|
||||
if (var->k == VGLOBAL) { /* global name? */
|
||||
expdesc key;
|
||||
int info = var->u.info;
|
||||
/* global by default in the scope of a global declaration? */
|
||||
if (info == -2)
|
||||
luaK_semerror(ls, "variable '%s' not declared", getstr(varname));
|
||||
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
|
||||
if (var->k == VGLOBAL)
|
||||
luaK_semerror(ls, "_ENV is global when accessing variable '%s'",
|
||||
getstr(varname));
|
||||
luaK_exp2anyregup(fs, var); /* but could be a constant */
|
||||
codestring(&key, varname); /* key is variable name */
|
||||
luaK_indexed(fs, var, &key); /* env[varname] */
|
||||
buildglobal(ls, varname, var);
|
||||
if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST)
|
||||
var->u.ind.ro = 1; /* mark variable as read-only */
|
||||
else /* anyway must be a global */
|
||||
@ -665,7 +672,7 @@ static void createlabel (LexState *ls, TString *name, int line, int last) {
|
||||
|
||||
|
||||
/*
|
||||
** Traverse the pending goto's of the finishing block checking whether
|
||||
** Traverse the pending gotos of the finishing block checking whether
|
||||
** each match some label of that block. Those that do not match are
|
||||
** "exported" to the outer block, to be solved there. In particular,
|
||||
** its 'nactvar' is updated with the level of the inner block,
|
||||
@ -1435,6 +1442,15 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Create code to store the "top" register in 'var' */
|
||||
static void storevartop (FuncState *fs, expdesc *var) {
|
||||
expdesc e;
|
||||
init_exp(&e, VNONRELOC, fs->freereg - 1);
|
||||
luaK_storevar(fs, var, &e); /* will also free the top register */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Parse and compile a multiple assignment. The first "variable"
|
||||
** (a 'suffixedexp') was already read by the caller.
|
||||
@ -1468,8 +1484,7 @@ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
|
||||
return; /* avoid default */
|
||||
}
|
||||
}
|
||||
init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */
|
||||
luaK_storevar(ls->fs, &lh->v, &e);
|
||||
storevartop(ls->fs, &lh->v); /* default assignment */
|
||||
}
|
||||
|
||||
|
||||
@ -1821,25 +1836,45 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
|
||||
}
|
||||
|
||||
|
||||
static void globalnames (LexState *ls, lu_byte defkind) {
|
||||
FuncState *fs = ls->fs;
|
||||
int nvars = 0;
|
||||
int lastidx; /* index of last registered variable */
|
||||
do { /* for each name */
|
||||
TString *vname = str_checkname(ls);
|
||||
lu_byte kind = getglobalattribute(ls, defkind);
|
||||
lastidx = new_varkind(ls, vname, kind);
|
||||
nvars++;
|
||||
} while (testnext(ls, ','));
|
||||
if (testnext(ls, '=')) { /* initialization? */
|
||||
expdesc e;
|
||||
int i;
|
||||
int nexps = explist(ls, &e); /* read list of expressions */
|
||||
adjust_assign(ls, nvars, nexps, &e);
|
||||
for (i = 0; i < nvars; i++) { /* for each variable */
|
||||
expdesc var;
|
||||
TString *varname = getlocalvardesc(fs, lastidx - i)->vd.name;
|
||||
buildglobal(ls, varname, &var); /* create global variable in 'var' */
|
||||
storevartop(fs, &var);
|
||||
}
|
||||
}
|
||||
fs->nactvar = cast_short(fs->nactvar + nvars); /* activate declaration */
|
||||
}
|
||||
|
||||
|
||||
static void globalstat (LexState *ls) {
|
||||
/* globalstat -> (GLOBAL) attrib '*'
|
||||
globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */
|
||||
FuncState *fs = ls->fs;
|
||||
/* get prefixed attribute (if any); default is regular global variable */
|
||||
lu_byte defkind = getglobalattribute(ls, GDKREG);
|
||||
if (testnext(ls, '*')) {
|
||||
if (!testnext(ls, '*'))
|
||||
globalnames(ls, defkind);
|
||||
else {
|
||||
/* use NULL as name to represent '*' entries */
|
||||
new_varkind(ls, NULL, defkind);
|
||||
fs->nactvar++; /* activate declaration */
|
||||
}
|
||||
else {
|
||||
do { /* list of names */
|
||||
TString *vname = str_checkname(ls);
|
||||
lu_byte kind = getglobalattribute(ls, defkind);
|
||||
new_varkind(ls, vname, kind);
|
||||
fs->nactvar++; /* activate declaration */
|
||||
} while (testnext(ls, ','));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1850,7 +1885,7 @@ static void globalfunc (LexState *ls, int line) {
|
||||
TString *fname = str_checkname(ls);
|
||||
new_varkind(ls, fname, GDKREG); /* declare global variable */
|
||||
fs->nactvar++; /* enter its scope */
|
||||
buildvar(ls, fname, &var);
|
||||
buildglobal(ls, fname, &var);
|
||||
body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */
|
||||
luaK_storevar(fs, &var, &b);
|
||||
luaK_fixline(fs, line); /* definition "happens" in the first line */
|
||||
|
||||
@ -229,7 +229,7 @@ as the following example illustrates:
|
||||
@verbatim{
|
||||
X = 1 -- Ok, global by default
|
||||
do
|
||||
global Y -- voids implicit initial declaration
|
||||
global Y -- voids the implicit initial declaration
|
||||
Y = 1 -- Ok, Y declared as global
|
||||
X = 1 -- ERROR, X not declared
|
||||
end
|
||||
@ -269,7 +269,7 @@ print(x) --> 10 (the global one)
|
||||
|
||||
Notice that, in a declaration like @T{local x = x},
|
||||
the new @id{x} being declared is not in scope yet,
|
||||
and so the @id{x} in the left-hand side refers to the outside variable.
|
||||
and so the @id{x} in the right-hand side refers to the outside variable.
|
||||
|
||||
Because of the @x{lexical scoping} rules,
|
||||
local variables can be freely accessed by functions
|
||||
@ -1651,11 +1651,12 @@ Function calls are explained in @See{functioncall}.
|
||||
|
||||
@sect3{localvar| @title{Variable Declarations}
|
||||
Local and global variables can be declared anywhere inside a block.
|
||||
The declaration for locals can include an initialization:
|
||||
The declaration can include an initialization:
|
||||
@Produc{
|
||||
@producname{stat}@producbody{@Rw{local}
|
||||
attnamelist @bnfopt{@bnfter{=} explist}}
|
||||
@producname{stat}@producbody{@Rw{global} attnamelist}
|
||||
@producname{stat}@producbody{@Rw{global}
|
||||
attnamelist @bnfopt{@bnfter{=} explist}}
|
||||
}
|
||||
If present, an initial assignment has the same semantics
|
||||
of a multiple assignment @see{assignment}.
|
||||
@ -1712,7 +1713,8 @@ and a program that starts with any other global declaration
|
||||
(e.g., @T{global none}) can only refer to declared variables.
|
||||
|
||||
Note that, for global variables,
|
||||
the effect of any declaration is only syntactical:
|
||||
the effect of any declaration is only syntactical
|
||||
(except for the optional assignment):
|
||||
@verbatim{
|
||||
global X <const>, _G
|
||||
X = 1 -- ERROR
|
||||
@ -3924,8 +3926,8 @@ This macro may evaluate its arguments more than once.
|
||||
|
||||
}
|
||||
|
||||
@APIEntry{unsigned (lua_numbertocstring) (lua_State *L, int idx,
|
||||
char *buff);|
|
||||
@APIEntry{unsigned lua_numbertocstring (lua_State *L, int idx,
|
||||
char *buff);|
|
||||
@apii{0,0,-}
|
||||
|
||||
Converts the number at acceptable index @id{idx} to a string
|
||||
@ -4050,7 +4052,7 @@ This function is equivalent to @Lid{lua_pushcclosure} with no upvalues.
|
||||
|
||||
}
|
||||
|
||||
@APIEntry{const char *(lua_pushexternalstring) (lua_State *L,
|
||||
@APIEntry{const char *lua_pushexternalstring (lua_State *L,
|
||||
const char *s, size_t len, lua_Alloc falloc, void *ud);|
|
||||
@apii{0,1,m}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ local strsub = string.sub
|
||||
|
||||
local print = print
|
||||
|
||||
_ENV = nil
|
||||
global none
|
||||
|
||||
-- Try to convert a value to an integer, without assuming any coercion.
|
||||
local function toint (x)
|
||||
|
||||
@ -24,7 +24,7 @@ assert(not pcall(type))
|
||||
|
||||
|
||||
-- testing local-function recursion
|
||||
global fact; fact = false
|
||||
global fact = false
|
||||
do
|
||||
local res = 1
|
||||
local function fact (n)
|
||||
@ -65,7 +65,7 @@ a.b.c:f2('k', 12); assert(a.b.c.k == 12)
|
||||
|
||||
print('+')
|
||||
|
||||
global t; t = nil -- 'declare' t
|
||||
global t = nil -- 'declare' t
|
||||
function f(a,b,c) local d = 'a'; t={a,b,c,d} end
|
||||
|
||||
f( -- this line change must be valid
|
||||
|
||||
@ -715,7 +715,7 @@ do
|
||||
end
|
||||
|
||||
|
||||
if T and T.nonblock then
|
||||
if T and T.nonblock and not _port then
|
||||
print("testing failed write")
|
||||
|
||||
-- unable to write anything to /dev/full
|
||||
@ -840,7 +840,7 @@ assert(os.date("!\0\0") == "\0\0")
|
||||
local x = string.rep("a", 10000)
|
||||
assert(os.date(x) == x)
|
||||
local t = os.time()
|
||||
global D; D = os.date("*t", t)
|
||||
global D = os.date("*t", t)
|
||||
assert(os.date(string.rep("%d", 1000), t) ==
|
||||
string.rep(os.date("%d", t), 1000))
|
||||
assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
|
||||
|
||||
@ -380,7 +380,7 @@ do
|
||||
global *
|
||||
Y = x + Y
|
||||
assert(_ENV.Y == 20)
|
||||
|
||||
Y = nil
|
||||
end
|
||||
|
||||
|
||||
@ -411,5 +411,26 @@ do -- mixing lots of global/local declarations
|
||||
_ENV.x200 = nil
|
||||
end
|
||||
|
||||
do print "testing initialization in global declarations"
|
||||
global<const> a, b, c = 10, 20, 30
|
||||
assert(_ENV.a == 10 and b == 20 and c == 30)
|
||||
|
||||
global<const> a, b, c = 10
|
||||
assert(_ENV.a == 10 and b == nil and c == nil)
|
||||
|
||||
global table
|
||||
global a, b, c, d = table.unpack{1, 2, 3, 6, 5}
|
||||
assert(_ENV.a == 1 and b == 2 and c == 3 and d == 6)
|
||||
|
||||
local a, b = 100, 200
|
||||
do
|
||||
global a, b = a, b
|
||||
end
|
||||
assert(_ENV.a == 100 and _ENV.b == 200)
|
||||
|
||||
|
||||
_ENV.a, _ENV.b, _ENV.c, _ENV.d = nil -- erase these globals
|
||||
end
|
||||
|
||||
print'OK'
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ local M = {}
|
||||
local setmetatable, stderr, collectgarbage =
|
||||
setmetatable, io.stderr, collectgarbage
|
||||
|
||||
_ENV = nil
|
||||
global none
|
||||
|
||||
local active = false
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user