diff options
-rw-r--r-- | miscutils/bc.c | 177 | ||||
-rwxr-xr-x | testsuite/bc.tests | 16 |
2 files changed, 110 insertions, 83 deletions
diff --git a/miscutils/bc.c b/miscutils/bc.c index 23b3521..1b9cdce 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -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(""); diff --git a/testsuite/bc.tests b/testsuite/bc.tests index 1d45455..13525ea 100755 --- a/testsuite/bc.tests +++ b/testsuite/bc.tests @@ -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" \ |