[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: defer patch
- From: pocomane <pocomane_7a@...>
- Date: Sat, 12 Oct 2019 15:55:59 +0200
I added a new annotation to the my previous patch to the "do" block:
<defer>. A block like:
```
do <defer>
-- ...
end
```
is equivalent to:
```
local _ <close> = defer(function()
-- ...
end)
```
except for the variable name. "defer" is a new global function equivalent to:
```
function defer(func)
return setmetatable({},{__close = func })
end
```
You can redefine it to change the behaviour of `do<defer>end`.
The result is that the content of the `do<defer>end` block will be run
at end of the scope.
I attached the whole patch that enables the other attributes too (they
are all listed in the README).
pocomane
PS: In the C code, I create an auxiliary <close> variable for each
defer block. Probably there are simpler methods, but i do not know the
lua internals that much.
diff -ruN lua-5.4.0/README lua-5.4.0-beta/README
--- lua-5.4.0/README 2019-10-10 08:33:36.535412785 -0400
+++ lua-5.4.0-beta/README 2019-10-10 08:30:55.627412785 -0400
@@ -4,3 +4,46 @@
For installation instructions, license details, and
further information about Lua, see doc/readme.html.
+###
+THIS IS A PATCHED VERSION
+###
+
+The patch lets you to mark a do/end block in this way:
+`do <attrib> --[[block-content]] end`
+
+The accepted attibutes are:
+
+- <defer> - the block is executed at end of the current scope instead of
+immediately
+- <withupvalue> - a free name inside this block raises an error instead
+of being translated to `__ENV.name`
+- <localonly> - an error is raised when accessing an upvalue too (so
+local variables only are allowed)
+- <autoglobal> - it overwrites <withupvalue> or <localonly> to make the block
+work again like the common do/end block
+
+NOTE:
+The body of a <defer> block works like the body of a function set as __close
+metatamethod of a <close> variable (i.e. the return statements are executed
+outside the current thread).
+
+NOTE:
+<defer> uses a new global function "defer" to work. It is equivalent to
+`local _--[[var-name]] <close> = defer(function() --[[block-content]] end)`.
+The user can change its behaviour by re-defining "defer".
+
+NOTE:
+<autoglobal>, <wihtupvalue> and <localonly> logic:
+- A non-marked do/end block inherits the behaviour from the parent block.
+- The main chunk starts in the <autoglobal> mode (as usual).
+
+NOTE:
+<autoglobal>, <wihtupvalue> and <localonly> rises error at compile time, so the
+performance should not be affected.
+
+NOTE:
+<autoglobal>, <wihtupvalue> and <localonly> are somehow "Compatible" with the
+regular lua: if you write a script that works as expected with the patch, you
+can remove any mark from the do/end blocks, and the result is valid for the
+regular lua too.
+
diff -ruN lua-5.4.0/src/lbaselib.c lua-5.4.0-beta/src/lbaselib.c
--- lua-5.4.0/src/lbaselib.c 2019-10-10 08:33:36.535412785 -0400
+++ lua-5.4.0-beta/src/lbaselib.c 2019-10-10 08:30:07.379412785 -0400
@@ -481,6 +481,22 @@
}
+
+static int luaB_defer (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_createtable(L, 0, 0);
+ lua_insert(L, 1);
+ lua_createtable(L, 0, 0);
+ lua_insert(L, 1);
+ /* stack: 1.tab.new 2.tab.new 3.func.arg */
+ lua_setfield(L, 2, "__close");
+ lua_setmetatable(L, 1);
+ lua_settop(L, 1);
+ return 1;
+}
+
+
+
static const luaL_Reg base_funcs[] = {
{"assert", luaB_assert},
{"collectgarbage", luaB_collectgarbage},
@@ -505,6 +521,7 @@
{"tostring", luaB_tostring},
{"type", luaB_type},
{"xpcall", luaB_xpcall},
+ {"defer", luaB_defer},
/* placeholders */
{LUA_GNAME, NULL},
{"_VERSION", NULL},
diff -ruN lua-5.4.0/src/llex.h lua-5.4.0-beta/src/llex.h
--- lua-5.4.0/src/llex.h 2019-10-10 08:33:36.535412785 -0400
+++ lua-5.4.0-beta/src/llex.h 2019-10-10 08:30:07.379412785 -0400
@@ -69,6 +69,7 @@
struct Dyndata *dyd; /* dynamic structures used by the parser */
TString *source; /* current source name */
TString *envn; /* environment variable name */
+ int compilemode; /* enable/disable complier options -- PATCH */
} LexState;
diff -ruN lua-5.4.0/src/lparser.c lua-5.4.0-beta/src/lparser.c
--- lua-5.4.0/src/lparser.c 2019-10-10 08:33:36.535412785 -0400
+++ lua-5.4.0-beta/src/lparser.c 2019-10-10 08:31:20.107412785 -0400
@@ -413,7 +413,7 @@
** this upvalue into all intermediate functions. If it is a global, set
** 'var' as 'void' as a flag.
*/
-static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
+static void singlevaraux (LexState *ls, FuncState *fs, TString *n, expdesc *var, int base) { /* PATCH */
if (fs == NULL) /* no more levels? */
init_exp(var, VVOID, 0); /* default is global */
else {
@@ -423,9 +423,10 @@
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
}
else { /* not found as local at current level; try upvalues */
+ if (ls->compilemode == DOATTRIB_LOCALONLY) luaX_syntaxerror(ls, "non local name in a protected block"); /* does not return - PATCH */
int idx = searchupvalue(fs, n); /* try existing upvalues */
if (idx < 0) { /* not found? */
- singlevaraux(fs->prev, n, var, 0); /* try upper levels */
+ singlevaraux(ls, fs->prev, n, var, 0); /* try upper levels - do_attrib_patch */
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 */
@@ -437,23 +438,29 @@
}
-/*
-** 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 findvariable (LexState *ls, TString *varname, expdesc *var) {
FuncState *fs = ls->fs;
- singlevaraux(fs, varname, var, 1);
+ singlevaraux(ls, fs, varname, var, 1); /* PATCH */
if (var->k == VVOID) { /* global name? */
+ if (ls->compilemode != DOATTRIB_AUTOGLOBAL) luaX_syntaxerror(ls, "free name found in a protected block"); /* does not return - PATCH */
expdesc key;
- singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
+ singlevaraux(ls, fs, ls->envn, var, 1); /* get environment variable */
lua_assert(var->k != VVOID); /* this one must exist */
codestring(&key, varname); /* key is variable name */
luaK_indexed(fs, var, &key); /* env[varname] */
}
}
+/*
+** Find a variable with the given name 'n', handling global variables
+** too.
+*/
+static void singlevar (LexState *ls, expdesc *var) {
+ TString *varname = str_checkname(ls);
+ return findvariable(ls, varname, var); /* PATCH */
+}
+
/*
** Adjust the number of results from an expression list 'e' with 'nexps'
@@ -1723,7 +1730,7 @@
checknext(ls, '>');
if (strcmp(attr, "const") == 0)
return RDKCONST; /* read-only variable */
- else if (strcmp(attr, "close") == 0)
+ else if (strcmp(attr, "close") == 0) /* PATCH */
return RDKTOCLOSE; /* to-be-closed variable */
else
luaK_semerror(ls,
@@ -1733,6 +1740,28 @@
}
+static int getdoattribute (LexState *ls) { /* PATCH */
+ /* ATTRIB -> ['<' Name '>'] */
+ if (testnext(ls, '<')) {
+ const char *attr = getstr(str_checkname(ls));
+ checknext(ls, '>');
+ if (0); /* PATCH */
+ else if (strcmp(attr, "autoglobal") == 0) /* PATCH */
+ return DOATTRIB_AUTOGLOBAL; /* default do/end mode, allow upvalues and globals - PATCH */
+ else if (strcmp(attr, "withupvalue") == 0) /* PATCH */
+ return DOATTRIB_WITHUPVALUE; /* forbid globals - PATCH */
+ else if (strcmp(attr, "localonly") == 0) /* PATCH */
+ return DOATTRIB_LOCALONLY; /* forbid upvales and globals - PATCH */
+ else if (strcmp(attr, "defer") == 0) /* PATCH */
+ return DOATTRIB_DEFER; /* evaluate at end of the scope - PATCH */
+ else
+ luaK_semerror(ls,
+ luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); /* PATCH */
+ } /* PATCH */
+ return VDKREG; /* PATCH */
+} /* PATCH */
+
+
static void checktoclose (LexState *ls, int level) {
if (level != -1) { /* is there a to-be-closed variable? */
FuncState *fs = ls->fs;
@@ -1755,6 +1784,7 @@
do {
ivar = new_localvar(ls, str_checkname(ls));
kind = getlocalattribute(ls);
+ if (kind != RDKCONST && kind != RDKTOCLOSE && kind != VDKREG) luaX_syntaxerror(ls, "attribute not allowed for a local variable declaration"); /* does not return - PATCH */
getlocalvardesc(fs, ivar)->vd.kind = kind;
if (kind == RDKTOCLOSE) { /* to-be-closed? */
if (toclose != -1) /* one already present? */
@@ -1860,7 +1890,47 @@
}
+
+/* PATCH */
+static void adddeferblock(LexState *ls) {
+
+ /* Get defer function from the env */
+ TString * varname = luaX_newstring(ls, "defer",strlen("defer"));
+ expdesc defer;
+ findvariable(ls, varname, &defer);
+ luaK_exp2anyreg(ls->fs,&defer);
+
+ /* Create a function to pass to defer */
+ expdesc b;
+ FuncState new_fs;
+ BlockCnt bl;
+ new_fs.f = addprototype(ls);
+ new_fs.f->linedefined = ls->linenumber;
+ open_func(ls, &new_fs, &bl);
+ statlist(ls);
+ new_fs.f->lastlinedefined = ls->linenumber;
+ check_match(ls, TK_END, TK_FUNCTION, ls->linenumber);
+ codeclosure(ls, &b);
+ close_func(ls);
+ luaK_fixline(ls->fs, ls->linenumber);
+
+ /* Call defer function */
+ luaK_codeABC(ls->fs, OP_CALL, defer.u.info, 2, 2);
+
+ /* Lock the register allocating new local variables */
+ int varreg = ls->fs->nactvar;
+ const char auxnm[] = "(close-aux)";
+ new_localvar(ls, luaX_newstring(ls, auxnm, sizeof(auxnm)-1));
+ adjustlocalvars(ls, 1);
+ markupval(ls->fs, luaY_nvarstack(ls->fs));
+
+ // Generate To-Be-Closed handling opcode
+ luaK_codeABC(ls->fs, OP_TBC, varreg, 0, 0);
+}
+
+
static void statement (LexState *ls) {
+ int oldcompilemode; /* PATCH */
int line = ls->linenumber; /* may be needed for error messages */
enterlevel(ls);
switch (ls->t.token) {
@@ -1878,8 +1948,17 @@
}
case TK_DO: { /* stat -> DO block END */
luaX_next(ls); /* skip DO */
- block(ls);
- check_match(ls, TK_END, TK_DO, line);
+ oldcompilemode = ls->compilemode; /* PATCH */
+ int kind = getdoattribute(ls); /* PATCH */
+ if (kind == DOATTRIB_DEFER) { /* PATCH */
+ adddeferblock(ls); /* PATCH */
+ } else { /* PATCH */
+ ls->compilemode = kind; /* PATCH */
+ if (kind == VDKREG) ls->compilemode = oldcompilemode; /* PATCH */
+ block(ls);
+ ls->compilemode = oldcompilemode; /* PATCH */
+ check_match(ls, TK_END, TK_DO, line);
+ } /* PATCH */
break;
}
case TK_FOR: { /* stat -> forstat */
@@ -1959,6 +2038,7 @@
LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar) {
LexState lexstate;
+ lexstate.compilemode = DOATTRIB_AUTOGLOBAL; /* default mode for the main chunk - PATCH */
FuncState funcstate;
LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
setclLvalue2s(L, L->top, cl); /* anchor it (to avoid being collected) */
diff -ruN lua-5.4.0/src/lparser.h lua-5.4.0-beta/src/lparser.h
--- lua-5.4.0/src/lparser.h 2019-10-10 08:33:36.535412785 -0400
+++ lua-5.4.0-beta/src/lparser.h 2019-10-10 08:30:07.383412785 -0400
@@ -90,6 +90,10 @@
#define RDKCONST 1 /* constant */
#define RDKTOCLOSE 2 /* to-be-closed */
#define RDKCTC 3 /* compile-time constant */
+#define DOATTRIB_LOCALONLY 601 /* not really a variable - PATCH */
+#define DOATTRIB_WITHUPVALUE 602 /* not really a variable - PATCH */
+#define DOATTRIB_AUTOGLOBAL 603 /* not really a variable - PATCH */
+#define DOATTRIB_DEFER 604 /* not really a variable - PATCH */
/* description of an active local variable */
typedef union Vardesc {