Optional initialization for global declarations

This commit is contained in:
Roberto Ierusalimschy 2025-07-08 13:33:57 -03:00
parent 8485687908
commit 942c10a5e3
7 changed files with 96 additions and 38 deletions

View File

@ -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 */

View File

@ -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}

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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'

View File

@ -6,7 +6,7 @@ local M = {}
local setmetatable, stderr, collectgarbage =
setmetatable, io.stderr, collectgarbage
_ENV = nil
global none
local active = false