bc: fixes for multi-line if/while/for
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 16 Dec 2018 16:30:35 +0000 (17:30 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 16 Dec 2018 16:30:35 +0000 (17:30 +0100)
function                                             old     new   delta
zbc_vm_process                                       561     589     +28
zbc_lex_next_and_skip_NLINE                            -      22     +22
zbc_parse_stmt_possibly_auto                        2232    2253     +21
zbc_lex_skip_if_at_NLINE                               -      14     +14
zbc_lex_number                                       192     200      +8
zbc_num_divmod                                       150     156      +6
bc_vm_run                                            134     139      +5
bc_vm_init                                           757     760      +3
bc_num_printNewline                                   51      54      +3
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 7/0 up/down: 110/0)             Total: 110 bytes
   text    data     bss     dec     hex filename
 982138     485    7296  989919   f1adf busybox_old
 982275     485    7296  990056   f1b68 busybox_unstripped

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

index 4dc382476cd5ec816f5bbbc2cbe95f676045e550..a5d7a01c0bb7ca463669de893af956636c38a63e 100644 (file)
@@ -3018,6 +3018,29 @@ static BC_STATUS zbc_lex_next(BcLex *l)
 # define zbc_lex_next(...) (zbc_lex_next(__VA_ARGS__), BC_STATUS_SUCCESS)
 #endif
 
+static BC_STATUS zbc_lex_skip_if_at_NLINE(BcLex *l)
+{
+       if (l->t.t == BC_LEX_NLINE)
+               RETURN_STATUS(zbc_lex_next(l));
+       RETURN_STATUS(BC_STATUS_SUCCESS);
+}
+#if ERRORS_ARE_FATAL
+# define zbc_lex_skip_if_at_NLINE(...) (zbc_lex_skip_if_at_NLINE(__VA_ARGS__), BC_STATUS_SUCCESS)
+#endif
+
+static BC_STATUS zbc_lex_next_and_skip_NLINE(BcLex *l)
+{
+       BcStatus s;
+       s = zbc_lex_next(l);
+       if (s) RETURN_STATUS(s);
+       // if(cond)<newline>stmt is accepted too (but not 2+ newlines)
+       s = zbc_lex_skip_if_at_NLINE(l);
+       RETURN_STATUS(s);
+}
+#if ERRORS_ARE_FATAL
+# define zbc_lex_next_and_skip_NLINE(...) (zbc_lex_next_and_skip_NLINE(__VA_ARGS__), BC_STATUS_SUCCESS)
+#endif
+
 static BC_STATUS zbc_lex_text_init(BcLex *l, const char *text)
 {
        l->buf = text;
@@ -4152,7 +4175,8 @@ static BC_STATUS zbc_parse_if(BcParse *p)
        if (s) RETURN_STATUS(s);
 
        if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
-       s = zbc_lex_next(&p->l);
+       // if(cond)<newline>stmt is accepted too (but not 2+ newlines)
+       s = zbc_lex_next_and_skip_NLINE(&p->l);
        if (s) RETURN_STATUS(s);
 
        bc_parse_push(p, BC_INST_JUMP_ZERO);
@@ -4216,12 +4240,15 @@ static BC_STATUS zbc_parse_while(BcParse *p)
        s = zbc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel);
        if (s) RETURN_STATUS(s);
        if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
-       s = zbc_lex_next(&p->l);
+
+       // while(cond)<newline>stmt is accepted too
+       s = zbc_lex_next_and_skip_NLINE(&p->l);
        if (s) RETURN_STATUS(s);
 
        bc_parse_push(p, BC_INST_JUMP_ZERO);
        bc_parse_pushIndex(p, ip.idx);
 
+//TODO: diagnose "while(cond)<newline><newline>"? Now it is seen as "while() with empty body"
        s = zbc_parse_stmt(p);
        if (s) RETURN_STATUS(s);
 
@@ -4321,7 +4348,9 @@ static BC_STATUS zbc_parse_for(BcParse *p)
 
        bc_vec_push(&p->exits, &ip);
        bc_vec_push(&p->func->labels, &ip.idx);
-       s = zbc_lex_next(&p->l);
+
+       // for(...)<newline>stmt is accepted as well
+       s = zbc_lex_next_and_skip_NLINE(&p->l);
        if (s) RETURN_STATUS(s);
 
        s = zbc_parse_stmt(p);
@@ -4453,11 +4482,9 @@ static BC_STATUS zbc_parse_funcdef(BcParse *p)
        if (p->l.t.t != BC_LEX_LBRACE)
                s = bc_POSIX_requires("the left brace be on the same line as the function header");
 
-       // Prevent "define z()<newline>" to be interpreted as function with empty stmt as body
-       while (p->l.t.t == BC_LEX_NLINE) {
-               s = zbc_lex_next(&p->l);
-               if (s) RETURN_STATUS(s);
-       }
+       // Prevent "define z()<newline>" from being interpreted as function with empty stmt as body
+       s = zbc_lex_skip_if_at_NLINE(&p->l);
+       if (s) RETURN_STATUS(s);
 //TODO: GNU bc requires a {} block even if function body has single stmt, enforce this?
 
        p->in_funcdef++; // to determine whether "return" stmt is allowed, and such
index 093b3950e15f79553b595b141dadf0f256cc69ff..95cc28dad147d6277efcbf5104d9ec6b423d8c01 100755 (executable)
@@ -56,6 +56,16 @@ testing "bc define with body on next line" \
        "8\n9\n" \
        "" "define w()\n{ auto z; return 8; }\nw()\n9"
 
+testing "bc if(cond)<NL>" \
+       "bc" \
+       "9\n" \
+       "" "if(0)\n3\n9"
+
+testing "bc while(cond)<NL>" \
+       "bc" \
+       "8\n7\n6\n5\n4\n3\n2\n1\n9\n" \
+       "" "i=9;while(--i)\ni\n9"
+
 tar xJf bc_large.tar.xz
 
 for f in bc*.bc; do