bc: fix handling of 'return' not in functions, and 'define f()<newline>{...}'
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 16 Dec 2018 16:06:07 +0000 (17:06 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 16 Dec 2018 16:06:07 +0000 (17:06 +0100)
function                                             old     new   delta
zbc_vm_process                                       561     597     +36
zbc_parse_stmt_possibly_auto                        2232    2253     +21
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: 0/0 grow/shrink: 7/0 up/down: 82/0)               Total: 82 bytes
   text    data     bss     dec     hex filename
 982138     485    7296  989919   f1adf busybox_old
 982247     485    7296  990028   f1b4c busybox_unstripped

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

index 95ba8b09439fe30dc3d51bb5268d26a28c8eae6f..4dc382476cd5ec816f5bbbc2cbe95f676045e550 100644 (file)
@@ -591,6 +591,8 @@ typedef struct BcParse {
 
 //TODO: needed? Example?
        size_t nbraces;
+
+       size_t in_funcdef;
 } BcParse;
 
 typedef struct BcProgram {
@@ -4057,19 +4059,16 @@ static BC_STATUS zbc_parse_return(BcParse *p)
 {
        BcStatus s;
        BcLexType t;
-       bool paren;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
-
        s = zbc_lex_next(&p->l);
        if (s) RETURN_STATUS(s);
 
        t = p->l.t.t;
-       paren = t == BC_LEX_LPAREN;
-
        if (t == BC_LEX_NLINE || t == BC_LEX_SCOLON)
                bc_parse_push(p, BC_INST_RET0);
        else {
+               bool paren = (t == BC_LEX_LPAREN);
                s = bc_parse_expr_empty_ok(p, 0, bc_parse_next_expr);
                if (s == BC_STATUS_PARSE_EMPTY_EXP) {
                        bc_parse_push(p, BC_INST_RET0);
@@ -4390,7 +4389,7 @@ static BC_STATUS zbc_parse_break_or_continue(BcParse *p, BcLexType type)
 # define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__), BC_STATUS_SUCCESS)
 #endif
 
-static BC_STATUS zbc_parse_func(BcParse *p)
+static BC_STATUS zbc_parse_funcdef(BcParse *p)
 {
        BcStatus s;
        bool var, comma = false;
@@ -4454,7 +4453,16 @@ static BC_STATUS zbc_parse_func(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);
+       }
+//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
        s = zbc_parse_stmt_possibly_auto(p, true);
+       p->in_funcdef--;
        if (s) RETURN_STATUS(s);
 
        bc_parse_push(p, BC_INST_RET0);
@@ -4467,7 +4475,7 @@ err:
        RETURN_STATUS(s);
 }
 #if ERRORS_ARE_FATAL
-# define zbc_parse_func(...) (zbc_parse_func(__VA_ARGS__), BC_STATUS_SUCCESS)
+# define zbc_parse_funcdef(...) (zbc_parse_funcdef(__VA_ARGS__), BC_STATUS_SUCCESS)
 #endif
 
 static BC_STATUS zbc_parse_auto(BcParse *p)
@@ -4626,6 +4634,8 @@ static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed)
                        // not when it is executed
                        QUIT_OR_RETURN_TO_MAIN;
                case BC_LEX_KEY_RETURN:
+                       if (!p->in_funcdef)
+                               RETURN_STATUS(bc_error("'return' not in a function"));
                        s = zbc_parse_return(p);
                        break;
                case BC_LEX_KEY_WHILE:
@@ -4657,7 +4667,7 @@ static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p)
                s = bc_error("end of file");
        else if (p->l.t.t == BC_LEX_KEY_DEFINE) {
                dbg_lex("%s:%d p->l.t.t:BC_LEX_KEY_DEFINE", __func__, __LINE__);
-               s = zbc_parse_func(p);
+               s = zbc_parse_funcdef(p);
        } else {
                dbg_lex("%s:%d p->l.t.t:%d (not BC_LEX_KEY_DEFINE)", __func__, __LINE__, p->l.t.t);
                s = zbc_parse_stmt(p);
@@ -7021,7 +7031,13 @@ static BC_STATUS zbc_vm_stdin(void)
        bc_lex_file(&G.prs.l);
 
        G.use_stdin = 1;
-       s = zbc_vm_process("");
+       do {
+               s = zbc_vm_process("");
+               // We do not stop looping on errors here.
+               // Example: start interactive bc and enter "return".
+               // It should say "'return' not in a function"
+               // but should not exit.
+       } while (G.use_stdin);
        RETURN_STATUS(s);
 }
 #if ERRORS_ARE_FATAL
index 86220ad19d5c6ddb12bf6cffd008079860b30259..093b3950e15f79553b595b141dadf0f256cc69ff 100755 (executable)
@@ -51,6 +51,11 @@ testing "bc define auto" \
        "8\n9\n" \
        "" "define w() { auto z; return 8; }; w(); 9"
 
+testing "bc define with body on next line" \
+       "bc" \
+       "8\n9\n" \
+       "" "define w()\n{ auto z; return 8; }\nw()\n9"
+
 tar xJf bc_large.tar.xz
 
 for f in bc*.bc; do