bc: shrink error handling code by enabling tail-call optimization

function                                             old     new   delta
bc_posix_error_fmt                                     -      60     +60
bc_error_fmt                                           -      38     +38
bc_error_bad_character                                 -      17     +17
bc_parse_auto                                        302     313     +11
bc_num_ulong                                          93     103     +10
bc_error_variable_is_wrong_type                        -      10     +10
bc_error_stack_has_too_few_elements                    -      10     +10
bc_error_nested_read_call                              -      10     +10
bc_error_bad_token                                     -      10     +10
bc_error_bad_expression                                -      10     +10
bc_num_d                                             561     564      +3
bc_program_assign                                    484     486      +2
dc_parse_parse                                        60      59      -1
bc_program_num                                      1130    1129      -1
bc_program_call                                      367     366      -1
bc_parse_stmt                                       2277    2276      -1
bc_lex_number                                        299     298      -1
bc_lex_name                                           91      90      -1
bc_func_insert                                        98      97      -1
bc_num_shift                                          74      72      -2
bc_vm_run                                           1924    1921      -3
bc_parse_endBody                                     424     420      -4
bc_lex_next                                           81      77      -4
bc_program_read                                      343     338      -5
bc_program_asciify                                   469     464      -5
bc_program_pushVar                                   221     215      -6
bc_program_print                                     784     778      -6
bc_program_prep                                       99      93      -6
bc_program_assignStr                                 167     161      -6
bc_parse_else                                        143     137      -6
bc_parse_body                                        124     118      -6
dc_parse_register                                     61      53      -8
dc_lex_token                                         699     691      -8
bc_program_modexp                                    756     746     -10
bc_parse_name                                        593     581     -12
dc_parse_expr                                        775     762     -13
bc_program_copyToVar                                 336     323     -13
bc_program_binOpPrep                                 324     311     -13
bc_program_execStr                                   561     543     -18
bc_program_exec                                     4150    4132     -18
bc_parse_parse                                       483     464     -19
bc_error                                              38      14     -24
bc_lex_token                                        1266    1237     -29
bc_posix_error                                        60      14     -46
bc_parse_expr                                       2194    2146     -48
------------------------------------------------------------------------------
(add/remove: 8/0 grow/shrink: 4/33 up/down: 191/-345)        Total: -154 bytes
   text	   data	    bss	    dec	    hex	filename
 987226	    485	   7296	 995007	  f2ebf	busybox_old
 987072	    485	   7296	 994853	  f2e25	busybox_unstripped

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2018-12-05 16:03:46 +01:00
parent f359e004b0
commit 24fb2cd62f

View File

@ -1110,7 +1110,7 @@ static void quit(void)
exit(0); exit(0);
} }
static int bc_error(const char *fmt, ...) static NOINLINE int bc_error_fmt(const char *fmt, ...)
{ {
va_list p; va_list p;
@ -1122,12 +1122,13 @@ static int bc_error(const char *fmt, ...)
return BC_STATUS_FAILURE; return BC_STATUS_FAILURE;
} }
static int bc_posix_error(const char *fmt, ...) static NOINLINE int bc_posix_error_fmt(const char *fmt, ...)
{ {
va_list p; va_list p;
// Are non-POSIX constructs totally ok?
if (!(option_mask32 & (BC_FLAG_S|BC_FLAG_W))) if (!(option_mask32 & (BC_FLAG_S|BC_FLAG_W)))
return BC_STATUS_SUCCESS; return BC_STATUS_SUCCESS; // yes
va_start(p, fmt); va_start(p, fmt);
bb_verror_msg(fmt, p, NULL); bb_verror_msg(fmt, p, NULL);
@ -1141,6 +1142,44 @@ static int bc_posix_error(const char *fmt, ...)
return BC_STATUS_FAILURE; return BC_STATUS_FAILURE;
} }
// We use error functions with "return bc_error(FMT[, PARAMS])" idiom.
// This idiom begs for tail-call optimization, but for it to work,
// function must not have calller-cleaned parameters on stack.
// Unfortunately, vararg functions do exactly that on most arches.
// Thus, these shims for the cases when we have no PARAMS:
static int bc_error(const char *msg)
{
return bc_error_fmt("%s", msg);
}
static int bc_posix_error(const char *msg)
{
return bc_posix_error_fmt("%s", msg);
}
static int bc_error_bad_character(char c)
{
return bc_error_fmt("bad character '%c'", c);
}
static int bc_error_bad_expression(void)
{
return bc_error("bad expression");
}
static int bc_error_bad_token(void)
{
return bc_error("bad token");
}
static int bc_error_stack_has_too_few_elements(void)
{
return bc_error("stack has too few elements");
}
static int bc_error_variable_is_wrong_type(void)
{
return bc_error("variable is wrong type");
}
static int bc_error_nested_read_call(void)
{
return bc_error("read() call inside of a read() call");
}
static void bc_vec_grow(BcVec *v, size_t n) static void bc_vec_grow(BcVec *v, size_t n)
{ {
size_t cap = v->cap * 2; size_t cap = v->cap * 2;
@ -1349,7 +1388,7 @@ static BcStatus bc_read_line(BcVec *vec, const char *prompt)
|| i > 0x7e || i > 0x7e
) { ) {
// Bad chars on this line, ignore entire line // Bad chars on this line, ignore entire line
bc_error("illegal character 0x%02x", i); bc_error_fmt("illegal character 0x%02x", i);
bad_chars = 1; bad_chars = 1;
} }
c = (char) i; c = (char) i;
@ -3214,7 +3253,7 @@ static BcStatus bc_lex_token(BcLex *l)
} }
else { else {
l->t.t = BC_LEX_INVALID; l->t.t = BC_LEX_INVALID;
s = bc_error("bad character '%c'", '&'); s = bc_error_bad_character('&');
} }
break; break;
@ -3343,7 +3382,7 @@ static BcStatus bc_lex_token(BcLex *l)
++l->i; ++l->i;
} }
else else
s = bc_error("bad character '%c'", c); s = bc_error_bad_character(c);
break; break;
} }
@ -3404,7 +3443,7 @@ static BcStatus bc_lex_token(BcLex *l)
} }
else { else {
l->t.t = BC_LEX_INVALID; l->t.t = BC_LEX_INVALID;
s = bc_error("bad character '%c'", c); s = bc_error_bad_character(c);
} }
break; break;
@ -3413,7 +3452,7 @@ static BcStatus bc_lex_token(BcLex *l)
default: default:
{ {
l->t.t = BC_LEX_INVALID; l->t.t = BC_LEX_INVALID;
s = bc_error("bad character '%c'", c); s = bc_error_bad_character(c);
break; break;
} }
} }
@ -3526,7 +3565,7 @@ static BcStatus dc_lex_token(BcLex *l)
else if (c2 == '>') else if (c2 == '>')
l->t.t = BC_LEX_OP_REL_GE; l->t.t = BC_LEX_OP_REL_GE;
else else
return bc_error("bad character '%c'", c); return bc_error_bad_character(c);
++l->i; ++l->i;
break; break;
@ -3543,7 +3582,7 @@ static BcStatus dc_lex_token(BcLex *l)
if (isdigit(l->buf[l->i])) if (isdigit(l->buf[l->i]))
s = bc_lex_number(l, c); s = bc_lex_number(l, c);
else else
s = bc_error("bad character '%c'", c); s = bc_error_bad_character(c);
break; break;
} }
@ -3577,7 +3616,7 @@ static BcStatus dc_lex_token(BcLex *l)
default: default:
{ {
l->t.t = BC_LEX_INVALID; l->t.t = BC_LEX_INVALID;
s = bc_error("bad character '%c'", c); s = bc_error_bad_character(c);
break; break;
} }
} }
@ -3733,7 +3772,7 @@ static BcStatus bc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs)
BcLexType top; BcLexType top;
if (p->ops.len <= ops_bgn) if (p->ops.len <= ops_bgn)
return bc_error("bad expression"); return bc_error_bad_expression();
top = BC_PARSE_TOP_OP(p); top = BC_PARSE_TOP_OP(p);
while (top != BC_LEX_LPAREN) { while (top != BC_LEX_LPAREN) {
@ -3744,7 +3783,7 @@ static BcStatus bc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs)
*nexs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_NEG; *nexs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_NEG;
if (p->ops.len <= ops_bgn) if (p->ops.len <= ops_bgn)
return bc_error("bad expression"); return bc_error_bad_expression();
top = BC_PARSE_TOP_OP(p); top = BC_PARSE_TOP_OP(p);
} }
@ -3775,7 +3814,7 @@ static BcStatus bc_parse_params(BcParse *p, uint8_t flags)
} }
} }
if (comma) return bc_error("bad token"); if (comma) return bc_error_bad_token();
bc_parse_push(p, BC_INST_CALL); bc_parse_push(p, BC_INST_CALL);
bc_parse_pushIndex(p, nparams); bc_parse_pushIndex(p, nparams);
@ -3794,7 +3833,7 @@ static BcStatus bc_parse_call(BcParse *p, char *name, uint8_t flags)
if (s) goto err; if (s) goto err;
if (p->l.t.t != BC_LEX_RPAREN) { if (p->l.t.t != BC_LEX_RPAREN) {
s = bc_error("bad token"); s = bc_error_bad_token();
goto err; goto err;
} }
@ -3836,7 +3875,7 @@ static BcStatus bc_parse_name(BcParse *p, BcInst *type, uint8_t flags)
if (p->l.t.t == BC_LEX_RBRACKET) { if (p->l.t.t == BC_LEX_RBRACKET) {
if (!(flags & BC_PARSE_ARRAY)) { if (!(flags & BC_PARSE_ARRAY)) {
s = bc_error("bad expression"); s = bc_error_bad_expression();
goto err; goto err;
} }
@ -3859,7 +3898,7 @@ static BcStatus bc_parse_name(BcParse *p, BcInst *type, uint8_t flags)
else if (p->l.t.t == BC_LEX_LPAREN) { else if (p->l.t.t == BC_LEX_LPAREN) {
if (flags & BC_PARSE_NOCALL) { if (flags & BC_PARSE_NOCALL) {
s = bc_error("bad token"); s = bc_error_bad_token();
goto err; goto err;
} }
@ -3885,11 +3924,11 @@ static BcStatus bc_parse_read(BcParse *p)
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token();
bc_parse_push(p, BC_INST_READ); bc_parse_push(p, BC_INST_READ);
@ -3903,7 +3942,7 @@ static BcStatus bc_parse_builtin(BcParse *p, BcLexType type, uint8_t flags,
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token();
flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY; flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY;
@ -3913,7 +3952,7 @@ static BcStatus bc_parse_builtin(BcParse *p, BcLexType type, uint8_t flags,
s = bc_parse_expr(p, flags, bc_parse_next_rel); s = bc_parse_expr(p, flags, bc_parse_next_rel);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token();
*prev = (type == BC_LEX_KEY_LENGTH) ? BC_INST_LENGTH : BC_INST_SQRT; *prev = (type == BC_LEX_KEY_LENGTH) ? BC_INST_LENGTH : BC_INST_SQRT;
bc_parse_push(p, *prev); bc_parse_push(p, *prev);
@ -3942,7 +3981,7 @@ static BcStatus bc_parse_scale(BcParse *p, BcInst *type, uint8_t flags)
s = bc_parse_expr(p, flags, bc_parse_next_rel); s = bc_parse_expr(p, flags, bc_parse_next_rel);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token();
bc_parse_push(p, BC_INST_SCALE_FUNC); bc_parse_push(p, BC_INST_SCALE_FUNC);
return bc_lex_next(&p->l); return bc_lex_next(&p->l);
@ -3999,7 +4038,7 @@ static BcStatus bc_parse_incdec(BcParse *p, BcInst *prev, bool *paren_expr,
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
if (p->l.t.t == BC_LEX_LPAREN) if (p->l.t.t == BC_LEX_LPAREN)
s = bc_error("bad token"); s = bc_error_bad_token();
else else
bc_parse_push(p, BC_INST_SCALE); bc_parse_push(p, BC_INST_SCALE);
break; break;
@ -4007,7 +4046,7 @@ static BcStatus bc_parse_incdec(BcParse *p, BcInst *prev, bool *paren_expr,
default: default:
{ {
s = bc_error("bad token"); s = bc_error_bad_token();
break; break;
} }
} }
@ -4088,7 +4127,7 @@ static BcStatus bc_parse_print(BcParse *p)
} }
if (s) return s; if (s) return s;
if (comma) return bc_error("bad token"); if (comma) return bc_error_bad_token();
return bc_lex_next(&p->l); return bc_lex_next(&p->l);
} }
@ -4099,7 +4138,7 @@ static BcStatus bc_parse_return(BcParse *p)
BcLexType t; BcLexType t;
bool paren; bool paren;
if (!BC_PARSE_FUNC(p)) return bc_error("bad token"); if (!BC_PARSE_FUNC(p)) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
@ -4137,18 +4176,18 @@ static BcStatus bc_parse_endBody(BcParse *p, bool brace)
BcStatus s = BC_STATUS_SUCCESS; BcStatus s = BC_STATUS_SUCCESS;
if (p->flags.len <= 1 || (brace && p->nbraces == 0)) if (p->flags.len <= 1 || (brace && p->nbraces == 0))
return bc_error("bad token"); return bc_error_bad_token();
if (brace) { if (brace) {
if (p->l.t.t == BC_LEX_RBRACE) { if (p->l.t.t == BC_LEX_RBRACE) {
if (!p->nbraces) return bc_error("bad token"); if (!p->nbraces) return bc_error_bad_token();
--p->nbraces; --p->nbraces;
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
} }
else else
return bc_error("bad token"); return bc_error_bad_token();
} }
if (BC_PARSE_IF(p)) { if (BC_PARSE_IF(p)) {
@ -4234,13 +4273,13 @@ static BcStatus bc_parse_if(BcParse *p)
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel); s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
@ -4261,7 +4300,7 @@ static BcStatus bc_parse_else(BcParse *p)
{ {
BcInstPtr ip; BcInstPtr ip;
if (!BC_PARSE_IF_END(p)) return bc_error("bad token"); if (!BC_PARSE_IF_END(p)) return bc_error_bad_token();
ip.idx = p->func->labels.len; ip.idx = p->func->labels.len;
ip.func = ip.len = 0; ip.func = ip.len = 0;
@ -4285,7 +4324,7 @@ static BcStatus bc_parse_while(BcParse *p)
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
@ -4303,7 +4342,7 @@ static BcStatus bc_parse_while(BcParse *p)
s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel); s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
@ -4322,7 +4361,7 @@ static BcStatus bc_parse_for(BcParse *p)
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
@ -4332,7 +4371,7 @@ static BcStatus bc_parse_for(BcParse *p)
s = bc_posix_error("POSIX does not allow an empty init expression in a for loop"); s = bc_posix_error("POSIX does not allow an empty init expression in a for loop");
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_SCOLON) return bc_error("bad token"); if (p->l.t.t != BC_LEX_SCOLON) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
@ -4349,7 +4388,7 @@ static BcStatus bc_parse_for(BcParse *p)
s = bc_posix_error("POSIX does not allow an empty condition expression in a for loop"); s = bc_posix_error("POSIX does not allow an empty condition expression in a for loop");
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_SCOLON) return bc_error("bad token"); if (p->l.t.t != BC_LEX_SCOLON) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
@ -4371,7 +4410,7 @@ static BcStatus bc_parse_for(BcParse *p)
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token();
bc_parse_push(p, BC_INST_JUMP); bc_parse_push(p, BC_INST_JUMP);
bc_parse_pushIndex(p, cond_idx); bc_parse_pushIndex(p, cond_idx);
bc_vec_push(&p->func->labels, &p->func->code.len); bc_vec_push(&p->func->labels, &p->func->code.len);
@ -4394,17 +4433,17 @@ static BcStatus bc_parse_loopExit(BcParse *p, BcLexType type)
size_t i; size_t i;
BcInstPtr *ip; BcInstPtr *ip;
if (!BC_PARSE_LOOP(p)) return bc_error("bad token"); if (!BC_PARSE_LOOP(p)) return bc_error_bad_token();
if (type == BC_LEX_KEY_BREAK) { if (type == BC_LEX_KEY_BREAK) {
if (p->exits.len == 0) return bc_error("bad token"); if (p->exits.len == 0) return bc_error_bad_token();
i = p->exits.len - 1; i = p->exits.len - 1;
ip = bc_vec_item(&p->exits, i); ip = bc_vec_item(&p->exits, i);
while (!ip->func && i < p->exits.len) ip = bc_vec_item(&p->exits, i--); while (!ip->func && i < p->exits.len) ip = bc_vec_item(&p->exits, i--);
if (i >= p->exits.len && !ip->func) return bc_error("bad token"); if (i >= p->exits.len && !ip->func) return bc_error_bad_token();
i = ip->idx; i = ip->idx;
} }
@ -4418,7 +4457,7 @@ static BcStatus bc_parse_loopExit(BcParse *p, BcLexType type)
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_SCOLON && p->l.t.t != BC_LEX_NLINE) if (p->l.t.t != BC_LEX_SCOLON && p->l.t.t != BC_LEX_NLINE)
return bc_error("bad token"); return bc_error_bad_token();
return bc_lex_next(&p->l); return bc_lex_next(&p->l);
} }
@ -4506,7 +4545,7 @@ static BcStatus bc_parse_auto(BcParse *p)
bool comma, var, one; bool comma, var, one;
char *name; char *name;
if (!p->auto_part) return bc_error("bad token"); if (!p->auto_part) return bc_error_bad_token();
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
@ -4548,7 +4587,7 @@ static BcStatus bc_parse_auto(BcParse *p)
if (!one) return bc_error("no auto variable found"); if (!one) return bc_error("no auto variable found");
if (p->l.t.t != BC_LEX_NLINE && p->l.t.t != BC_LEX_SCOLON) if (p->l.t.t != BC_LEX_NLINE && p->l.t.t != BC_LEX_SCOLON)
return bc_error("bad token"); return bc_error_bad_token();
return bc_lex_next(&p->l); return bc_lex_next(&p->l);
@ -4566,7 +4605,7 @@ static BcStatus bc_parse_body(BcParse *p, bool brace)
if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) { if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) {
if (!brace) return bc_error("bad token"); if (!brace) return bc_error_bad_token();
p->auto_part = p->l.t.t != BC_LEX_KEY_AUTO; p->auto_part = p->l.t.t != BC_LEX_KEY_AUTO;
if (!p->auto_part) { if (!p->auto_part) {
@ -4603,7 +4642,7 @@ static BcStatus bc_parse_stmt(BcParse *p)
case BC_LEX_LBRACE: case BC_LEX_LBRACE:
{ {
if (!BC_PARSE_BODY(p)) return bc_error("bad token"); if (!BC_PARSE_BODY(p)) return bc_error_bad_token();
++p->nbraces; ++p->nbraces;
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
@ -4748,7 +4787,7 @@ static BcStatus bc_parse_stmt(BcParse *p)
default: default:
{ {
s = bc_error("bad token"); s = bc_error_bad_token();
break; break;
} }
} }
@ -4763,7 +4802,7 @@ static BcStatus bc_parse_parse(BcParse *p)
if (p->l.t.t == BC_LEX_EOF) if (p->l.t.t == BC_LEX_EOF)
s = p->flags.len > 0 ? bc_error("block end could not be found") : bc_error("end of file"); s = p->flags.len > 0 ? bc_error("block end could not be found") : bc_error("end of file");
else if (p->l.t.t == BC_LEX_KEY_DEFINE) { else if (p->l.t.t == BC_LEX_KEY_DEFINE) {
if (!BC_PARSE_CAN_EXEC(p)) return bc_error("bad token"); if (!BC_PARSE_CAN_EXEC(p)) return bc_error_bad_token();
s = bc_parse_func(p); s = bc_parse_func(p);
} }
else else
@ -4849,7 +4888,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
if (((t == BC_LEX_OP_BOOL_NOT) != bin_last) if (((t == BC_LEX_OP_BOOL_NOT) != bin_last)
|| (t != BC_LEX_OP_BOOL_NOT && prev == BC_INST_BOOL_NOT) || (t != BC_LEX_OP_BOOL_NOT && prev == BC_INST_BOOL_NOT)
) { ) {
return bc_error("bad expression"); return bc_error_bad_expression();
} }
nrelops += t >= BC_LEX_OP_REL_EQ && t <= BC_LEX_OP_REL_GT; nrelops += t >= BC_LEX_OP_REL_EQ && t <= BC_LEX_OP_REL_GT;
@ -4864,7 +4903,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
case BC_LEX_LPAREN: case BC_LEX_LPAREN:
{ {
if (BC_PARSE_LEAF(prev, rprn)) if (BC_PARSE_LEAF(prev, rprn))
return bc_error("bad expression"); return bc_error_bad_expression();
++nparens; ++nparens;
paren_expr = rprn = bin_last = false; paren_expr = rprn = bin_last = false;
get_token = true; get_token = true;
@ -4876,7 +4915,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
case BC_LEX_RPAREN: case BC_LEX_RPAREN:
{ {
if (bin_last || prev == BC_INST_BOOL_NOT) if (bin_last || prev == BC_INST_BOOL_NOT)
return bc_error("bad expression"); return bc_error_bad_expression();
if (nparens == 0) { if (nparens == 0) {
s = BC_STATUS_SUCCESS; s = BC_STATUS_SUCCESS;
@ -4899,7 +4938,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
case BC_LEX_NAME: case BC_LEX_NAME:
{ {
if (BC_PARSE_LEAF(prev, rprn)) if (BC_PARSE_LEAF(prev, rprn))
return bc_error("bad expression"); return bc_error_bad_expression();
paren_expr = true; paren_expr = true;
rprn = get_token = bin_last = false; rprn = get_token = bin_last = false;
s = bc_parse_name(p, &prev, flags & ~BC_PARSE_NOCALL); s = bc_parse_name(p, &prev, flags & ~BC_PARSE_NOCALL);
@ -4911,7 +4950,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
case BC_LEX_NUMBER: case BC_LEX_NUMBER:
{ {
if (BC_PARSE_LEAF(prev, rprn)) if (BC_PARSE_LEAF(prev, rprn))
return bc_error("bad expression"); return bc_error_bad_expression();
bc_parse_number(p, &prev, &nexprs); bc_parse_number(p, &prev, &nexprs);
paren_expr = get_token = true; paren_expr = get_token = true;
rprn = bin_last = false; rprn = bin_last = false;
@ -4924,7 +4963,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
case BC_LEX_KEY_OBASE: case BC_LEX_KEY_OBASE:
{ {
if (BC_PARSE_LEAF(prev, rprn)) if (BC_PARSE_LEAF(prev, rprn))
return bc_error("bad expression"); return bc_error_bad_expression();
prev = (char) (t - BC_LEX_KEY_IBASE + BC_INST_IBASE); prev = (char) (t - BC_LEX_KEY_IBASE + BC_INST_IBASE);
bc_parse_push(p, (char) prev); bc_parse_push(p, (char) prev);
@ -4939,7 +4978,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
case BC_LEX_KEY_SQRT: case BC_LEX_KEY_SQRT:
{ {
if (BC_PARSE_LEAF(prev, rprn)) if (BC_PARSE_LEAF(prev, rprn))
return bc_error("bad expression"); return bc_error_bad_expression();
s = bc_parse_builtin(p, t, flags, &prev); s = bc_parse_builtin(p, t, flags, &prev);
paren_expr = true; paren_expr = true;
rprn = get_token = bin_last = false; rprn = get_token = bin_last = false;
@ -4951,9 +4990,9 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
case BC_LEX_KEY_READ: case BC_LEX_KEY_READ:
{ {
if (BC_PARSE_LEAF(prev, rprn)) if (BC_PARSE_LEAF(prev, rprn))
return bc_error("bad expression"); return bc_error_bad_expression();
else if (flags & BC_PARSE_NOREAD) else if (flags & BC_PARSE_NOREAD)
s = bc_error("read() call inside of a read() call"); s = bc_error_nested_read_call();
else else
s = bc_parse_read(p); s = bc_parse_read(p);
@ -4968,7 +5007,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
case BC_LEX_KEY_SCALE: case BC_LEX_KEY_SCALE:
{ {
if (BC_PARSE_LEAF(prev, rprn)) if (BC_PARSE_LEAF(prev, rprn))
return bc_error("bad expression"); return bc_error_bad_expression();
s = bc_parse_scale(p, &prev, flags); s = bc_parse_scale(p, &prev, flags);
paren_expr = true; paren_expr = true;
rprn = get_token = bin_last = false; rprn = get_token = bin_last = false;
@ -4980,7 +5019,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
default: default:
{ {
s = bc_error("bad token"); s = bc_error_bad_token();
break; break;
} }
} }
@ -4997,7 +5036,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN; assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN;
if (top == BC_LEX_LPAREN || top == BC_LEX_RPAREN) if (top == BC_LEX_LPAREN || top == BC_LEX_RPAREN)
return bc_error("bad expression"); return bc_error_bad_expression();
bc_parse_push(p, BC_PARSE_TOKEN_INST(top)); bc_parse_push(p, BC_PARSE_TOKEN_INST(top));
@ -5006,12 +5045,12 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next)
} }
if (prev == BC_INST_BOOL_NOT || nexprs != 1) if (prev == BC_INST_BOOL_NOT || nexprs != 1)
return bc_error("bad expression"); return bc_error_bad_expression();
for (i = 0; i < next.len; ++i) for (i = 0; i < next.len; ++i)
if (t == next.tokens[i]) if (t == next.tokens[i])
goto ok; goto ok;
return bc_error("bad expression"); return bc_error_bad_expression();
ok: ok:
if (!(flags & BC_PARSE_REL) && nrelops) { if (!(flags & BC_PARSE_REL) && nrelops) {
@ -5050,7 +5089,7 @@ static BcStatus dc_parse_register(BcParse *p)
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_NAME) return bc_error("bad token"); if (p->l.t.t != BC_LEX_NAME) return bc_error_bad_token();
name = xstrdup(p->l.t.v.v); name = xstrdup(p->l.t.v.v);
bc_parse_pushName(p, name); bc_parse_pushName(p, name);
@ -5158,7 +5197,7 @@ static BcStatus dc_parse_token(BcParse *p, BcLexType t, uint8_t flags)
s = bc_lex_next(&p->l); s = bc_lex_next(&p->l);
if (s) return s; if (s) return s;
if (p->l.t.t != BC_LEX_NUMBER) if (p->l.t.t != BC_LEX_NUMBER)
return bc_error("bad token"); return bc_error_bad_token();
} }
bc_parse_number(p, &prev, &p->nbraces); bc_parse_number(p, &prev, &p->nbraces);
@ -5172,7 +5211,7 @@ static BcStatus dc_parse_token(BcParse *p, BcLexType t, uint8_t flags)
case BC_LEX_KEY_READ: case BC_LEX_KEY_READ:
{ {
if (flags & BC_PARSE_NOREAD) if (flags & BC_PARSE_NOREAD)
s = bc_error("read() call inside of a read() call"); s = bc_error_nested_read_call();
else else
bc_parse_push(p, BC_INST_READ); bc_parse_push(p, BC_INST_READ);
get_token = true; get_token = true;
@ -5207,7 +5246,7 @@ static BcStatus dc_parse_token(BcParse *p, BcLexType t, uint8_t flags)
default: default:
{ {
s = bc_error("bad token"); s = bc_error_bad_token();
get_token = true; get_token = true;
break; break;
} }
@ -5393,7 +5432,7 @@ static BcStatus bc_program_binOpPrep(BcResult **l, BcNum **ln,
BcResultType lt, rt; BcResultType lt, rt;
if (!BC_PROG_STACK(&G.prog.results, 2)) if (!BC_PROG_STACK(&G.prog.results, 2))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
*r = bc_vec_item_rev(&G.prog.results, 0); *r = bc_vec_item_rev(&G.prog.results, 0);
*l = bc_vec_item_rev(&G.prog.results, 1); *l = bc_vec_item_rev(&G.prog.results, 1);
@ -5415,9 +5454,9 @@ static BcStatus bc_program_binOpPrep(BcResult **l, BcNum **ln,
} }
if (!BC_PROG_NUM((*l), (*ln)) && (!assign || (*l)->t != BC_RESULT_VAR)) if (!BC_PROG_NUM((*l), (*ln)) && (!assign || (*l)->t != BC_RESULT_VAR))
return bc_error("variable is wrong type"); return bc_error_variable_is_wrong_type();
if (!assign && !BC_PROG_NUM((*r), (*ln))) if (!assign && !BC_PROG_NUM((*r), (*ln)))
return bc_error("variable is wrong type"); return bc_error_variable_is_wrong_type();
return s; return s;
} }
@ -5435,14 +5474,14 @@ static BcStatus bc_program_prep(BcResult **r, BcNum **n)
BcStatus s; BcStatus s;
if (!BC_PROG_STACK(&G.prog.results, 1)) if (!BC_PROG_STACK(&G.prog.results, 1))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
*r = bc_vec_top(&G.prog.results); *r = bc_vec_top(&G.prog.results);
s = bc_program_num(*r, n, false); s = bc_program_num(*r, n, false);
if (s) return s; if (s) return s;
if (!BC_PROG_NUM((*r), (*n))) if (!BC_PROG_NUM((*r), (*n)))
return bc_error("variable is wrong type"); return bc_error_variable_is_wrong_type();
return s; return s;
} }
@ -5487,7 +5526,7 @@ static BcStatus bc_program_read(void)
for (i = 0; i < G.prog.stack.len; ++i) { for (i = 0; i < G.prog.stack.len; ++i) {
BcInstPtr *ip_ptr = bc_vec_item(&G.prog.stack, i); BcInstPtr *ip_ptr = bc_vec_item(&G.prog.stack, i);
if (ip_ptr->func == BC_PROG_READ) if (ip_ptr->func == BC_PROG_READ)
return bc_error("read() call inside of a read() call"); return bc_error_nested_read_call();
} }
bc_vec_pop_all(&f->code); bc_vec_pop_all(&f->code);
@ -5649,7 +5688,7 @@ static BcStatus bc_program_print(char inst, size_t idx)
bool pop = inst != BC_INST_PRINT; bool pop = inst != BC_INST_PRINT;
if (!BC_PROG_STACK(&G.prog.results, idx + 1)) if (!BC_PROG_STACK(&G.prog.results, idx + 1))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
r = bc_vec_item_rev(&G.prog.results, idx); r = bc_vec_item_rev(&G.prog.results, idx);
s = bc_program_num(r, &num, false); s = bc_program_num(r, &num, false);
@ -5781,7 +5820,7 @@ static BcStatus bc_program_assignStr(BcResult *r, BcVec *v,
if (!push) { if (!push) {
if (!BC_PROG_STACK(&G.prog.results, 2)) if (!BC_PROG_STACK(&G.prog.results, 2))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
bc_vec_pop(v); bc_vec_pop(v);
bc_vec_pop(&G.prog.results); bc_vec_pop(&G.prog.results);
} }
@ -5803,16 +5842,16 @@ static BcStatus bc_program_copyToVar(char *name, bool var)
BcNum *n; BcNum *n;
if (!BC_PROG_STACK(&G.prog.results, 1)) if (!BC_PROG_STACK(&G.prog.results, 1))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
ptr = bc_vec_top(&G.prog.results); ptr = bc_vec_top(&G.prog.results);
if ((ptr->t == BC_RESULT_ARRAY) != !var) if ((ptr->t == BC_RESULT_ARRAY) != !var)
return bc_error("variable is wrong type"); return bc_error_variable_is_wrong_type();
v = bc_program_search(name, var); v = bc_program_search(name, var);
#if ENABLE_DC #if ENABLE_DC
if (ptr->t == BC_RESULT_STR && !var) if (ptr->t == BC_RESULT_STR && !var)
return bc_error("variable is wrong type"); return bc_error_variable_is_wrong_type();
if (ptr->t == BC_RESULT_STR) return bc_program_assignStr(ptr, v, true); if (ptr->t == BC_RESULT_STR) return bc_program_assignStr(ptr, v, true);
#endif #endif
@ -5858,7 +5897,7 @@ static BcStatus bc_program_assign(char inst)
BcVec *v; BcVec *v;
if (left->t != BC_RESULT_VAR) if (left->t != BC_RESULT_VAR)
return bc_error("variable is wrong type"); return bc_error_variable_is_wrong_type();
v = bc_program_search(left->d.id.name, true); v = bc_program_search(left->d.id.name, true);
return bc_program_assignStr(right, v, false); return bc_program_assignStr(right, v, false);
@ -5952,7 +5991,7 @@ static BcStatus bc_program_pushVar(char *code, size_t *bgn,
if (!BC_PROG_STACK(v, 2 - copy)) { if (!BC_PROG_STACK(v, 2 - copy)) {
free(name); free(name);
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
} }
free(name); free(name);
@ -6068,7 +6107,7 @@ static BcStatus bc_program_call(char *code, size_t *idx)
return bc_error("undefined function"); return bc_error("undefined function");
} }
if (nparams != func->nparams) { if (nparams != func->nparams) {
return bc_error("function has %u parameters, but called with %u", func->nparams, nparams); return bc_error_fmt("function has %u parameters, but called with %u", func->nparams, nparams);
} }
ip.len = G.prog.results.len - nparams; ip.len = G.prog.results.len - nparams;
@ -6078,7 +6117,7 @@ static BcStatus bc_program_call(char *code, size_t *idx)
arg = bc_vec_top(&G.prog.results); arg = bc_vec_top(&G.prog.results);
if ((!a->idx) != (arg->t == BC_RESULT_ARRAY) || arg->t == BC_RESULT_STR) if ((!a->idx) != (arg->t == BC_RESULT_ARRAY) || arg->t == BC_RESULT_STR)
return bc_error("variable is wrong type"); return bc_error_variable_is_wrong_type();
s = bc_program_copyToVar(a->name, a->idx); s = bc_program_copyToVar(a->name, a->idx);
if (s) return s; if (s) return s;
@ -6114,7 +6153,7 @@ static BcStatus bc_program_return(char inst)
BcInstPtr *ip = bc_vec_top(&G.prog.stack); BcInstPtr *ip = bc_vec_top(&G.prog.stack);
if (!BC_PROG_STACK(&G.prog.results, ip->len + inst == BC_INST_RET)) if (!BC_PROG_STACK(&G.prog.results, ip->len + inst == BC_INST_RET))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
f = bc_vec_item(&G.prog.fns, ip->func); f = bc_vec_item(&G.prog.fns, ip->func);
res.t = BC_RESULT_TEMP; res.t = BC_RESULT_TEMP;
@ -6177,7 +6216,7 @@ static BcStatus bc_program_builtin(char inst)
bool len = inst == BC_INST_LENGTH; bool len = inst == BC_INST_LENGTH;
if (!BC_PROG_STACK(&G.prog.results, 1)) if (!BC_PROG_STACK(&G.prog.results, 1))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
opnd = bc_vec_top(&G.prog.results); opnd = bc_vec_top(&G.prog.results);
s = bc_program_num(opnd, &num, false); s = bc_program_num(opnd, &num, false);
@ -6185,7 +6224,7 @@ static BcStatus bc_program_builtin(char inst)
#if ENABLE_DC #if ENABLE_DC
if (!BC_PROG_NUM(opnd, num) && !len) if (!BC_PROG_NUM(opnd, num) && !len)
return bc_error("variable is wrong type"); return bc_error_variable_is_wrong_type();
#endif #endif
bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); bc_num_init(&res.d.n, BC_NUM_DEF_SIZE);
@ -6251,7 +6290,7 @@ static BcStatus bc_program_modexp(void)
BcNum *n1, *n2, *n3; BcNum *n1, *n2, *n3;
if (!BC_PROG_STACK(&G.prog.results, 3)) if (!BC_PROG_STACK(&G.prog.results, 3))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
s = bc_program_binOpPrep(&r2, &n2, &r3, &n3, false); s = bc_program_binOpPrep(&r2, &n2, &r3, &n3, false);
if (s) return s; if (s) return s;
@ -6259,7 +6298,7 @@ static BcStatus bc_program_modexp(void)
s = bc_program_num(r1, &n1, false); s = bc_program_num(r1, &n1, false);
if (s) return s; if (s) return s;
if (!BC_PROG_NUM(r1, n1)) if (!BC_PROG_NUM(r1, n1))
return bc_error("variable is wrong type"); return bc_error_variable_is_wrong_type();
// Make sure that the values have their pointers updated, if necessary. // Make sure that the values have their pointers updated, if necessary.
if (r1->t == BC_RESULT_VAR || r1->t == BC_RESULT_ARRAY_ELEM) { if (r1->t == BC_RESULT_VAR || r1->t == BC_RESULT_ARRAY_ELEM) {
@ -6311,7 +6350,7 @@ static BcStatus bc_program_asciify(void)
unsigned long val; unsigned long val;
if (!BC_PROG_STACK(&G.prog.results, 1)) if (!BC_PROG_STACK(&G.prog.results, 1))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
r = bc_vec_top(&G.prog.results); r = bc_vec_top(&G.prog.results);
s = bc_program_num(r, &num, false); s = bc_program_num(r, &num, false);
@ -6380,7 +6419,7 @@ static BcStatus bc_program_printStream(void)
char *str; char *str;
if (!BC_PROG_STACK(&G.prog.results, 1)) if (!BC_PROG_STACK(&G.prog.results, 1))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
r = bc_vec_top(&G.prog.results); r = bc_vec_top(&G.prog.results);
s = bc_program_num(r, &n, false); s = bc_program_num(r, &n, false);
@ -6412,7 +6451,7 @@ static BcStatus bc_program_nquit(void)
bc_vec_pop(&G.prog.results); bc_vec_pop(&G.prog.results);
if (G.prog.stack.len < val) if (G.prog.stack.len < val)
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
if (G.prog.stack.len == val) if (G.prog.stack.len == val)
quit(); quit();
@ -6435,7 +6474,7 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn,
bool exec; bool exec;
if (!BC_PROG_STACK(&G.prog.results, 1)) if (!BC_PROG_STACK(&G.prog.results, 1))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
r = bc_vec_top(&G.prog.results); r = bc_vec_top(&G.prog.results);
@ -6468,7 +6507,7 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn,
if (!exec) goto exit; if (!exec) goto exit;
if (!BC_PROG_STR(n)) { if (!BC_PROG_STR(n)) {
s = bc_error("variable is wrong type"); s = bc_error_variable_is_wrong_type();
goto exit; goto exit;
} }
@ -6500,7 +6539,7 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn,
if (s) goto err; if (s) goto err;
if (prs.l.t.t != BC_LEX_EOF) { if (prs.l.t.t != BC_LEX_EOF) {
s = bc_error("bad expression"); s = bc_error_bad_expression();
goto err; goto err;
} }
@ -6723,7 +6762,7 @@ static BcStatus bc_program_exec(void)
case BC_INST_POP: case BC_INST_POP:
{ {
if (!BC_PROG_STACK(&G.prog.results, 1)) if (!BC_PROG_STACK(&G.prog.results, 1))
s = bc_error("stack has too few elements"); s = bc_error_stack_has_too_few_elements();
else else
bc_vec_pop(&G.prog.results); bc_vec_pop(&G.prog.results);
break; break;
@ -6836,7 +6875,7 @@ static BcStatus bc_program_exec(void)
case BC_INST_DUPLICATE: case BC_INST_DUPLICATE:
{ {
if (!BC_PROG_STACK(&G.prog.results, 1)) if (!BC_PROG_STACK(&G.prog.results, 1))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
ptr = bc_vec_top(&G.prog.results); ptr = bc_vec_top(&G.prog.results);
bc_result_copy(&r, ptr); bc_result_copy(&r, ptr);
bc_vec_push(&G.prog.results, &r); bc_vec_push(&G.prog.results, &r);
@ -6848,7 +6887,7 @@ static BcStatus bc_program_exec(void)
BcResult *ptr2; BcResult *ptr2;
if (!BC_PROG_STACK(&G.prog.results, 2)) if (!BC_PROG_STACK(&G.prog.results, 2))
return bc_error("stack has too few elements"); return bc_error_stack_has_too_few_elements();
ptr = bc_vec_item_rev(&G.prog.results, 0); ptr = bc_vec_item_rev(&G.prog.results, 0);
ptr2 = bc_vec_item_rev(&G.prog.results, 1); ptr2 = bc_vec_item_rev(&G.prog.results, 1);
@ -7009,7 +7048,7 @@ static BcStatus bc_vm_file(const char *file)
G.prog.file = file; G.prog.file = file;
data = bc_read_file(file); data = bc_read_file(file);
if (!data) return bc_error("file '%s' is not text", file); if (!data) return bc_error_fmt("file '%s' is not text", file);
bc_lex_file(&G.prs.l, file); bc_lex_file(&G.prs.l, file);
s = bc_vm_process(data); s = bc_vm_process(data);
@ -7019,7 +7058,7 @@ static BcStatus bc_vm_file(const char *file)
ip = bc_vec_item(&G.prog.stack, 0); ip = bc_vec_item(&G.prog.stack, 0);
if (main_func->code.len < ip->idx) if (main_func->code.len < ip->idx)
s = bc_error("file '%s' is not executable", file); s = bc_error_fmt("file '%s' is not executable", file);
err: err:
free(data); free(data);