awk: fix 'delete array[var--]' decrementing var twice
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 7 Jan 2018 00:19:08 +0000 (01:19 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 7 Jan 2018 00:19:08 +0000 (01:19 +0100)
function                                             old     new   delta
evaluate                                            3395    3390      -5

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
editors/awk.c
testsuite/awk.tests

index d40c7816ac8252755794a91ae7a7126eca6a48d0..8f523ea28fbfedaa22499789bbd5bb77138088de 100644 (file)
@@ -2514,6 +2514,32 @@ static var *evaluate(node *op, var *res)
                op1 = op->l.n;
                debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn);
 
+               /* "delete" is special:
+                * "delete array[var--]" must evaluate index expr only once,
+                * must not evaluate it in "execute inevitable things" part.
+                */
+               if (XC(opinfo & OPCLSMASK) == XC(OC_DELETE)) {
+                       uint32_t info = op1->info & OPCLSMASK;
+                       var *v;
+
+                       debug_printf_eval("DELETE\n");
+                       if (info == OC_VAR) {
+                               v = op1->l.v;
+                       } else if (info == OC_FNARG) {
+                               v = &fnargs[op1->l.aidx];
+                       } else {
+                               syntax_error(EMSG_NOT_ARRAY);
+                       }
+                       if (op1->r.n) { /* array ref? */
+                               const char *s;
+                               s = getvar_s(evaluate(op1->r.n, v1));
+                               hash_remove(iamarray(v), s);
+                       } else {
+                               clear_array(iamarray(v));
+                       }
+                       goto next;
+               }
+
                /* execute inevitable things */
                if (opinfo & OF_RES1)
                        L.v = evaluate(op1, v1);
@@ -2621,28 +2647,7 @@ static var *evaluate(node *op, var *res)
                        break;
                }
 
-               case XC( OC_DELETE ): {
-                       uint32_t info = op1->info & OPCLSMASK;
-                       var *v;
-
-                       if (info == OC_VAR) {
-                               v = op1->l.v;
-                       } else if (info == OC_FNARG) {
-                               v = &fnargs[op1->l.aidx];
-                       } else {
-                               syntax_error(EMSG_NOT_ARRAY);
-                       }
-
-                       if (op1->r.n) {
-                               const char *s;
-                               clrvar(L.v);
-                               s = getvar_s(evaluate(op1->r.n, v1));
-                               hash_remove(iamarray(v), s);
-                       } else {
-                               clear_array(iamarray(v));
-                       }
-                       break;
-               }
+               /* case XC( OC_DELETE ): - moved to happen before arg evaluation */
 
                case XC( OC_NEWSOURCE ):
                        g_progname = op->l.new_progname;
@@ -2666,12 +2671,14 @@ static var *evaluate(node *op, var *res)
                /* -- recursive node type -- */
 
                case XC( OC_VAR ):
+                       debug_printf_eval("VAR\n");
                        L.v = op->l.v;
                        if (L.v == intvar[NF])
                                split_f0();
                        goto v_cont;
 
                case XC( OC_FNARG ):
+                       debug_printf_eval("FNARG[%d]\n", op->l.aidx);
                        L.v = &fnargs[op->l.aidx];
  v_cont:
                        res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v;
@@ -3035,7 +3042,8 @@ static var *evaluate(node *op, var *res)
 
                default:
                        syntax_error(EMSG_POSSIBLE_ERROR);
-               }
+               } /* switch */
+ next:
                if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS)
                        op = op->a.n;
                if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS)
index 82937bc10b41bf907573fe8d87a31212706b8d82..ad0583afbbe8033be71759119efe0e96e7bcbda8 100755 (executable)
@@ -261,6 +261,25 @@ end d
 " \
        "" ""
 
+prg='
+BEGIN{
+cnt = 0
+a[cnt] = "zeroth"
+a[++cnt] = "first"
+delete a[cnt--]
+print cnt
+print "[0]:" a[0]
+print "[1]:" a[1]
+}'
+testing "awk 'delete a[v--]' evaluates v-- once" \
+       "awk '$prg'" \
+       "\
+0
+[0]:zeroth
+[1]:
+" \
+       "" ""
+
 testing "awk handles empty ()" \
        "awk 'BEGIN {print()}' 2>&1" "awk: cmd. line:1: Empty sequence\n" "" ""