X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=miscutils%2Fbc.c;h=c7246ea1a1610195e78de07d9e09b4e38535fcdd;hb=008413754ba588e6168c3d15280181fb2c331770;hp=febf51cfda21df8ab4849a261d99ce777200b635;hpb=8797adc1c6e84789c261ee24afe5a1cbfaddba6b;p=oweals%2Fbusybox.git diff --git a/miscutils/bc.c b/miscutils/bc.c index febf51cfd..c7246ea1a 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -4,9 +4,8 @@ * Adapted from https://github.com/gavinhoward/bc * Original code copyright (c) 2018 Gavin D. Howard and contributors. */ -//TODO: GNU extensions: -// support "define void f()..." -// support "define f(*param[])" - "pass array by reference" syntax +//TODO: +// maybe implement a^b for non-integer b? #define DEBUG_LEXER 0 #define DEBUG_COMPILE 0 @@ -204,7 +203,6 @@ static uint8_t lex_indent; typedef enum BcStatus { BC_STATUS_SUCCESS = 0, BC_STATUS_FAILURE = 1, - BC_STATUS_PARSE_EMPTY_EXP = 2, // bc_parse_expr_empty_ok() uses this } BcStatus; #define BC_VEC_INVALID_IDX ((size_t) -1) @@ -344,10 +342,12 @@ typedef struct BcFunc { IF_BC(BcVec strs;) IF_BC(BcVec consts;) IF_BC(size_t nparams;) + IF_BC(bool voidfunc;) } BcFunc; typedef enum BcResultType { XC_RESULT_TEMP, + IF_BC(BC_RESULT_VOID,) // same as TEMP, but INST_PRINT will ignore it XC_RESULT_VAR, XC_RESULT_ARRAY_ELEM, @@ -378,9 +378,14 @@ typedef struct BcResult { typedef struct BcInstPtr { size_t func; size_t inst_idx; - IF_BC(size_t results_len_before_call;) } BcInstPtr; +typedef enum BcType { + BC_TYPE_VAR, + BC_TYPE_ARRAY, + BC_TYPE_REF, +} BcType; + typedef enum BcLexType { XC_LEX_EOF, XC_LEX_INVALID, @@ -643,11 +648,13 @@ dc_char_to_LEX[] ALIGN1 = { // IJKLMNOP DC_LEX_IBASE, XC_LEX_INVALID, DC_LEX_SCALE, DC_LEX_LOAD_POP, XC_LEX_INVALID, DC_LEX_OP_BOOL_NOT, DC_LEX_OBASE, DC_LEX_PRINT_STREAM, - // QRSTUVWXY - DC_LEX_NQUIT, DC_LEX_POP, DC_LEX_STORE_PUSH, XC_LEX_INVALID, XC_LEX_INVALID, - XC_LEX_INVALID, XC_LEX_INVALID, DC_LEX_SCALE_FACTOR, XC_LEX_INVALID, - // Z[\] - DC_LEX_LENGTH, XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, + // QRSTUVWX + DC_LEX_NQUIT, DC_LEX_POP, DC_LEX_STORE_PUSH, XC_LEX_INVALID, + XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, DC_LEX_SCALE_FACTOR, + // YZ + XC_LEX_INVALID, DC_LEX_LENGTH, + // [\] + XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, // ^_` XC_LEX_OP_POWER, XC_LEX_NEG, XC_LEX_INVALID, // abcdefgh @@ -753,7 +760,7 @@ struct globals { // For error messages. Can be set to current parsed line, // or [TODO] to current executing line (can be before last parsed one) - unsigned err_line; + size_t err_line; BcVec input_buffer; @@ -822,10 +829,8 @@ struct globals { #define BC_MAX_STRING ((unsigned) UINT_MAX - 1) #define BC_MAX_NUM BC_MAX_STRING // Unused apart from "limits" message. Just show a "biggish number" there. -//#define BC_MAX_NAME BC_MAX_STRING //#define BC_MAX_EXP ((unsigned long) LONG_MAX) //#define BC_MAX_VARS ((unsigned long) SIZE_MAX - 1) -#define BC_MAX_NAME_STR "999999999" #define BC_MAX_EXP_STR "999999999" #define BC_MAX_VARS_STR "999999999" @@ -839,10 +844,10 @@ struct globals { # error Strange INT_MAX #endif -#if UINT_MAX == 4294967295 +#if UINT_MAX == 4294967295U # define BC_MAX_SCALE_STR "4294967295" # define BC_MAX_STRING_STR "4294967294" -#elif UINT_MAX == 18446744073709551615 +#elif UINT_MAX == 18446744073709551615U # define BC_MAX_SCALE_STR "18446744073709551615" # define BC_MAX_STRING_STR "18446744073709551614" #else @@ -888,7 +893,7 @@ static void fflush_and_check(void) { fflush_all(); if (ferror(stdout) || ferror(stderr)) - bb_perror_msg_and_die("output error"); + bb_simple_perror_msg_and_die("output error"); } #if ENABLE_FEATURE_CLEAN_UP @@ -903,7 +908,7 @@ static void quit(void) NORETURN; static void quit(void) { if (ferror(stdin)) - bb_perror_msg_and_die("input error"); + bb_simple_perror_msg_and_die("input error"); fflush_and_check(); dbg_exec("quit(): exiting with exitcode SUCCESS"); exit(0); @@ -916,7 +921,9 @@ static void bc_verror_msg(const char *fmt, va_list p) const char *sv = sv; // for compiler if (G.prs.lex_filename) { sv = applet_name; - applet_name = xasprintf("%s: %s:%u", applet_name, G.prs.lex_filename, G.err_line); + applet_name = xasprintf("%s: %s:%lu", applet_name, + G.prs.lex_filename, (unsigned long)G.err_line + ); } bb_verror_msg(fmt, p, NULL); if (G.prs.lex_filename) { @@ -971,19 +978,44 @@ 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); } +#if ENABLE_BC +static ERRORFUNC int bc_error_bad_function_definition(void) +{ + IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad function definition"); +} +#endif 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) { @@ -1068,15 +1100,25 @@ static void bc_vec_pop_all(BcVec *v) bc_vec_npop(v, v->len); } -static size_t bc_vec_push(BcVec *v, const void *data) +static size_t bc_vec_npush(BcVec *v, size_t n, const void *data) { size_t len = v->len; - if (len >= v->cap) bc_vec_grow(v, 1); - memmove(v->v + (v->size * len), data, v->size); - v->len++; + if (len + n > v->cap) bc_vec_grow(v, n); + memmove(v->v + (v->size * len), data, v->size * n); + v->len = len + n; return len; } +static size_t bc_vec_push(BcVec *v, const void *data) +{ + return bc_vec_npush(v, 1, data); + //size_t len = v->len; + //if (len >= v->cap) bc_vec_grow(v, 1); + //memmove(v->v + (v->size * len), data, v->size); + //v->len = len + 1; + //return len; +} + // G.prog.results often needs "pop old operand, push result" idiom. // Can do this without a few extra ops static size_t bc_result_pop_and_push(const void *data) @@ -1233,14 +1275,12 @@ static int bc_map_insert(BcVec *v, const void *ptr, size_t *i) return 1; // "was inserted" } -#if ENABLE_BC static size_t bc_map_find_exact(const BcVec *v, const void *ptr) { size_t i = bc_map_find_ge(v, ptr); if (i >= v->len) return BC_VEC_INVALID_IDX; return bc_id_cmp(ptr, bc_vec_item(v, i)) ? BC_VEC_INVALID_IDX : i; } -#endif static void bc_num_setToZero(BcNum *n, size_t scale) { @@ -1425,7 +1465,10 @@ static ssize_t bc_num_cmp(BcNum *a, BcNum *b) b_int = BC_NUM_INT(b); a_int -= b_int; - if (a_int != 0) return (ssize_t) a_int; + if (a_int != 0) { + if (neg) return - (ssize_t) a_int; + return (ssize_t) a_int; + } a_max = (a->rdx > b->rdx); if (a_max) { @@ -2427,11 +2470,12 @@ static void dc_result_copy(BcResult *d, BcResult *src) d->d.id.name = xstrdup(src->d.id.name); break; case XC_RESULT_CONSTANT: - IF_BC(case BC_RESULT_LAST:) - IF_BC(case BC_RESULT_ONE:) case XC_RESULT_STR: memcpy(&d->d.n, &src->d.n, sizeof(BcNum)); break; + default: // placate compiler + // BC_RESULT_VOID, BC_RESULT_LAST, BC_RESULT_ONE - do not happen + break; } } #endif // ENABLE_DC @@ -2442,6 +2486,7 @@ static FAST_FUNC void bc_result_free(void *result) switch (r->t) { case XC_RESULT_TEMP: + IF_BC(case BC_RESULT_VOID:) case XC_RESULT_IBASE: case XC_RESULT_SCALE: case XC_RESULT_OBASE: @@ -2509,7 +2554,7 @@ static void xc_read_line(BcVec *vec, FILE *fp) i = 0; for (;;) { char c = line_buf[i++]; - if (!c) break; + if (c == '\0') break; if (bad_input_byte(c)) goto again; } bc_vec_string(vec, n, line_buf); @@ -2522,17 +2567,19 @@ static void xc_read_line(BcVec *vec, FILE *fp) bool bad_chars = 0; do { + get_char: #if ENABLE_FEATURE_BC_INTERACTIVE if (G_interrupt) { // ^C was pressed: ignore entire line, get another one - bc_vec_pop_all(vec); - goto intr; + goto again; } #endif - do c = fgetc(fp); while (c == '\0'); + c = fgetc(fp); + if (c == '\0') + goto get_char; if (c == EOF) { if (ferror(fp)) - bb_perror_msg_and_die("input error"); + bb_simple_perror_msg_and_die("input error"); // Note: EOF does not append '\n' break; } @@ -2851,6 +2898,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; } @@ -2917,6 +2965,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) @@ -3029,8 +3078,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++; } @@ -3077,8 +3128,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 '/' @@ -3103,6 +3156,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': @@ -3339,8 +3393,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++; } @@ -3397,6 +3453,7 @@ static BC_STATUS zdc_lex_token(void) // IOW: typing "1p" 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': @@ -3459,11 +3516,11 @@ static BC_STATUS zdc_lex_token(void) #define zdc_lex_token(...) (zdc_lex_token(__VA_ARGS__) COMMA_SUCCESS) #endif // ENABLE_DC -static void xc_parse_push(char i) +static void xc_parse_push(unsigned i) { BcVec *code = &G.prs.func->code; dbg_compile("%s:%d pushing bytecode %zd:%d", __func__, __LINE__, code->len, i); - bc_vec_pushByte(code, i); + bc_vec_pushByte(code, (uint8_t)i); } static void xc_parse_pushName(char *name) @@ -3490,14 +3547,15 @@ static void xc_parse_pushName(char *name) // (The above describes 32-bit case). #define SMALL_INDEX_LIMIT (0x100 - sizeof(size_t)) -static void xc_parse_pushIndex(size_t idx) +static void bc_vec_pushIndex(BcVec *v, size_t idx) { size_t mask; unsigned amt; dbg_lex("%s:%d pushing index %zd", __func__, __LINE__, idx); if (idx < SMALL_INDEX_LIMIT) { - goto push_idx; + bc_vec_pushByte(v, idx); + return; } mask = ((size_t)0xff) << (sizeof(idx) * 8 - 8); @@ -3509,26 +3567,34 @@ static void xc_parse_pushIndex(size_t idx) } // amt is at least 1 here - "one byte of length data follows" - xc_parse_push((SMALL_INDEX_LIMIT - 1) + amt); + bc_vec_pushByte(v, (SMALL_INDEX_LIMIT - 1) + amt); - while (idx != 0) { - push_idx: - xc_parse_push((unsigned char)idx); + do { + bc_vec_pushByte(v, (unsigned char)idx); idx >>= 8; - } + } while (idx != 0); +} + +static void xc_parse_pushIndex(size_t idx) +{ + bc_vec_pushIndex(&G.prs.func->code, idx); +} + +static void xc_parse_pushInst_and_Index(unsigned inst, size_t idx) +{ + xc_parse_push(inst); + xc_parse_pushIndex(idx); } #if ENABLE_BC static void bc_parse_pushJUMP(size_t idx) { - xc_parse_push(BC_INST_JUMP); - xc_parse_pushIndex(idx); + xc_parse_pushInst_and_Index(BC_INST_JUMP, idx); } static void bc_parse_pushJUMP_ZERO(size_t idx) { - xc_parse_push(BC_INST_JUMP_ZERO); - xc_parse_pushIndex(idx); + xc_parse_pushInst_and_Index(BC_INST_JUMP_ZERO, idx); } static BC_STATUS zbc_parse_pushSTR(void) @@ -3536,8 +3602,7 @@ static BC_STATUS zbc_parse_pushSTR(void) BcParse *p = &G.prs; char *str = xstrdup(p->lex_strnumbuf.v); - xc_parse_push(XC_INST_STR); - xc_parse_pushIndex(p->func->strs.len); + xc_parse_pushInst_and_Index(XC_INST_STR, p->func->strs.len); bc_vec_push(&p->func->strs, &str); RETURN_STATUS(zxc_lex_next()); @@ -3556,8 +3621,7 @@ static void xc_parse_pushNUM(void) #else // DC size_t idx = bc_vec_push(&G.prog.consts, &num); #endif - xc_parse_push(XC_INST_NUM); - xc_parse_pushIndex(idx); + xc_parse_pushInst_and_Index(XC_INST_NUM, idx); } static BC_STATUS zxc_parse_text_init(const char *text) @@ -3676,17 +3740,7 @@ static size_t bc_program_addFunc(char *name) // first in the expr enum. Note: This only works for binary operators. #define BC_TOKEN_2_INST(t) ((char) ((t) - XC_LEX_OP_POWER + XC_INST_POWER)) -static BcStatus bc_parse_expr_empty_ok(uint8_t flags); - -static BC_STATUS zbc_parse_expr(uint8_t flags) -{ - BcStatus s; - - s = bc_parse_expr_empty_ok(flags); - if (s == BC_STATUS_PARSE_EMPTY_EXP) - RETURN_STATUS(bc_error("empty expression")); - RETURN_STATUS(s); -} +static BC_STATUS zbc_parse_expr(uint8_t flags); #define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__) COMMA_SUCCESS) static BC_STATUS zbc_parse_stmt_possibly_auto(bool auto_allowed); @@ -3787,8 +3841,7 @@ static BC_STATUS zbc_parse_params(uint8_t flags) } } - xc_parse_push(BC_INST_CALL); - xc_parse_pushIndex(nparams); + xc_parse_pushInst_and_Index(BC_INST_CALL, nparams); RETURN_STATUS(BC_STATUS_SUCCESS); } @@ -3958,8 +4011,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; @@ -3976,7 +4028,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); @@ -3984,7 +4035,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: @@ -4016,36 +4067,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 @@ -4095,18 +4137,16 @@ static BC_STATUS zbc_parse_return(void) if (s) RETURN_STATUS(s); t = p->lex; - if (t == XC_LEX_NLINE || t == BC_LEX_SCOLON) + if (t == XC_LEX_NLINE || t == BC_LEX_SCOLON || t == BC_LEX_RBRACE) xc_parse_push(BC_INST_RET0); else { - bool paren = (t == BC_LEX_LPAREN); - s = bc_parse_expr_empty_ok(0); - if (s == BC_STATUS_PARSE_EMPTY_EXP) { - xc_parse_push(BC_INST_RET0); - s = zxc_lex_next(); - } +//TODO: if (p->func->voidfunc) ERROR + s = zbc_parse_expr(0); if (s) RETURN_STATUS(s); - if (!paren || p->lex_last != BC_LEX_RPAREN) { + if (t != BC_LEX_LPAREN // "return EXPR", no () + || p->lex_last != BC_LEX_RPAREN // example: "return (a) + b" + ) { s = zbc_POSIX_requires("parentheses around return expressions"); if (s) RETURN_STATUS(s); } @@ -4324,7 +4364,7 @@ static BC_STATUS zbc_parse_break_or_continue(BcLexType type) } #define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__) COMMA_SUCCESS) -static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var) +static BC_STATUS zbc_func_insert(BcFunc *f, char *name, BcType type) { BcId *autoid; BcId a; @@ -4332,11 +4372,14 @@ 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 + && type == (BcType) autoid->idx + ) { RETURN_STATUS(bc_error("duplicate function parameter or auto name")); + } } - a.idx = var; + a.idx = type; a.name = name; bc_vec_push(&f->autos, &a); @@ -4349,29 +4392,56 @@ static BC_STATUS zbc_parse_funcdef(void) { BcParse *p = &G.prs; BcStatus s; - bool var, comma = false; + bool comma, voidfunc; char *name; dbg_lex_enter("%s:%d entered", __func__, __LINE__); 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); - p->func = xc_program_func(p->fidx); + // To be maximally both POSIX and GNU-compatible, + // "void" is not treated as a normal keyword: + // you can have variable named "void", and even a function + // named "void": "define void() { return 6; }" is ok. + // _Only_ "define void f() ..." syntax treats "void" + // specially. + voidfunc = (strcmp(p->lex_strnumbuf.v, "void") == 0); s = zxc_lex_next(); if (s) RETURN_STATUS(s); + + voidfunc = (voidfunc && p->lex == XC_LEX_NAME); + if (voidfunc) { + 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()); + + p->fidx = bc_program_addFunc(xstrdup(p->lex_strnumbuf.v)); + p->func = xc_program_func(p->fidx); + p->func->voidfunc = voidfunc; + s = zxc_lex_next(); if (s) RETURN_STATUS(s); + comma = false; while (p->lex != BC_LEX_RPAREN) { + BcType t = BC_TYPE_VAR; + + if (p->lex == XC_LEX_OP_MULTIPLY) { + t = BC_TYPE_REF; + s = zxc_lex_next(); + if (s) RETURN_STATUS(s); + s = zbc_POSIX_does_not_allow("references"); + 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()); ++p->func->nparams; @@ -4379,20 +4449,23 @@ static BC_STATUS zbc_parse_funcdef(void) s = zxc_lex_next(); if (s) goto err; - var = p->lex != BC_LEX_LBRACKET; - - if (!var) { + if (p->lex == BC_LEX_LBRACKET) { + if (t == BC_TYPE_VAR) t = BC_TYPE_ARRAY; s = zxc_lex_next(); if (s) goto err; if (p->lex != BC_LEX_RBRACKET) { - s = bc_error("bad function definition"); + s = bc_error_bad_function_definition(); goto err; } s = zxc_lex_next(); if (s) goto err; } + else if (t == BC_TYPE_REF) { + s = bc_error_at("vars can't be references"); + goto err; + } comma = p->lex == BC_LEX_COMMA; if (comma) { @@ -4400,11 +4473,11 @@ static BC_STATUS zbc_parse_funcdef(void) if (s) goto err; } - s = zbc_func_insert(p->func, name, var); + s = zbc_func_insert(p->func, name, t); 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); @@ -4417,7 +4490,7 @@ static BC_STATUS zbc_parse_funcdef(void) // Prevent "define z()" from being interpreted as function with empty stmt as body s = zbc_lex_skip_if_at_NLINE(); if (s) RETURN_STATUS(s); - //GNU bc requires a {} block even if function body has single stmt, enforce this? + // GNU bc requires a {} block even if function body has single stmt, enforce this if (p->lex != BC_LEX_LBRACE) RETURN_STATUS(bc_error("function { body } expected")); @@ -4452,29 +4525,30 @@ static BC_STATUS zbc_parse_auto(void) if (s) RETURN_STATUS(s); for (;;) { - bool var; + BcType t; 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(); if (s) goto err; - var = (p->lex != BC_LEX_LBRACKET); - if (!var) { + t = BC_TYPE_VAR; + if (p->lex == BC_LEX_LBRACKET) { + t = BC_TYPE_ARRAY; s = zxc_lex_next(); 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(); if (s) goto err; } - s = zbc_func_insert(p->func, name, var); + s = zbc_func_insert(p->func, name, t); if (s) goto err; if (p->lex == XC_LEX_NLINE @@ -4484,7 +4558,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); } @@ -4508,11 +4582,11 @@ static BC_STATUS zbc_parse_stmt_possibly_auto(bool auto_allowed) if (p->lex == XC_LEX_NLINE) { dbg_lex_done("%s:%d done (seen XC_LEX_NLINE)", __func__, __LINE__); - RETURN_STATUS(zxc_lex_next()); + RETURN_STATUS(s); } if (p->lex == BC_LEX_SCOLON) { dbg_lex_done("%s:%d done (seen BC_LEX_SCOLON)", __func__, __LINE__); - RETURN_STATUS(zxc_lex_next()); + RETURN_STATUS(s); } if (p->lex == BC_LEX_LBRACE) { @@ -4530,6 +4604,17 @@ static BC_STATUS zbc_parse_stmt_possibly_auto(bool auto_allowed) dbg_lex("%s:%d block parsing loop", __func__, __LINE__); s = zbc_parse_stmt(); if (s) RETURN_STATUS(s); + // Check that next token is a correct stmt delimiter - + // disallows "print 1 print 2" and such. + if (p->lex == BC_LEX_RBRACE) + break; + if (p->lex != BC_LEX_SCOLON + && p->lex != XC_LEX_NLINE + ) { + RETURN_STATUS(bc_error_at("bad statement terminator")); + } + s = zxc_lex_next(); + if (s) RETURN_STATUS(s); } s = zxc_lex_next(); dbg_lex_done("%s:%d done (seen BC_LEX_RBRACE)", __func__, __LINE__); @@ -4580,8 +4665,7 @@ static BC_STATUS zbc_parse_stmt_possibly_auto(bool auto_allowed) "BC_DIM_MAX = "BC_MAX_DIM_STR "\n" "BC_SCALE_MAX = "BC_MAX_SCALE_STR "\n" "BC_STRING_MAX = "BC_MAX_STRING_STR"\n" - "BC_NAME_MAX = "BC_MAX_NAME_STR "\n" - "BC_NUM_MAX = "BC_MAX_NUM_STR "\n" + // "BC_NUM_MAX = "BC_MAX_NUM_STR "\n" - GNU bc does not show this "MAX Exponent = "BC_MAX_EXP_STR "\n" "Number of vars = "BC_MAX_VARS_STR "\n" ); @@ -4619,9 +4703,11 @@ static BC_STATUS zbc_parse_stmt_or_funcdef(void) BcStatus s; dbg_lex_enter("%s:%d entered", __func__, __LINE__); - if (p->lex == XC_LEX_EOF) - s = bc_error("end of file"); - else if (p->lex == BC_LEX_KEY_DEFINE) { +//why? +// if (p->lex == XC_LEX_EOF) +// s = bc_error("end of file"); +// else + if (p->lex == BC_LEX_KEY_DEFINE) { dbg_lex("%s:%d p->lex:BC_LEX_KEY_DEFINE", __func__, __LINE__); s = zbc_parse_funcdef(); } else { @@ -4634,19 +4720,19 @@ static BC_STATUS zbc_parse_stmt_or_funcdef(void) } #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(uint8_t flags) +#undef zbc_parse_expr +static BC_STATUS zbc_parse_expr(uint8_t flags) { BcParse *p = &G.prs; 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 (;;) { @@ -4664,16 +4750,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_STATUS(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: @@ -4687,10 +4776,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_STATUS(bc_error_bad_assignment()); } // Fallthrough. case XC_LEX_OP_POWER: @@ -4708,64 +4794,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) - ) { - return bc_error_bad_expression(); + if (t == BC_LEX_OP_BOOL_NOT) { + if (!bin_last && p->lex_last != BC_LEX_OP_BOOL_NOT) + RETURN_STATUS(bc_error_bad_expression()); + } else if (prev == XC_INST_BOOL_NOT) { + RETURN_STATUS(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)) - return bc_error_bad_expression(); + if (BC_PARSE_LEAF(prev, bin_last, rprn)) + RETURN_STATUS(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__); +//why? +// if (p->lex_last == BC_LEX_LPAREN) { +// RETURN_STATUS(bc_error_at("empty expression")); +// } if (bin_last || prev == XC_INST_BOOL_NOT) - return bc_error_bad_expression(); + RETURN_STATUS(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)) - return bc_error_bad_expression(); + if (BC_PARSE_LEAF(prev, bin_last, rprn)) + RETURN_STATUS(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)) - return bc_error_bad_expression(); + if (BC_PARSE_LEAF(prev, bin_last, rprn)) + RETURN_STATUS(bc_error_bad_expression()); xc_parse_pushNUM(); prev = XC_INST_NUM; get_token = true; - paren_expr = true; rprn = bin_last = false; nexprs++; break; @@ -4773,57 +4858,52 @@ 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)) - return bc_error_bad_expression(); + if (BC_PARSE_LEAF(prev, bin_last, rprn)) + RETURN_STATUS(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)) - return bc_error_bad_expression(); + if (BC_PARSE_LEAF(prev, bin_last, rprn)) + RETURN_STATUS(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)) - return bc_error_bad_expression(); + if (BC_PARSE_LEAF(prev, bin_last, rprn)) + RETURN_STATUS(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)) - return bc_error_bad_expression(); + if (BC_PARSE_LEAF(prev, bin_last, rprn)) + RETURN_STATUS(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; default: - return bc_error_bad_token(); + RETURN_STATUS(bc_error_bad_token()); } if (s || G_interrupt) // error, or ^C: stop parsing - return BC_STATUS_FAILURE; + RETURN_STATUS(BC_STATUS_FAILURE); if (get_token) { s = zxc_lex_next(); - if (s) return s; + if (s) RETURN_STATUS(s); } } exit_loop: @@ -4833,7 +4913,7 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags) assign = (top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN); if (top == BC_LEX_LPAREN || top == BC_LEX_RPAREN) - return bc_error_bad_expression(); + RETURN_STATUS(bc_error_bad_expression()); xc_parse_push(BC_TOKEN_2_INST(top)); @@ -4842,16 +4922,16 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags) } if (prev == XC_INST_BOOL_NOT || nexprs != 1) - return bc_error_bad_expression(); + RETURN_STATUS(bc_error_bad_expression()); if (!(flags & BC_PARSE_REL) && nrelops) { BcStatus s; s = zbc_POSIX_does_not_allow("comparison operators outside if or loops"); - if (s) return s; + if (s) RETURN_STATUS(s); } else if ((flags & BC_PARSE_REL) && nrelops > 1) { BcStatus s; s = zbc_POSIX_requires("exactly one comparison operator per condition"); - if (s) return s; + if (s) RETURN_STATUS(s); } if (flags & BC_PARSE_PRINT) { @@ -4861,8 +4941,9 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags) } dbg_lex_done("%s:%d done", __func__, __LINE__); - return BC_STATUS_SUCCESS; + RETURN_STATUS(BC_STATUS_SUCCESS); } +#define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__) COMMA_SUCCESS) #endif // ENABLE_BC @@ -4892,11 +4973,12 @@ static void dc_parse_string(void) dbg_lex_enter("%s:%d entered", __func__, __LINE__); str = xstrdup(p->lex_strnumbuf.v); - xc_parse_push(XC_INST_STR); - xc_parse_pushIndex(len); + xc_parse_pushInst_and_Index(XC_INST_STR, len); bc_vec_push(&G.prog.strs, &str); - // Explanation needed here + // Add an empty function so that if zdc_program_execStr ever needs to + // parse the string into code (from the 'x' command) there's somewhere + // to store the bytecode. xc_program_add_fn(); p->func = xc_program_func(p->fidx); @@ -5035,6 +5117,9 @@ static BC_STATUS zdc_parse_expr(void) BcParse *p = &G.prs; int i; + if (p->lex == XC_LEX_NLINE) + RETURN_STATUS(zxc_lex_next()); + i = (int)p->lex - (int)XC_LEX_OP_POWER; if (i >= 0) { BcInst inst = dc_LEX_to_INST[i]; @@ -5074,12 +5159,64 @@ static BC_STATUS zdc_parse_exprs_until_eof(void) #define STACK_HAS_MORE_THAN(s, n) ((s)->len > ((size_t)(n))) #define STACK_HAS_EQUAL_OR_MORE_THAN(s, n) ((s)->len >= ((size_t)(n))) -static BcVec* xc_program_search(char *id, bool var) +static size_t xc_program_index(char *code, size_t *bgn) +{ + unsigned char *bytes = (void*)(code + *bgn); + unsigned amt; + unsigned i; + size_t res; + + amt = *bytes++; + if (amt < SMALL_INDEX_LIMIT) { + *bgn += 1; + return amt; + } + amt -= (SMALL_INDEX_LIMIT - 1); // amt is 1 or more here + *bgn += amt + 1; + + res = 0; + i = 0; + do { + res |= (size_t)(*bytes++) << i; + i += 8; + } while (--amt != 0); + + return res; +} + +static char *xc_program_name(char *code, size_t *bgn) +{ + code += *bgn; + *bgn += strlen(code) + 1; + + return xstrdup(code); +} + +static BcVec* xc_program_dereference(BcVec *vec) +{ + BcVec *v; + size_t vidx, nidx, i = 0; + + //assert(vec->size == sizeof(uint8_t)); + + vidx = xc_program_index(vec->v, &i); + nidx = xc_program_index(vec->v, &i); + + v = bc_vec_item(&G.prog.arrs, vidx); + v = bc_vec_item(v, nidx); + + //assert(v->size != sizeof(uint8_t)); + + return v; +} + +static BcVec* xc_program_search(char *id, BcType type) { BcId e, *ptr; BcVec *v, *map; size_t i; int new; + bool var = (type == BC_TYPE_VAR); v = var ? &G.prog.vars : &G.prog.arrs; map = var ? &G.prog.var_map : &G.prog.arr_map; @@ -5105,6 +5242,7 @@ static BC_STATUS zxc_program_num(BcResult *r, BcNum **num) switch (r->t) { case XC_RESULT_STR: case XC_RESULT_TEMP: + IF_BC(case BC_RESULT_VOID:) case XC_RESULT_IBASE: case XC_RESULT_SCALE: case XC_RESULT_OBASE: @@ -5132,17 +5270,20 @@ static BC_STATUS zxc_program_num(BcResult *r, BcNum **num) case XC_RESULT_VAR: case XC_RESULT_ARRAY: case XC_RESULT_ARRAY_ELEM: { - BcVec *v; - void *p; - v = xc_program_search(r->d.id.name, r->t == XC_RESULT_VAR); -// dc variables are all stacks, so here we have this: - p = bc_vec_top(v); -// TODO: eliminate these stacks for bc-only config? + BcType type = (r->t == XC_RESULT_VAR) ? BC_TYPE_VAR : BC_TYPE_ARRAY; + BcVec *v = xc_program_search(r->d.id.name, type); + void *p = bc_vec_top(v); + if (r->t == XC_RESULT_ARRAY_ELEM) { + size_t idx = r->d.id.idx; + v = p; - if (v->len <= r->d.id.idx) - bc_array_expand(v, r->d.id.idx + 1); - *num = bc_vec_item(v, r->d.id.idx); + if (v->size == sizeof(uint8_t)) + v = xc_program_dereference(v); + //assert(v->size == sizeof(BcNum)); + if (v->len <= idx) + bc_array_expand(v, idx + 1); + *num = bc_vec_item(v, idx); } else { *num = p; } @@ -5284,14 +5425,13 @@ 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); ip.func = BC_PROG_READ; ip.inst_idx = 0; - IF_BC(ip.results_len_before_call = G.prog.results.len;) bc_vec_push(&G.prog.exestack, &ip); exec_err: @@ -5302,43 +5442,10 @@ static BC_STATUS zxc_program_read(void) } #define zxc_program_read(...) (zxc_program_read(__VA_ARGS__) COMMA_SUCCESS) -static size_t xc_program_index(char *code, size_t *bgn) -{ - unsigned char *bytes = (void*)(code + *bgn); - unsigned amt; - unsigned i; - size_t res; - - amt = *bytes++; - if (amt < SMALL_INDEX_LIMIT) { - *bgn += 1; - return amt; - } - amt -= (SMALL_INDEX_LIMIT - 1); // amt is 1 or more here - *bgn += amt + 1; - - res = 0; - i = 0; - do { - res |= (size_t)(*bytes++) << i; - i += 8; - } while (--amt != 0); - - return res; -} - -static char *xc_program_name(char *code, size_t *bgn) -{ - code += *bgn; - *bgn += strlen(code) + 1; - - return xstrdup(code); -} - static void xc_program_printString(const char *str) { #if ENABLE_DC - if (!str[0]) { + if (!str[0] && IS_DC) { // Example: echo '[]ap' | dc // should print two bytes: 0x00, 0x0A bb_putchar('\0'); @@ -5346,46 +5453,27 @@ static void xc_program_printString(const char *str) } #endif while (*str) { - int c = *str++; - if (c != '\\' || !*str) - bb_putchar(c); - else { + char c = *str++; + if (c == '\\') { + static const char esc[] ALIGN1 = "nabfrt""e\\"; + char *n; + c = *str++; - switch (c) { - case 'a': - bb_putchar('\a'); - break; - case 'b': - bb_putchar('\b'); - break; - case '\\': - case 'e': - bb_putchar('\\'); - break; - case 'f': - bb_putchar('\f'); - break; - case 'n': - bb_putchar('\n'); - G.prog.nchars = SIZE_MAX; - break; - case 'r': - bb_putchar('\r'); - break; - case 'q': - bb_putchar('"'); - break; - case 't': - bb_putchar('\t'); - break; - default: - // Just print the backslash and following character. + n = strchr(esc, c); // note: if c is NUL, n = \0 at end of esc + if (!n || !c) { + // Just print the backslash and following character bb_putchar('\\'); ++G.prog.nchars; - bb_putchar(c); - break; + // But if we're at the end of the string, stop + if (!c) break; + } else { + if (n - esc == 0) // "\n" ? + G.prog.nchars = SIZE_MAX; + c = "\n\a\b\f\r\t""\\\\""\\"[n - esc]; + // n a b f r t e \ \ } } + putchar(c); ++G.prog.nchars; } } @@ -5584,22 +5672,32 @@ static BC_STATUS zxc_num_print(BcNum *n, bool newline) } #define zxc_num_print(...) (zxc_num_print(__VA_ARGS__) COMMA_SUCCESS) -static BC_STATUS zxc_program_print(char inst, size_t idx) +#if !ENABLE_DC +// for bc, idx is always 0 +#define xc_program_print(inst, idx) \ + xc_program_print(inst) +#endif +static BC_STATUS xc_program_print(char inst, size_t idx) { BcStatus s; BcResult *r; BcNum *num; - bool pop = (inst != XC_INST_PRINT); + IF_NOT_DC(size_t idx = 0); if (!STACK_HAS_MORE_THAN(&G.prog.results, idx)) RETURN_STATUS(bc_error_stack_has_too_few_elements()); r = bc_vec_item_rev(&G.prog.results, idx); +#if ENABLE_BC + if (inst == XC_INST_PRINT && r->t == BC_RESULT_VOID) + // void function's result on stack, ignore + RETURN_STATUS(BC_STATUS_SUCCESS); +#endif s = zxc_program_num(r, &num); if (s) RETURN_STATUS(s); if (BC_PROG_NUM(r, num)) { - s = zxc_num_print(num, !pop); + s = zxc_num_print(num, /*newline:*/ inst == XC_INST_PRINT); #if ENABLE_BC if (!s && IS_BC) bc_num_copy(&G.prog.last, num); #endif @@ -5610,24 +5708,23 @@ static BC_STATUS zxc_program_print(char inst, size_t idx) str = *xc_program_str(idx); if (inst == XC_INST_PRINT_STR) { - for (;;) { - char c = *str++; - if (c == '\0') break; - bb_putchar(c); - ++G.prog.nchars; - if (c == '\n') G.prog.nchars = 0; - } + char *nl; + G.prog.nchars += printf("%s", str); + nl = strrchr(str, '\n'); + if (nl) + G.prog.nchars = strlen(nl + 1); } else { xc_program_printString(str); - if (inst == XC_INST_PRINT) bb_putchar('\n'); + if (inst == XC_INST_PRINT) + bb_putchar('\n'); } } - if (!s && pop) bc_vec_pop(&G.prog.results); + if (!s && inst != XC_INST_PRINT) bc_vec_pop(&G.prog.results); RETURN_STATUS(s); } -#define zxc_program_print(...) (zxc_program_print(__VA_ARGS__) COMMA_SUCCESS) +#define zxc_program_print(...) (xc_program_print(__VA_ARGS__) COMMA_SUCCESS) static BC_STATUS zxc_program_negate(void) { @@ -5722,48 +5819,86 @@ static BC_STATUS zdc_program_assignStr(BcResult *r, BcVec *v, bool push) #define zdc_program_assignStr(...) (zdc_program_assignStr(__VA_ARGS__) COMMA_SUCCESS) #endif // ENABLE_DC -static BC_STATUS zxc_program_copyToVar(char *name, bool var) +static BC_STATUS zxc_program_popResultAndCopyToVar(char *name, BcType t) { BcStatus s; BcResult *ptr, r; - BcVec *v; + BcVec *vec; BcNum *n; + bool var = (t == BC_TYPE_VAR); if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) RETURN_STATUS(bc_error_stack_has_too_few_elements()); ptr = bc_vec_top(&G.prog.results); - if ((ptr->t == XC_RESULT_ARRAY) != !var) + if ((ptr->t == XC_RESULT_ARRAY) == var) RETURN_STATUS(bc_error_variable_is_wrong_type()); - v = xc_program_search(name, var); + vec = xc_program_search(name, t); #if ENABLE_DC - if (ptr->t == XC_RESULT_STR && !var) - RETURN_STATUS(bc_error_variable_is_wrong_type()); - if (ptr->t == XC_RESULT_STR) - RETURN_STATUS(zdc_program_assignStr(ptr, v, true)); + if (ptr->t == XC_RESULT_STR) { + if (!var) + RETURN_STATUS(bc_error_variable_is_wrong_type()); + RETURN_STATUS(zdc_program_assignStr(ptr, vec, true)); + } #endif s = zxc_program_num(ptr, &n); if (s) RETURN_STATUS(s); // Do this once more to make sure that pointers were not invalidated. - v = xc_program_search(name, var); + vec = xc_program_search(name, t); if (var) { bc_num_init_DEF_SIZE(&r.d.n); bc_num_copy(&r.d.n, n); } else { + BcVec *v = (BcVec*) n; + bool ref, ref_size; + + ref = (v->size == sizeof(BcVec) && t != BC_TYPE_ARRAY); + ref_size = (v->size == sizeof(uint8_t)); + + if (ref || (ref_size && t == BC_TYPE_REF)) { + bc_vec_init(&r.d.v, sizeof(uint8_t), NULL); + if (ref) { + size_t vidx, idx; + BcId id; + + id.name = ptr->d.id.name; + v = xc_program_search(ptr->d.id.name, BC_TYPE_REF); + + // Make sure the pointer was not invalidated. + vec = xc_program_search(name, t); + + vidx = bc_map_find_exact(&G.prog.arr_map, &id); + //assert(vidx != BC_VEC_INVALID_IDX); + vidx = ((BcId*) bc_vec_item(&G.prog.arr_map, vidx))->idx; + idx = v->len - 1; + + bc_vec_pushIndex(&r.d.v, vidx); + bc_vec_pushIndex(&r.d.v, idx); + } + // If we get here, we are copying a ref to a ref. + else bc_vec_npush(&r.d.v, v->len, v->v); + + // We need to return early. + goto ret; + } + + if (ref_size && t != BC_TYPE_REF) + v = xc_program_dereference(v); + bc_array_init(&r.d.v, true); - bc_array_copy(&r.d.v, (BcVec *) n); + bc_array_copy(&r.d.v, v); } - - bc_vec_push(v, &r.d); + ret: + bc_vec_push(vec, &r.d); bc_vec_pop(&G.prog.results); RETURN_STATUS(s); } -#define zxc_program_copyToVar(...) (zxc_program_copyToVar(__VA_ARGS__) COMMA_SUCCESS) +#define zxc_program_popResultAndCopyToVar(...) (zxc_program_popResultAndCopyToVar(__VA_ARGS__) COMMA_SUCCESS) static BC_STATUS zxc_program_assign(char inst) { @@ -5785,22 +5920,20 @@ static BC_STATUS zxc_program_assign(char inst) if (left->t != XC_RESULT_VAR) RETURN_STATUS(bc_error_variable_is_wrong_type()); - v = xc_program_search(left->d.id.name, true); + v = xc_program_search(left->d.id.name, BC_TYPE_VAR); RETURN_STATUS(zdc_program_assignStr(right, v, false)); } #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 + if (left->t == XC_RESULT_CONSTANT + || left->t == XC_RESULT_TEMP + IF_BC(|| left->t == BC_RESULT_VOID) + ) { + RETURN_STATUS(bc_error_bad_assignment()); + } #if ENABLE_BC - if (inst == BC_INST_ASSIGN_DIVIDE && !bc_num_cmp(r, &G.prog.zero)) - RETURN_STATUS(bc_error("divide by zero")); - if (assign) bc_num_copy(l, r); else { @@ -5866,7 +5999,7 @@ static BC_STATUS xc_program_pushVar(char *code, size_t *bgn, #if ENABLE_DC if (pop || copy) { - BcVec *v = xc_program_search(name, true); + BcVec *v = xc_program_search(name, BC_TYPE_VAR); BcNum *num = bc_vec_top(v); free(name); @@ -5965,12 +6098,10 @@ static BC_STATUS zbc_program_call(char *code, size_t *idx) { BcInstPtr ip; size_t i, nparams; - BcFunc *func; BcId *a; - BcResult *arg; + BcFunc *func; nparams = xc_program_index(code, idx); - ip.inst_idx = 0; ip.func = xc_program_index(code, idx); func = xc_program_func(ip.func); @@ -5980,18 +6111,24 @@ static BC_STATUS zbc_program_call(char *code, size_t *idx) if (nparams != func->nparams) { RETURN_STATUS(bc_error_fmt("function has %u parameters, but called with %u", func->nparams, nparams)); } - ip.results_len_before_call = G.prog.results.len - nparams; + ip.inst_idx = 0; for (i = 0; i < nparams; ++i) { + BcResult *arg; BcStatus s; + bool arr; a = bc_vec_item(&func->autos, nparams - 1 - i); arg = bc_vec_top(&G.prog.results); - if ((!a->idx) != (arg->t == XC_RESULT_ARRAY) || arg->t == XC_RESULT_STR) - RETURN_STATUS(bc_error_variable_is_wrong_type()); + arr = (a->idx == BC_TYPE_ARRAY || a->idx == BC_TYPE_REF); - s = zxc_program_copyToVar(a->name, a->idx); + if (arr != (arg->t == XC_RESULT_ARRAY) // array/variable mismatch + // || arg->t == XC_RESULT_STR - impossible, f("str") is not a legal syntax (strings are not bc expressions) + ) { + RETURN_STATUS(bc_error_variable_is_wrong_type()); + } + s = zxc_program_popResultAndCopyToVar(a->name, (BcType) a->idx); if (s) RETURN_STATUS(s); } @@ -5999,12 +6136,13 @@ static BC_STATUS zbc_program_call(char *code, size_t *idx) for (; i < func->autos.len; i++, a++) { BcVec *v; - v = xc_program_search(a->name, a->idx); - if (a->idx) { + v = xc_program_search(a->name, (BcType) a->idx); + if (a->idx == BC_TYPE_VAR) { BcNum n2; bc_num_init_DEF_SIZE(&n2); bc_vec_push(v, &n2); } else { + //assert(a->idx == BC_TYPE_ARRAY); BcVec v2; bc_array_init(&v2, true); bc_vec_push(v, &v2); @@ -6025,13 +6163,13 @@ static BC_STATUS zbc_program_return(char inst) size_t i; BcInstPtr *ip = bc_vec_top(&G.prog.exestack); - if (!STACK_HAS_EQUAL_OR_MORE_THAN(&G.prog.results, ip->results_len_before_call + (inst == XC_INST_RET))) - RETURN_STATUS(bc_error_stack_has_too_few_elements()); - f = xc_program_func(ip->func); - res.t = XC_RESULT_TEMP; + res.t = XC_RESULT_TEMP; if (inst == XC_INST_RET) { + // bc needs this for e.g. RESULT_CONSTANT ("return 5") + // because bc constants are per-function. + // TODO: maybe avoid if value is already RESULT_TEMP? BcStatus s; BcNum *num; BcResult *operand = bc_vec_top(&G.prog.results); @@ -6040,23 +6178,25 @@ static BC_STATUS zbc_program_return(char inst) if (s) RETURN_STATUS(s); bc_num_init(&res.d.n, num->len); bc_num_copy(&res.d.n, num); + bc_vec_pop(&G.prog.results); } else { + if (f->voidfunc) + res.t = BC_RESULT_VOID; bc_num_init_DEF_SIZE(&res.d.n); //bc_num_zero(&res.d.n); - already is } + bc_vec_push(&G.prog.results, &res); + + bc_vec_pop(&G.prog.exestack); // We need to pop arguments as well, so this takes that into account. a = (void*)f->autos.v; for (i = 0; i < f->autos.len; i++, a++) { BcVec *v; - v = xc_program_search(a->name, a->idx); + v = xc_program_search(a->name, (BcType) a->idx); bc_vec_pop(v); } - bc_vec_npop(&G.prog.results, G.prog.results.len - ip->results_len_before_call); - bc_vec_push(&G.prog.results, &res); - bc_vec_pop(&G.prog.exestack); - RETURN_STATUS(BC_STATUS_SUCCESS); } #define zbc_program_return(...) (zbc_program_return(__VA_ARGS__) COMMA_SUCCESS) @@ -6265,7 +6405,11 @@ static BC_STATUS zdc_program_asciify(void) str = xzalloc(2); str[0] = c; //str[1] = '\0'; - already is - bc_vec_push(&G.prog.strs, &str); + idx = bc_vec_push(&G.prog.strs, &str); + // Add an empty function so that if zdc_program_execStr ever needs to + // parse the string into code (from the 'x' command) there's somewhere + // to store the bytecode. + xc_program_add_fn(); dup: res.t = XC_RESULT_STR; res.d.id.idx = idx; @@ -6365,7 +6509,7 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond) if (exec) { BcVec *v; - v = xc_program_search(name, true); + v = xc_program_search(name, BC_TYPE_VAR); n = bc_vec_top(v); } @@ -6388,7 +6532,7 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond) if (s || !BC_PROG_STR(n)) goto exit; sidx = n->rdx; } else - goto exit; + goto exit_nopop; } fidx = sidx + BC_PROG_REQ_FUNCS; @@ -6428,6 +6572,7 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond) RETURN_STATUS(BC_STATUS_SUCCESS); exit: bc_vec_pop(&G.prog.results); + exit_nopop: RETURN_STATUS(s); } #define zdc_program_execStr(...) (zdc_program_execStr(__VA_ARGS__) COMMA_SUCCESS) @@ -6466,7 +6611,17 @@ static BC_STATUS zxc_program_exec(void) dbg_exec("inst at %zd:%d results.len:%d", ip->inst_idx - 1, inst, G.prog.results.len); switch (inst) { + case XC_INST_RET: + if (IS_DC) { // end of '?' reached + bc_vec_pop(&G.prog.exestack); + goto read_updated_ip; + } + // bc: fall through #if ENABLE_BC + case BC_INST_RET0: + dbg_exec("BC_INST_RET[0]:"); + s = zbc_program_return(inst); + goto read_updated_ip; case BC_INST_JUMP_ZERO: { BcNum *num; bool zero; @@ -6503,11 +6658,6 @@ static BC_STATUS zxc_program_exec(void) dbg_exec("BC_INST_HALT:"); QUIT_OR_RETURN_TO_MAIN; break; - case XC_INST_RET: - case BC_INST_RET0: - dbg_exec("BC_INST_RET[0]:"); - s = zbc_program_return(inst); - goto read_updated_ip; case XC_INST_BOOL_OR: case XC_INST_BOOL_AND: #endif // ENABLE_BC @@ -6568,7 +6718,7 @@ static BC_STATUS zxc_program_exec(void) case XC_INST_PRINT: case XC_INST_PRINT_POP: case XC_INST_PRINT_STR: - dbg_exec("XC_INST_PRINTxyz:"); + dbg_exec("XC_INST_PRINTxyz(%d):", inst - XC_INST_PRINT); s = zxc_program_print(inst, 0); break; case XC_INST_STR: @@ -6685,7 +6835,7 @@ static BC_STATUS zxc_program_exec(void) } case DC_INST_PUSH_TO_VAR: { char *name = xc_program_name(code, &ip->inst_idx); - s = zxc_program_copyToVar(name, true); + s = zxc_program_popResultAndCopyToVar(name, BC_TYPE_VAR); free(name); break; } @@ -6744,7 +6894,6 @@ static BC_STATUS zxc_vm_process(const char *text) s = zxc_parse_text_init(text); // does the first zxc_lex_next() if (s) RETURN_STATUS(s); - IF_BC(check_eof:) while (G.prs.lex != XC_LEX_EOF) { BcInstPtr *ip; BcFunc *f; @@ -6752,14 +6901,6 @@ static BC_STATUS zxc_vm_process(const char *text) dbg_lex("%s:%d G.prs.lex:%d, parsing...", __func__, __LINE__, G.prs.lex); if (IS_BC) { #if ENABLE_BC - if (G.prs.lex == BC_LEX_SCOLON - || G.prs.lex == XC_LEX_NLINE - ) { - s = zxc_lex_next(); - if (s) goto err; - goto check_eof; - } - s = zbc_parse_stmt_or_funcdef(); if (s) goto err; @@ -6769,13 +6910,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: @@ -6783,14 +6918,6 @@ static BC_STATUS zxc_vm_process(const char *text) #endif } else { #if ENABLE_DC - // Most of dc parsing assumes all whitespace, - // including '\n', is eaten. - while (G.prs.lex == XC_LEX_NLINE) { - s = zxc_lex_next(); - if (s) goto err; - if (G.prs.lex == XC_LEX_EOF) - goto done; - } s = zdc_parse_expr(); #endif } @@ -6810,9 +6937,9 @@ static BC_STATUS zxc_vm_process(const char *text) ip = (void*)G.prog.exestack.v; #if SANITY_CHECKS if (G.prog.exestack.len != 1) // should have only main's IP - bb_error_msg_and_die("BUG:call stack"); + bb_simple_error_msg_and_die("BUG:call stack"); if (ip->func != BC_PROG_MAIN) - bb_error_msg_and_die("BUG:not MAIN"); + bb_simple_error_msg_and_die("BUG:not MAIN"); #endif f = xc_program_func_BC_PROG_MAIN(); // bc discards strings, constants and code after each @@ -6828,10 +6955,13 @@ static BC_STATUS zxc_vm_process(const char *text) if (IS_BC) { #if SANITY_CHECKS if (G.prog.results.len != 0) // should be empty - bb_error_msg_and_die("BUG:data stack"); + bb_simple_error_msg_and_die("BUG:data stack"); #endif IF_BC(bc_vec_pop_all(&f->strs);) IF_BC(bc_vec_pop_all(&f->consts);) + // We are at SCOLON/NLINE, skip it: + s = zxc_lex_next(); + if (s) goto err; } else { if (G.prog.results.len == 0 && G.prog.vars.len == 0 @@ -6853,7 +6983,7 @@ static BC_STATUS zxc_vm_process(const char *text) bc_vec_pop_all(&f->code); ip->inst_idx = 0; } - IF_DC(done:) + dbg_lex_done("%s:%d done", __func__, __LINE__); RETURN_STATUS(s); } @@ -6869,6 +6999,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(""); @@ -7383,4 +7514,3 @@ int dc_main(int argc UNUSED_PARAM, char **argv) #endif #endif // DC_BIG -