Collective declaration for globals ('global *')
This commit is contained in:
49
lparser.c
49
lparser.c
@@ -405,7 +405,12 @@ static int searchvar (FuncState *fs, TString *n, expdesc *var) {
|
||||
int i;
|
||||
for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
|
||||
Vardesc *vd = getlocalvardesc(fs, i);
|
||||
if (eqstr(n, vd->vd.name)) { /* found? */
|
||||
if (vd->vd.name == NULL) { /* 'global *'? */
|
||||
if (var->u.info == -1) { /* no previous collective declaration? */
|
||||
var->u.info = fs->firstlocal + i; /* will use this one as default */
|
||||
}
|
||||
}
|
||||
else if (eqstr(n, vd->vd.name)) { /* found? */
|
||||
if (vd->vd.kind == RDKCTC) /* compile-time constant? */
|
||||
init_exp(var, VCONST, fs->firstlocal + i);
|
||||
else if (vd->vd.kind == GDKREG || vd->vd.kind == GDKCONST)
|
||||
@@ -449,18 +454,16 @@ static void marktobeclosed (FuncState *fs) {
|
||||
** 'var' as 'void' as a flag.
|
||||
*/
|
||||
static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
||||
int v = searchvar(fs, n, var); /* look up locals at current level */
|
||||
int v = searchvar(fs, n, var); /* look up variables at current level */
|
||||
if (v >= 0) { /* found? */
|
||||
if (v == VLOCAL && !base)
|
||||
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
|
||||
}
|
||||
else { /* not found as local at current level; try upvalues */
|
||||
else { /* not found at current level; try upvalues */
|
||||
int idx = searchupvalue(fs, n); /* try existing upvalues */
|
||||
if (idx < 0) { /* not found? */
|
||||
if (fs->prev != NULL) /* more levels? */
|
||||
singlevaraux(fs->prev, n, var, 0); /* try upper levels */
|
||||
else /* no more levels */
|
||||
init_exp(var, VGLOBAL, -1); /* global by default */
|
||||
if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
|
||||
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
||||
else /* it is a global or a constant */
|
||||
@@ -477,6 +480,7 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
||||
*/
|
||||
static void buildvar (LexState *ls, TString *varname, expdesc *var) {
|
||||
FuncState *fs = ls->fs;
|
||||
init_exp(var, VGLOBAL, -1); /* global by default */
|
||||
singlevaraux(fs, varname, var, 1);
|
||||
if (var->k == VGLOBAL) { /* global name? */
|
||||
expdesc key;
|
||||
@@ -1796,20 +1800,33 @@ static void localstat (LexState *ls) {
|
||||
}
|
||||
|
||||
|
||||
static void globalstat (LexState *ls) {
|
||||
/* globalstat -> (GLOBAL) NAME attrib {',' NAME attrib} */
|
||||
FuncState *fs = ls->fs;
|
||||
do {
|
||||
TString *vname = str_checkname(ls);
|
||||
static lu_byte getglobalattribute (LexState *ls) {
|
||||
lu_byte kind = getvarattribute(ls);
|
||||
if (kind == RDKTOCLOSE)
|
||||
luaK_semerror(ls, "global variable ('%s') cannot be to-be-closed",
|
||||
getstr(vname));
|
||||
luaK_semerror(ls, "global variables cannot be to-be-closed");
|
||||
/* adjust kind for global variable */
|
||||
kind = (kind == VDKREG) ? GDKREG : GDKCONST;
|
||||
return (kind == VDKREG) ? GDKREG : GDKCONST;
|
||||
}
|
||||
|
||||
|
||||
static void globalstat (LexState *ls) {
|
||||
/* globalstat -> (GLOBAL) '*' attrib
|
||||
globalstat -> (GLOBAL) NAME attrib {',' NAME attrib} */
|
||||
FuncState *fs = ls->fs;
|
||||
if (testnext(ls, '*')) {
|
||||
lu_byte kind = getglobalattribute(ls);
|
||||
/* use NULL as name to represent '*' entries */
|
||||
new_varkind(ls, NULL, kind);
|
||||
fs->nactvar++; /* activate declaration */
|
||||
}
|
||||
else {
|
||||
do {
|
||||
TString *vname = str_checkname(ls);
|
||||
lu_byte kind = getglobalattribute(ls);
|
||||
new_varkind(ls, vname, kind);
|
||||
fs->nactvar++; /* activate declaration */
|
||||
} while (testnext(ls, ','));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1983,10 +2000,10 @@ static void statement (LexState *ls) {
|
||||
case TK_NAME: {
|
||||
/* compatibility code to parse global keyword when "global"
|
||||
is not reserved */
|
||||
if (eqstr(ls->t.seminfo.ts, luaS_newliteral(ls->L, "global"))) {
|
||||
if (strcmp(getstr(ls->t.seminfo.ts), "global") == 0) {
|
||||
int lk = luaX_lookahead(ls);
|
||||
if (lk == TK_NAME || lk == TK_FUNCTION) {
|
||||
/* 'global <name>' or 'global function' */
|
||||
if (lk == TK_NAME || lk == '*' || lk == TK_FUNCTION) {
|
||||
/* 'global <name>' or 'global *' or 'global function' */
|
||||
globalstatfunc(ls, line);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -223,14 +223,15 @@ a function's formal parameter is equivalent to a local variable.)
|
||||
|
||||
All chunks start with an implicit declaration @T{global *},
|
||||
which declares all free names as global variables;
|
||||
this implicit declaration becomes void inside the scope of any other
|
||||
@Rw{global} declaration, regardless of the names being declared.
|
||||
this preambular declaration becomes void inside the scope of any other
|
||||
@Rw{global} declaration,
|
||||
as the following example illustrates:
|
||||
@verbatim{
|
||||
X = 1 -- Ok, global by default
|
||||
do
|
||||
global Y -- voids implicit initial declaration
|
||||
X = 1 -- ERROR, X not declared
|
||||
Y = 1 -- Ok, Y declared as global
|
||||
X = 1 -- ERROR, X not declared
|
||||
end
|
||||
X = 2 -- Ok, global by default again
|
||||
}
|
||||
@@ -1110,9 +1111,9 @@ and cannot be used as names:
|
||||
@index{reserved words}
|
||||
@verbatim{
|
||||
and break do else elseif end
|
||||
false for function goto if in
|
||||
local nil not or repeat return
|
||||
then true until while
|
||||
false for function global goto if
|
||||
in local nil not or repeat
|
||||
return then true until while
|
||||
}
|
||||
|
||||
Lua is a case-sensitive language:
|
||||
@@ -1653,7 +1654,8 @@ The declaration for locals can include an initialization:
|
||||
@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}}
|
||||
@producname{stat}@producbody{@Rw{global} attnamelist}
|
||||
@producname{attnamelist}@producbody{
|
||||
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
|
||||
@bnfNter{Name} @bnfopt{attrib}
|
||||
@bnfrep{@bnfter{,} @bnfNter{Name} @bnfopt{attrib}}}
|
||||
}
|
||||
If present, an initial assignment has the same semantics
|
||||
of a multiple assignment @see{assignment}.
|
||||
@@ -1662,24 +1664,55 @@ Otherwise, all local variables are initialized with @nil.
|
||||
Each variable name may be postfixed by an attribute
|
||||
(a name between angle brackets):
|
||||
@Produc{
|
||||
@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
|
||||
@producname{attrib}@producbody{@bnfter{<} @bnfNter{Name} @bnfter{>}}
|
||||
}
|
||||
There are two possible attributes:
|
||||
@id{const}, which declares a @emph{constant} or @emph{read-only} variable,
|
||||
@index{constant variable}
|
||||
that is, a variable that cannot be assigned to
|
||||
after its initialization;
|
||||
that is, a variable that cannot be used as the left-hand side of an
|
||||
assignment,
|
||||
and @id{close}, which declares a to-be-closed variable @see{to-be-closed}.
|
||||
A list of variables can contain at most one to-be-closed variable.
|
||||
Only local variables can have the @id{close} attribute.
|
||||
|
||||
Note that, for global variables,
|
||||
the @emph{read-only} atribute is only a syntactical restriction:
|
||||
Lua offers also a collective declaration for global variables:
|
||||
@Produc{
|
||||
@producname{stat}@producbody{@Rw{global} @bnfter{*} @bnfopt{attrib}}
|
||||
}
|
||||
This special form implicitly declares
|
||||
as globals all names not explicitly declared previously.
|
||||
In particular,
|
||||
@T{global * <const>} implicitly declares
|
||||
as read-only globals all names not explicitly declared previously;
|
||||
see the following example:
|
||||
@verbatim{
|
||||
global X <const>
|
||||
global X
|
||||
global * <const>
|
||||
print(math.pi) -- Ok, 'print' and 'math' are read-only
|
||||
X = 1 -- Ok, declared as read-write
|
||||
Y = 1 -- Error, Y is read-only
|
||||
}
|
||||
|
||||
As noted in @See{globalenv},
|
||||
all chunks start with an implicit declaration @T{global *},
|
||||
but this preambular declaration becomes void inside
|
||||
the scope of any other @Rw{global} declaration.
|
||||
Therefore, a program that does not use global declarations
|
||||
or start with @T{global *}
|
||||
has free read-write access to any global;
|
||||
a program that starts with @T{global * <const>}
|
||||
has free read-only access to any global;
|
||||
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:
|
||||
@verbatim{
|
||||
global X <const>, _G
|
||||
X = 1 -- ERROR
|
||||
_ENV.X = 1 -- Ok
|
||||
foo() -- 'foo' can freely change the global X
|
||||
_G.print(X) -- Ok
|
||||
foo() -- 'foo' can freely change any global
|
||||
}
|
||||
|
||||
A chunk is also a block @see{chunks},
|
||||
@@ -9453,7 +9486,12 @@ change between versions.
|
||||
@itemize{
|
||||
|
||||
@item{
|
||||
The control variable in @Rw{for} loops are read only.
|
||||
The word @Rw{global} is a reserved word.
|
||||
Do not use it as a regular name.
|
||||
}
|
||||
|
||||
@item{
|
||||
The control variable in @Rw{for} loops is read only.
|
||||
If you need to change it,
|
||||
declare a local variable with the same name in the loop body.
|
||||
}
|
||||
@@ -9582,12 +9620,14 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
|
||||
@OrNL @Rw{global} @Rw{function} @bnfNter{Name} funcbody
|
||||
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
|
||||
@OrNL @Rw{global} attnamelist
|
||||
@OrNL @Rw{global} @bnfter{*} @bnfopt{attrib}
|
||||
}
|
||||
|
||||
@producname{attnamelist}@producbody{
|
||||
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
|
||||
@bnfNter{Name} @bnfopt{attrib}
|
||||
@bnfrep{@bnfter{,} @bnfNter{Name} @bnfopt{attrib}}}
|
||||
|
||||
@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
|
||||
@producname{attrib}@producbody{@bnfter{<} @bnfNter{Name} @bnfter{>}}
|
||||
|
||||
@producname{retstat}@producbody{@Rw{return}
|
||||
@bnfopt{explist} @bnfopt{@bnfter{;}}}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
-- $Id: testes/all.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global * <const>
|
||||
|
||||
global _soft, _port, _nomsg
|
||||
global T
|
||||
|
||||
local version = "Lua 5.5"
|
||||
if _VERSION ~= version then
|
||||
@@ -34,7 +38,7 @@ if usertests then
|
||||
end
|
||||
|
||||
-- tests should require debug when needed
|
||||
debug = nil
|
||||
global debug; debug = nil
|
||||
|
||||
|
||||
if usertests then
|
||||
@@ -71,7 +75,7 @@ do -- (
|
||||
|
||||
-- track messages for tests not performed
|
||||
local msgs = {}
|
||||
function Message (m)
|
||||
global function Message (m)
|
||||
if not _nomsg then
|
||||
print(m)
|
||||
msgs[#msgs+1] = string.sub(m, 3, -3)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
-- $Id: testes/calls.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global * <const>
|
||||
|
||||
print("testing functions and calls")
|
||||
|
||||
local debug = require "debug"
|
||||
@@ -22,7 +24,7 @@ assert(not pcall(type))
|
||||
|
||||
|
||||
-- testing local-function recursion
|
||||
fact = false
|
||||
global fact; fact = false
|
||||
do
|
||||
local res = 1
|
||||
local function fact (n)
|
||||
@@ -63,7 +65,7 @@ a.b.c:f2('k', 12); assert(a.b.c.k == 12)
|
||||
|
||||
print('+')
|
||||
|
||||
t = nil -- 'declare' t
|
||||
global t; 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
|
||||
@@ -75,7 +77,7 @@ assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a')
|
||||
|
||||
t = nil -- delete 't'
|
||||
|
||||
function fat(x)
|
||||
global function fat(x)
|
||||
if x <= 1 then return 1
|
||||
else return x*load("return fat(" .. x-1 .. ")", "")()
|
||||
end
|
||||
@@ -107,7 +109,7 @@ end
|
||||
|
||||
_G.deep = nil -- "declaration" (used by 'all.lua')
|
||||
|
||||
function deep (n)
|
||||
global function deep (n)
|
||||
if n>0 then deep(n-1) end
|
||||
end
|
||||
deep(10)
|
||||
@@ -352,7 +354,7 @@ assert(not load(function () return true end))
|
||||
|
||||
-- small bug
|
||||
local t = {nil, "return ", "3"}
|
||||
f, msg = load(function () return table.remove(t, 1) end)
|
||||
local f, msg = load(function () return table.remove(t, 1) end)
|
||||
assert(f() == nil) -- should read the empty chunk
|
||||
|
||||
-- another small bug (in 5.2.1)
|
||||
@@ -388,7 +390,8 @@ assert(load("return _ENV", nil, nil, 123)() == 123)
|
||||
|
||||
|
||||
-- load when _ENV is not first upvalue
|
||||
local x; XX = 123
|
||||
global XX; local x
|
||||
XX = 123
|
||||
local function h ()
|
||||
local y=x -- use 'x', so that it becomes 1st upvalue
|
||||
return XX -- global name
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
-- $Id: testes/closure.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global * <const>
|
||||
|
||||
print "testing closures"
|
||||
|
||||
do -- bug in 5.4.7
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
-- $Id: testes/code.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global * <const>
|
||||
|
||||
if T==nil then
|
||||
(Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')
|
||||
return
|
||||
@@ -405,8 +407,8 @@ do -- tests for table access in upvalues
|
||||
end
|
||||
|
||||
-- de morgan
|
||||
checkequal(function () local a; if not (a or b) then b=a end end,
|
||||
function () local a; if (not a and not b) then b=a end end)
|
||||
checkequal(function () local a, b; if not (a or b) then b=a end end,
|
||||
function () local a, b; if (not a and not b) then b=a end end)
|
||||
|
||||
checkequal(function (l) local a; return 0 <= a and a <= l end,
|
||||
function (l) local a; return not (not(a >= 0) or not(a <= l)) end)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
-- $Id: testes/files.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global * <const>
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
local maxint = math.maxinteger
|
||||
@@ -838,13 +840,13 @@ assert(os.date("!\0\0") == "\0\0")
|
||||
local x = string.rep("a", 10000)
|
||||
assert(os.date(x) == x)
|
||||
local t = os.time()
|
||||
D = os.date("*t", t)
|
||||
global D; 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))
|
||||
|
||||
local function checkDateTable (t)
|
||||
_G.D = os.date("*t", t)
|
||||
D = os.date("*t", t)
|
||||
assert(os.time(D) == t)
|
||||
load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and
|
||||
D.hour==%H and D.min==%M and D.sec==%S and
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
-- $Id: testes/goto.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global require
|
||||
global print, load, assert, string, setmetatable
|
||||
global collectgarbage, error
|
||||
|
||||
print("testing goto and global declarations")
|
||||
|
||||
collectgarbage()
|
||||
@@ -254,6 +258,8 @@ assert(testG(5) == 10)
|
||||
|
||||
do -- test goto's around to-be-closed variable
|
||||
|
||||
global *
|
||||
|
||||
-- set 'var' and return an object that will reset 'var' when
|
||||
-- it goes out of scope
|
||||
local function newobj (var)
|
||||
@@ -265,16 +271,16 @@ do -- test goto's around to-be-closed variable
|
||||
|
||||
goto L1
|
||||
|
||||
::L4:: assert(not X); goto L5 -- varX dead here
|
||||
::L4:: assert(not varX); goto L5 -- varX dead here
|
||||
|
||||
::L1::
|
||||
local varX <close> = newobj("X")
|
||||
assert(X); goto L2 -- varX alive here
|
||||
assert(varX); goto L2 -- varX alive here
|
||||
|
||||
::L3::
|
||||
assert(X); goto L4 -- varX alive here
|
||||
assert(varX); goto L4 -- varX alive here
|
||||
|
||||
::L2:: assert(X); goto L3 -- varX alive here
|
||||
::L2:: assert(varX); goto L3 -- varX alive here
|
||||
|
||||
::L5:: -- return
|
||||
end
|
||||
@@ -285,8 +291,7 @@ foo()
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
do
|
||||
global print, load, T<const>; global assert<const>
|
||||
global string
|
||||
global T<const>
|
||||
|
||||
local function checkerr (code, err)
|
||||
local st, msg = load(code)
|
||||
@@ -299,6 +304,7 @@ do
|
||||
|
||||
-- global variables cannot be to-be-closed
|
||||
checkerr("global X<close>", "cannot be")
|
||||
checkerr("global * <close>", "cannot be")
|
||||
|
||||
do
|
||||
local X = 10
|
||||
@@ -350,6 +356,12 @@ do
|
||||
end
|
||||
]], "%:2%:") -- correct line in error message
|
||||
|
||||
checkerr([[
|
||||
global * <const>;
|
||||
print(X) -- Ok to use
|
||||
Y = 1 -- ERROR
|
||||
]], "assign to const variable 'Y'")
|
||||
|
||||
end
|
||||
|
||||
print'OK'
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
print('testing scanner')
|
||||
|
||||
global * <const>
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
-- $Id: testes/locals.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global * <const>
|
||||
|
||||
print('testing local variables and environments')
|
||||
|
||||
local debug = require"debug"
|
||||
@@ -39,9 +41,11 @@ f = nil
|
||||
local f
|
||||
local x = 1
|
||||
|
||||
a = nil
|
||||
load('local a = {}')()
|
||||
assert(a == nil)
|
||||
do
|
||||
global a; a = nil
|
||||
load('local a = {}')()
|
||||
assert(a == nil)
|
||||
end
|
||||
|
||||
function f (a)
|
||||
local _1, _2, _3, _4, _5
|
||||
@@ -154,7 +158,7 @@ local _ENV = (function (...) return ... end)(_G, dummy) -- {
|
||||
do local _ENV = {assert=assert}; assert(true) end
|
||||
local mt = {_G = _G}
|
||||
local foo,x
|
||||
A = false -- "declare" A
|
||||
global A; A = false -- "declare" A
|
||||
do local _ENV = mt
|
||||
function foo (x)
|
||||
A = x
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
-- $Id: testes/nextvar.lua $
|
||||
-- See Copyright Notice in file lua.h
|
||||
|
||||
global * <const>
|
||||
|
||||
print('testing tables, next, and for')
|
||||
|
||||
local function checkerror (msg, f, ...)
|
||||
@@ -345,9 +347,6 @@ end
|
||||
|
||||
local nofind = {}
|
||||
|
||||
a,b,c = 1,2,3
|
||||
a,b,c = nil
|
||||
|
||||
|
||||
-- next uses always the same iteration function
|
||||
assert(next{} == next{})
|
||||
@@ -396,7 +395,7 @@ for i=0,10000 do
|
||||
end
|
||||
end
|
||||
|
||||
n = {n=0}
|
||||
local n = {n=0}
|
||||
for i,v in pairs(a) do
|
||||
n.n = n.n+1
|
||||
assert(i and v and a[i] == v)
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
print('testing pattern matching')
|
||||
|
||||
global * <const>
|
||||
|
||||
local function checkerror (msg, f, ...)
|
||||
local s, err = pcall(f, ...)
|
||||
assert(not s and string.find(err, msg))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
-- ISO Latin encoding
|
||||
|
||||
global * <const>
|
||||
|
||||
print('testing strings and string library')
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
-- UTF-8 file
|
||||
|
||||
global * <const>
|
||||
|
||||
print "testing UTF-8 library"
|
||||
|
||||
local utf8 = require'utf8'
|
||||
|
||||
Reference in New Issue
Block a user