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.
diff -urN lua-5.4.0-beta/src/llex.c lua-5.4-test/src/llex.c
--- lua-5.4.0-beta/src/llex.c2019年09月30日 18:52:15.000000000 -0500
+++ lua-5.4-test/src/llex.c2020年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.h2019年09月30日 18:52:15.000000000 -0500
+++ lua-5.4-test/src/llex.h2019年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.c2019年10月08日 05:18:16.000000000 -0500
+++ lua-5.4-test/src/lparser.c2020年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);
}