lua-users home
lua-l archive

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


Brief summary in case you haven't been following along. I'm hacking Lua to 
accept 'nil' instead of a variable name in an assignment. Like the way 'undef' 
is used in Perl. It lets you drop values from a list without using a dummy 
variable.

I've reached a stable point in the patch. Everything (seems to) work as 
expected.

1. nil as an lvalue in regular assignment statements. This does what you'd 
expect, it skips the corresponding rvalue without creating an unnecessary 
local variable as assigning to '_' would.

2. nil as an lvalue in local statements. A local statement without 'nil' acts 
exactly the same as before. When there is a 'nil', it acts like a local 
declaration followed by an assignment statement. i.e.

    local a,nil,b = 1,2,3

is the same as

    local a,b
    a,nil,b = 1,2,3

3. nil as the lvalue in a for statement. A dummy variable(s) with the name 
'(nil)' is created so there's no upside to doing it, except for consistency 
with the other uses.

It doesn't generate the most ideal bytecode but that wasn't a primary goal. I 
did see some opportunities for improving, particularly with for loops. The 
current handling of loops uses a hidden control variable in addition to the 
declared variable. The value is implicitly copied during the FORLOOP opcode. 
If the control variable is not named, by assigning it to nil, then there is 
only need to copy it. My idea is to make the copying from the hidden variable 
explicit in the bytecode.

        4       [1]     FORPREP         0 3     ; to 8
        5       [1]     MOVE            3 0
        ...
        8       [1]     FORLOOP         0 -4    ; to 5

When the loop variable is unnamed, the MOVE opcode can be omitted.

The next question becomes, can we eliminate the assignment list in the syntax?

    for 1,10 do end

    for in io.lines() do end

Or is that getting too carried away?

Anyway, attached patch made against Lua 5.3work2
 src/lcode.c   |   4 +++
 src/lparser.c | 102 
++++++++++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 86 insertions(+), 20 deletions(-)


-- 
tom <telliamed@whoopdedo.org>
diff --git a/src/lcode.c b/src/lcode.c
index 2e8b3fb..6d719ee 100644
--- a/src/lcode.c
+++ b/src/lcode.c
@@ -613,6 +613,10 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
       luaK_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e);
       break;
     }
+    case VNIL: {
+      freeexp(fs, ex);
+      return;
+    }
     default: {
       lua_assert(0);  /* invalid var kind to store */
       break;
diff --git a/src/lparser.c b/src/lparser.c
index 9349149..2dfbf94 100644
--- a/src/lparser.c
+++ b/src/lparser.c
@@ -860,7 +860,7 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
 */
 
 
-static void primaryexp (LexState *ls, expdesc *v) {
+static int primaryexp (LexState *ls, expdesc *v) {
   /* primaryexp -> NAME | '(' expr ')' */
   switch (ls->t.token) {
     case '(': {
@@ -869,14 +869,20 @@ static void primaryexp (LexState *ls, expdesc *v) {
       expr(ls, v);
       check_match(ls, ')', '(', line);
       luaK_dischargevars(ls->fs, v);
-      return;
+      return 1;
+    }
+    case TK_NIL: {
+      init_exp(v, VNIL, 0);
+      luaX_next(ls);
+      return 0;
     }
     case TK_NAME: {
       singlevar(ls, v);
-      return;
+      return 1;
     }
     default: {
       luaX_syntaxerror(ls, "unexpected symbol");
+      return 0;
     }
   }
 }
@@ -884,10 +890,11 @@ static void primaryexp (LexState *ls, expdesc *v) {
 
 static void suffixedexp (LexState *ls, expdesc *v) {
   /* suffixedexp ->
-       primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
+       primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } |
+       nil */
   FuncState *fs = ls->fs;
   int line = ls->linenumber;
-  primaryexp(ls, v);
+  if (!primaryexp(ls, v)) return;
   for (;;) {
     switch (ls->t.token) {
       case '.': {  /* fieldsel */
@@ -1138,7 +1145,7 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
 
 static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
   expdesc e;
-  check_condition(ls, vkisvar(lh->v.k), "syntax error");
+  check_condition(ls, (vkisvar(lh->v.k) | (lh->v.k==VNIL)), "syntax error");
   if (testnext(ls, ',')) {  /* assignment -> ',' suffixedexp assignment */
     struct LHS_assign nv;
     nv.prev = lh;
@@ -1310,13 +1317,16 @@ static void forbody (LexState *ls, int base, int line, int nvars, int isnum) {
 
 
 static void fornum (LexState *ls, TString *varname, int line) {
-  /* fornum -> NAME = exp1,exp1[,exp1] forbody */
+  /* fornum -> (NAME | nil) = exp1,exp1[,exp1] forbody */
   FuncState *fs = ls->fs;
   int base = fs->freereg;
   new_localvarliteral(ls, "(for index)");
   new_localvarliteral(ls, "(for limit)");
   new_localvarliteral(ls, "(for step)");
-  new_localvar(ls, varname);
+  if (varname)
+    new_localvar(ls, varname);
+  else
+    new_localvarliteral(ls, "(for variable)");
   checknext(ls, '=');
   exp1(ls);  /* initial value */
   checknext(ls, ',');
@@ -1332,7 +1342,7 @@ static void fornum (LexState *ls, TString *varname, int line) {
 
 
 static void forlist (LexState *ls, TString *indexname) {
-  /* forlist -> NAME {,NAME} IN explist forbody */
+  /* forlist -> (NAME | nil) {, (NAME | nil)} IN explist forbody */
   FuncState *fs = ls->fs;
   expdesc e;
   int nvars = 4;  /* gen, state, control, plus at least one declared var */
@@ -1343,10 +1353,19 @@ static void forlist (LexState *ls, TString *indexname) {
   new_localvarliteral(ls, "(for state)");
   new_localvarliteral(ls, "(for control)");
   /* create declared variables */
-  new_localvar(ls, indexname);
+  if (indexname)
+    new_localvar(ls, indexname);
+  else
+    new_localvarliteral(ls, "(for variable)");
   while (testnext(ls, ',')) {
-    new_localvar(ls, str_checkname(ls));
-    nvars++;
+    if (testnext(ls, TK_NIL)) {
+      new_localvarliteral(ls, "(nil)");
+      nvars++;
+    }
+    else {
+      new_localvar(ls, str_checkname(ls));
+      nvars++;
+    }
   }
   checknext(ls, TK_IN);
   line = ls->linenumber;
@@ -1363,7 +1382,8 @@ static void forstat (LexState *ls, int line) {
   BlockCnt bl;
   enterblock(fs, &bl, 1);  /* scope for loop and control variables */
   luaX_next(ls);  /* skip `for' */
-  varname = str_checkname(ls);  /* first variable name */
+  varname = testnext(ls, TK_NIL) ? NULL
+            : str_checkname(ls);  /* first variable name */
   switch (ls->t.token) {
     case '=': fornum(ls, varname, line); break;
     case ',': case TK_IN: forlist(ls, varname); break;
@@ -1434,23 +1454,65 @@ static void localfunc (LexState *ls) {
 }
 
 
-static void localstat (LexState *ls) {
-  /* stat -> LOCAL NAME {`,' NAME} [`=' explist] */
+static int localstat (LexState *ls, int prev, int level) {
+  /* stat -> LOCAL (NAME | nil) {`,' (NAME | nil)} [`=' explist] */
+  int first = ls->fs->nactvar + prev;
   int nvars = 0;
+  int total;
   int nexps;
   expdesc e;
   do {
-    new_localvar(ls, str_checkname(ls));
-    nvars++;
+    if (testnext(ls, TK_NIL)) {
+      /* slot is empty, push and handle the next group of assignments */
+      if (testnext(ls, ',')) {
+        nexps = localstat(ls, prev+nvars, level+1);
+        if (nexps == 0) return 0;
+        if (nexps > prev+nvars) {  /* drop the unused value */
+          ls->fs->freereg--;
+          nexps--;
+        }
+        goto lexp;
+      } else
+        break;
+    }
+    else {
+      new_localvar(ls, str_checkname(ls));
+      nvars++;
+    }
   } while (testnext(ls, ','));
+  if (level > 0) {
+    /* assignment list has holes, initialize the vars */
+    e.k = VVOID;
+    adjust_assign(ls, prev+nvars, 0, &e);
+    adjustlocalvars(ls, prev+nvars);
+  }
   if (testnext(ls, '='))
     nexps = explist(ls, &e);
   else {
+    if (level > 0) return 0;
     e.k = VVOID;
     nexps = 0;
   }
-  adjust_assign(ls, nvars, nexps, &e);
-  adjustlocalvars(ls, nvars);
+  /* each level is one nil */
+  total = level + prev + nvars;
+  adjust_assign(ls, total, nexps, &e);
+  if (level == 0) {
+    /* no holes in the assignment list, set vars in-place */
+    adjustlocalvars(ls, total);
+    return 0;
+  }
+  if (nexps > total)
+    ls->fs->freereg -= nexps - total;  /* remove extra values */
+  nexps = total;
+  lexp:
+  while (nvars-- > 0) {
+    expdesc var;
+    init_exp(&var, VLOCAL, first+nvars);
+    init_exp(&e, VNONRELOC, ls->fs->freereg-1);
+    luaK_storevar(ls->fs, &var, &e);
+    nexps--;
+  }
+  return nexps;
 }
 
 
@@ -1568,7 +1630,7 @@ static void statement (LexState *ls) {
       if (testnext(ls, TK_FUNCTION))  /* local function? */
         localfunc(ls);
       else
-        localstat(ls);
+        localstat(ls, 0, 0);
       break;
     }
     case TK_DBCOLON: {  /* stat -> label */