compound assignment patch for 5.4.0-beta

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

compound assignment patch for 5.4.0-beta

Dave Hayden
The old patch from Sven Olsen for compound assignment broke in 5.4.0 beta, so I pieced together something that seems to be working. I moved the operator+equals parsing to the lexer and took out the tuple handling to simplify things. (Now that it’s [maybe] working I might take a stab at adding it back in, though I doubt I’d ever use it.) I know there’s no interest in adding compound assignment to Standard Lua, but I understand it’s a pretty popular patch. I’d certainly have a hard time without it..

Hopefully this is useful to others, and please let me know what I did wrong! :)

Thanks,
-Dave


diff -r -u lua-5.4.0-beta/src/llex.c lua-5.4-test/src/llex.c
--- lua-5.4.0-beta/src/llex.c 2019-09-30 18:52:15.000000000 -0500
+++ lua-5.4-test/src/llex.c 2019-12-26 18:33:48.000000000 -0600
@@ -453,6 +453,7 @@
       }
       case '-': {  /* '-' or '--' (comment) */
         next(ls);
+        if (check_next1(ls, '=')) return TK_MINUSEQ;
         if (ls->current != '-') return '-';
         /* else is a comment */
         next(ls);
@@ -488,20 +489,52 @@
       case '<': {
         next(ls);
         if (check_next1(ls, '=')) return TK_LE;
-        else if (check_next1(ls, '<')) return TK_SHL;
+        else if (check_next1(ls, '<')) {
+          if ( check_next1(ls, '=') ) return TK_SHLEQ;
+          else return TK_SHL;
+        }
         else return '<';
       }
       case '>': {
         next(ls);
         if (check_next1(ls, '=')) return TK_GE;
-        else if (check_next1(ls, '>')) return TK_SHR;
+        else if (check_next1(ls, '>')) {
+          if ( check_next1(ls, '=') ) return TK_SHREQ;
+          else return TK_SHR;
+        }
         else return '>';
       }
       case '/': {
         next(ls);
         if (check_next1(ls, '/')) return TK_IDIV;
+        if (check_next1(ls, '=')) return TK_DIVEQ;
         else return '/';
       }
+      case '+': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_PLUSEQ;
+        else return '+';
+      }
+      case '*': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_MULTEQ;
+        else return '*';
+      }
+      case '&': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_BANDEQ;
+        else return '*';
+      }
+      case '|': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_BOREQ;
+        else return '|';
+      }
+      case '^': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_BXOREQ;
+        else return '^';
+      }
       case '~': {
         next(ls);
         if (check_next1(ls, '=')) return TK_NE;
diff -r -u lua-5.4.0-beta/src/llex.h lua-5.4-test/src/llex.h
--- lua-5.4.0-beta/src/llex.h 2019-09-30 18:52:15.000000000 -0500
+++ lua-5.4-test/src/llex.h 2019-12-26 18:33:52.000000000 -0600
@@ -33,7 +33,8 @@
   TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
   TK_SHL, TK_SHR,
   TK_DBCOLON, TK_EOS,
-  TK_FLT, TK_INT, TK_NAME, TK_STRING
+  TK_FLT, TK_INT, TK_NAME, TK_STRING,
+  TK_PLUSEQ, TK_MINUSEQ, TK_MULTEQ, TK_DIVEQ, TK_SHLEQ, TK_SHREQ, TK_BANDEQ, TK_BOREQ, TK_BXOREQ
 };
 
 /* number of reserved words */
diff -r -u lua-5.4.0-beta/src/lparser.c lua-5.4-test/src/lparser.c
--- lua-5.4.0-beta/src/lparser.c 2019-10-08 05:18:16.000000000 -0500
+++ lua-5.4-test/src/lparser.c 2019-12-26 18:33:55.000000000 -0600
@@ -1206,6 +1206,15 @@
     case TK_GE: return OPR_GE;
     case TK_AND: return OPR_AND;
     case TK_OR: return OPR_OR;
+    case TK_PLUSEQ: return OPR_ADD;
+    case TK_MINUSEQ: return OPR_SUB;
+    case TK_MULTEQ: return OPR_MUL;
+    case TK_DIVEQ: return OPR_DIV;
+    case TK_SHLEQ: return OPR_SHL;
+    case TK_SHREQ: return OPR_SHR;
+    case TK_BANDEQ: return OPR_BAND;
+    case TK_BOREQ: return OPR_BOR;
+    case TK_BXOREQ: return OPR_BXOR;
     default: return OPR_NOBINOPR;
   }
 }
@@ -1345,12 +1354,58 @@
   }
 }
 
+
+static void compound_assignment(LexState *ls, expdesc* v, int nvars) {
+  BinOpr op = getbinopr(ls->t.token);
+  FuncState * fs=ls->fs;
+  int tolevel=fs->nactvar;
+  int old_free=fs->freereg;
+  expdesc e,infix;
+  int line=ls->linenumber;
+  int nextra, i;
+  luaX_next(ls);
+
+  /* create temporary local variables to lock up any registers needed
+     by VINDEXED lvalues. */
+  lu_byte top=fs->nactvar;
+  /* protect both the table and index result registers,
+  ** ensuring that they won't be overwritten prior to the
+  ** storevar calls. */
+  if(v->k==VINDEXED) {
+    if( /*!ISK( v->u.ind.t ) &&*/ v->u.ind.t >= top) {
+      top= v->u.ind.t+1;
+    }
+    if( /*!ISK( v->u.ind.idx ) &&*/ v->u.ind.idx >= top) {
+      top= v->u.ind.idx+1;
+    }
+  }
+  nextra=top-fs->nactvar;
+  if(nextra) {
+    for(i=0;i<nextra;i++) {
+      new_localvarliteral(ls,"(temp)");
+    }
+    adjustlocalvars(ls,nextra);
+  }
+
+  infix = *v;
+  luaK_infix(fs,op,&infix);
+  expr(ls, &e);
+  luaK_posfix(fs, op, &infix, &e, line);
+  luaK_storevar(fs, v, &infix);
+  removevars(fs,tolevel);
+
+  if(old_free<fs->freereg) {
+    fs->freereg=old_free;
+  }
+}
+
+
 /*
 ** Parse and compile a multiple assignment. The first "variable"
 ** (a 'suffixedexp') was already read by the caller.
 **
 ** assignment -> suffixedexp restassign
-** restassign -> ',' suffixedexp restassign | '=' explist
+** restassign -> ',' suffixedexp restassign | '=' explist | opeq expr
 */
 static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
   expdesc e;
@@ -1365,21 +1420,21 @@
     enterlevel(ls);  /* control recursion depth */
     restassign(ls, &nv, nvars+1);
     leavelevel(ls);
+    init_exp(&e, VNONRELOC, ls->fs->freereg-1);  /* default assignment */
+    luaK_storevar(ls->fs, &lh->v, &e);
   }
-  else {  /* restassign -> '=' explist */
-    int nexps;
-    checknext(ls, '=');
-    nexps = explist(ls, &e);
+  else if (testnext(ls, '=')) {  /* restassign -> '=' explist */
+    int nexps = explist(ls, &e);
     if (nexps != nvars)
       adjust_assign(ls, nvars, nexps, &e);
     else {
       luaK_setoneret(ls->fs, &e);  /* close last expression */
       luaK_storevar(ls->fs, &lh->v, &e);
-      return;  /* avoid default */
     }
   }
-  init_exp(&e, VNONRELOC, ls->fs->freereg-1);  /* default assignment */
-  luaK_storevar(ls->fs, &lh->v, &e);
+  else if ( ls->t.token >= TK_PLUSEQ && ls->t.token <= TK_BXOREQ ) { /* restassign -> opeq expr */
+   compound_assignment(ls,&lh->v,nvars);
+  }
 }
 
 
@@ -1816,7 +1871,7 @@
   FuncState *fs = ls->fs;
   struct LHS_assign v;
   suffixedexp(ls, &v.v);
-  if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */
+  if (ls->t.token == '=' || ls->t.token == ',' || (ls->t.token >= TK_PLUSEQ && ls->t.token <= TK_BXOREQ) ) { /* stat -> assignment ? */
     v.prev = NULL;
     restassign(ls, &v, 1);
   }

Reply | Threaded
Open this post in threaded view
|

Re: compound assignment patch for 5.4.0-beta

Sven Olsen
... took out the tuple handling to simplify things. (Now that it’s [maybe] working I might take a stab at adding it back in, though I doubt I’d ever use it.)

Wait!  Before you go digging any deeper, let's talk about the target semantics!

High level, one of the big warts on any compound assignment implementation is going to be the way it handles (or fails to handle) multi-value assignments.  I've experimented with a couple different rules for multi-value compound assignments.  And, with the benefit of hindsight, I'd say that the one I included in my 2013 power-patch implementation is actually one of the worst rules I've tried.

To review the semantics of that old patch, what I did was to make statements like this one legal:

  a,b,c+=2,f()

Expanding them to something like:
  
  do
   local t1,t2=f()
   a,b,c=a+2,b+t1,c+t2
  end

I did that because it seemed like the natural "Lua-like" way of combining compound assignment with a multi-value assignment statement.

However, in the 6+ years since I added that rule to my parser, I've almost never used it.  And when I have used it, I've often regretted doing so, as the implied code is hard to read in practice, and tends to encourage various kinds of bugs.

I have experimented with one other multi-value compound assignment rule, however, one that converts

   a,b,c+=1

to 

  a,b,c=a+1,b+1,c+1

(or, in a more complex case)

   a,b[d[i]],c+=f()

to

  do 
    local t1=d[i]
    local t2=f()
    a,b[t1],c=a+t2,b[t1]+t2,c+t2
  end


In my experience, that second rule is much more useful in practice, and it's far less bug or typo prone.  

I'm not sure how difficult it would be to implement under 5.4.0-beta, but, if someone is going to code up an updated tuple-semantic for compound assignment, that's the one I'd recommend.  My original semantics are really too complex for their own good.   I think what you want, if anything, is a simpler semantic that dodges all the complexities that come up when you try to integrate compound assignments and open expressions.

Happy holidays,

-Sven
Reply | Threaded
Open this post in threaded view
|

Re: compound assignment patch for 5.4.0-beta

Dave Hayden
In reply to this post by Dave Hayden
On Dec 26, 2019, at 7:27 PM, Dave Hayden <[hidden email]> wrote:
>
> Hopefully this is useful to others, and please let me know what I did wrong! :)

Whoops! I bungled an assignment in the left-hand list case. Here’s that fixed, before it gives anyone any grief:


diff -r lua-5.4.0-beta/src/llex.c lua-5.4-test/src/llex.c
455a456
>         if (check_next1(ls, '=')) return TK_MINUSEQ;
491c492,495
<         else if (check_next1(ls, '<')) return TK_SHL;
---
>         else if (check_next1(ls, '<')) {
>           if ( check_next1(ls, '=') ) return TK_SHLEQ;
>           else return TK_SHL;
>         }
497c501,504
<         else if (check_next1(ls, '>')) return TK_SHR;
---
>         else if (check_next1(ls, '>')) {
>           if ( check_next1(ls, '=') ) return TK_SHREQ;
>           else return TK_SHR;
>         }
502a510
>         if (check_next1(ls, '=')) return TK_DIVEQ;
504a513,537

>       case '+': {
>         next(ls);
>         if (check_next1(ls, '=')) return TK_PLUSEQ;
>         else return '+';
>       }
>       case '*': {
>         next(ls);
>         if (check_next1(ls, '=')) return TK_MULTEQ;
>         else return '*';
>       }
>       case '&': {
>         next(ls);
>         if (check_next1(ls, '=')) return TK_BANDEQ;
>         else return '*';
>       }
>       case '|': {
>         next(ls);
>         if (check_next1(ls, '=')) return TK_BOREQ;
>         else return '|';
>       }
>       case '^': {
>         next(ls);
>         if (check_next1(ls, '=')) return TK_BXOREQ;
>         else return '^';
>       }
diff -r lua-5.4.0-beta/src/llex.h lua-5.4-test/src/llex.h
36c36,37
<   TK_FLT, TK_INT, TK_NAME, TK_STRING
---
>   TK_FLT, TK_INT, TK_NAME, TK_STRING,
>   TK_PLUSEQ, TK_MINUSEQ, TK_MULTEQ, TK_DIVEQ, TK_SHLEQ, TK_SHREQ, TK_BANDEQ, TK_BOREQ, TK_BXOREQ
diff -r lua-5.4.0-beta/src/lparser.c lua-5.4-test/src/lparser.c
1208a1209,1219

>     // PANIC
>     case TK_PLUSEQ: return OPR_ADD;
>     case TK_MINUSEQ: return OPR_SUB;
>     case TK_MULTEQ: return OPR_MUL;
>     case TK_DIVEQ: return OPR_DIV;
>     case TK_SHLEQ: return OPR_SHL;
>     case TK_SHREQ: return OPR_SHR;
>     case TK_BANDEQ: return OPR_BAND;
>     case TK_BOREQ: return OPR_BOR;
>     case TK_BXOREQ: return OPR_BXOR;
>     // end PANIC
1347a1359,1404

> // PANIC
>
> static void compound_assignment(LexState *ls, expdesc* v, int nvars) {
>   BinOpr op = getbinopr(ls->t.token);
>   FuncState * fs=ls->fs;
>   int tolevel=fs->nactvar;
>   int old_free=fs->freereg;
>   expdesc e,infix;
>   int line=ls->linenumber;
>   int nextra, i;
>   luaX_next(ls);
>
>   /* create temporary local variables to lock up any registers needed
>      by VINDEXED lvalues. */
>   lu_byte top=fs->nactvar;
>   /* protect both the table and index result registers,
>   ** ensuring that they won't be overwritten prior to the
>   ** storevar calls. */
>   if(v->k==VINDEXED) {
>     if( /*!ISK( v->u.ind.t ) &&*/ v->u.ind.t >= top) {
>       top= v->u.ind.t+1;
>     }
>     if( /*!ISK( v->u.ind.idx ) &&*/ v->u.ind.idx >= top) {
>       top= v->u.ind.idx+1;
>     }
>   }
>   nextra=top-fs->nactvar;
>   if(nextra) {
>     for(i=0;i<nextra;i++) {
>       new_localvarliteral(ls,"(temp)");
>     }
>     adjustlocalvars(ls,nextra);
>   }
>
>   infix = *v;
>   luaK_infix(fs,op,&infix);
>   expr(ls, &e);
>   luaK_posfix(fs, op, &infix, &e, line);
>   luaK_storevar(fs, v, &infix);
>   removevars(fs,tolevel);
>
>   if(old_free<fs->freereg) {
>     fs->freereg=old_free;
>   }
> }
>
1353c1410
< ** restassign -> ',' suffixedexp restassign | '=' explist
---
> ** restassign -> ',' suffixedexp restassign | '=' explist | opeq expr
1369,1372c1426,1427
<   else {  /* restassign -> '=' explist */
<     int nexps;
<     checknext(ls, '=');
<     nexps = explist(ls, &e);
---
>   else if (testnext(ls, '=')) {  /* restassign -> '=' explist */
>     int nexps = explist(ls, &e);
1378c1433
<       return;  /* avoid default */
---
>       return;
1380a1436,1439
>   else if ( ls->t.token >= TK_PLUSEQ && ls->t.token <= TK_BXOREQ ) { /* restassign -> opeq expr */
>  compound_assignment(ls,&lh->v,nvars);
>       return;
>   }
1384a1444
> // end PANIC
1819c1879
<   if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */
---
>   if (ls->t.token == '=' || ls->t.token == ',' || (ls->t.token >= TK_PLUSEQ && ls->t.token <= TK_BXOREQ) ) { /* stat -> assignment ? */


Reply | Threaded
Open this post in threaded view
|

Re: compound assignment patch for 5.4.0-beta

Dave Hayden
In reply to this post by Sven Olsen
On Dec 27, 2019, at 2:20 PM, Sven Olsen <[hidden email]> wrote:

... took out the tuple handling to simplify things. (Now that it’s [maybe] working I might take a stab at adding it back in, though I doubt I’d ever use it.)

Wait!  Before you go digging any deeper, let's talk about the target semantics!

High level, one of the big warts on any compound assignment implementation is going to be the way it handles (or fails to handle) multi-value assignments.  I've experimented with a couple different rules for multi-value compound assignments.  And, with the benefit of hindsight, I'd say that the one I included in my 2013 power-patch implementation is actually one of the worst rules I've tried.

To review the semantics of that old patch, what I did was to make statements like this one legal:

  a,b,c+=2,f()

That seems like the “correct Lua” way to do it, since that’s how it works with with a normal =. Which isn’t to say I like it--I agree that opens the door to some very confusing code. On the other hand, having a,b,c += 1 increment all three also conflicts with Lua’s = operator:

  a,b,c = 1
  print(a,b,c) -- outputs 1, nil, nil

I’ll dodge the question and throw a parse error if it’s compound-assigning to a list. I still need to add that to the patch..

Many thanks for your feedback, and thank you for that patch in the first place! Every time I have to use Lua without it I lose a bit more hair (and I don’t have much left). :)

-Dave
Reply | Threaded
Open this post in threaded view
|

Re: compound assignment patch for 5.4.0-beta

Sven Olsen
I’ll dodge the question and throw a parse error if it’s compound-assigning to a list. I still need to add that to the patch..

Yeah, for all my enthusiasm for the other possibilities, I think that probably is the wisest course.  Compound assignment is a shorthand that really doesn't work that well with assignment lists.

Many thanks for your feedback, and thank you for that patch in the first place! Every time I have to use Lua without it I lose a bit more hair (and I don’t have much left). :)  

Glad to see the code used by other people -- as I recall, getting the register handling right took some effort -- and from my reading (but not testing) of the patches you've been posting, that part of the logic still survives in your more modern incarnation.

Are you planning to update the Lua Power Patches wiki page?

-Sven


Reply | Threaded
Open this post in threaded view
|

Re: compound assignment patch for 5.4.0-beta

Dave Hayden
In reply to this post by Dave Hayden
One last (I hope) fix for this. I don’t totally grok the finer points between the expkind VINDEX* cases and where we do or don’t need to tuck intermediate values in registers, but I came up with code that produces each and confirmed that the += operator generates the same bytecode (or close enough) as doing it long form. I also added an error if the compound operator appears after a tuple, because I don’t want to touch that.

Let me know if it’s helpful! Or hurtful!

-Dave


diff -urN lua-5.4.0-beta/src/llex.c lua-5.4-test/src/llex.c
--- lua-5.4.0-beta/src/llex.c 2019-09-30 18:52:15.000000000 -0500
+++ lua-5.4-test/src/llex.c 2020-01-03 22:39:27.000000000 -0600
@@ -44,7 +44,8 @@
     "return", "then", "true", "until", "while",
     "//", "..", "...", "==", ">=", "<=", "~=",
     "<<", ">>", "::", "<eof>",
-    "<number>", "<integer>", "<name>", "<string>"
+    "<number>", "<integer>", "<name>", "<string>",
+    "+=", "-=", "*=", "/=", "<<=", ">>=", "&=", "|=", "^="
 };
 
 
@@ -453,6 +454,7 @@
       }
       case '-': {  /* '-' or '--' (comment) */
         next(ls);
+        if (check_next1(ls, '=')) return TK_MINUSEQ;
         if (ls->current != '-') return '-';
         /* else is a comment */
         next(ls);
@@ -488,20 +490,52 @@
       case '<': {
         next(ls);
         if (check_next1(ls, '=')) return TK_LE;
-        else if (check_next1(ls, '<')) return TK_SHL;
+        else if (check_next1(ls, '<')) {
+          if (check_next1(ls, '=')) return TK_SHLEQ;
+          else return TK_SHL;
+        }
         else return '<';
       }
       case '>': {
         next(ls);
         if (check_next1(ls, '=')) return TK_GE;
-        else if (check_next1(ls, '>')) return TK_SHR;
+        else if (check_next1(ls, '>')) {
+          if (check_next1(ls, '=')) return TK_SHREQ;
+          else return TK_SHR;
+        }
         else return '>';
       }
       case '/': {
         next(ls);
         if (check_next1(ls, '/')) return TK_IDIV;
+        if (check_next1(ls, '=')) return TK_DIVEQ;
         else return '/';
       }
+      case '+': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_PLUSEQ;
+        else return '+';
+      }
+      case '*': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_MULTEQ;
+        else return '*';
+      }
+      case '&': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_BANDEQ;
+        else return '*';
+      }
+      case '|': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_BOREQ;
+        else return '|';
+      }
+      case '^': {
+        next(ls);
+        if (check_next1(ls, '=')) return TK_BXOREQ;
+        else return '^';
+      }
       case '~': {
         next(ls);
         if (check_next1(ls, '=')) return TK_NE;
diff -urN lua-5.4.0-beta/src/llex.h lua-5.4-test/src/llex.h
--- lua-5.4.0-beta/src/llex.h 2019-09-30 18:52:15.000000000 -0500
+++ lua-5.4-test/src/llex.h 2019-12-26 18:33:52.000000000 -0600
@@ -33,7 +33,8 @@
   TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
   TK_SHL, TK_SHR,
   TK_DBCOLON, TK_EOS,
-  TK_FLT, TK_INT, TK_NAME, TK_STRING
+  TK_FLT, TK_INT, TK_NAME, TK_STRING,
+  TK_PLUSEQ, TK_MINUSEQ, TK_MULTEQ, TK_DIVEQ, TK_SHLEQ, TK_SHREQ, TK_BANDEQ, TK_BOREQ, TK_BXOREQ
 };
 
 /* number of reserved words */
diff -urN lua-5.4.0-beta/src/lparser.c lua-5.4-test/src/lparser.c
--- lua-5.4.0-beta/src/lparser.c 2019-10-08 05:18:16.000000000 -0500
+++ lua-5.4-test/src/lparser.c 2020-01-03 22:46:21.000000000 -0600
@@ -1206,6 +1206,15 @@
     case TK_GE: return OPR_GE;
     case TK_AND: return OPR_AND;
     case TK_OR: return OPR_OR;
+    case TK_PLUSEQ: return OPR_ADD;
+    case TK_MINUSEQ: return OPR_SUB;
+    case TK_MULTEQ: return OPR_MUL;
+    case TK_DIVEQ: return OPR_DIV;
+    case TK_SHLEQ: return OPR_SHL;
+    case TK_SHREQ: return OPR_SHR;
+    case TK_BANDEQ: return OPR_BAND;
+    case TK_BOREQ: return OPR_BOR;
+    case TK_BXOREQ: return OPR_BXOR;
     default: return OPR_NOBINOPR;
   }
 }
@@ -1345,12 +1354,55 @@
   }
 }
 
+
+static void compound_assignment(LexState *ls, expdesc* v) {
+  BinOpr op = getbinopr(ls->t.token);
+  FuncState * fs=ls->fs;
+  int tolevel=fs->nactvar;
+  int old_free=fs->freereg;
+  expdesc e,infix;
+  int line=ls->linenumber;
+  int nextra, i;
+  luaX_next(ls);
+
+  /* create temporary local variables to lock up any registers needed
+     by indexed lvalues. */
+  lu_byte top=fs->nactvar;
+  /* protect both the table and index result registers,
+  ** ensuring that they won't be overwritten prior to the
+  ** storevar calls. */
+  if (vkisindexed(v->k)) {
+    if (v->u.ind.t>=top)
+      top = v->u.ind.t+1;
+    if (v->k == VINDEXED && v->u.ind.idx >= top)
+      top = v->u.ind.idx+1;
+  }
+  nextra=top-fs->nactvar;
+  if(nextra) {
+    for(i=0;i<nextra;i++) {
+      new_localvarliteral(ls,"(temp)");
+    }
+    adjustlocalvars(ls,nextra);
+  }
+
+  infix = *v;
+  luaK_infix(fs,op,&infix);
+  expr(ls, &e);
+  luaK_posfix(fs, op, &infix, &e, line);
+  luaK_storevar(fs, v, &infix);
+  removevars(fs,tolevel);
+
+  if(old_free<fs->freereg) {
+    fs->freereg=old_free;
+  }
+}
+
 /*
 ** Parse and compile a multiple assignment. The first "variable"
 ** (a 'suffixedexp') was already read by the caller.
 **
 ** assignment -> suffixedexp restassign
-** restassign -> ',' suffixedexp restassign | '=' explist
+** restassign -> ',' suffixedexp restassign | '=' explist | opeq expr
 */
 static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
   expdesc e;
@@ -1366,10 +1418,8 @@
     restassign(ls, &nv, nvars+1);
     leavelevel(ls);
   }
-  else {  /* restassign -> '=' explist */
-    int nexps;
-    checknext(ls, '=');
-    nexps = explist(ls, &e);
+  else if (testnext(ls, '=')) {  /* restassign -> '=' explist */
+    int nexps = explist(ls, &e);
     if (nexps != nvars)
       adjust_assign(ls, nvars, nexps, &e);
     else {
@@ -1378,6 +1428,11 @@
       return;  /* avoid default */
     }
   }
+  else if ( ls->t.token >= TK_PLUSEQ && ls->t.token <= TK_BXOREQ ) { /* restassign -> opeq expr */
+   check_condition(ls, nvars == 1, "compound assignment not allowed on tuples");
+   compound_assignment(ls,&lh->v);
+      return;
+  }
   init_exp(&e, VNONRELOC, ls->fs->freereg-1);  /* default assignment */
   luaK_storevar(ls->fs, &lh->v, &e);
 }
@@ -1816,7 +1871,7 @@
   FuncState *fs = ls->fs;
   struct LHS_assign v;
   suffixedexp(ls, &v.v);
-  if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */
+  if (ls->t.token == '=' || ls->t.token == ',' || (ls->t.token >= TK_PLUSEQ && ls->t.token <= TK_BXOREQ) ) { /* stat -> assignment ? */
     v.prev = NULL;
     restassign(ls, &v, 1);
   }

Reply | Threaded
Open this post in threaded view
|

Re: compound assignment patch for 5.4.0-beta

Dave Hayden
In reply to this post by Sven Olsen
On Dec 31, 2019, at 3:37 PM, Sven Olsen <[hidden email]> wrote:

> Glad to see the code used by other people -- as I recall, getting the register handling right took some effort -- and from my reading (but not testing) of the patches you've been posting, that part of the logic still survives in your more modern incarnation.

I _think_ I have that right (or right enough) now. VINDEXED has three new siblings (to make up for the loss of the K flag I think..?) but I came up with test code that uses each and tweaked things until it compiled to the same bytecode as doing the assignment the long way.

> Are you planning to update the Lua Power Patches wiki page?

Did that this afternoon, finally! I debated adding you to the Author credit since a lot of it is your code, but didn’t want you to catch any blame for anything that’s wrong with it. :)

-Dave