From 3f0ea90aa8b8493485637f6e8d2a070a1ac0d5cb Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 8 May 2025 11:08:03 -0300 Subject: New syntax 'global function' --- lparser.c | 49 +++++++++++++++++++++++++++++++++++++++---------- manual/manual.of | 15 +++++++++++++-- testes/errors.lua | 8 ++++++++ testes/goto.lua | 23 ++++++++++++++++++++++- 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/lparser.c b/lparser.c index 61ce0908..a11f1dd3 100644 --- a/lparser.c +++ b/lparser.c @@ -477,8 +477,7 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { ** Find a variable with the given name 'n', handling global variables ** too. */ -static void singlevar (LexState *ls, expdesc *var) { - TString *varname = str_checkname(ls); +static void buildvar (LexState *ls, TString *varname, expdesc *var) { FuncState *fs = ls->fs; singlevaraux(fs, varname, var, 1); if (var->k == VGLOBAL) { /* global name? */ @@ -501,6 +500,11 @@ static void singlevar (LexState *ls, expdesc *var) { } +static void singlevar (LexState *ls, expdesc *var) { + buildvar(ls, str_checkname(ls), var); +} + + /* ** Adjust the number of results from an expression list 'e' with 'nexps' ** expressions to 'nvars' values. @@ -1727,7 +1731,7 @@ static void localfunc (LexState *ls) { static lu_byte getvarattribute (LexState *ls) { - /* ATTRIB -> ['<' Name '>'] */ + /* attrib -> ['<' NAME '>'] */ if (testnext(ls, '<')) { TString *ts = str_checkname(ls); const char *attr = getstr(ts); @@ -1752,7 +1756,7 @@ static void checktoclose (FuncState *fs, int level) { static void localstat (LexState *ls) { - /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */ + /* stat -> LOCAL NAME attrib { ',' NAME attrib } ['=' explist] */ FuncState *fs = ls->fs; int toclose = -1; /* index of to-be-closed variable (if any) */ Vardesc *var; /* last variable */ @@ -1794,8 +1798,8 @@ static void localstat (LexState *ls) { static void globalstat (LexState *ls) { + /* globalstat -> (GLOBAL) NAME attrib {',' NAME attrib} */ FuncState *fs = ls->fs; - luaX_next(ls); /* skip 'global' */ do { TString *vname = str_checkname(ls); lu_byte kind = getvarattribute(ls); @@ -1807,7 +1811,31 @@ static void globalstat (LexState *ls) { new_varkind(ls, vname, kind); fs->nactvar++; /* activate declaration */ } while (testnext(ls, ',')); - fs->bl->globdec = 1; /* code is in the scope of a global declaration */ +} + + +static void globalfunc (LexState *ls, int line) { + /* globalfunc -> (GLOBAL FUNCTION) NAME body */ + expdesc var, b; + FuncState *fs = ls->fs; + TString *fname = str_checkname(ls); + new_varkind(ls, fname, GDKREG); /* declare global variable */ + fs->nactvar++; /* enter its scope */ + buildvar(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 */ +} + + +static void globalstatfunc (LexState *ls, int line) { + /* stat -> GLOBAL globalfunc | GLOBAL globalstat */ + luaX_next(ls); /* skip 'global' */ + ls->fs->bl->globdec = 1; /* in the scope of a global declaration */ + if (testnext(ls, TK_FUNCTION)) + globalfunc(ls, line); + else + globalstat(ls); } @@ -1930,8 +1958,8 @@ static void statement (LexState *ls) { localstat(ls); break; } - case TK_GLOBAL: { /* stat -> globalstat */ - globalstat(ls); + case TK_GLOBAL: { /* stat -> globalstatfunc */ + globalstatfunc(ls, line); break; } case TK_DBCOLON: { /* stat -> label */ @@ -1958,8 +1986,9 @@ static void statement (LexState *ls) { is not reserved */ if (eqstr(ls->t.seminfo.ts, luaS_newliteral(ls->L, "global"))) { int lk = luaX_lookahead(ls); - if (lk == TK_NAME) { /* 'global name'? */ - globalstat(ls); + if (lk == TK_NAME || lk == TK_FUNCTION) { + /* 'global ' or 'global function' */ + globalstatfunc(ls, line); break; } } /* else... */ diff --git a/manual/manual.of b/manual/manual.of index ace5d375..cc71aaad 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -2229,6 +2229,7 @@ The following syntactic sugar simplifies function definitions: @Produc{ @producname{stat}@producbody{@Rw{function} funcname funcbody} @producname{stat}@producbody{@Rw{local} @Rw{function} @bnfNter{Name} funcbody} +@producname{stat}@producbody{@Rw{global} @Rw{function} @bnfNter{Name} funcbody} @producname{funcname}@producbody{@bnfNter{Name} @bnfrep{@bnfter{.} @bnfNter{Name}} @bnfopt{@bnfter{:} @bnfNter{Name}}} } The statement @@ -2247,6 +2248,7 @@ translates to @verbatim{ t.a.b.c.f = function () @rep{body} end } + The statement @verbatim{ local function f () @rep{body} end @@ -2260,7 +2262,15 @@ not to local f = function () @rep{body} end } (This only makes a difference when the body of the function -contains references to @id{f}.) +contains recursive references to @id{f}.) +Similarly, the statement +@verbatim{ +global function f () @rep{body} end +} +translates to +@verbatim{ +global f; f = function () @rep{body} end +} A function definition is an executable expression, whose value has type @emph{function}. @@ -2323,7 +2333,7 @@ then the function returns with no results. @index{multiple return} There is a system-dependent limit on the number of values that a function may return. -This limit is guaranteed to be greater than 1000. +This limit is guaranteed to be at least 1000. The @emphx{colon} syntax is used to emulate @def{methods}, @@ -9569,6 +9579,7 @@ and @bnfNter{LiteralString}, see @See{lexical}.) @OrNL @Rw{for} namelist @Rw{in} explist @Rw{do} block @Rw{end} @OrNL @Rw{function} funcname funcbody @OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody +@OrNL @Rw{global} @Rw{function} @bnfNter{Name} funcbody @OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist} @OrNL @Rw{global} attnamelist } diff --git a/testes/errors.lua b/testes/errors.lua index c80051fc..6c76a99a 100644 --- a/testes/errors.lua +++ b/testes/errors.lua @@ -489,6 +489,14 @@ if not b then end end]], 5) +lineerror([[ +_ENV = 1 +global function foo () + local a = 10 + return a +end +]], 2) + -- bug in 5.4.0 lineerror([[ diff --git a/testes/goto.lua b/testes/goto.lua index fdfddb85..b41399ff 100644 --- a/testes/goto.lua +++ b/testes/goto.lua @@ -293,8 +293,9 @@ do assert(not st and string.find(msg, err)) end - -- globals must be declared after a global declaration + -- globals must be declared, after a global declaration checkerr("global none; X = 1", "variable 'X'") + checkerr("global none; function XX() end", "variable 'XX'") -- global variables cannot be to-be-closed checkerr("global X", "cannot be") @@ -321,6 +322,26 @@ do -- "global" reserved, cannot be used as a variable assert(not load("global = 1; return global")) end + + local foo = 20 + do + global function foo (x) + if x == 0 then return 1 else return 2 * foo(x - 1) end + end + assert(foo == _ENV.foo and foo(4) == 16) + end + assert(_ENV.foo(4) == 16) + assert(foo == 20) -- local one is in context here + + do + global foo; + function foo (x) return end -- Ok after declaration + end + + checkerr([[ + global foo ; + function foo (x) return end -- ERROR: foo is read-only + ]], "assign to const variable 'foo'") end -- cgit v1.2.3-55-g6feb