lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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 {