bc: upstream fixes
authorDenys Vlasenko <vda.linux@googlemail.com>
Tue, 1 Jan 2019 20:50:14 +0000 (21:50 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 1 Jan 2019 20:50:14 +0000 (21:50 +0100)
function                                             old     new   delta
bc_parse_expr_empty_ok                              1764    1843     +79
bc_error_at                                            -      62     +62
bc_parse_inst_isLeaf                                   -      30     +30
zbc_func_insert                                      100     120     +20
bc_error_bad_function_definition                       -      10     +10
bc_error_bad_assignment                                -      10     +10
zxc_lex_next                                        1608    1614      +6
ok_in_expr                                            30       -     -30
zxc_vm_process                                       874     839     -35
------------------------------------------------------------------------------
(add/remove: 4/1 grow/shrink: 3/1 up/down: 217/-65)           Total: 152 bytes

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

index 23b3521d4c51d90e7c33f3546221b3d683c87f48..1b9cdce5e3255f8147641d20e77200938992acbb 100644 (file)
@@ -971,19 +971,42 @@ static ERRORFUNC int bc_error(const char *msg)
 {
        IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("%s", msg);
 }
+static ERRORFUNC int bc_error_at(const char *msg)
+{
+       const char *err_at = G.prs.lex_next_at;
+       if (err_at) {
+               IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt(
+                       "%s at '%.*s'",
+                       msg,
+                       (int)(strchrnul(err_at, '\n') - err_at),
+                       err_at
+               );
+       }
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("%s", msg);
+}
 static ERRORFUNC int bc_error_bad_character(char c)
 {
        if (!c)
                IF_ERROR_RETURN_POSSIBLE(return) bc_error("NUL character");
        IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("bad character '%c'", c);
 }
+static ERRORFUNC int bc_error_bad_function_definition(void)
+{
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad function definition");
+}
 static ERRORFUNC int bc_error_bad_expression(void)
 {
-       IF_ERROR_RETURN_POSSIBLE(return) bc_error("bad expression");
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad expression");
+}
+static ERRORFUNC int bc_error_bad_assignment(void)
+{
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_at(
+               "bad assignment: left side must be variable or array element"
+       );
 }
 static ERRORFUNC int bc_error_bad_token(void)
 {
-       IF_ERROR_RETURN_POSSIBLE(return) bc_error("bad token");
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad token");
 }
 static ERRORFUNC int bc_error_stack_has_too_few_elements(void)
 {
@@ -2853,6 +2876,7 @@ static BC_STATUS zxc_lex_number(char last)
                if (c == '\\' && p->lex_inbuf[1] == '\n') {
                        p->lex_inbuf += 2;
                        p->lex_line++;
+                       dbg_lex("++p->lex_line=%zd", p->lex_line);
                        c = peek_inbuf(); // force next line to be read
                        goto check_c;
                }
@@ -2919,6 +2943,7 @@ static BC_STATUS zxc_lex_next(void)
        BcParse *p = &G.prs;
        BcStatus s;
 
+       G.err_line = p->lex_line;
        p->lex_last = p->lex;
 //why?
 //     if (p->lex_last == XC_LEX_EOF)
@@ -3031,8 +3056,10 @@ static BC_STATUS zbc_lex_string(void)
                }
                if (c == '"')
                        break;
-               if (c == '\n')
+               if (c == '\n') {
                        p->lex_line++;
+                       dbg_lex("++p->lex_line=%zd", p->lex_line);
+               }
                bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf);
                p->lex_inbuf++;
        }
@@ -3079,8 +3106,10 @@ static BC_STATUS zbc_lex_comment(void)
                if (c == '\0') {
                        RETURN_STATUS(bc_error("unterminated comment"));
                }
-               if (c == '\n')
+               if (c == '\n') {
                        p->lex_line++;
+                       dbg_lex("++p->lex_line=%zd", p->lex_line);
+               }
        }
        p->lex_inbuf++; // skip trailing '/'
 
@@ -3105,6 +3134,7 @@ static BC_STATUS zbc_lex_token(void)
 //             break;
        case '\n':
                p->lex_line++;
+               dbg_lex("++p->lex_line=%zd", p->lex_line);
                p->lex = XC_LEX_NLINE;
                break;
        case '\t':
@@ -3341,8 +3371,10 @@ static BC_STATUS zdc_lex_string(void)
                if (c == ']')
                        if (--depth == 0)
                                break;
-               if (c == '\n')
+               if (c == '\n') {
                        p->lex_line++;
+                       dbg_lex("++p->lex_line=%zd", p->lex_line);
+               }
                bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf);
                p->lex_inbuf++;
        }
@@ -3399,6 +3431,7 @@ static BC_STATUS zdc_lex_token(void)
                // IOW: typing "1p<enter>" should print "1" _at once_,
                // not after some more input.
                p->lex_line++;
+               dbg_lex("++p->lex_line=%zd", p->lex_line);
                p->lex = XC_LEX_NLINE;
                break;
        case '\t':
@@ -3960,8 +3993,7 @@ static BC_STATUS zbc_parse_scale(BcInst *type, uint8_t flags)
 }
 #define zbc_parse_scale(...) (zbc_parse_scale(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_incdec(BcInst *prev, bool *paren_expr,
-                               size_t *nexprs, uint8_t flags)
+static BC_STATUS zbc_parse_incdec(BcInst *prev, size_t *nexs, uint8_t flags)
 {
        BcParse *p = &G.prs;
        BcStatus s;
@@ -3978,7 +4010,6 @@ static BC_STATUS zbc_parse_incdec(BcInst *prev, bool *paren_expr,
                s = zxc_lex_next();
        } else {
                *prev = inst = BC_INST_INC_PRE + (p->lex != BC_LEX_OP_INC);
-               *paren_expr = true;
 
                s = zxc_lex_next();
                if (s) RETURN_STATUS(s);
@@ -3986,7 +4017,7 @@ static BC_STATUS zbc_parse_incdec(BcInst *prev, bool *paren_expr,
 
                // Because we parse the next part of the expression
                // right here, we need to increment this.
-               *nexprs = *nexprs + 1;
+               *nexs = *nexs + 1;
 
                switch (type) {
                case XC_LEX_NAME:
@@ -4018,36 +4049,27 @@ static BC_STATUS zbc_parse_incdec(BcInst *prev, bool *paren_expr,
 }
 #define zbc_parse_incdec(...) (zbc_parse_incdec(__VA_ARGS__) COMMA_SUCCESS)
 
-#if 0
-#define BC_PARSE_LEAF(p, rparen) \
-       ((rparen) \
-        || ((p) >= XC_INST_NUM && (p) <= XC_INST_SQRT) \
-        || (p) == BC_INST_INC_POST \
-        || (p) == BC_INST_DEC_POST \
-       )
-#else
-static int ok_in_expr(BcInst p)
+static int bc_parse_inst_isLeaf(BcInst p)
 {
        return (p >= XC_INST_NUM && p <= XC_INST_SQRT)
                || p == BC_INST_INC_POST
                || p == BC_INST_DEC_POST
                ;
 }
-#define BC_PARSE_LEAF(p, rparen) ((rparen) || ok_in_expr(p))
-#endif
+#define BC_PARSE_LEAF(prev, bin_last, rparen) \
+       (!(bin_last) && ((rparen) || bc_parse_inst_isLeaf(prev)))
 
 static BC_STATUS zbc_parse_minus(BcInst *prev, size_t ops_bgn,
-                               bool rparen, size_t *nexprs)
+                               bool rparen, bool bin_last, size_t *nexprs)
 {
        BcParse *p = &G.prs;
        BcStatus s;
        BcLexType type;
-       BcInst etype = *prev;
 
        s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       type = BC_PARSE_LEAF(etype, rparen) ? XC_LEX_OP_MINUS : XC_LEX_NEG;
+       type = BC_PARSE_LEAF(*prev, bin_last, rparen) ? XC_LEX_OP_MINUS : XC_LEX_NEG;
        *prev = BC_TOKEN_2_INST(type);
 
        // We can just push onto the op stack because this is the largest
@@ -4334,8 +4356,11 @@ static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var)
 
        autoid = (void*)f->autos.v;
        for (i = 0; i < f->autos.len; i++, autoid++) {
-               if (strcmp(name, autoid->name) == 0)
+               if (strcmp(name, autoid->name) == 0
+                && var == autoid->idx
+               ) {
                        RETURN_STATUS(bc_error("duplicate function parameter or auto name"));
+               }
        }
 
        a.idx = var;
@@ -4358,7 +4383,7 @@ static BC_STATUS zbc_parse_funcdef(void)
        s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
        if (p->lex != XC_LEX_NAME)
-               RETURN_STATUS(bc_error("bad function definition"));
+               RETURN_STATUS(bc_error_bad_function_definition());
 
        name = xstrdup(p->lex_strnumbuf.v);
        p->fidx = bc_program_addFunc(name);
@@ -4367,13 +4392,13 @@ static BC_STATUS zbc_parse_funcdef(void)
        s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
        if (p->lex != BC_LEX_LPAREN)
-               RETURN_STATUS(bc_error("bad function definition"));
+               RETURN_STATUS(bc_error_bad_function_definition());
        s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
        while (p->lex != BC_LEX_RPAREN) {
                if (p->lex != XC_LEX_NAME)
-                       RETURN_STATUS(bc_error("bad function definition"));
+                       RETURN_STATUS(bc_error_bad_function_definition());
 
                ++p->func->nparams;
 
@@ -4388,7 +4413,7 @@ static BC_STATUS zbc_parse_funcdef(void)
                        if (s) goto err;
 
                        if (p->lex != BC_LEX_RBRACKET) {
-                               s = bc_error("bad function definition");
+                               s = bc_error_bad_function_definition();
                                goto err;
                        }
 
@@ -4406,7 +4431,7 @@ static BC_STATUS zbc_parse_funcdef(void)
                if (s) goto err;
        }
 
-       if (comma) RETURN_STATUS(bc_error("bad function definition"));
+       if (comma) RETURN_STATUS(bc_error_bad_function_definition());
 
        s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
@@ -4457,7 +4482,7 @@ static BC_STATUS zbc_parse_auto(void)
                bool var;
 
                if (p->lex != XC_LEX_NAME)
-                       RETURN_STATUS(bc_error("bad 'auto' syntax"));
+                       RETURN_STATUS(bc_error_at("bad 'auto' syntax"));
 
                name = xstrdup(p->lex_strnumbuf.v);
                s = zxc_lex_next();
@@ -4469,7 +4494,7 @@ static BC_STATUS zbc_parse_auto(void)
                        if (s) goto err;
 
                        if (p->lex != BC_LEX_RBRACKET) {
-                               s = bc_error("bad 'auto' syntax");
+                               s = bc_error_at("bad 'auto' syntax");
                                goto err;
                        }
                        s = zxc_lex_next();
@@ -4486,7 +4511,7 @@ static BC_STATUS zbc_parse_auto(void)
                        break;
                }
                if (p->lex != BC_LEX_COMMA)
-                       RETURN_STATUS(bc_error("bad 'auto' syntax"));
+                       RETURN_STATUS(bc_error_at("bad 'auto' syntax"));
                s = zxc_lex_next(); // skip comma
                if (s) RETURN_STATUS(s);
        }
@@ -4643,12 +4668,12 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
        BcInst prev = XC_INST_PRINT;
        size_t nexprs = 0, ops_bgn = p->ops.len;
        unsigned nparens, nrelops;
-       bool paren_first, paren_expr, rprn, assign, bin_last;
+       bool paren_first, rprn, assign, bin_last, incdec;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
        paren_first = (p->lex == BC_LEX_LPAREN);
        nparens = nrelops = 0;
-       paren_expr = rprn = assign = false;
+       rprn = assign = incdec = false;
        bin_last = true;
 
        for (;;) {
@@ -4666,16 +4691,19 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
                case BC_LEX_OP_INC:
                case BC_LEX_OP_DEC:
                        dbg_lex("%s:%d LEX_OP_INC/DEC", __func__, __LINE__);
-                       s = zbc_parse_incdec(&prev, &paren_expr, &nexprs, flags);
+                       if (incdec) return bc_error_bad_assignment();
+                       s = zbc_parse_incdec(&prev, &nexprs, flags);
+                       incdec = true;
                        rprn = bin_last = false;
                        //get_token = false; - already is
                        break;
                case XC_LEX_OP_MINUS:
                        dbg_lex("%s:%d LEX_OP_MINUS", __func__, __LINE__);
-                       s = zbc_parse_minus(&prev, ops_bgn, rprn, &nexprs);
+                       s = zbc_parse_minus(&prev, ops_bgn, rprn, bin_last, &nexprs);
                        rprn = false;
                        //get_token = false; - already is
                        bin_last = (prev == XC_INST_MINUS);
+                       if (bin_last) incdec = false;
                        break;
                case BC_LEX_OP_ASSIGN_POWER:
                case BC_LEX_OP_ASSIGN_MULTIPLY:
@@ -4689,10 +4717,7 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
                         && prev != XC_INST_SCALE && prev != XC_INST_IBASE
                         && prev != XC_INST_OBASE && prev != BC_INST_LAST
                        ) {
-                               return bc_error("bad assignment:"
-                                       " left side must be variable"
-                                       " or array element"
-                               ); // note: shared string
+                               return bc_error_bad_assignment();
                        }
                // Fallthrough.
                case XC_LEX_OP_POWER:
@@ -4710,64 +4735,63 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
                case BC_LEX_OP_BOOL_OR:
                case BC_LEX_OP_BOOL_AND:
                        dbg_lex("%s:%d LEX_OP_xyz", __func__, __LINE__);
-                       if (((t == BC_LEX_OP_BOOL_NOT) != bin_last)
-                        || (t != BC_LEX_OP_BOOL_NOT && prev == XC_INST_BOOL_NOT)
-                       ) {
+                       if (t == BC_LEX_OP_BOOL_NOT) {
+                               if (!bin_last && p->lex_last != BC_LEX_OP_BOOL_NOT)
+                                       return bc_error_bad_expression();
+                       } else if (prev == XC_INST_BOOL_NOT) {
                                return bc_error_bad_expression();
                        }
+
                        nrelops += (t >= XC_LEX_OP_REL_EQ && t <= XC_LEX_OP_REL_GT);
                        prev = BC_TOKEN_2_INST(t);
                        bc_parse_operator(t, ops_bgn, &nexprs);
-                       s = zxc_lex_next();
-                       rprn = false;
-                       //get_token = false; - already is
+                       rprn = incdec = false;
+                       get_token = true;
                        bin_last = (t != BC_LEX_OP_BOOL_NOT);
                        break;
                case BC_LEX_LPAREN:
                        dbg_lex("%s:%d LEX_LPAREN", __func__, __LINE__);
-                       if (BC_PARSE_LEAF(prev, rprn))
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
                                return bc_error_bad_expression();
                        bc_vec_push(&p->ops, &t);
                        nparens++;
                        get_token = true;
-                       paren_expr = false;
-                       rprn = bin_last = false;
+                       rprn = incdec = false;
                        break;
                case BC_LEX_RPAREN:
                        dbg_lex("%s:%d LEX_RPAREN", __func__, __LINE__);
+                       if (p->lex_last == BC_LEX_LPAREN) {
+                               dbg_lex_done("%s:%d done (returning EMPTY_EXP)", __func__, __LINE__);
+                               return BC_STATUS_PARSE_EMPTY_EXP;
+                       }
                        if (bin_last || prev == XC_INST_BOOL_NOT)
                                return bc_error_bad_expression();
                        if (nparens == 0) {
                                goto exit_loop;
                        }
-                       if (!paren_expr) {
-                               dbg_lex_done("%s:%d done (returning EMPTY_EXP)", __func__, __LINE__);
-                               return BC_STATUS_PARSE_EMPTY_EXP;
-                       }
                        s = zbc_parse_rightParen(ops_bgn, &nexprs);
                        nparens--;
                        get_token = true;
-                       paren_expr = rprn = true;
-                       bin_last = false;
+                       rprn = true;
+                       bin_last = incdec = false;
                        break;
                case XC_LEX_NAME:
                        dbg_lex("%s:%d LEX_NAME", __func__, __LINE__);
-                       if (BC_PARSE_LEAF(prev, rprn))
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
                                return bc_error_bad_expression();
                        s = zbc_parse_name(&prev, flags & ~BC_PARSE_NOCALL);
-                       paren_expr = true;
-                       rprn = bin_last = false;
+                       rprn = (prev == BC_INST_CALL);
+                       bin_last = false;
                        //get_token = false; - already is
                        nexprs++;
                        break;
                case XC_LEX_NUMBER:
                        dbg_lex("%s:%d LEX_NUMBER", __func__, __LINE__);
-                       if (BC_PARSE_LEAF(prev, rprn))
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
                                return bc_error_bad_expression();
                        xc_parse_pushNUM();
                        prev = XC_INST_NUM;
                        get_token = true;
-                       paren_expr = true;
                        rprn = bin_last = false;
                        nexprs++;
                        break;
@@ -4775,45 +4799,40 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
                case BC_LEX_KEY_LAST:
                case BC_LEX_KEY_OBASE:
                        dbg_lex("%s:%d LEX_IBASE/LAST/OBASE", __func__, __LINE__);
-                       if (BC_PARSE_LEAF(prev, rprn))
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
                                return bc_error_bad_expression();
                        prev = (char) (t - BC_LEX_KEY_IBASE + XC_INST_IBASE);
                        xc_parse_push((char) prev);
                        get_token = true;
-                       paren_expr = true;
                        rprn = bin_last = false;
                        nexprs++;
                        break;
                case BC_LEX_KEY_LENGTH:
                case BC_LEX_KEY_SQRT:
                        dbg_lex("%s:%d LEX_LEN/SQRT", __func__, __LINE__);
-                       if (BC_PARSE_LEAF(prev, rprn))
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
                                return bc_error_bad_expression();
                        s = zbc_parse_builtin(t, flags, &prev);
                        get_token = true;
-                       paren_expr = true;
-                       rprn = bin_last = false;
+                       rprn = bin_last = incdec = false;
                        nexprs++;
                        break;
                case BC_LEX_KEY_READ:
                        dbg_lex("%s:%d LEX_READ", __func__, __LINE__);
-                       if (BC_PARSE_LEAF(prev, rprn))
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
                                return bc_error_bad_expression();
                        s = zbc_parse_read();
                        prev = XC_INST_READ;
                        get_token = true;
-                       paren_expr = true;
-                       rprn = bin_last = false;
+                       rprn = bin_last = incdec = false;
                        nexprs++;
                        break;
                case BC_LEX_KEY_SCALE:
                        dbg_lex("%s:%d LEX_SCALE", __func__, __LINE__);
-                       if (BC_PARSE_LEAF(prev, rprn))
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
                                return bc_error_bad_expression();
                        s = zbc_parse_scale(&prev, flags);
-                       prev = XC_INST_SCALE;
                        //get_token = false; - already is
-                       paren_expr = true;
                        rprn = bin_last = false;
                        nexprs++;
                        break;
@@ -5286,7 +5305,7 @@ static BC_STATUS zxc_program_read(void)
        }
        if (s) goto exec_err;
        if (G.prs.lex != XC_LEX_NLINE && G.prs.lex != XC_LEX_EOF) {
-               s = bc_error("bad read() expression");
+               s = bc_error_at("bad read() expression");
                goto exec_err;
        }
        xc_parse_push(XC_INST_RET);
@@ -5794,10 +5813,7 @@ static BC_STATUS zxc_program_assign(char inst)
 #endif
 
        if (left->t == XC_RESULT_CONSTANT || left->t == XC_RESULT_TEMP)
-               RETURN_STATUS(bc_error("bad assignment:"
-                               " left side must be variable"
-                               " or array element"
-               )); // note: shared string
+               RETURN_STATUS(bc_error_bad_assignment());
 
 #if ENABLE_BC
        if (inst == BC_INST_ASSIGN_DIVIDE && !bc_num_cmp(r, &G.prog.zero))
@@ -6771,13 +6787,7 @@ static BC_STATUS zxc_vm_process(const char *text)
                         && G.prs.lex != XC_LEX_NLINE
                         && G.prs.lex != XC_LEX_EOF
                        ) {
-                               const char *err_at;
-//TODO: commonalize for other parse errors:
-                               err_at = G.prs.lex_next_at ? G.prs.lex_next_at : "UNKNOWN";
-                               bc_error_fmt("bad statement terminator at '%.*s'",
-                                       (int)(strchrnul(err_at, '\n') - err_at),
-                                       err_at
-                               );
+                               bc_error_at("bad statement terminator");
                                goto err;
                        }
                        // The above logic is fragile. Check these examples:
@@ -6871,6 +6881,7 @@ static BC_STATUS zxc_vm_execute_FILE(FILE *fp, const char *filename)
        G.prs.lex_filename = filename;
        G.prs.lex_input_fp = fp;
        G.err_line = G.prs.lex_line = 1;
+       dbg_lex("p->lex_line reset to 1");
 
        do {
                s = zxc_vm_process("");
index 1d4545559b76d9f6ade839e506678d181a71b8a0..13525ea52dc7f6501aa6423063d82870d9b60d33 100755 (executable)
@@ -108,6 +108,11 @@ testing "bc define auto" \
        "8\n9\n" \
        "" "define w() { auto z; return 8; }; w(); 9"
 
+testing "bc define auto array same name" \
+       "bc" \
+       "8\n9\n" \
+       "" "define w(x) { auto x[]; return x; }; w(8); 9"
+
 testing "bc define with body on next line" \
        "bc" \
        "8\n9\n" \
@@ -133,6 +138,17 @@ testing "bc ifz does not match if keyword" \
        "1\n2\n2\n3\n" \
        "" "ifz=1;ifz\n++ifz;ifz++\nifz"
 
+# had parse error on "f()-N"
+testing "bc -l 'e(0)-2'" \
+       "bc -l" \
+       "-1.00000000000000000000\n" \
+       "" "e(0)-2"
+
+testing "bc (!a&&b)" \
+       "bc" \
+       "0\n" \
+       "" "(!a&&b)"
+
 testing "bc print 1,2,3" \
        "bc" \
        "123" \