bc: remove "error after expression parsing" check
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 17 Dec 2018 15:54:37 +0000 (16:54 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 17 Dec 2018 16:00:29 +0000 (17:00 +0100)
It is misplaced: caller knows better what can or cannot follow the expression.
Sometimes even caller's caller: "if (1) return a+b else..." -
parser of "return" does not know that "else" after it is valid,
parser of stmt does not know it either, - only parser of
"if" knows it!

The removed code balked on e.g. "{ print 1 }" statement.

This does not break any valid programs, but starts accepting some
invalid ones, e.g. "print 1 print 2" would work.

function                                             old     new   delta
zcommon_parse_expr                                    40      32      -8
zbc_parse_name                                       509     494     -15
zbc_parse_stmt_possibly_auto                        1678    1638     -40
bc_parse_expr_empty_ok                              2025    1977     -48
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/4 up/down: 0/-111)           Total: -111 bytes
   text    data     bss     dec     hex filename
 981599     485    7296  989380   f18c4 busybox_old
 981488     485    7296  989269   f1855 busybox_unstripped

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

index 214ea44abbbed2b34a923a75bfa40bdada79e792..45d9eb8eb5d7481717b6a38ed31ff2684c20ea2f 100644 (file)
@@ -390,7 +390,6 @@ typedef struct BcInstPtr {
 
 // BC_LEX_NEG is not used in lexing; it is only for parsing.
 typedef enum BcLexType {
-
        BC_LEX_EOF,
        BC_LEX_INVALID,
 
@@ -553,7 +552,93 @@ enum {
                | (1 << 19) // 19
 };
 #define bc_lex_kws_POSIX(i) ((1 << (i)) & POSIX_KWORD_MASK)
+
+// This is a bit array that corresponds to token types. An entry is
+// true if the token is valid in an expression, false otherwise.
+// Used to figure out when expr parsing should stop *without error message*
+// - 0 element indicates this condition. 1 means "this token is to be eaten
+// as part of the expression", token can them still be determined to be invalid
+// by later processing.
+enum {
+#define EXBITS(a,b,c,d,e,f,g,h) \
+       ((uint64_t)((a << 0)+(b << 1)+(c << 2)+(d << 3)+(e << 4)+(f << 5)+(g << 6)+(h << 7)))
+       BC_PARSE_EXPRS_BITS = 0
+       + (EXBITS(0,0,1,1,1,1,1,1) << (0*8)) //  0: eof    inval ++    --     -     ^     *    /
+       + (EXBITS(1,1,1,1,1,1,1,1) << (1*8)) //  8: %      +     -     ==     <=    >=    !=   <
+       + (EXBITS(1,1,1,1,1,1,1,1) << (2*8)) // 16: >      !     ||    &&     ^=    *=    /=   %=
+       + (EXBITS(1,1,1,0,0,1,1,0) << (3*8)) // 24: +=     -=    =     NL     WS    (     )    [
+       + (EXBITS(0,0,0,0,0,0,1,1) << (4*8)) // 32: ,      ]     {     ;      }     STR   NAME NUM
+       + (EXBITS(0,0,0,0,0,0,0,1) << (5*8)) // 40: auto   break cont  define else  for   halt ibase
+       + (EXBITS(0,1,1,1,1,0,0,1) << (6*8)) // 48: if     last  len   limits obase print quit read - bug, why "limits" is allowed?
+       + (EXBITS(0,1,1,0,0,0,0,0) << (7*8)) // 56: return scale sqrt  while
+#undef EXBITS
+};
+static ALWAYS_INLINE long bc_parse_exprs(unsigned i)
+{
+#if ULONG_MAX > 0xffffffff
+       // 64-bit version (will not work correctly for 32-bit longs!)
+       return BC_PARSE_EXPRS_BITS & (1UL << i);
+#else
+       // 32-bit version
+       unsigned long m = (uint32_t)BC_PARSE_EXPRS_BITS;
+       if (i >= 32) {
+               m = (uint32_t)(BC_PARSE_EXPRS_BITS >> 32);
+               i &= 31;
+       }
+       return m & (1UL << i);
 #endif
+}
+
+// This is an array of data for operators that correspond to
+// [BC_LEX_OP_INC...BC_LEX_OP_ASSIGN] token types.
+static const uint8_t bc_parse_ops[] = {
+#define OP(p,l) ((int)(l) * 0x10 + (p))
+       OP(0, false), OP( 0, false ), // inc dec
+       OP(1, false), // neg
+       OP(2, false), // pow
+       OP(3, true ), OP( 3, true  ), OP( 3, true  ), // mul div mod
+       OP(4, true ), OP( 4, true  ), // + -
+       OP(6, true ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true ), // == <= >= != < >
+       OP(1, false), // not
+       OP(7, true ), OP( 7, true  ), // or and
+       OP(5, false), OP( 5, false ), OP( 5, false ), OP( 5, false ), OP( 5, false ), // ^= *= /= %= +=
+       OP(5, false), OP( 5, false ), // -= =
+#undef OP
+};
+#define bc_parse_op_PREC(i) (bc_parse_ops[i] & 0x0f)
+#define bc_parse_op_LEFT(i) (bc_parse_ops[i] & 0x10)
+#endif // ENABLE_BC
+
+#if ENABLE_DC
+static const //BcInst - should be this type. Using signed narrow type since BC_INST_INVALID is -1
+int8_t
+dc_parse_insts[] = {
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE,
+       BC_INST_INVALID, BC_INST_POWER, BC_INST_MULTIPLY, BC_INST_DIVIDE,
+       BC_INST_MODULUS, BC_INST_PLUS, BC_INST_MINUS,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_BOOL_NOT, BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GT, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE,
+       BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_IBASE,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_LENGTH, BC_INST_INVALID,
+       BC_INST_OBASE, BC_INST_PRINT, BC_INST_QUIT, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_SCALE, BC_INST_SQRT, BC_INST_INVALID,
+       BC_INST_REL_EQ, BC_INST_MODEXP, BC_INST_DIVMOD, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_EXECUTE, BC_INST_PRINT_STACK, BC_INST_CLEAR_STACK,
+       BC_INST_STACK_LEN, BC_INST_DUPLICATE, BC_INST_SWAP, BC_INST_POP,
+       BC_INST_ASCIIFY, BC_INST_PRINT_STREAM, BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
+       BC_INST_PRINT, BC_INST_NQUIT, BC_INST_SCALE_FUNC,
+};
+#endif // ENABLE_DC
+
 
 typedef struct BcLex {
        const char *buf;
@@ -735,106 +820,6 @@ struct globals {
 #define IS_BC (ENABLE_BC && (!ENABLE_DC || applet_name[0] == 'b'))
 #define IS_DC (ENABLE_DC && (!ENABLE_BC || applet_name[0] != 'b'))
 
-#if ENABLE_BC
-
-// This is a bit array that corresponds to token types. An entry is
-// true if the token is valid in an expression, false otherwise.
-enum {
-       BC_PARSE_EXPRS_BITS = 0
-       + ((uint64_t)((0 << 0)+(0 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(1 << 5)+(1 << 6)+(1 << 7)) << (0*8))
-       + ((uint64_t)((1 << 0)+(1 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(1 << 5)+(1 << 6)+(1 << 7)) << (1*8))
-       + ((uint64_t)((1 << 0)+(1 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(1 << 5)+(1 << 6)+(1 << 7)) << (2*8))
-       + ((uint64_t)((1 << 0)+(1 << 1)+(1 << 2)+(0 << 3)+(0 << 4)+(1 << 5)+(1 << 6)+(0 << 7)) << (3*8))
-       + ((uint64_t)((0 << 0)+(0 << 1)+(0 << 2)+(0 << 3)+(0 << 4)+(0 << 5)+(1 << 6)+(1 << 7)) << (4*8))
-       + ((uint64_t)((0 << 0)+(0 << 1)+(0 << 2)+(0 << 3)+(0 << 4)+(0 << 5)+(0 << 6)+(1 << 7)) << (5*8))
-       + ((uint64_t)((0 << 0)+(1 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(0 << 5)+(0 << 6)+(1 << 7)) << (6*8))
-       + ((uint64_t)((0 << 0)+(1 << 1)+(1 << 2)+(0 << 3)                                    ) << (7*8))
-};
-static ALWAYS_INLINE long bc_parse_exprs(unsigned i)
-{
-#if ULONG_MAX > 0xffffffff
-       // 64-bit version (will not work correctly for 32-bit longs!)
-       return BC_PARSE_EXPRS_BITS & (1UL << i);
-#else
-       // 32-bit version
-       unsigned long m = (uint32_t)BC_PARSE_EXPRS_BITS;
-       if (i >= 32) {
-               m = (uint32_t)(BC_PARSE_EXPRS_BITS >> 32);
-               i &= 31;
-       }
-       return m & (1UL << i);
-#endif
-}
-
-// This is an array of data for operators that correspond to token types.
-static const uint8_t bc_parse_ops[] = {
-#define OP(p,l) ((int)(l) * 0x10 + (p))
-       OP(0, false), OP( 0, false ), // inc dec
-       OP(1, false), // neg
-       OP(2, false),
-       OP(3, true ), OP( 3, true  ), OP( 3, true  ), // pow mul div
-       OP(4, true ), OP( 4, true  ), // mod + -
-       OP(6, true ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true ), // == <= >= != < >
-       OP(1, false), // not
-       OP(7, true ), OP( 7, true  ), // or and
-       OP(5, false), OP( 5, false ), OP( 5, false ), OP( 5, false ), OP( 5, false ), // ^= *= /= %= +=
-       OP(5, false), OP( 5, false ), // -= =
-#undef OP
-};
-#define bc_parse_op_PREC(i) (bc_parse_ops[i] & 0x0f)
-#define bc_parse_op_LEFT(i) (bc_parse_ops[i] & 0x10)
-
-// Byte array of up to 4 BC_LEX's, packed into 32-bit word
-typedef uint32_t BcParseNext;
-
-// These identify what tokens can come after expressions in certain cases.
-enum {
-#define BC_PARSE_NEXT4(a,b,c,d) ( (a) | ((b)<<8) | ((c)<<16) | ((((d)|0x80)<<24)) )
-#define BC_PARSE_NEXT2(a,b)     BC_PARSE_NEXT4(a,b,0xff,0xff)
-#define BC_PARSE_NEXT1(a)       BC_PARSE_NEXT4(a,0xff,0xff,0xff)
-       bc_parse_next_expr  = BC_PARSE_NEXT4(BC_LEX_NLINE,  BC_LEX_SCOLON, BC_LEX_RBRACE, BC_LEX_EOF),
-       bc_parse_next_param = BC_PARSE_NEXT2(BC_LEX_RPAREN, BC_LEX_COMMA),
-       bc_parse_next_print = BC_PARSE_NEXT4(BC_LEX_COMMA,  BC_LEX_NLINE,  BC_LEX_SCOLON, BC_LEX_EOF),
-       bc_parse_next_rel   = BC_PARSE_NEXT1(BC_LEX_RPAREN),
-       bc_parse_next_elem  = BC_PARSE_NEXT1(BC_LEX_RBRACKET),
-       bc_parse_next_for   = BC_PARSE_NEXT1(BC_LEX_SCOLON),
-       bc_parse_next_read  = BC_PARSE_NEXT2(BC_LEX_NLINE,  BC_LEX_EOF),
-#undef BC_PARSE_NEXT4
-#undef BC_PARSE_NEXT2
-#undef BC_PARSE_NEXT1
-};
-#endif // ENABLE_BC
-
-#if ENABLE_DC
-static const //BcInst - should be this type. Using signed narrow type since BC_INST_INVALID is -1
-int8_t
-dc_parse_insts[] = {
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE,
-       BC_INST_INVALID, BC_INST_POWER, BC_INST_MULTIPLY, BC_INST_DIVIDE,
-       BC_INST_MODULUS, BC_INST_PLUS, BC_INST_MINUS,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_BOOL_NOT, BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GT, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE,
-       BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_IBASE,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_LENGTH, BC_INST_INVALID,
-       BC_INST_OBASE, BC_INST_PRINT, BC_INST_QUIT, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_SCALE, BC_INST_SQRT, BC_INST_INVALID,
-       BC_INST_REL_EQ, BC_INST_MODEXP, BC_INST_DIVMOD, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_EXECUTE, BC_INST_PRINT_STACK, BC_INST_CLEAR_STACK,
-       BC_INST_STACK_LEN, BC_INST_DUPLICATE, BC_INST_SWAP, BC_INST_POP,
-       BC_INST_ASCIIFY, BC_INST_PRINT_STREAM, BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
-       BC_INST_PRINT, BC_INST_NQUIT, BC_INST_SCALE_FUNC,
-};
-#endif // ENABLE_DC
-
 // In configurations where errors abort instead of propagating error
 // return code up the call chain, functions returning BC_STATUS
 // actually don't return anything, they always succeed and return "void".
@@ -3535,9 +3520,15 @@ static void bc_parse_number(BcParse *p)
 IF_BC(static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p);)
 IF_DC(static BC_STATUS zdc_parse_parse(BcParse *p);)
 
+// "Parse" half of "parse,execute,repeat" main loop
 static BC_STATUS zcommon_parse(BcParse *p)
 {
        if (IS_BC) {
+// FIXME: "eating" of stmt delemiters is coded inconsistently
+// (sometimes zbc_parse_stmt() eats the delimiter, sometimes don't),
+// which causes bugs such as "print 1 print 2" erroneously accepted,
+// or "print 1 else 2" detecting parse error only after executing
+// "print 1" part.
                IF_BC(RETURN_STATUS(zbc_parse_stmt_or_funcdef(p));)
        }
        IF_DC(RETURN_STATUS(zdc_parse_parse(p));)
@@ -3625,10 +3616,20 @@ static void bc_parse_create(BcParse *p, size_t func)
 // first in the expr enum. Note: This only works for binary operators.
 #define BC_TOKEN_2_INST(t) ((char) ((t) - BC_LEX_NEG + BC_INST_NEG))
 
-static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed);
-static BC_STATUS zbc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next);
-static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext next);
+static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags);
+
+static BC_STATUS zbc_parse_expr(BcParse *p, uint8_t flags)
+{
+       BcStatus s;
+
+       s = bc_parse_expr_empty_ok(p, flags);
+       if (s == BC_STATUS_PARSE_EMPTY_EXP)
+               RETURN_STATUS(bc_error("empty expression"));
+       RETURN_STATUS(s);
+}
 #define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__) COMMA_SUCCESS)
+
+static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed);
 #define zbc_parse_stmt_possibly_auto(...) (zbc_parse_stmt_possibly_auto(__VA_ARGS__) COMMA_SUCCESS)
 
 static BC_STATUS zbc_parse_stmt(BcParse *p)
@@ -3699,26 +3700,30 @@ static BC_STATUS zbc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs)
 static BC_STATUS zbc_parse_params(BcParse *p, uint8_t flags)
 {
        BcStatus s;
-       bool comma = false;
        size_t nparams;
 
        dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
+       flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY;
+
        s = zbc_lex_next(&p->l);
        if (s) RETURN_STATUS(s);
 
-       for (nparams = 0; p->l.t.t != BC_LEX_RPAREN; ++nparams) {
-               flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY;
-               s = zbc_parse_expr(p, flags, bc_parse_next_param);
-               if (s) RETURN_STATUS(s);
-
-               comma = p->l.t.t == BC_LEX_COMMA;
-               if (comma) {
+       nparams = 0;
+       if (p->l.t.t != BC_LEX_RPAREN) {
+               for (;;) {
+                       s = zbc_parse_expr(p, flags);
+                       if (s) RETURN_STATUS(s);
+                       nparams++;
+                       if (p->l.t.t != BC_LEX_COMMA) {
+                               if (p->l.t.t == BC_LEX_RPAREN)
+                                       break;
+                               RETURN_STATUS(bc_error_bad_token());
+                       }
                        s = zbc_lex_next(&p->l);
                        if (s) RETURN_STATUS(s);
                }
        }
 
-       if (comma) RETURN_STATUS(bc_error_bad_token());
        bc_parse_push(p, BC_INST_CALL);
        bc_parse_pushIndex(p, nparams);
 
@@ -3785,7 +3790,7 @@ static BC_STATUS zbc_parse_name(BcParse *p, BcInst *type, uint8_t flags)
                } else {
                        *type = BC_INST_ARRAY_ELEM;
                        flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
-                       s = zbc_parse_expr(p, flags, bc_parse_next_elem);
+                       s = zbc_parse_expr(p, flags);
                        if (s) goto err;
                }
                s = zbc_lex_next(&p->l);
@@ -3848,7 +3853,7 @@ static BC_STATUS zbc_parse_builtin(BcParse *p, BcLexType type, uint8_t flags,
        s = zbc_lex_next(&p->l);
        if (s) RETURN_STATUS(s);
 
-       s = zbc_parse_expr(p, flags, bc_parse_next_rel);
+       s = zbc_parse_expr(p, flags);
        if (s) RETURN_STATUS(s);
 
        if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
@@ -3879,7 +3884,7 @@ static BC_STATUS zbc_parse_scale(BcParse *p, BcInst *type, uint8_t flags)
        s = zbc_lex_next(&p->l);
        if (s) RETURN_STATUS(s);
 
-       s = zbc_parse_expr(p, flags, bc_parse_next_rel);
+       s = zbc_parse_expr(p, flags);
        if (s) RETURN_STATUS(s);
        if (p->l.t.t != BC_LEX_RPAREN)
                RETURN_STATUS(bc_error_bad_token());
@@ -3999,7 +4004,7 @@ static BC_STATUS zbc_parse_print(BcParse *p)
                if (type == BC_LEX_STR) {
                        s = zbc_parse_string(p, BC_INST_PRINT_POP);
                } else {
-                       s = zbc_parse_expr(p, 0, bc_parse_next_print);
+                       s = zbc_parse_expr(p, 0);
                        bc_parse_push(p, BC_INST_PRINT_POP);
                }
                if (s) RETURN_STATUS(s);
@@ -4025,7 +4030,7 @@ static BC_STATUS zbc_parse_return(BcParse *p)
                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);
+               s = bc_parse_expr_empty_ok(p, 0);
                if (s == BC_STATUS_PARSE_EMPTY_EXP) {
                        bc_parse_push(p, BC_INST_RET0);
                        s = zbc_lex_next(&p->l);
@@ -4063,7 +4068,7 @@ static BC_STATUS zbc_parse_if(BcParse *p)
 
        s = zbc_lex_next(&p->l);
        if (s) RETURN_STATUS(s);
-       s = zbc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel);
+       s = zbc_parse_expr(p, BC_PARSE_REL);
        if (s) RETURN_STATUS(s);
 
        if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
@@ -4123,7 +4128,7 @@ static BC_STATUS zbc_parse_while(BcParse *p)
        bc_vec_push(&p->exits, &ip_idx);
        bc_vec_push(&p->func->labels, &ip_idx);
 
-       s = zbc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel);
+       s = zbc_parse_expr(p, BC_PARSE_REL);
        if (s) RETURN_STATUS(s);
        if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
 
@@ -4158,7 +4163,7 @@ static BC_STATUS zbc_parse_for(BcParse *p)
        if (s) RETURN_STATUS(s);
 
        if (p->l.t.t != BC_LEX_SCOLON)
-               s = zbc_parse_expr(p, 0, bc_parse_next_for);
+               s = zbc_parse_expr(p, 0);
        else
                s = bc_POSIX_does_not_allow_empty_X_expression_in_for("init");
 
@@ -4175,7 +4180,7 @@ static BC_STATUS zbc_parse_for(BcParse *p)
        bc_vec_push(&p->func->labels, &p->func->code.len);
 
        if (p->l.t.t != BC_LEX_SCOLON)
-               s = zbc_parse_expr(p, BC_PARSE_REL, bc_parse_next_for);
+               s = zbc_parse_expr(p, BC_PARSE_REL);
        else
                s = bc_POSIX_does_not_allow_empty_X_expression_in_for("condition");
 
@@ -4192,7 +4197,7 @@ static BC_STATUS zbc_parse_for(BcParse *p)
        bc_vec_push(&p->func->labels, &p->func->code.len);
 
        if (p->l.t.t != BC_LEX_RPAREN)
-               s = zbc_parse_expr(p, 0, bc_parse_next_rel);
+               s = zbc_parse_expr(p, 0);
        else
                s = bc_POSIX_does_not_allow_empty_X_expression_in_for("update");
 
@@ -4444,7 +4449,7 @@ static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed)
                case BC_LEX_KEY_READ:
                case BC_LEX_KEY_SCALE:
                case BC_LEX_KEY_SQRT:
-                       s = zbc_parse_expr(p, BC_PARSE_PRINT, bc_parse_next_expr);
+                       s = zbc_parse_expr(p, BC_PARSE_PRINT);
                        break;
                case BC_LEX_STR:
                        s = zbc_parse_string(p, BC_INST_PRINT_STR);
@@ -4530,7 +4535,7 @@ static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p)
 #define zbc_parse_stmt_or_funcdef(...) (zbc_parse_stmt_or_funcdef(__VA_ARGS__) COMMA_SUCCESS)
 
 // This is not a "z" function: can also return BC_STATUS_PARSE_EMPTY_EXP
-static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext next)
+static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags)
 {
        BcStatus s = BC_STATUS_SUCCESS;
        BcInst prev = BC_INST_PRINT;
@@ -4766,19 +4771,6 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext ne
        if (prev == BC_INST_BOOL_NOT || nexprs != 1)
                return bc_error_bad_expression();
 
-//TODO: why is this needed at all?
-       // next is BcParseNext, byte array of up to 4 BC_LEX's, packed into 32-bit word
-       for (;;) {
-               if (t == (next & 0x7f))
-                       goto ok;
-               if (next & 0x80) // last element?
-                       break;
-               next >>= 8;
-       }
-       if (t != BC_LEX_KEY_ELSE)
-               return bc_error_bad_expression();
- ok:
-
        if (!(flags & BC_PARSE_REL) && nrelops) {
                s = bc_POSIX_does_not_allow("comparison operators outside if or loops");
                IF_ERROR_RETURN_POSSIBLE(if (s) return s);
@@ -4797,18 +4789,6 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext ne
        return s;
 }
 
-#undef zbc_parse_expr
-static BC_STATUS zbc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
-{
-       BcStatus s;
-
-       s = bc_parse_expr_empty_ok(p, flags, next);
-       if (s == BC_STATUS_PARSE_EMPTY_EXP)
-               RETURN_STATUS(bc_error("empty expression"));
-       RETURN_STATUS(s);
-}
-#define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__) COMMA_SUCCESS)
-
 #endif // ENABLE_BC
 
 #if ENABLE_DC
@@ -5008,7 +4988,7 @@ static BC_STATUS zdc_parse_parse(BcParse *p)
 static BC_STATUS zcommon_parse_expr(BcParse *p, uint8_t flags)
 {
        if (IS_BC) {
-               IF_BC(RETURN_STATUS(zbc_parse_expr(p, flags, bc_parse_next_read)));
+               IF_BC(RETURN_STATUS(zbc_parse_expr(p, flags)));
        } else {
                IF_DC(RETURN_STATUS(zdc_parse_expr(p, flags)));
        }
index d057bea17476467036c4fe820a4d463e146d812d..e0a45a8bd9042c67f1a04cb11108bf9d0beec003 100755 (executable)
@@ -76,6 +76,11 @@ testing "bc print 1,2,3" \
        "123" \
        "" "print 1,2,3"
 
+testing "bc { print 1 }" \
+       "bc" \
+       "1" \
+       "" "{ print 1 }"
+
 testing "bc nested loops and breaks" \
        "bc" \
        "\