X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=miscutils%2Fbc.c;h=26ab94cbda9f8bf72e7c63ac01e3e429616b4f98;hb=dafbc2cdb8825ed36a25f9a6275d5226f35d3bd3;hp=b454d4b2bd85a325b8b6a6b9f13d93884abfd847;hpb=452df923f7f5ae47f3076d7da562946af6123ca9;p=oweals%2Fbusybox.git diff --git a/miscutils/bc.c b/miscutils/bc.c index b454d4b2b..26ab94cbd 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -2,9 +2,6 @@ /* * Licensed under GPLv2 or later, see file LICENSE in this source tree. * Copyright (c) 2018 Gavin D. Howard and contributors. - * - * ** Automatically generated from https://github.com/gavinhoward/bc ** - * ** Do not edit unless you know what you are doing. ** */ //config:config BC //config: bool "bc (45 kb; 49 kb when combined with dc)" @@ -94,17 +91,30 @@ //config: Also note that, like the FreeBSD dc, extended registers are not //config: allowed unless the "-x" option is given. //config: +//config:config FEATURE_DC_SMALL +//config: bool "Minimal dc implementation (4.2 kb), not using bc code base" +//config: depends on DC && !BC +//config: default n +//config: +//config:config FEATURE_DC_LIBM +//config: bool "Enable power and exp functions (requires libm)" +//config: default y +//config: depends on FEATURE_DC_SMALL +//config: help +//config: Enable power and exp functions. +//config: NOTE: This will require libm to be present for linking. +//config: //config:config FEATURE_BC_SIGNALS //config: bool "Enable bc/dc signal handling" //config: default y -//config: depends on BC || DC +//config: depends on (BC || DC) && !FEATURE_DC_SMALL //config: help //config: Enable signal handling for bc and dc. //config: //config:config FEATURE_BC_LONG_OPTIONS //config: bool "Enable bc/dc long options" //config: default y -//config: depends on BC || DC +//config: depends on (BC || DC) && !FEATURE_DC_SMALL //config: help //config: Enable long options for bc and dc. @@ -116,17 +126,19 @@ //See www.gnu.org/software/bc/manual/bc.html //usage:#define bc_trivial_usage -//usage: "[-sqli] FILE..." +//usage: "[-sqliw] FILE..." //usage: //usage:#define bc_full_usage "\n" //usage: "\nArbitrary precision calculator" //usage: "\n" -//usage: "\n -i Interactive" +///////: "\n -i Interactive" - has no effect for now +//usage: "\n -q Quiet" //usage: "\n -l Load standard math library" //usage: "\n -s Be POSIX compatible" -//usage: "\n -q Quiet" //usage: "\n -w Warn if extensions are used" ///////: "\n -v Version" +//usage: "\n" +//usage: "\n$BC_LINE_LENGTH changes output width" //usage: //usage:#define bc_example_usage //usage: "3 + 4.129\n" @@ -140,43 +152,48 @@ //usage: "obase = A\n" //usage: //usage:#define dc_trivial_usage -//usage: "EXPRESSION..." +//usage: IF_NOT_FEATURE_DC_SMALL("[-x] ")"[-eSCRIPT]... [-fFILE]... [FILE]..." //usage: -//usage:#define dc_full_usage "\n\n" -//usage: "Tiny RPN calculator. Operations:\n" -//usage: "+, add, -, sub, *, mul, /, div, %, mod, ^, exp, ~, divmod, |, " -//usage: "modular exponentiation,\n" -//usage: "p - print top of the stack (without popping),\n" -//usage: "f - print entire stack,\n" -//usage: "k - pop the value and set the precision.\n" -//usage: "i - pop the value and set input radix.\n" -//usage: "o - pop the value and set output radix.\n" -//usage: "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16" +//usage:#define dc_full_usage "\n" +//usage: "\nTiny RPN calculator. Operations:" +//usage: "\n+, -, *, /, %, ~, ^," IF_NOT_FEATURE_DC_SMALL(" |,") +//usage: "\np - print top of the stack (without popping)" +//usage: "\nf - print entire stack" +//usage: "\nk - pop the value and set the precision" +//usage: "\ni - pop the value and set input radix" +//usage: "\no - pop the value and set output radix" +//usage: "\nExamples: dc -e'2 2 + p' -> 4, dc -e'8 8 * 2 2 + / p' -> 16" //usage: //usage:#define dc_example_usage -//usage: "$ dc 2 2 + p\n" +//usage: "$ dc -e'2 2 + p'\n" //usage: "4\n" -//usage: "$ dc 8 8 \\* 2 2 + / p\n" +//usage: "$ dc -e'8 8 \\* 2 2 + / p'\n" //usage: "16\n" -//usage: "$ dc 0 1 and p\n" +//usage: "$ dc -e'0 1 & p'\n" //usage: "0\n" -//usage: "$ dc 0 1 or p\n" +//usage: "$ dc -e'0 1 | p'\n" //usage: "1\n" -//usage: "$ echo 72 9 div 8 mul p | dc\n" +//usage: "$ echo '72 9 / 8 * p' | dc\n" //usage: "64\n" #include "libbb.h" +#include "common_bufsiz.h" + +#if ENABLE_FEATURE_DC_SMALL +# include "dc.c" +#else typedef enum BcStatus { BC_STATUS_SUCCESS = 0, BC_STATUS_FAILURE = 1, BC_STATUS_PARSE_EMPTY_EXP = 2, // bc_parse_expr() uses this + BC_STATUS_EOF = 3, // bc_vm_stdin() uses this } BcStatus; #define BC_VEC_INVALID_IDX ((size_t) -1) #define BC_VEC_START_CAP (1 << 5) -typedef void (*BcVecFree)(void *); +typedef void (*BcVecFree)(void *) FAST_FUNC; typedef struct BcVec { char *v; @@ -186,9 +203,6 @@ typedef struct BcVec { BcVecFree dtor; } BcVec; -#define bc_vec_pop(v) (bc_vec_npop((v), 1)) -#define bc_vec_top(v) (bc_vec_item_rev((v), 0)) - typedef signed char BcDig; typedef struct BcNum { @@ -199,38 +213,24 @@ typedef struct BcNum { bool neg; } BcNum; -#define BC_NUM_MIN_BASE ((unsigned long) 2) -#define BC_NUM_MAX_IBASE ((unsigned long) 16) -#define BC_NUM_DEF_SIZE (16) -#define BC_NUM_PRINT_WIDTH (69) - -#define BC_NUM_KARATSUBA_LEN (32) - -#define BC_NUM_NEG(n, neg) ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg)) -#define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1) -#define BC_NUM_INT(n) ((n)->len - (n)->rdx) -#define BC_NUM_AREQ(a, b) \ - (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1) -#define BC_NUM_MREQ(a, b, scale) \ - (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1) - -typedef BcStatus (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t); -typedef void (*BcNumDigitOp)(size_t, size_t, bool, size_t *, size_t); - -static void bc_num_init(BcNum *n, size_t req); -static void bc_num_expand(BcNum *n, size_t req); -static void bc_num_copy(BcNum *d, BcNum *s); -static void bc_num_free(void *num); - -static BcStatus bc_num_ulong(BcNum *n, unsigned long *result); -static void bc_num_ulong2num(BcNum *n, unsigned long val); - -static BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale); -static BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale); -static BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale); -static BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale); -static BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale); -static BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale); +#define BC_NUM_MIN_BASE ((unsigned long) 2) +#define BC_NUM_MAX_IBASE ((unsigned long) 16) +// larger value might speed up BIGNUM calculations a bit: +#define BC_NUM_DEF_SIZE (16) +#define BC_NUM_PRINT_WIDTH (69) + +#define BC_NUM_KARATSUBA_LEN (32) + +typedef void (*BcNumDigitOp)(size_t, size_t, bool) FAST_FUNC; + +typedef BcStatus (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t) FAST_FUNC; + +static BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) FAST_FUNC; +static BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) FAST_FUNC; +static BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) FAST_FUNC; +static BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) FAST_FUNC; +static BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) FAST_FUNC; +static BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) FAST_FUNC; static BcStatus bc_num_sqrt(BcNum *a, BcNum *b, size_t scale); static BcStatus bc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, size_t scale); @@ -387,9 +387,6 @@ typedef struct BcInstPtr { size_t len; } BcInstPtr; -static void bc_array_expand(BcVec *a, size_t len); -static int bc_id_cmp(const void *e1, const void *e2); - // BC_LEX_NEG is not used in lexing; it is only for parsing. typedef enum BcLexType { @@ -502,34 +499,35 @@ typedef enum BcLexType { struct BcLexKeyword { char name8[8]; }; -#define BC_LEX_KW_ENTRY(a, b, c) \ - { .name8 = a /*, .len = b, .posix = c*/ } +#define BC_LEX_KW_ENTRY(a, b) \ + { .name8 = a /*, .posix = b */ } static const struct BcLexKeyword bc_lex_kws[20] = { - BC_LEX_KW_ENTRY("auto" , 4, 1), // 0 - BC_LEX_KW_ENTRY("break" , 5, 1), // 1 - BC_LEX_KW_ENTRY("continue", 8, 0), // 2 note: this one has no terminating NUL - BC_LEX_KW_ENTRY("define" , 6, 1), // 3 - - BC_LEX_KW_ENTRY("else" , 4, 0), // 4 - BC_LEX_KW_ENTRY("for" , 3, 1), // 5 - BC_LEX_KW_ENTRY("halt" , 4, 0), // 6 - BC_LEX_KW_ENTRY("ibase" , 5, 1), // 7 - - BC_LEX_KW_ENTRY("if" , 2, 1), // 8 - BC_LEX_KW_ENTRY("last" , 4, 0), // 9 - BC_LEX_KW_ENTRY("length" , 6, 1), // 10 - BC_LEX_KW_ENTRY("limits" , 6, 0), // 11 - - BC_LEX_KW_ENTRY("obase" , 5, 1), // 12 - BC_LEX_KW_ENTRY("print" , 5, 0), // 13 - BC_LEX_KW_ENTRY("quit" , 4, 1), // 14 - BC_LEX_KW_ENTRY("read" , 4, 0), // 15 - - BC_LEX_KW_ENTRY("return" , 6, 1), // 16 - BC_LEX_KW_ENTRY("scale" , 5, 1), // 17 - BC_LEX_KW_ENTRY("sqrt" , 4, 1), // 18 - BC_LEX_KW_ENTRY("while" , 5, 1), // 19 + BC_LEX_KW_ENTRY("auto" , 1), // 0 + BC_LEX_KW_ENTRY("break" , 1), // 1 + BC_LEX_KW_ENTRY("continue", 0), // 2 note: this one has no terminating NUL + BC_LEX_KW_ENTRY("define" , 1), // 3 + + BC_LEX_KW_ENTRY("else" , 0), // 4 + BC_LEX_KW_ENTRY("for" , 1), // 5 + BC_LEX_KW_ENTRY("halt" , 0), // 6 + BC_LEX_KW_ENTRY("ibase" , 1), // 7 + + BC_LEX_KW_ENTRY("if" , 1), // 8 + BC_LEX_KW_ENTRY("last" , 0), // 9 + BC_LEX_KW_ENTRY("length" , 1), // 10 + BC_LEX_KW_ENTRY("limits" , 0), // 11 + + BC_LEX_KW_ENTRY("obase" , 1), // 12 + BC_LEX_KW_ENTRY("print" , 0), // 13 + BC_LEX_KW_ENTRY("quit" , 1), // 14 + BC_LEX_KW_ENTRY("read" , 0), // 15 + + BC_LEX_KW_ENTRY("return" , 1), // 16 + BC_LEX_KW_ENTRY("scale" , 1), // 17 + BC_LEX_KW_ENTRY("sqrt" , 1), // 18 + BC_LEX_KW_ENTRY("while" , 1), // 19 }; +#undef BC_LEX_KW_ENTRY enum { POSIX_KWORD_MASK = 0 | (1 << 0) @@ -557,10 +555,11 @@ enum { | (1 << 18) | (1 << 19) }; +#define bc_lex_kws_POSIX(i) ((1 << (i)) & POSIX_KWORD_MASK) #endif struct BcLex; -typedef BcStatus (*BcLexNext)(struct BcLex *); +typedef BcStatus (*BcLexNext)(struct BcLex *) FAST_FUNC; typedef struct BcLex { @@ -582,15 +581,11 @@ typedef struct BcLex { #define BC_PARSE_STREND ((char) UCHAR_MAX) -#define bc_parse_push(p, i) (bc_vec_pushByte(&(p)->func->code, (char) (i))) -#define bc_parse_updateFunc(p, f) \ - ((p)->func = bc_vec_item(&G.prog.fns, ((p)->fidx = (f)))) - -#define BC_PARSE_REL (1 << 0) -#define BC_PARSE_PRINT (1 << 1) +#define BC_PARSE_REL (1 << 0) +#define BC_PARSE_PRINT (1 << 1) #define BC_PARSE_NOCALL (1 << 2) #define BC_PARSE_NOREAD (1 << 3) -#define BC_PARSE_ARRAY (1 << 4) +#define BC_PARSE_ARRAY (1 << 4) #define BC_PARSE_TOP_FLAG_PTR(parse) ((uint8_t *) bc_vec_top(&(parse)->flags)) #define BC_PARSE_TOP_FLAG(parse) (*(BC_PARSE_TOP_FLAG_PTR(parse))) @@ -627,22 +622,11 @@ typedef struct BcLex { BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER | BC_PARSE_FLAG_IF | \ BC_PARSE_FLAG_ELSE | BC_PARSE_FLAG_IF_END))) -typedef struct BcParseNext { - uint32_t len; - BcLexType tokens[4]; -} BcParseNext; - -#define BC_PARSE_NEXT_TOKENS(...) .tokens = { __VA_ARGS__ } -#define BC_PARSE_NEXT(a, ...) \ - { \ - .len = (a), BC_PARSE_NEXT_TOKENS(__VA_ARGS__) \ - } - struct BcParse; struct BcProgram; -typedef BcStatus (*BcParseParse)(struct BcParse *); +typedef BcStatus (*BcParseParse)(struct BcParse *) FAST_FUNC; typedef struct BcParse { @@ -665,34 +649,6 @@ typedef struct BcParse { } BcParse; -#if ENABLE_BC - -static BcStatus bc_lex_token(BcLex *l); - -#define BC_PARSE_TOP_OP(p) (*((BcLexType *) bc_vec_top(&(p)->ops))) -#define BC_PARSE_LEAF(p, rparen) \ - (((p) >= BC_INST_NUM && (p) <= BC_INST_SQRT) || (rparen) || \ - (p) == BC_INST_INC_POST || (p) == BC_INST_DEC_POST) - -// We can calculate the conversion between tokens and exprs by subtracting the -// position of the first operator in the lex enum and adding the position of the -// first in the expr enum. Note: This only works for binary operators. -#define BC_PARSE_TOKEN_INST(t) ((char) ((t) -BC_LEX_NEG + BC_INST_NEG)) - -static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next); - -#endif // ENABLE_BC - -#if ENABLE_DC - -#define DC_PARSE_BUF_LEN ((int) (sizeof(uint32_t) * CHAR_BIT)) - -static BcStatus dc_lex_token(BcLex *l); - -static BcStatus dc_parse_expr(BcParse *p, uint8_t flags); - -#endif // ENABLE_DC - typedef struct BcProgram { size_t len; @@ -738,7 +694,6 @@ typedef struct BcProgram { #define BC_PROG_MAIN (0) #define BC_PROG_READ (1) - #if ENABLE_DC #define BC_PROG_REQ_FUNCS (2) #endif @@ -747,34 +702,54 @@ typedef struct BcProgram { #define BC_PROG_NUM(r, n) \ ((r)->t != BC_RESULT_ARRAY && (r)->t != BC_RESULT_STR && !BC_PROG_STR(n)) -typedef unsigned long (*BcProgramBuiltIn)(BcNum *); - -static void bc_program_addFunc(char *name, size_t *idx); -static void bc_program_reset(void); - -#define BC_FLAG_X (1 << 0) -#define BC_FLAG_W (1 << 1) -#define BC_FLAG_V (1 << 2) -#define BC_FLAG_S (1 << 3) -#define BC_FLAG_Q (1 << 4) -#define BC_FLAG_L (1 << 5) -#define BC_FLAG_I (1 << 6) +#define BC_FLAG_W (1 << 0) +#define BC_FLAG_V (1 << 1) +#define BC_FLAG_S (1 << 2) +#define BC_FLAG_Q (1 << 3) +#define BC_FLAG_L (1 << 4) +#define BC_FLAG_I (1 << 5) +#define DC_FLAG_X (1 << 6) #define BC_MAX(a, b) ((a) > (b) ? (a) : (b)) #define BC_MIN(a, b) ((a) < (b) ? (a) : (b)) -#define BC_MAX_OBASE ((unsigned) 999) -#define BC_MAX_DIM ((unsigned) INT_MAX) -#define BC_MAX_SCALE ((unsigned) UINT_MAX) -#define BC_MAX_STRING ((unsigned) UINT_MAX - 1) -#define BC_MAX_NAME BC_MAX_STRING -#define BC_MAX_NUM BC_MAX_STRING -#define BC_MAX_EXP ((unsigned long) LONG_MAX) -#define BC_MAX_VARS ((unsigned long) SIZE_MAX - 1) +#define BC_MAX_OBASE ((unsigned) 999) +#define BC_MAX_DIM ((unsigned) INT_MAX) +#define BC_MAX_SCALE ((unsigned) UINT_MAX) +#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" + +#define BC_MAX_OBASE_STR "999" + +#if INT_MAX == 2147483647 +# define BC_MAX_DIM_STR "2147483647" +#elif INT_MAX == 9223372036854775807 +# define BC_MAX_DIM_STR "9223372036854775807" +#else +# error Strange INT_MAX +#endif + +#if UINT_MAX == 4294967295 +# define BC_MAX_SCALE_STR "4294967295" +# define BC_MAX_STRING_STR "4294967294" +#elif UINT_MAX == 18446744073709551615 +# define BC_MAX_SCALE_STR "18446744073709551615" +# define BC_MAX_STRING_STR "18446744073709551614" +#else +# error Strange UINT_MAX +#endif +#define BC_MAX_NUM_STR BC_MAX_STRING_STR struct globals { - smallint ttyin; - smallint eof; + IF_FEATURE_BC_SIGNALS(smallint ttyin;) + IF_FEATURE_CLEAN_UP(smallint exiting;) char sbgn; char send; @@ -788,21 +763,35 @@ struct globals { BcVec files; char *env_args; + +#if ENABLE_FEATURE_EDITING + line_input_t *line_input_state; +#endif } FIX_ALIASING; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) +#define FREE_G() do { \ + FREE_PTR_TO_GLOBALS(); \ +} while (0) #define G_posix (ENABLE_BC && (option_mask32 & BC_FLAG_S)) #define G_warn (ENABLE_BC && (option_mask32 & BC_FLAG_W)) -#define G_exreg (ENABLE_DC && (option_mask32 & BC_FLAG_X)) -#define G_interrupt (ENABLE_FEATURE_BC_SIGNALS ? bb_got_signal : 0) - - +#define G_exreg (ENABLE_DC && (option_mask32 & DC_FLAG_X)) +#if ENABLE_FEATURE_BC_SIGNALS +# define G_interrupt bb_got_signal +# define G_ttyin G.ttyin +#else +# define G_interrupt 0 +# define G_ttyin 0 +#endif +#if ENABLE_FEATURE_CLEAN_UP +# define G_exiting G.exiting +#else +# define G_exiting 0 +#endif #define IS_BC (ENABLE_BC && (!ENABLE_DC || applet_name[0] == 'b')) -static void bc_vm_info(void); - #if ENABLE_BC // This is a bit array that corresponds to token types. An entry is @@ -852,29 +841,40 @@ static const uint8_t bc_parse_ops[] = { #define bc_parse_op_PREC(i) (bc_parse_ops[i] & 0x0f) #define bc_parse_op_LEFT(i) (bc_parse_ops[i] & 0x10) +// Byte array of up to 4 BC_LEX's, packed into 32-bit word +typedef uint32_t BcParseNext; + // These identify what tokens can come after expressions in certain cases. -static const BcParseNext bc_parse_next_expr = - BC_PARSE_NEXT(4, BC_LEX_NLINE, BC_LEX_SCOLON, BC_LEX_RBRACE, BC_LEX_EOF); -static const BcParseNext bc_parse_next_param = - BC_PARSE_NEXT(2, BC_LEX_RPAREN, BC_LEX_COMMA); -static const BcParseNext bc_parse_next_print = - BC_PARSE_NEXT(4, BC_LEX_COMMA, BC_LEX_NLINE, BC_LEX_SCOLON, BC_LEX_EOF); -static const BcParseNext bc_parse_next_rel = BC_PARSE_NEXT(1, BC_LEX_RPAREN); -static const BcParseNext bc_parse_next_elem = BC_PARSE_NEXT(1, BC_LEX_RBRACKET); -static const BcParseNext bc_parse_next_for = BC_PARSE_NEXT(1, BC_LEX_SCOLON); -static const BcParseNext bc_parse_next_read = - BC_PARSE_NEXT(2, BC_LEX_NLINE, BC_LEX_EOF); +enum { +#define BC_PARSE_NEXT4(a,b,c,d) ( (a) | ((b)<<8) | ((c)<<16) | ((((d)|0x80)<<24)) ) +#define BC_PARSE_NEXT2(a,b) BC_PARSE_NEXT4(a,b,0xff,0xff) +#define BC_PARSE_NEXT1(a) BC_PARSE_NEXT4(a,0xff,0xff,0xff) + bc_parse_next_expr = BC_PARSE_NEXT4(BC_LEX_NLINE, BC_LEX_SCOLON, BC_LEX_RBRACE, BC_LEX_EOF), + bc_parse_next_param = BC_PARSE_NEXT2(BC_LEX_RPAREN, BC_LEX_COMMA), + bc_parse_next_print = BC_PARSE_NEXT4(BC_LEX_COMMA, BC_LEX_NLINE, BC_LEX_SCOLON, BC_LEX_EOF), + bc_parse_next_rel = BC_PARSE_NEXT1(BC_LEX_RPAREN), + bc_parse_next_elem = BC_PARSE_NEXT1(BC_LEX_RBRACKET), + bc_parse_next_for = BC_PARSE_NEXT1(BC_LEX_SCOLON), + bc_parse_next_read = BC_PARSE_NEXT2(BC_LEX_NLINE, BC_LEX_EOF), +#undef BC_PARSE_NEXT4 +#undef BC_PARSE_NEXT2 +#undef BC_PARSE_NEXT1 +}; #endif // ENABLE_BC #if ENABLE_DC -static const BcLexType dc_lex_regs[] = { +static const //BcLexType - should be this type, but narrower type saves size: +uint8_t +dc_lex_regs[] = { BC_LEX_OP_REL_EQ, BC_LEX_OP_REL_LE, BC_LEX_OP_REL_GE, BC_LEX_OP_REL_NE, BC_LEX_OP_REL_LT, BC_LEX_OP_REL_GT, BC_LEX_SCOLON, BC_LEX_COLON, BC_LEX_ELSE, BC_LEX_LOAD, BC_LEX_LOAD_POP, BC_LEX_OP_ASSIGN, BC_LEX_STORE_PUSH, }; -static const BcLexType dc_lex_tokens[] = { +static const //BcLexType - should be this type +uint8_t +dc_lex_tokens[] = { BC_LEX_OP_MODULUS, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_LPAREN, BC_LEX_INVALID, BC_LEX_OP_MULTIPLY, BC_LEX_OP_PLUS, BC_LEX_INVALID, BC_LEX_OP_MINUS, BC_LEX_INVALID, BC_LEX_OP_DIVIDE, @@ -902,7 +902,9 @@ static const BcLexType dc_lex_tokens[] = { BC_LEX_INVALID }; -static const BcInst dc_parse_insts[] = { +static const //BcInst - should be this type. Using signed narrow type since BC_INST_INVALID is -1 +int8_t +dc_parse_insts[] = { BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE, BC_INST_INVALID, BC_INST_POWER, BC_INST_MULTIPLY, BC_INST_DIVIDE, BC_INST_MODULUS, BC_INST_PLUS, BC_INST_MINUS, @@ -940,6 +942,17 @@ static void fflush_and_check(void) bb_perror_msg_and_die("output error"); } +#if ENABLE_FEATURE_CLEAN_UP +#define QUIT_OR_RETURN_TO_MAIN \ +do { \ + IF_FEATURE_BC_SIGNALS(G_ttyin = 0;) /* do not loop in main loop anymore */ \ + G_exiting = 1; \ + return BC_STATUS_FAILURE; \ +} while (0) +#else +#define QUIT_OR_RETURN_TO_MAIN quit() +#endif + static void quit(void) NORETURN; static void quit(void) { @@ -971,11 +984,12 @@ static NOINLINE int bc_error_fmt(const char *fmt, ...) bc_verror_msg(fmt, p); va_end(p); - if (!G.ttyin) + if (!ENABLE_FEATURE_CLEAN_UP && !G_ttyin) exit(1); return BC_STATUS_FAILURE; } +#if ENABLE_BC static NOINLINE int bc_posix_error_fmt(const char *fmt, ...) { va_list p; @@ -991,23 +1005,25 @@ static NOINLINE int bc_posix_error_fmt(const char *fmt, ...) // Do we treat non-POSIX constructs as errors? if (!(option_mask32 & BC_FLAG_S)) return BC_STATUS_SUCCESS; // no, it's a warning - if (!G.ttyin) + if (!ENABLE_FEATURE_CLEAN_UP && !G_ttyin) exit(1); return BC_STATUS_FAILURE; } +#endif // 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: +// function must not have caller-cleaned parameters on stack. +// Unfortunately, vararg function API does exactly that on most arches. +// Thus, use these shims for the cases when we have no vararg PARAMS: static int bc_error(const char *msg) { return bc_error_fmt("%s", msg); } -static int bc_posix_error(const char *msg) +#if ENABLE_BC +static int bc_POSIX_requires(const char *msg) { - return bc_posix_error_fmt("%s", msg); + return bc_posix_error_fmt("POSIX requires %s", msg); } static int bc_POSIX_does_not_allow(const char *msg) { @@ -1021,6 +1037,7 @@ static int bc_POSIX_does_not_allow_empty_X_expression_in_for(const char *msg) { return bc_posix_error_fmt("%san empty %s expression in a for loop", "POSIX does not allow ", msg); } +#endif static int bc_error_bad_character(char c) { return bc_error_fmt("bad character '%c'", c); @@ -1076,6 +1093,13 @@ static void bc_vec_expand(BcVec *v, size_t req) } } +static void bc_vec_pop(BcVec *v) +{ + v->len--; + if (v->dtor) + v->dtor(v->v + (v->size * v->len)); +} + static void bc_vec_npop(BcVec *v, size_t n) { if (!v->dtor) @@ -1139,14 +1163,15 @@ static void bc_vec_string(BcVec *v, size_t len, const char *str) static void bc_vec_concat(BcVec *v, const char *str) { - size_t len; + size_t len, slen; if (v->len == 0) bc_vec_pushZeroByte(v); - len = v->len + strlen(str); + slen = strlen(str); + len = v->len + slen; - if (v->cap < len) bc_vec_grow(v, len - v->len); - strcat(v->v, str); + if (v->cap < len) bc_vec_grow(v, slen); + strcpy(v->v + v->len - 1, str); v->len = len; } @@ -1156,18 +1181,43 @@ static void *bc_vec_item(const BcVec *v, size_t idx) return v->v + v->size * idx; } +static char** bc_program_str(size_t idx) +{ + return bc_vec_item(&G.prog.strs, idx); +} + +static BcFunc* bc_program_func(size_t idx) +{ + return bc_vec_item(&G.prog.fns, idx); +} + static void *bc_vec_item_rev(const BcVec *v, size_t idx) { return v->v + v->size * (v->len - idx - 1); } -static void bc_vec_free(void *vec) +static void *bc_vec_top(const BcVec *v) +{ + return v->v + v->size * (v->len - 1); +} + +static FAST_FUNC void bc_vec_free(void *vec) { BcVec *v = (BcVec *) vec; bc_vec_pop_all(v); free(v->v); } +static int bc_id_cmp(const void *e1, const void *e2) +{ + return strcmp(((const BcId *) e1)->name, ((const BcId *) e2)->name); +} + +static FAST_FUNC void bc_id_free(void *id) +{ + free(((BcId *) id)->name); +} + static size_t bc_map_find(const BcVec *v, const void *ptr) { size_t low = 0, high = v->len; @@ -1202,74 +1252,100 @@ 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_index(const BcVec *v, const void *ptr) { size_t i = bc_map_find(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 int push_input_byte(BcVec *vec, char c) +{ + if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'? + || c > 0x7e + ) { + // Bad chars on this line, ignore entire line + bc_error_fmt("illegal character 0x%02x", c); + return 1; + } + bc_vec_pushByte(vec, (char)c); + return 0; +} -static BcStatus bc_read_line(BcVec *vec, const char *prompt) +static BcStatus bc_read_line(BcVec *vec) { + BcStatus s; bool bad_chars; + s = BC_STATUS_SUCCESS; do { - int i; + int c; bad_chars = 0; bc_vec_pop_all(vec); fflush_and_check(); -#if ENABLE_FEATURE_BC_SIGNALS - if (bb_got_signal) { // ^C was pressed - intr: - bb_got_signal = 0; // resets G_interrupt to zero - fputs(IS_BC - ? "\ninterrupt (type \"quit\" to exit)\n" - : "\ninterrupt (type \"q\" to exit)\n" - , stderr); - } -#endif - if (G.ttyin && !G_posix) - fputs(prompt, stderr); #if ENABLE_FEATURE_BC_SIGNALS - errno = 0; + if (G_interrupt) { // ^C was pressed + intr: + G_interrupt = 0; + // GNU bc says "interrupted execution." + // GNU dc says "Interrupt!" + fputs("\ninterrupted execution\n", stderr); + } +# if ENABLE_FEATURE_EDITING + if (G_ttyin) { + int n, i; +# define line_buf bb_common_bufsiz1 + n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE); + if (n <= 0) { // read errors or EOF, or ^D, or ^C + if (n == 0) // ^C + goto intr; + s = BC_STATUS_EOF; + break; + } + i = 0; + for (;;) { + c = line_buf[i++]; + if (!c) break; + bad_chars |= push_input_byte(vec, c); + } +# undef line_buf + } else +# endif #endif - do { - i = fgetc(stdin); - if (i == EOF) { -#if ENABLE_FEATURE_BC_SIGNALS + { + IF_FEATURE_BC_SIGNALS(errno = 0;) + do { + c = fgetc(stdin); +#if ENABLE_FEATURE_BC_SIGNALS && !ENABLE_FEATURE_EDITING // Both conditions appear simultaneously, check both just in case - if (errno == EINTR || bb_got_signal) { + if (errno == EINTR || G_interrupt) { // ^C was pressed clearerr(stdin); goto intr; } #endif - if (ferror(stdin)) - quit(); // this emits error message - G.eof = 1; - // Note: EOF does not append '\n', therefore: - // printf 'print 123\n' | bc - works - // printf 'print 123' | bc - fails (syntax error) - break; - } - - if ((i < ' ' && i != '\t' && i != '\r' && i != '\n') // also allow '\v' '\f'? - || i > 0x7e - ) { - // Bad chars on this line, ignore entire line - bc_error_fmt("illegal character 0x%02x", i); - bad_chars = 1; - } - bc_vec_pushByte(vec, (char)i); - } while (i != '\n'); + if (c == EOF) { + if (ferror(stdin)) + quit(); // this emits error message + s = BC_STATUS_EOF; + // Note: EOF does not append '\n', therefore: + // printf 'print 123\n' | bc - works + // printf 'print 123' | bc - fails (syntax error) + break; + } + bad_chars |= push_input_byte(vec, c); + } while (c != '\n'); + } } while (bad_chars); bc_vec_pushZeroByte(vec); - return BC_STATUS_SUCCESS; + return s; } static char* bc_read_file(const char *path) @@ -1278,7 +1354,8 @@ static char* bc_read_file(const char *path) size_t size = ((size_t) -1); size_t i; - buf = xmalloc_open_read_close(path, &size); + // Never returns NULL (dies on errors) + buf = xmalloc_xopen_read_close(path, &size); for (i = 0; i < size; ++i) { char c = buf[i]; @@ -1294,36 +1371,6 @@ static char* bc_read_file(const char *path) return buf; } -static void bc_args(int argc, char **argv) -{ - unsigned opts; - int i; - - GETOPT_RESET(); -#if ENABLE_FEATURE_BC_LONG_OPTIONS - opts = getopt32long(argv, "xwvsqli", - "extended-register\0" No_argument "x" - "warn\0" No_argument "w" - "version\0" No_argument "v" - "standard\0" No_argument "s" - "quiet\0" No_argument "q" - "mathlib\0" No_argument "l" - "interactive\0" No_argument "i" - ); -#else - opts = getopt32(argv, "xwvsqli"); -#endif - if (getenv("POSIXLY_CORRECT")) - option_mask32 |= BC_FLAG_S; - - if (opts & BC_FLAG_V) bc_vm_info(); - // should not be necessary, getopt32() handles this?? - //if (argv[optind] && !strcmp(argv[optind], "--")) ++optind; - - for (i = optind; i < argc; ++i) - bc_vec_push(&G.files, argv + i); -} - static void bc_num_setToZero(BcNum *n, size_t scale) { n->len = 0; @@ -1351,6 +1398,95 @@ static void bc_num_ten(BcNum *n) n->num[1] = 1; } +// Note: this also sets BcNum to zero +static void bc_num_init(BcNum *n, size_t req) +{ + req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; + //memset(n, 0, sizeof(BcNum)); - cleared by assignments below + n->num = xmalloc(req); + n->cap = req; + n->rdx = 0; + n->len = 0; + n->neg = false; +} + +static void bc_num_init_DEF_SIZE(BcNum *n) +{ + bc_num_init(n, BC_NUM_DEF_SIZE); +} + +static void bc_num_expand(BcNum *n, size_t req) +{ + req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; + if (req > n->cap) { + n->num = xrealloc(n->num, req); + n->cap = req; + } +} + +static FAST_FUNC void bc_num_free(void *num) +{ + free(((BcNum *) num)->num); +} + +static void bc_num_copy(BcNum *d, BcNum *s) +{ + if (d != s) { + bc_num_expand(d, s->cap); + d->len = s->len; + d->neg = s->neg; + d->rdx = s->rdx; + memcpy(d->num, s->num, sizeof(BcDig) * d->len); + } +} + +static BcStatus bc_num_ulong(BcNum *n, unsigned long *result_p) +{ + size_t i; + unsigned long pow, result; + + if (n->neg) return bc_error("negative number"); + + for (result = 0, pow = 1, i = n->rdx; i < n->len; ++i) { + + unsigned long prev = result, powprev = pow; + + result += ((unsigned long) n->num[i]) * pow; + pow *= 10; + + if (result < prev || pow < powprev) + return bc_error("overflow"); + prev = result; + powprev = pow; + } + *result_p = result; + + return BC_STATUS_SUCCESS; +} + +static void bc_num_ulong2num(BcNum *n, unsigned long val) +{ + BcDig *ptr; + + bc_num_zero(n); + + if (val == 0) return; + + if (ULONG_MAX == 0xffffffffUL) + bc_num_expand(n, 10); // 10 digits: 4294967295 + if (ULONG_MAX == 0xffffffffffffffffULL) + bc_num_expand(n, 20); // 20 digits: 18446744073709551615 + BUILD_BUG_ON(ULONG_MAX > 0xffffffffffffffffULL); + + ptr = n->num; + for (;;) { + n->len++; + *ptr++ = val % 10; + val /= 10; + if (val == 0) break; + } +} + static void bc_num_subArrays(BcDig *restrict a, BcDig *restrict b, size_t len) { @@ -1363,6 +1499,20 @@ static void bc_num_subArrays(BcDig *restrict a, BcDig *restrict b, } } +#define BC_NUM_NEG(n, neg) ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg)) +#define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1) +#define BC_NUM_INT(n) ((n)->len - (n)->rdx) +//#define BC_NUM_AREQ(a, b) (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1) +static /*ALWAYS_INLINE*/ size_t BC_NUM_AREQ(BcNum *a, BcNum *b) +{ + return BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1; +} +//#define BC_NUM_MREQ(a, b, scale) (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1) +static /*ALWAYS_INLINE*/ size_t BC_NUM_MREQ(BcNum *a, BcNum *b, size_t scale) +{ + return BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx) + 1; +} + static ssize_t bc_num_compare(BcDig *restrict a, BcDig *restrict b, size_t len) { size_t i; @@ -1492,8 +1642,12 @@ static void bc_num_split(BcNum *restrict n, size_t idx, BcNum *restrict a, static BcStatus bc_num_shift(BcNum *n, size_t places) { if (places == 0 || n->len == 0) return BC_STATUS_SUCCESS; - if (places + n->len > BC_MAX_NUM) - return bc_error("number too long: must be [1, BC_NUM_MAX]"); + + // This check makes sense only if size_t is (much) larger than BC_MAX_NUM. + if (SIZE_MAX > (BC_MAX_NUM | 0xff)) { + if (places + n->len > BC_MAX_NUM) + return bc_error("number too long: must be [1,"BC_MAX_NUM_STR"]"); + } if (n->rdx >= places) n->rdx -= places; @@ -1519,7 +1673,7 @@ static BcStatus bc_num_inv(BcNum *a, BcNum *b, size_t scale) return bc_num_div(&one, a, b, scale); } -static BcStatus bc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) +static FAST_FUNC BcStatus bc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) { BcDig *ptr, *ptr_a, *ptr_b, *ptr_c; size_t i, max, min_rdx, min_int, diff, a_int, b_int; @@ -1590,7 +1744,7 @@ static BcStatus bc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) return BC_STATUS_SUCCESS; // can't make void, see bc_num_binary() } -static BcStatus bc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) +static FAST_FUNC BcStatus bc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) { ssize_t cmp; BcNum *minuend, *subtrahend; @@ -1652,7 +1806,7 @@ static BcStatus bc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) return BC_STATUS_SUCCESS; // can't make void, see bc_num_binary() } -static BcStatus bc_num_k(BcNum *restrict a, BcNum *restrict b, +static FAST_FUNC BcStatus bc_num_k(BcNum *restrict a, BcNum *restrict b, BcNum *restrict c) { BcStatus s; @@ -1758,7 +1912,7 @@ err: return s; } -static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) +static FAST_FUNC BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s; BcNum cpa, cpb; @@ -1800,7 +1954,7 @@ err: return s; } -static BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) +static FAST_FUNC BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s = BC_STATUS_SUCCESS; BcDig *n, *p, q; @@ -1872,7 +2026,7 @@ static BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) return s; } -static BcStatus bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c, +static FAST_FUNC BcStatus bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c, BcNum *restrict d, size_t scale, size_t ts) { BcStatus s; @@ -1909,7 +2063,7 @@ err: return s; } -static BcStatus bc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) +static FAST_FUNC BcStatus bc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s; BcNum c1; @@ -1922,7 +2076,7 @@ static BcStatus bc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) return s; } -static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) +static FAST_FUNC BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s = BC_STATUS_SUCCESS; BcNum copy; @@ -1957,7 +2111,12 @@ static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) bc_num_init(©, a->len); bc_num_copy(©, a); - if (!neg) scale = BC_MIN(a->rdx * pow, BC_MAX(scale, a->rdx)); + if (!neg) { + if (a->rdx > scale) + scale = a->rdx; + if (a->rdx * pow < scale) + scale = a->rdx * pow; + } b->neg = neg; @@ -2045,207 +2204,70 @@ static BcStatus bc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale, return s; } -static bool bc_num_strValid(const char *val, size_t base) +static void bc_num_printNewline(void) { - BcDig b; - bool small, radix = false; - size_t i, len = strlen(val); + if (G.prog.nchars == G.prog.len - 1) { + bb_putchar('\\'); + bb_putchar('\n'); + G.prog.nchars = 0; + } +} - if (!len) return true; +#if ENABLE_DC +static FAST_FUNC void bc_num_printChar(size_t num, size_t width, bool radix) +{ + (void) radix; + bb_putchar((char) num); + G.prog.nchars += width; +} +#endif - small = base <= 10; - b = (BcDig)(small ? base + '0' : base - 10 + 'A'); +static FAST_FUNC void bc_num_printDigits(size_t num, size_t width, bool radix) +{ + size_t exp, pow; - for (i = 0; i < len; ++i) { + bc_num_printNewline(); + bb_putchar(radix ? '.' : ' '); + ++G.prog.nchars; - BcDig c = val[i]; + bc_num_printNewline(); + for (exp = 0, pow = 1; exp < width - 1; ++exp, pow *= 10) + continue; - if (c == '.') { + for (exp = 0; exp < width; pow /= 10, ++G.prog.nchars, ++exp) { + size_t dig; + bc_num_printNewline(); + dig = num / pow; + num -= dig * pow; + bb_putchar(((char) dig) + '0'); + } +} - if (radix) return false; +static FAST_FUNC void bc_num_printHex(size_t num, size_t width, bool radix) +{ + if (radix) { + bc_num_printNewline(); + bb_putchar('.'); + G.prog.nchars += 1; + } - radix = true; - continue; - } - - if (c < '0' || (small && c >= b) || (c > '9' && (c < 'A' || c >= b))) - return false; - } - - return true; -} - -static void bc_num_parseDecimal(BcNum *n, const char *val) -{ - size_t len, i; - const char *ptr; - bool zero = true; - - for (i = 0; val[i] == '0'; ++i); - - val += i; - len = strlen(val); - bc_num_zero(n); - - if (len != 0) { - for (i = 0; zero && i < len; ++i) zero = val[i] == '0' || val[i] == '.'; - bc_num_expand(n, len); - } - - ptr = strchr(val, '.'); - - n->rdx = 0; - if (ptr != NULL) - n->rdx = (size_t)((val + len) - (ptr + 1)); - - if (!zero) { - for (i = len - 1; i < len; ++n->len, i -= 1 + (i && val[i - 1] == '.')) - n->num[n->len] = val[i] - '0'; - } -} - -static void bc_num_parseBase(BcNum *n, const char *val, BcNum *base) -{ - BcStatus s; - BcNum temp, mult, result; - BcDig c = '\0'; - bool zero = true; - unsigned long v; - size_t i, digits, len = strlen(val); - - bc_num_zero(n); - - for (i = 0; zero && i < len; ++i) zero = (val[i] == '.' || val[i] == '0'); - if (zero) return; - - bc_num_init(&temp, BC_NUM_DEF_SIZE); - bc_num_init(&mult, BC_NUM_DEF_SIZE); - - for (i = 0; i < len; ++i) { - - c = val[i]; - if (c == '.') break; - - v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10); - - s = bc_num_mul(n, base, &mult, 0); - if (s) goto int_err; - bc_num_ulong2num(&temp, v); - s = bc_num_add(&mult, &temp, n, 0); - if (s) goto int_err; - } - - if (i == len) { - c = val[i]; - if (c == 0) goto int_err; - } - - bc_num_init(&result, base->len); - bc_num_zero(&result); - bc_num_one(&mult); - - for (i += 1, digits = 0; i < len; ++i, ++digits) { - - c = val[i]; - if (c == 0) break; - - v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10); - - s = bc_num_mul(&result, base, &result, 0); - if (s) goto err; - bc_num_ulong2num(&temp, v); - s = bc_num_add(&result, &temp, &result, 0); - if (s) goto err; - s = bc_num_mul(&mult, base, &mult, 0); - if (s) goto err; - } - - s = bc_num_div(&result, &mult, &result, digits); - if (s) goto err; - s = bc_num_add(n, &result, n, digits); - if (s) goto err; - - if (n->len != 0) { - if (n->rdx < digits) bc_num_extend(n, digits - n->rdx); - } - else - bc_num_zero(n); - -err: - bc_num_free(&result); -int_err: - bc_num_free(&mult); - bc_num_free(&temp); -} - -static void bc_num_printNewline(size_t *nchars, size_t line_len) -{ - if (*nchars == line_len - 1) { - bb_putchar('\\'); - bb_putchar('\n'); - *nchars = 0; - } -} - -#if ENABLE_DC -static void bc_num_printChar(size_t num, size_t width, bool radix, - size_t *nchars, size_t line_len) -{ - (void) radix, (void) line_len; - bb_putchar((char) num); - *nchars = *nchars + width; -} -#endif - -static void bc_num_printDigits(size_t num, size_t width, bool radix, - size_t *nchars, size_t line_len) -{ - size_t exp, pow; - - bc_num_printNewline(nchars, line_len); - bb_putchar(radix ? '.' : ' '); - ++(*nchars); - - bc_num_printNewline(nchars, line_len); - for (exp = 0, pow = 1; exp < width - 1; ++exp, pow *= 10) - continue; - - for (exp = 0; exp < width; pow /= 10, ++(*nchars), ++exp) { - size_t dig; - bc_num_printNewline(nchars, line_len); - dig = num / pow; - num -= dig * pow; - bb_putchar(((char) dig) + '0'); - } -} - -static void bc_num_printHex(size_t num, size_t width, bool radix, - size_t *nchars, size_t line_len) -{ - if (radix) { - bc_num_printNewline(nchars, line_len); - bb_putchar('.'); - *nchars += 1; - } - - bc_num_printNewline(nchars, line_len); + bc_num_printNewline(); bb_putchar(bb_hexdigits_upcase[num]); - *nchars = *nchars + width; + G.prog.nchars += width; } -static void bc_num_printDecimal(BcNum *n, size_t *nchars, size_t len) +static void bc_num_printDecimal(BcNum *n) { size_t i, rdx = n->rdx - 1; if (n->neg) bb_putchar('-'); - (*nchars) += n->neg; + G.prog.nchars += n->neg; for (i = n->len - 1; i < n->len; --i) - bc_num_printHex((size_t) n->num[i], 1, i == rdx, nchars, len); + bc_num_printHex((size_t) n->num[i], 1, i == rdx); } -static BcStatus bc_num_printNum(BcNum *n, BcNum *base, size_t width, - size_t *nchars, size_t len, BcNumDigitOp print) +static BcStatus bc_num_printNum(BcNum *n, BcNum *base, size_t width, BcNumDigitOp print) { BcStatus s; BcVec stack; @@ -2255,7 +2277,7 @@ static BcStatus bc_num_printNum(BcNum *n, BcNum *base, size_t width, bool radix; if (n->len == 0) { - print(0, width, false, nchars, len); + print(0, width, false); return BC_STATUS_SUCCESS; } @@ -2281,7 +2303,7 @@ static BcStatus bc_num_printNum(BcNum *n, BcNum *base, size_t width, for (i = 0; i < stack.len; ++i) { ptr = bc_vec_item_rev(&stack, i); - print(*ptr, width, false, nchars, len); + print(*ptr, width, false); } if (!n->rdx) goto err; @@ -2294,7 +2316,7 @@ static BcStatus bc_num_printNum(BcNum *n, BcNum *base, size_t width, bc_num_ulong2num(&intp, dig); s = bc_num_sub(&fracp, &intp, &fracp, 0); if (s) goto err; - print(dig, width, radix, nchars, len); + print(dig, width, radix); s = bc_num_mul(&frac_len, base, &frac_len, 0); if (s) goto err; } @@ -2308,72 +2330,171 @@ err: return s; } -static BcStatus bc_num_printBase(BcNum *n, BcNum *base, size_t base_t, - size_t *nchars, size_t line_len) +static BcStatus bc_num_printBase(BcNum *n) { BcStatus s; size_t width, i; BcNumDigitOp print; bool neg = n->neg; - if (neg) bb_putchar('-'); - (*nchars) += neg; + if (neg) { + bb_putchar('-'); + G.prog.nchars++; + } n->neg = false; - if (base_t <= BC_NUM_MAX_IBASE) { + if (G.prog.ob_t <= BC_NUM_MAX_IBASE) { width = 1; print = bc_num_printHex; } else { - for (i = base_t - 1, width = 0; i != 0; i /= 10, ++width); + for (i = G.prog.ob_t - 1, width = 0; i != 0; i /= 10, ++width) + continue; print = bc_num_printDigits; } - s = bc_num_printNum(n, base, width, nchars, line_len, print); + s = bc_num_printNum(n, &G.prog.ob, width, print); n->neg = neg; return s; } #if ENABLE_DC -static BcStatus bc_num_stream(BcNum *n, BcNum *base, size_t *nchars, size_t len) +static BcStatus bc_num_stream(BcNum *n, BcNum *base) { - return bc_num_printNum(n, base, 1, nchars, len, bc_num_printChar); + return bc_num_printNum(n, base, 1, bc_num_printChar); } #endif -static void bc_num_init(BcNum *n, size_t req) +static bool bc_num_strValid(const char *val, size_t base) { - req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; - memset(n, 0, sizeof(BcNum)); - n->num = xmalloc(req); - n->cap = req; -} + BcDig b; + bool radix; -static void bc_num_expand(BcNum *n, size_t req) -{ - req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; - if (req > n->cap) { - n->num = xrealloc(n->num, req); - n->cap = req; + b = (BcDig)(base <= 10 ? base + '0' : base - 10 + 'A'); + radix = false; + for (;;) { + BcDig c = *val++; + if (c == '\0') + break; + if (c == '.') { + if (radix) return false; + radix = true; + continue; + } + if (c < '0' || c >= b || (c > '9' && c < 'A')) + return false; } + return true; } -static void bc_num_free(void *num) +// Note: n is already "bc_num_zero()"ed, +// leading zeroes in "val" are removed +static void bc_num_parseDecimal(BcNum *n, const char *val) { - free(((BcNum *) num)->num); + size_t len, i; + const char *ptr; + + len = strlen(val); + if (len == 0) + return; + + bc_num_expand(n, len); + + ptr = strchr(val, '.'); + + n->rdx = 0; + if (ptr != NULL) + n->rdx = (size_t)((val + len) - (ptr + 1)); + + for (i = 0; val[i]; ++i) { + if (val[i] != '0' && val[i] != '.') { + // Not entirely zero value - convert it, and exit + i = len - 1; + for (;;) { + n->num[n->len] = val[i] - '0'; + ++n->len; + skip_dot: + if ((ssize_t)--i == (ssize_t)-1) break; + if (val[i] == '.') goto skip_dot; + } + break; + } + } + // if this is reached, the value is entirely zero } -static void bc_num_copy(BcNum *d, BcNum *s) +// Note: n is already "bc_num_zero()"ed, +// leading zeroes in "val" are removed +static void bc_num_parseBase(BcNum *n, const char *val, BcNum *base) { - if (d != s) { - bc_num_expand(d, s->cap); - d->len = s->len; - d->neg = s->neg; - d->rdx = s->rdx; - memcpy(d->num, s->num, sizeof(BcDig) * d->len); + BcStatus s; + BcNum temp, mult, result; + BcDig c = '\0'; + unsigned long v; + size_t i, digits; + + for (i = 0; ; ++i) { + if (val[i] == '\0') + return; + if (val[i] != '.' && val[i] != '0') + break; + } + + bc_num_init_DEF_SIZE(&temp); + bc_num_init_DEF_SIZE(&mult); + + for (;;) { + c = *val++; + if (c == '\0') goto int_err; + if (c == '.') break; + + v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10); + + s = bc_num_mul(n, base, &mult, 0); + if (s) goto int_err; + bc_num_ulong2num(&temp, v); + s = bc_num_add(&mult, &temp, n, 0); + if (s) goto int_err; } + + bc_num_init(&result, base->len); + //bc_num_zero(&result); - already is + bc_num_one(&mult); + + digits = 0; + for (;;) { + c = *val++; + if (c == '\0') break; + digits++; + + v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10); + + s = bc_num_mul(&result, base, &result, 0); + if (s) goto err; + bc_num_ulong2num(&temp, v); + s = bc_num_add(&result, &temp, &result, 0); + if (s) goto err; + s = bc_num_mul(&mult, base, &mult, 0); + if (s) goto err; + } + + s = bc_num_div(&result, &mult, &result, digits); + if (s) goto err; + s = bc_num_add(n, &result, n, digits); + if (s) goto err; + + if (n->len != 0) { + if (n->rdx < digits) bc_num_extend(n, digits - n->rdx); + } else + bc_num_zero(n); + +err: + bc_num_free(&result); +int_err: + bc_num_free(&mult); + bc_num_free(&temp); } static BcStatus bc_num_parse(BcNum *n, const char *val, BcNum *base, @@ -2382,6 +2503,9 @@ static BcStatus bc_num_parse(BcNum *n, const char *val, BcNum *base, if (!bc_num_strValid(val, base_t)) return bc_error("bad number string"); + bc_num_zero(n); + while (*val == '0') val++; + if (base_t == 10) bc_num_parseDecimal(n, val); else @@ -2390,98 +2514,62 @@ static BcStatus bc_num_parse(BcNum *n, const char *val, BcNum *base, return BC_STATUS_SUCCESS; } -static BcStatus bc_num_print(BcNum *n, BcNum *base, size_t base_t, bool newline, - size_t *nchars, size_t line_len) +static BcStatus bc_num_print(BcNum *n, bool newline) { BcStatus s = BC_STATUS_SUCCESS; - bc_num_printNewline(nchars, line_len); + bc_num_printNewline(); if (n->len == 0) { bb_putchar('0'); - ++(*nchars); + ++G.prog.nchars; } - else if (base_t == 10) - bc_num_printDecimal(n, nchars, line_len); + else if (G.prog.ob_t == 10) + bc_num_printDecimal(n); else - s = bc_num_printBase(n, base, base_t, nchars, line_len); + s = bc_num_printBase(n); if (newline) { bb_putchar('\n'); - *nchars = 0; + G.prog.nchars = 0; } return s; } -static BcStatus bc_num_ulong(BcNum *n, unsigned long *result) -{ - size_t i; - unsigned long pow; - - if (n->neg) return bc_error("negative number"); - - for (*result = 0, pow = 1, i = n->rdx; i < n->len; ++i) { - - unsigned long prev = *result, powprev = pow; - - *result += ((unsigned long) n->num[i]) * pow; - pow *= 10; - - if (*result < prev || pow < powprev) - return bc_error("overflow"); - } - - return BC_STATUS_SUCCESS; -} - -static void bc_num_ulong2num(BcNum *n, unsigned long val) -{ - size_t len; - BcDig *ptr; - unsigned long i; - - bc_num_zero(n); - - if (val == 0) return; - - for (len = 1, i = ULONG_MAX; i != 0; i /= 10, ++len) bc_num_expand(n, len); - for (ptr = n->num, i = 0; val; ++i, ++n->len, val /= 10) ptr[i] = val % 10; -} - -static BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) +static FAST_FUNC BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) { BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_a : bc_num_s; (void) scale; return bc_num_binary(a, b, c, false, op, BC_NUM_AREQ(a, b)); } -static BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) +static FAST_FUNC BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) { BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_s : bc_num_a; (void) scale; return bc_num_binary(a, b, c, true, op, BC_NUM_AREQ(a, b)); } -static BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) +static FAST_FUNC BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) { size_t req = BC_NUM_MREQ(a, b, scale); return bc_num_binary(a, b, c, scale, bc_num_m, req); } -static BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) +static FAST_FUNC BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) { size_t req = BC_NUM_MREQ(a, b, scale); return bc_num_binary(a, b, c, scale, bc_num_d, req); } -static BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) +static FAST_FUNC BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) { size_t req = BC_NUM_MREQ(a, b, scale); return bc_num_binary(a, b, c, scale, bc_num_rem, req); } -static BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) +static FAST_FUNC BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) { return bc_num_binary(a, b, c, scale, bc_num_p, a->len * b->len + 1); } @@ -2513,7 +2601,7 @@ static BcStatus bc_num_sqrt(BcNum *a, BcNum *restrict b, size_t scale) bc_num_init(&num1, len); bc_num_init(&num2, len); - bc_num_init(&half, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&half); bc_num_one(&half); half.num[0] = 5; @@ -2630,7 +2718,7 @@ static BcStatus bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) bc_num_expand(d, c->len); bc_num_init(&base, c->len); bc_num_init(&exp, b->len); - bc_num_init(&two, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&two); bc_num_init(&temp, b->len); bc_num_one(&two); @@ -2668,16 +2756,7 @@ err: } #endif // ENABLE_DC -static int bc_id_cmp(const void *e1, const void *e2) -{ - return strcmp(((const BcId *) e1)->name, ((const BcId *) e2)->name); -} - -static void bc_id_free(void *id) -{ - free(((BcId *) id)->name); -} - +#if ENABLE_BC static BcStatus bc_func_insert(BcFunc *f, char *name, bool var) { BcId a; @@ -2695,6 +2774,7 @@ static BcStatus bc_func_insert(BcFunc *f, char *name, bool var) return BC_STATUS_SUCCESS; } +#endif static void bc_func_init(BcFunc *f) { @@ -2704,7 +2784,7 @@ static void bc_func_init(BcFunc *f) f->nparams = 0; } -static void bc_func_free(void *func) +static FAST_FUNC void bc_func_free(void *func) { BcFunc *f = (BcFunc *) func; bc_vec_free(&f->code); @@ -2712,6 +2792,8 @@ static void bc_func_free(void *func) bc_vec_free(&f->labels); } +static void bc_array_expand(BcVec *a, size_t len); + static void bc_array_init(BcVec *a, bool nums) { if (nums) @@ -2721,28 +2803,13 @@ static void bc_array_init(BcVec *a, bool nums) bc_array_expand(a, 1); } -static void bc_array_copy(BcVec *d, const BcVec *s) -{ - size_t i; - - bc_vec_pop_all(d); - bc_vec_expand(d, s->cap); - d->len = s->len; - - for (i = 0; i < s->len; ++i) { - BcNum *dnum = bc_vec_item(d, i), *snum = bc_vec_item(s, i); - bc_num_init(dnum, snum->len); - bc_num_copy(dnum, snum); - } -} - static void bc_array_expand(BcVec *a, size_t len) { BcResultData data; if (a->size == sizeof(BcNum) && a->dtor == bc_num_free) { while (len > a->len) { - bc_num_init(&data.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&data.n); bc_vec_push(a, &data.n); } } @@ -2754,7 +2821,22 @@ static void bc_array_expand(BcVec *a, size_t len) } } -static void bc_string_free(void *string) +static void bc_array_copy(BcVec *d, const BcVec *s) +{ + size_t i; + + bc_vec_pop_all(d); + bc_vec_expand(d, s->cap); + d->len = s->len; + + for (i = 0; i < s->len; ++i) { + BcNum *dnum = bc_vec_item(d, i), *snum = bc_vec_item(s, i); + bc_num_init(dnum, snum->len); + bc_num_copy(dnum, snum); + } +} + +static FAST_FUNC void bc_string_free(void *string) { free(*((char **) string)); } @@ -2796,7 +2878,7 @@ static void bc_result_copy(BcResult *d, BcResult *src) } #endif // ENABLE_DC -static void bc_result_free(void *result) +static FAST_FUNC void bc_result_free(void *result) { BcResult *r = (BcResult *) result; @@ -2867,8 +2949,11 @@ static BcStatus bc_lex_number(BcLex *l, char start) } len = i + !last_pt - bslashes * 2; - if (len > BC_MAX_NUM) - return bc_error("number too long: must be [1, BC_NUM_MAX]"); + // This check makes sense only if size_t is (much) larger than BC_MAX_NUM. + if (SIZE_MAX > (BC_MAX_NUM | 0xff)) { + if (len > BC_MAX_NUM) + return bc_error("number too long: must be [1,"BC_MAX_NUM_STR"]"); + } bc_vec_pop_all(&l->t.v); bc_vec_expand(&l->t.v, len + 1); @@ -2905,8 +2990,11 @@ static BcStatus bc_lex_name(BcLex *l) while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_') c = buf[++i]; - if (i > BC_MAX_STRING) - return bc_error("name too long: must be [1, BC_NAME_MAX]"); + // This check makes sense only if size_t is (much) larger than BC_MAX_STRING. + if (SIZE_MAX > (BC_MAX_STRING | 0xff)) { + if (i > BC_MAX_STRING) + return bc_error("name too long: must be [1,"BC_MAX_STRING_STR"]"); + } bc_vec_string(&l->t.v, i, buf); // Increment the index. We minus 1 because it has already been incremented. @@ -2983,7 +3071,7 @@ static BcStatus bc_lex_identifier(BcLex *l) match: // buf starts with keyword bc_lex_kws[i] l->t.t = BC_LEX_KEY_1st_keyword + i; - if (!((1 << i) & POSIX_KWORD_MASK)) { + if (!bc_lex_kws_POSIX(i)) { s = bc_posix_error_fmt("%sthe '%.8s' keyword", "POSIX does not allow ", bc_lex_kws[i].name8); if (s) return s; } @@ -3023,8 +3111,11 @@ static BcStatus bc_lex_string(BcLex *l) } len = i - l->i; - if (len > BC_MAX_STRING) - return bc_error("string too long: must be [1, BC_STRING_MAX]"); + // This check makes sense only if size_t is (much) larger than BC_MAX_STRING. + if (SIZE_MAX > (BC_MAX_STRING | 0xff)) { + if (len > BC_MAX_STRING) + return bc_error("string too long: must be [1,"BC_MAX_STRING_STR"]"); + } bc_vec_string(&l->t.v, len, l->buf + l->i); l->i = i + 1; @@ -3075,7 +3166,7 @@ static BcStatus bc_lex_comment(BcLex *l) return BC_STATUS_SUCCESS; } -static BcStatus bc_lex_token(BcLex *l) +static FAST_FUNC BcStatus bc_lex_token(BcLex *l) { BcStatus s = BC_STATUS_SUCCESS; char c = l->buf[l->i++], c2; @@ -3371,7 +3462,7 @@ static BcStatus dc_lex_register(BcLex *l) } else { bc_vec_pop_all(&l->t.v); - bc_vec_pushByte(&l->t.v, l->buf[l->i - 1]); + bc_vec_push(&l->t.v, &l->buf[l->i - 1]); bc_vec_pushZeroByte(&l->t.v); l->t.t = BC_LEX_NAME; } @@ -3402,8 +3493,11 @@ static BcStatus dc_lex_string(BcLex *l) } bc_vec_pushZeroByte(&l->t.v); - if (i - l->i > BC_MAX_STRING) - return bc_error("string too long: must be [1, BC_STRING_MAX]"); + // This check makes sense only if size_t is (much) larger than BC_MAX_STRING. + if (SIZE_MAX > (BC_MAX_STRING | 0xff)) { + if (i - l->i > BC_MAX_STRING) + return bc_error("string too long: must be [1,"BC_MAX_STRING_STR"]"); + } l->i = i; l->line += nls; @@ -3412,7 +3506,7 @@ static BcStatus dc_lex_string(BcLex *l) return BC_STATUS_SUCCESS; } -static BcStatus dc_lex_token(BcLex *l) +static FAST_FUNC BcStatus dc_lex_token(BcLex *l) { BcStatus s = BC_STATUS_SUCCESS; char c = l->buf[l->i++], c2; @@ -3521,12 +3615,16 @@ static BcStatus dc_lex_token(BcLex *l) } #endif // ENABLE_DC +static void bc_program_addFunc(char *name, size_t *idx); + static void bc_parse_addFunc(BcParse *p, char *name, size_t *idx) { bc_program_addFunc(name, idx); - p->func = bc_vec_item(&G.prog.fns, p->fidx); + p->func = bc_program_func(p->fidx); } +#define bc_parse_push(p, i) bc_vec_pushByte(&(p)->func->code, (char) (i)) + static void bc_parse_pushName(BcParse *p, char *name) { size_t i = 0, len = strlen(name); @@ -3568,7 +3666,7 @@ static BcStatus bc_parse_text(BcParse *p, const char *text) { BcStatus s; - p->func = bc_vec_item(&G.prog.fns, p->fidx); + p->func = bc_program_func(p->fidx); if (!text[0] && !BC_PARSE_CAN_EXEC(p)) { p->l.t.t = BC_LEX_INVALID; @@ -3581,6 +3679,24 @@ static BcStatus bc_parse_text(BcParse *p, const char *text) return bc_lex_text(&p->l, text); } +// Called when parsing or execution detects a failure, +// resets execution structures. +static void bc_program_reset(void) +{ + BcFunc *f; + BcInstPtr *ip; + + bc_vec_npop(&G.prog.stack, G.prog.stack.len - 1); + bc_vec_pop_all(&G.prog.results); + + f = bc_program_func(0); + ip = bc_vec_top(&G.prog.stack); + ip->idx = f->code.len; +} + +#define bc_parse_updateFunc(p, f) \ + ((p)->func = bc_program_func((p)->fidx = (f))) + // Called when bc/dc_parse_parse() detects a failure, // resets parsing structures. static void bc_parse_reset(BcParse *p) @@ -3632,9 +3748,22 @@ static void bc_parse_create(BcParse *p, size_t func, bc_parse_updateFunc(p, func); } -#if ENABLE_BC +#if ENABLE_BC + +#define BC_PARSE_TOP_OP(p) (*((BcLexType *) bc_vec_top(&(p)->ops))) +#define BC_PARSE_LEAF(p, rparen) \ + (((p) >= BC_INST_NUM && (p) <= BC_INST_SQRT) || (rparen) || \ + (p) == BC_INST_INC_POST || (p) == BC_INST_DEC_POST) + +// We can calculate the conversion between tokens and exprs by subtracting the +// position of the first operator in the lex enum and adding the position of the +// first in the expr enum. Note: This only works for binary operators. +#define BC_PARSE_TOKEN_INST(t) ((char) ((t) -BC_LEX_NEG + BC_INST_NEG)) + static BcStatus bc_parse_else(BcParse *p); static BcStatus bc_parse_stmt(BcParse *p); +static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next); +static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext next); static BcStatus bc_parse_operator(BcParse *p, BcLexType type, size_t start, size_t *nexprs, bool next) @@ -3995,7 +4124,7 @@ static BcStatus bc_parse_print(BcParse *p) { BcStatus s; BcLexType type; - bool comma = false; + bool comma; s = bc_lex_next(&p->l); if (s) return s; @@ -4005,24 +4134,26 @@ static BcStatus bc_parse_print(BcParse *p) if (type == BC_LEX_SCOLON || type == BC_LEX_NLINE) return bc_error("bad print statement"); - while (!s && type != BC_LEX_SCOLON && type != BC_LEX_NLINE) { + comma = false; + while (type != BC_LEX_SCOLON && type != BC_LEX_NLINE) { - if (type == BC_LEX_STR) + if (type == BC_LEX_STR) { s = bc_parse_string(p, BC_INST_PRINT_POP); - else { + if (s) return s; + } else { s = bc_parse_expr(p, 0, bc_parse_next_print); if (s) return s; bc_parse_push(p, BC_INST_PRINT_POP); } - if (s) return s; - comma = p->l.t.t == BC_LEX_COMMA; - if (comma) s = bc_lex_next(&p->l); + if (comma) { + s = bc_lex_next(&p->l); + if (s) return s; + } type = p->l.t.t; } - if (s) return s; if (comma) return bc_error_bad_token(); return bc_lex_next(&p->l); @@ -4046,7 +4177,7 @@ static BcStatus bc_parse_return(BcParse *p) bc_parse_push(p, BC_INST_RET0); else { - s = bc_parse_expr(p, 0, bc_parse_next_expr); + s = bc_parse_expr_empty_ok(p, 0, bc_parse_next_expr); if (s == BC_STATUS_PARSE_EMPTY_EXP) { bc_parse_push(p, BC_INST_RET0); s = bc_lex_next(&p->l); @@ -4054,7 +4185,7 @@ static BcStatus bc_parse_return(BcParse *p) if (s) return s; if (!paren || p->l.t.last != BC_LEX_RPAREN) { - s = bc_posix_error("POSIX requires parentheses around return expressions"); + s = bc_POSIX_requires("parentheses around return expressions"); if (s) return s; } @@ -4423,7 +4554,7 @@ static BcStatus bc_parse_func(BcParse *p) if (s) return s; if (p->l.t.t != BC_LEX_LBRACE) - s = bc_posix_error("POSIX requires the left brace be on the same line as the function header"); + s = bc_POSIX_requires("the left brace be on the same line as the function header"); return s; @@ -4641,14 +4772,16 @@ static BcStatus bc_parse_stmt(BcParse *p) // the output is produced at _parse time_. s = bc_lex_next(&p->l); if (s) return s; - printf("BC_BASE_MAX = %u\n", BC_MAX_OBASE); - printf("BC_DIM_MAX = %u\n", BC_MAX_DIM); - printf("BC_SCALE_MAX = %u\n", BC_MAX_SCALE); - printf("BC_STRING_MAX = %u\n", BC_MAX_STRING); - printf("BC_NAME_MAX = %u\n", BC_MAX_NAME); - printf("BC_NUM_MAX = %u\n", BC_MAX_NUM); - printf("MAX Exponent = %lu\n", BC_MAX_EXP); - printf("Number of vars = %lu\n", BC_MAX_VARS); + printf( + "BC_BASE_MAX = "BC_MAX_OBASE_STR "\n" + "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" + "MAX Exponent = "BC_MAX_EXP_STR "\n" + "Number of vars = "BC_MAX_VARS_STR "\n" + ); break; } @@ -4663,7 +4796,7 @@ static BcStatus bc_parse_stmt(BcParse *p) // "quit" is a compile-time command. For example, // "if (0 == 1) quit" terminates when parsing the statement, // not when it is executed - quit(); + QUIT_OR_RETURN_TO_MAIN; } case BC_LEX_KEY_RETURN: @@ -4688,7 +4821,7 @@ static BcStatus bc_parse_stmt(BcParse *p) return s; } -static BcStatus bc_parse_parse(BcParse *p) +static FAST_FUNC BcStatus bc_parse_parse(BcParse *p) { BcStatus s; @@ -4709,13 +4842,13 @@ static BcStatus bc_parse_parse(BcParse *p) return s; } -static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) +static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext next) { BcStatus s = BC_STATUS_SUCCESS; BcInst prev = BC_INST_PRINT; BcLexType top, t = p->l.t.t; size_t nexprs = 0, ops_bgn = p->ops.len; - uint32_t i, nparens, nrelops; + unsigned nparens, nrelops; bool paren_first, paren_expr, rprn, done, get_token, assign, bin_last; paren_first = p->l.t.t == BC_LEX_LPAREN; @@ -4940,9 +5073,14 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) if (prev == BC_INST_BOOL_NOT || nexprs != 1) return bc_error_bad_expression(); - for (i = 0; i < next.len; ++i) - if (t == next.tokens[i]) + // next is BcParseNext, byte array of up to 4 BC_LEX's, packed into 32-bit word + for (;;) { + if (t == (next & 0x7f)) goto ok; + if (next & 0x80) // last element? + break; + next >>= 8; + } return bc_error_bad_expression(); ok: @@ -4951,7 +5089,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) if (s) return s; } else if ((flags & BC_PARSE_REL) && nrelops > 1) { - s = bc_posix_error("POSIX requires exactly one comparison operator per condition"); + s = bc_POSIX_requires("exactly one comparison operator per condition"); if (s) return s; } @@ -4963,6 +5101,16 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) return s; } +static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) +{ + BcStatus s; + + s = bc_parse_expr_empty_ok(p, flags, next); + if (s == BC_STATUS_PARSE_EMPTY_EXP) + return bc_error("empty expression"); + return s; +} + static void bc_parse_init(BcParse *p, size_t func) { bc_parse_create(p, func, bc_parse_parse, bc_lex_token); @@ -4972,9 +5120,13 @@ static BcStatus bc_parse_expression(BcParse *p, uint8_t flags) { return bc_parse_expr(p, flags, bc_parse_next_read); } + #endif // ENABLE_BC #if ENABLE_DC + +#define DC_PARSE_BUF_LEN ((int) (sizeof(uint32_t) * CHAR_BIT)) + static BcStatus dc_parse_register(BcParse *p) { BcStatus s; @@ -5176,7 +5328,7 @@ static BcStatus dc_parse_expr(BcParse *p, uint8_t flags) return s; } -static BcStatus dc_parse_parse(BcParse *p) +static FAST_FUNC BcStatus dc_parse_parse(BcParse *p) { BcStatus s; @@ -5197,23 +5349,24 @@ static void dc_parse_init(BcParse *p, size_t func) { bc_parse_create(p, func, dc_parse_parse, dc_lex_token); } + #endif // ENABLE_DC static void common_parse_init(BcParse *p, size_t func) { if (IS_BC) { - bc_parse_init(p, func); + IF_BC(bc_parse_init(p, func);) } else { - dc_parse_init(p, func); + IF_DC(dc_parse_init(p, func);) } } static BcStatus common_parse_expr(BcParse *p, uint8_t flags) { if (IS_BC) { - return bc_parse_expression(p, flags); + IF_BC(return bc_parse_expression(p, flags);) } else { - return dc_parse_expr(p, flags); + IF_DC(return dc_parse_expr(p, flags);) } } @@ -5394,7 +5547,7 @@ static BcStatus bc_program_op(char inst) s = bc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false); if (s) return s; - bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&res.d.n); s = bc_program_ops[inst - BC_INST_POWER](n1, n2, &res.d.n, G.prog.scale); if (s) goto err; @@ -5415,7 +5568,7 @@ static BcStatus bc_program_read(void) BcVec buf; BcInstPtr ip; size_t i; - BcFunc *f = bc_vec_item(&G.prog.fns, BC_PROG_READ); + BcFunc *f = bc_program_func(BC_PROG_READ); for (i = 0; i < G.prog.stack.len; ++i) { BcInstPtr *ip_ptr = bc_vec_item(&G.prog.stack, i); @@ -5429,7 +5582,7 @@ static BcStatus bc_program_read(void) sv_file = G.prog.file; G.prog.file = NULL; - s = bc_read_line(&buf, "read> "); + s = bc_read_line(&buf); if (s) goto io_err; common_parse_init(&parse, BC_PROG_READ); @@ -5450,7 +5603,7 @@ static BcStatus bc_program_read(void) ip.len = G.prog.results.len; // Update this pointer, just in case. - f = bc_vec_item(&G.prog.fns, BC_PROG_READ); + f = bc_program_func(BC_PROG_READ); bc_vec_pushByte(&f->code, BC_INST_POP_EXEC); bc_vec_push(&G.prog.stack, &ip); @@ -5490,7 +5643,7 @@ static char *bc_program_name(char *code, size_t *bgn) return s; } -static void bc_program_printString(const char *str, size_t *nchars) +static void bc_program_printString(const char *str) { size_t i, len = strlen(str); @@ -5501,7 +5654,7 @@ static void bc_program_printString(const char *str, size_t *nchars) } #endif - for (i = 0; i < len; ++i, ++(*nchars)) { + for (i = 0; i < len; ++i, ++G.prog.nchars) { int c = str[i]; @@ -5541,7 +5694,7 @@ static void bc_program_printString(const char *str, size_t *nchars) case 'n': { bb_putchar('\n'); - *nchars = SIZE_MAX; + G.prog.nchars = SIZE_MAX; break; } @@ -5567,7 +5720,7 @@ static void bc_program_printString(const char *str, size_t *nchars) { // Just print the backslash and following character. bb_putchar('\\'); - ++(*nchars); + ++G.prog.nchars; bb_putchar(c); break; } @@ -5580,37 +5733,38 @@ static BcStatus bc_program_print(char inst, size_t idx) { BcStatus s = BC_STATUS_SUCCESS; BcResult *r; - size_t len, i; - char *str; - BcNum *num = NULL; + BcNum *num; bool pop = inst != BC_INST_PRINT; if (!BC_PROG_STACK(&G.prog.results, idx + 1)) return bc_error_stack_has_too_few_elements(); r = bc_vec_item_rev(&G.prog.results, idx); + num = NULL; // is this NULL necessary? s = bc_program_num(r, &num, false); if (s) return s; if (BC_PROG_NUM(r, num)) { - s = bc_num_print(num, &G.prog.ob, G.prog.ob_t, !pop, &G.prog.nchars, G.prog.len); + s = bc_num_print(num, !pop); if (!s) bc_num_copy(&G.prog.last, num); } else { + char *str; idx = (r->t == BC_RESULT_STR) ? r->d.id.idx : num->rdx; - str = *((char **) bc_vec_item(&G.prog.strs, idx)); + str = *bc_program_str(idx); if (inst == BC_INST_PRINT_STR) { - for (i = 0, len = strlen(str); i < len; ++i) { - char c = str[i]; + for (;;) { + char c = *str++; + if (c == '\0') break; bb_putchar(c); - if (c == '\n') G.prog.nchars = SIZE_MAX; ++G.prog.nchars; + if (c == '\n') G.prog.nchars = 0; } } else { - bc_program_printString(str, &G.prog.nchars); + bc_program_printString(str); if (inst == BC_INST_PRINT) bb_putchar('\n'); } } @@ -5648,7 +5802,7 @@ static BcStatus bc_program_logical(char inst) s = bc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false); if (s) return s; - bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&res.d.n); if (inst == BC_INST_BOOL_AND) cond = bc_num_cmp(n1, &G.prog.zero) && bc_num_cmp(n2, &G.prog.zero); @@ -5760,7 +5914,7 @@ static BcStatus bc_program_copyToVar(char *name, bool var) v = bc_program_search(name, var); if (var) { - bc_num_init(&r.d.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&r.d.n); bc_num_copy(&r.d.n, n); } else { @@ -5779,7 +5933,6 @@ static BcStatus bc_program_assign(char inst) BcStatus s; BcResult *left, *right, res; BcNum *l = NULL, *r = NULL; - unsigned long val, max; bool assign = inst == BC_INST_ASSIGN, ib, sc; s = bc_program_binOpPrep(&left, &l, &right, &r, assign); @@ -5825,14 +5978,15 @@ static BcStatus bc_program_assign(char inst) if (ib || sc || left->t == BC_RESULT_OBASE) { static const char *const msg[] = { - "bad ibase; must be [2, 16]", //BC_RESULT_IBASE - "bad scale; must be [0, BC_SCALE_MAX]", //BC_RESULT_SCALE - "?1", //BC_RESULT_LAST - "?2", //BC_RESULT_CONSTANT - "?3", //BC_RESULT_ONE - "bad obase; must be [2, BC_BASE_MAX]", //BC_RESULT_OBASE + "bad ibase; must be [2,16]", //BC_RESULT_IBASE + "bad scale; must be [0,"BC_MAX_SCALE_STR"]", //BC_RESULT_SCALE + NULL, //can't happen //BC_RESULT_LAST + NULL, //can't happen //BC_RESULT_CONSTANT + NULL, //can't happen //BC_RESULT_ONE + "bad obase; must be [2,"BC_MAX_OBASE_STR"]", //BC_RESULT_OBASE }; size_t *ptr; + unsigned long val, max; s = bc_num_ulong(l, &val); if (s) @@ -5899,7 +6053,7 @@ static BcStatus bc_program_pushVar(char *code, size_t *bgn, r.t = BC_RESULT_TEMP; - bc_num_init(&r.d.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&r.d.n); bc_num_copy(&r.d.n, num); } else { @@ -5941,7 +6095,7 @@ static BcStatus bc_program_pushArray(char *code, size_t *bgn, if (s) goto err; if (temp > BC_MAX_DIM) { - s = bc_error("array too long; must be [1, BC_DIM_MAX]"); + s = bc_error("array too long; must be [1,"BC_MAX_DIM_STR"]"); goto err; } @@ -5999,7 +6153,7 @@ static BcStatus bc_program_call(char *code, size_t *idx) ip.idx = 0; ip.func = bc_program_index(code, idx); - func = bc_vec_item(&G.prog.fns, ip.func); + func = bc_program_func(ip.func); if (func->code.len == 0) { return bc_error("undefined function"); @@ -6028,7 +6182,7 @@ static BcStatus bc_program_call(char *code, size_t *idx) v = bc_program_search(a->name, a->idx); if (a->idx) { - bc_num_init(¶m.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(¶m.n); bc_vec_push(v, ¶m.n); } else { @@ -6053,7 +6207,7 @@ static BcStatus bc_program_return(char inst) if (!BC_PROG_STACK(&G.prog.results, ip->len + inst == BC_INST_RET)) return bc_error_stack_has_too_few_elements(); - f = bc_vec_item(&G.prog.fns, ip->func); + f = bc_program_func(ip->func); res.t = BC_RESULT_TEMP; if (inst == BC_INST_RET) { @@ -6067,8 +6221,8 @@ static BcStatus bc_program_return(char inst) bc_num_copy(&res.d.n, num); } else { - bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); - bc_num_zero(&res.d.n); + bc_num_init_DEF_SIZE(&res.d.n); + //bc_num_zero(&res.d.n); - already is } // We need to pop arguments as well, so this takes that into account. @@ -6096,12 +6250,14 @@ static unsigned long bc_program_scale(BcNum *n) static unsigned long bc_program_len(BcNum *n) { - unsigned long len = n->len; - size_t i; - - if (n->rdx != n->len) return len; - for (i = n->len - 1; i < n->len && n->num[i] == 0; --len, --i); + size_t len = n->len; + if (n->rdx != len) return len; + for (;;) { + if (len == 0) break; + len--; + if (n->num[len] != 0) break; + } return len; } @@ -6125,7 +6281,7 @@ static BcStatus bc_program_builtin(char inst) return bc_error_variable_is_wrong_type(); #endif - bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&res.d.n); if (inst == BC_INST_SQRT) s = bc_num_sqrt(num, &res.d.n, G.prog.scale); #if ENABLE_BC @@ -6139,13 +6295,12 @@ static BcStatus bc_program_builtin(char inst) char **str; size_t idx = opnd->t == BC_RESULT_STR ? opnd->d.id.idx : num->rdx; - str = bc_vec_item(&G.prog.strs, idx); + str = bc_program_str(idx); bc_num_ulong2num(&res.d.n, strlen(*str)); } #endif else { - BcProgramBuiltIn f = len ? bc_program_len : bc_program_scale; - bc_num_ulong2num(&res.d.n, f(num)); + bc_num_ulong2num(&res.d.n, len ? bc_program_len(num) : bc_program_scale(num)); } bc_program_retire(&res, BC_RESULT_TEMP); @@ -6163,7 +6318,7 @@ static BcStatus bc_program_divmod(void) s = bc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false); if (s) return s; - bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&res.d.n); bc_num_init(&res2.d.n, n2->len); s = bc_num_divmod(n1, n2, &res2.d.n, &res.d.n, G.prog.scale); @@ -6233,7 +6388,7 @@ static void bc_program_stackLen(void) res.t = BC_RESULT_TEMP; - bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&res.d.n); bc_num_ulong2num(&res.d.n, len); bc_vec_push(&G.prog.results, &res); } @@ -6242,7 +6397,7 @@ static BcStatus bc_program_asciify(void) { BcStatus s; BcResult *r, res; - BcNum *num = NULL, n; + BcNum *num, n; char *str, *str2, c; size_t len = G.prog.strs.len, idx; unsigned long val; @@ -6251,12 +6406,13 @@ static BcStatus bc_program_asciify(void) return bc_error_stack_has_too_few_elements(); r = bc_vec_top(&G.prog.results); + num = NULL; // TODO: is this NULL needed? s = bc_program_num(r, &num, false); if (s) return s; if (BC_PROG_NUM(r, num)) { - bc_num_init(&n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&n); bc_num_copy(&n, num); bc_num_truncate(&n, n.rdx); @@ -6271,7 +6427,7 @@ static BcStatus bc_program_asciify(void) } else { idx = (r->t == BC_RESULT_STR) ? r->d.id.idx : num->rdx; - str2 = *((char **) bc_vec_item(&G.prog.strs, idx)); + str2 = *bc_program_str(idx); c = str2[0]; } @@ -6285,7 +6441,7 @@ static BcStatus bc_program_asciify(void) if (idx != len + BC_PROG_REQ_FUNCS) { for (idx = 0; idx < G.prog.strs.len; ++idx) { - if (!strcmp(*((char **) bc_vec_item(&G.prog.strs, idx)), str)) { + if (strcmp(*bc_program_str(idx), str) == 0) { len = idx; break; } @@ -6324,10 +6480,10 @@ static BcStatus bc_program_printStream(void) if (s) return s; if (BC_PROG_NUM(r, n)) - s = bc_num_stream(n, &G.prog.strmb, &G.prog.nchars, G.prog.len); + s = bc_num_stream(n, &G.prog.strmb); else { idx = (r->t == BC_RESULT_STR) ? r->d.id.idx : n->rdx; - str = *((char **) bc_vec_item(&G.prog.strs, idx)); + str = *bc_program_str(idx); printf("%s", str); } @@ -6350,8 +6506,9 @@ static BcStatus bc_program_nquit(void) if (G.prog.stack.len < val) return bc_error_stack_has_too_few_elements(); - if (G.prog.stack.len == val) - quit(); + if (G.prog.stack.len == val) { + QUIT_OR_RETURN_TO_MAIN; + } bc_vec_npop(&G.prog.stack, val); @@ -6368,8 +6525,6 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn, BcParse prs; BcInstPtr ip; size_t fidx, sidx; - BcNum *n; - bool exec; if (!BC_PROG_STACK(&G.prog.results, 1)) return bc_error_stack_has_too_few_elements(); @@ -6377,8 +6532,11 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn, r = bc_vec_top(&G.prog.results); if (cond) { - - char *name, *then_name = bc_program_name(code, bgn), *else_name = NULL; + BcNum *n = n; // for compiler + bool exec; + char *name; + char *then_name = bc_program_name(code, bgn); + char *else_name = NULL; if (code[*bgn] == BC_PARSE_STREND) (*bgn) += 1; @@ -6386,10 +6544,8 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn, else_name = bc_program_name(code, bgn); exec = r->d.n.len != 0; - - if (exec) - name = then_name; - else if (else_name != NULL) { + name = then_name; + if (!exec && else_name != NULL) { exec = true; name = else_name; } @@ -6410,24 +6566,22 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn, } sidx = n->rdx; - } - else { - - if (r->t == BC_RESULT_STR) + } else { + if (r->t == BC_RESULT_STR) { sidx = r->d.id.idx; - else if (r->t == BC_RESULT_VAR) { + } else if (r->t == BC_RESULT_VAR) { + BcNum *n; s = bc_program_num(r, &n, false); if (s || !BC_PROG_STR(n)) goto exit; sidx = n->rdx; - } - else + } else goto exit; } fidx = sidx + BC_PROG_REQ_FUNCS; - str = bc_vec_item(&G.prog.strs, sidx); - f = bc_vec_item(&G.prog.fns, fidx); + str = bc_program_str(sidx); + f = bc_program_func(fidx); if (f->code.len == 0) { common_parse_init(&prs, fidx); @@ -6455,7 +6609,7 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn, err: bc_parse_free(&prs); - f = bc_vec_item(&G.prog.fns, fidx); + f = bc_program_func(fidx); bc_vec_pop_all(&f->code); exit: bc_vec_pop(&G.prog.results); @@ -6476,7 +6630,7 @@ static void bc_program_pushGlobal(char inst) else val = (unsigned long) G.prog.ob_t; - bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&res.d.n); bc_num_ulong2num(&res.d.n, val); bc_vec_push(&G.prog.results, &res); } @@ -6498,7 +6652,7 @@ static void bc_program_addFunc(char *name, size_t *idx) if (!inserted) { - BcFunc *func = bc_vec_item(&G.prog.fns, entry_ptr->idx); + BcFunc *func = bc_program_func(entry_ptr->idx); // We need to reset these, so the function can be repopulated. func->nparams = 0; @@ -6512,87 +6666,50 @@ static void bc_program_addFunc(char *name, size_t *idx) } } -// Called when parsing or execution detects a failure, -// resets execution structures. -static void bc_program_reset(void) -{ - BcFunc *f; - BcInstPtr *ip; - - bc_vec_npop(&G.prog.stack, G.prog.stack.len - 1); - bc_vec_pop_all(&G.prog.results); - - f = bc_vec_item(&G.prog.fns, 0); - ip = bc_vec_top(&G.prog.stack); - ip->idx = f->code.len; - - // If !tty, no need to check for ^C: we don't have ^C handler, - // we would be killed by a signal and won't reach this place -} - static BcStatus bc_program_exec(void) { - BcStatus s = BC_STATUS_SUCCESS; - size_t idx; BcResult r, *ptr; BcNum *num; BcInstPtr *ip = bc_vec_top(&G.prog.stack); - BcFunc *func = bc_vec_item(&G.prog.fns, ip->func); + BcFunc *func = bc_program_func(ip->func); char *code = func->code.v; bool cond = false; - while (!s && ip->idx < func->code.len) { - + while (ip->idx < func->code.len) { + BcStatus s; char inst = code[(ip->idx)++]; switch (inst) { - #if ENABLE_BC case BC_INST_JUMP_ZERO: - { s = bc_program_prep(&ptr, &num); if (s) return s; cond = !bc_num_cmp(num, &G.prog.zero); bc_vec_pop(&G.prog.results); - } - // Fallthrough. - case BC_INST_JUMP: - { + // Fallthrough. + case BC_INST_JUMP: { size_t *addr; - idx = bc_program_index(code, &ip->idx); + size_t idx = bc_program_index(code, &ip->idx); addr = bc_vec_item(&func->labels, idx); if (inst == BC_INST_JUMP || cond) ip->idx = *addr; break; } - case BC_INST_CALL: - { s = bc_program_call(code, &ip->idx); break; - } - case BC_INST_INC_PRE: case BC_INST_DEC_PRE: case BC_INST_INC_POST: case BC_INST_DEC_POST: - { s = bc_program_incdec(inst); break; - } - case BC_INST_HALT: - { - quit(); + QUIT_OR_RETURN_TO_MAIN; break; - } - case BC_INST_RET: case BC_INST_RET0: - { s = bc_program_return(inst); break; - } - case BC_INST_BOOL_OR: case BC_INST_BOOL_AND: #endif // ENABLE_BC @@ -6602,121 +6719,76 @@ static BcStatus bc_program_exec(void) case BC_INST_REL_NE: case BC_INST_REL_LT: case BC_INST_REL_GT: - { s = bc_program_logical(inst); break; - } - case BC_INST_READ: - { s = bc_program_read(); break; - } - case BC_INST_VAR: - { s = bc_program_pushVar(code, &ip->idx, false, false); break; - } - case BC_INST_ARRAY_ELEM: case BC_INST_ARRAY: - { s = bc_program_pushArray(code, &ip->idx, inst); break; - } - case BC_INST_LAST: - { r.t = BC_RESULT_LAST; bc_vec_push(&G.prog.results, &r); break; - } - case BC_INST_IBASE: case BC_INST_SCALE: case BC_INST_OBASE: - { bc_program_pushGlobal(inst); break; - } - case BC_INST_SCALE_FUNC: case BC_INST_LENGTH: case BC_INST_SQRT: - { s = bc_program_builtin(inst); break; - } - case BC_INST_NUM: - { r.t = BC_RESULT_CONSTANT; r.d.id.idx = bc_program_index(code, &ip->idx); bc_vec_push(&G.prog.results, &r); break; - } - case BC_INST_POP: - { if (!BC_PROG_STACK(&G.prog.results, 1)) s = bc_error_stack_has_too_few_elements(); else bc_vec_pop(&G.prog.results); break; - } - case BC_INST_POP_EXEC: - { bc_vec_pop(&G.prog.stack); break; - } - case BC_INST_PRINT: case BC_INST_PRINT_POP: case BC_INST_PRINT_STR: - { s = bc_program_print(inst, 0); break; - } - case BC_INST_STR: - { r.t = BC_RESULT_STR; r.d.id.idx = bc_program_index(code, &ip->idx); bc_vec_push(&G.prog.results, &r); break; - } - case BC_INST_POWER: case BC_INST_MULTIPLY: case BC_INST_DIVIDE: case BC_INST_MODULUS: case BC_INST_PLUS: case BC_INST_MINUS: - { s = bc_program_op(inst); break; - } - case BC_INST_BOOL_NOT: - { s = bc_program_prep(&ptr, &num); if (s) return s; - - bc_num_init(&r.d.n, BC_NUM_DEF_SIZE); - (!bc_num_cmp(num, &G.prog.zero) ? bc_num_one : bc_num_zero)(&r.d.n); + bc_num_init_DEF_SIZE(&r.d.n); + if (!bc_num_cmp(num, &G.prog.zero)) + bc_num_one(&r.d.n); + //else bc_num_zero(&r.d.n); - already is bc_program_retire(&r, BC_RESULT_TEMP); - break; - } - case BC_INST_NEG: - { s = bc_program_negate(); break; - } - #if ENABLE_BC case BC_INST_ASSIGN_POWER: case BC_INST_ASSIGN_MULTIPLY: @@ -6726,150 +6798,138 @@ static BcStatus bc_program_exec(void) case BC_INST_ASSIGN_MINUS: #endif case BC_INST_ASSIGN: - { s = bc_program_assign(inst); break; - } #if ENABLE_DC case BC_INST_MODEXP: - { s = bc_program_modexp(); break; - } - case BC_INST_DIVMOD: - { s = bc_program_divmod(); break; - } - case BC_INST_EXECUTE: case BC_INST_EXEC_COND: - { cond = inst == BC_INST_EXEC_COND; s = bc_program_execStr(code, &ip->idx, cond); break; - } - - case BC_INST_PRINT_STACK: - { - for (idx = 0; !s && idx < G.prog.results.len; ++idx) + case BC_INST_PRINT_STACK: { + size_t idx; + for (idx = 0; idx < G.prog.results.len; ++idx) { s = bc_program_print(BC_INST_PRINT, idx); + if (s) break; + } break; } - case BC_INST_CLEAR_STACK: - { bc_vec_pop_all(&G.prog.results); break; - } - case BC_INST_STACK_LEN: - { bc_program_stackLen(); break; - } - case BC_INST_DUPLICATE: - { if (!BC_PROG_STACK(&G.prog.results, 1)) return bc_error_stack_has_too_few_elements(); ptr = bc_vec_top(&G.prog.results); bc_result_copy(&r, ptr); bc_vec_push(&G.prog.results, &r); break; - } - - case BC_INST_SWAP: - { + case BC_INST_SWAP: { BcResult *ptr2; - if (!BC_PROG_STACK(&G.prog.results, 2)) return bc_error_stack_has_too_few_elements(); - ptr = bc_vec_item_rev(&G.prog.results, 0); ptr2 = bc_vec_item_rev(&G.prog.results, 1); memcpy(&r, ptr, sizeof(BcResult)); memcpy(ptr, ptr2, sizeof(BcResult)); memcpy(ptr2, &r, sizeof(BcResult)); - break; } - case BC_INST_ASCIIFY: - { s = bc_program_asciify(); break; - } - case BC_INST_PRINT_STREAM: - { s = bc_program_printStream(); break; - } - case BC_INST_LOAD: - case BC_INST_PUSH_VAR: - { + case BC_INST_PUSH_VAR: { bool copy = inst == BC_INST_LOAD; s = bc_program_pushVar(code, &ip->idx, true, copy); break; } - - case BC_INST_PUSH_TO_VAR: - { + case BC_INST_PUSH_TO_VAR: { char *name = bc_program_name(code, &ip->idx); s = bc_program_copyToVar(name, true); free(name); break; } - case BC_INST_QUIT: - { if (G.prog.stack.len <= 2) - quit(); + QUIT_OR_RETURN_TO_MAIN; bc_vec_npop(&G.prog.stack, 2); break; - } - case BC_INST_NQUIT: - { s = bc_program_nquit(); break; - } #endif // ENABLE_DC } if (s || G_interrupt) { bc_program_reset(); - break; + return s; } // If the stack has changed, pointers may be invalid. ip = bc_vec_top(&G.prog.stack); - func = bc_vec_item(&G.prog.fns, ip->func); + func = bc_program_func(ip->func); code = func->code.v; } - return s; + return BC_STATUS_SUCCESS; } +#if ENABLE_BC static void bc_vm_info(void) { printf("%s "BB_VER"\n" "Copyright (c) 2018 Gavin D. Howard and contributors\n" - "Report bugs at: https://github.com/gavinhoward/bc\n" - "This is free software with ABSOLUTELY NO WARRANTY\n" , applet_name); } -#if ENABLE_BC -static void bc_vm_envArgs(void) +static void bc_args(char **argv) { - static const char* const bc_args_env_name = "BC_ENV_ARGS"; + unsigned opts; + int i; + + GETOPT_RESET(); +#if ENABLE_FEATURE_BC_LONG_OPTIONS + opts = option_mask32 |= getopt32long(argv, "wvsqli", + "warn\0" No_argument "w" + "version\0" No_argument "v" + "standard\0" No_argument "s" + "quiet\0" No_argument "q" + "mathlib\0" No_argument "l" + "interactive\0" No_argument "i" + ); +#else + opts = option_mask32 |= getopt32(argv, "wvsqli"); +#endif + if (getenv("POSIXLY_CORRECT")) + option_mask32 |= BC_FLAG_S; + + if (opts & BC_FLAG_V) { + bc_vm_info(); + exit(0); + } + + for (i = optind; argv[i]; ++i) + bc_vec_push(&G.files, argv + i); +} +static void bc_vm_envArgs(void) +{ BcVec v; - char *env_args = getenv(bc_args_env_name), *buf; + char *buf; + char *env_args = getenv("BC_ENV_ARGS"); if (!env_args) return; @@ -6877,40 +6937,39 @@ static void bc_vm_envArgs(void) buf = G.env_args; bc_vec_init(&v, sizeof(char *), NULL); - bc_vec_push(&v, &bc_args_env_name); - while (*buf != 0) { - if (!isspace(*buf)) { - bc_vec_push(&v, &buf); - while (*buf != 0 && !isspace(*buf)) ++buf; - if (*buf != 0) (*(buf++)) = '\0'; - } - else - ++buf; + while (*(buf = skip_whitespace(buf)) != '\0') { + bc_vec_push(&v, &buf); + buf = skip_non_whitespace(buf); + if (!*buf) + break; + *buf++ = '\0'; } - bc_args((int) v.len, (char **) v.v); + // NULL terminate, and pass argv[] so that first arg is argv[1] + if (sizeof(int) == sizeof(char*)) { + bc_vec_push(&v, &const_int_0); + } else { + static char *const nullptr = NULL; + bc_vec_push(&v, &nullptr); + } + bc_args(((char **)v.v) - 1); bc_vec_free(&v); } #endif // ENABLE_BC -static size_t bc_vm_envLen(const char *var) +static unsigned bc_vm_envLen(const char *var) { - char *lenv = getenv(var); - size_t i, len = BC_NUM_PRINT_WIDTH; - int num; + char *lenv; + unsigned len; + lenv = getenv(var); + len = BC_NUM_PRINT_WIDTH; if (!lenv) return len; - len = strlen(lenv); - - for (num = 1, i = 0; num && i < len; ++i) num = isdigit(lenv[i]); - if (num) { - len = (size_t) atoi(lenv) - 1; - if (len < 2 || len >= INT32_MAX) len = BC_NUM_PRINT_WIDTH; - } - else + len = bb_strtou(lenv, NULL, 10) - 1; + if (errno || len < 2 || len >= INT_MAX) len = BC_NUM_PRINT_WIDTH; return len; @@ -6954,7 +7013,7 @@ static BcStatus bc_vm_file(const char *file) s = bc_vm_process(data); if (s) goto err; - main_func = bc_vec_item(&G.prog.fns, BC_PROG_MAIN); + main_func = bc_program_func(BC_PROG_MAIN); ip = bc_vec_item(&G.prog.stack, 0); if (main_func->code.len < ip->idx) @@ -6984,8 +7043,7 @@ static BcStatus bc_vm_stdin(void) // with a backslash to the parser. The reason for that is because the parser // treats a backslash+newline combo as whitespace, per the bc spec. In that // case, and for strings and comments, the parser will expect more stuff. - s = BC_STATUS_SUCCESS; - while (!G.eof && (s = bc_read_line(&buf, ">>> ")) == BC_STATUS_SUCCESS) { + while ((s = bc_read_line(&buf)) == BC_STATUS_SUCCESS) { char *string = buf.v; @@ -7030,12 +7088,18 @@ static BcStatus bc_vm_stdin(void) bc_vec_concat(&buffer, buf.v); s = bc_vm_process(buffer.v); if (s) { - fflush_and_check(); - fputs("ready for more input\n", stderr); + if (ENABLE_FEATURE_CLEAN_UP && !G_ttyin) { + // Debug config, non-interactive mode: + // return all the way back to main. + // Non-debug builds do not come here, they exit. + break; + } } bc_vec_pop_all(&buffer); } + if (s == BC_STATUS_EOF) // input EOF (^D) is not an error + s = BC_STATUS_SUCCESS; if (str) { s = bc_error("string end could not be found"); @@ -7228,7 +7292,7 @@ static const char bc_lib[] = { static BcStatus bc_vm_exec(void) { - BcStatus s = BC_STATUS_SUCCESS; + BcStatus s; size_t i; #if ENABLE_BC @@ -7250,15 +7314,19 @@ static BcStatus bc_vm_exec(void) } #endif + s = BC_STATUS_SUCCESS; for (i = 0; !s && i < G.files.len; ++i) s = bc_vm_file(*((char **) bc_vec_item(&G.files, i))); - if (s) { - fflush_and_check(); - fputs("ready for more input\n", stderr); + if (ENABLE_FEATURE_CLEAN_UP && s && !G_ttyin) { + // Debug config, non-interactive mode: + // return all the way back to main. + // Non-debug builds do not come here, they exit. + return s; } - if (IS_BC || !G.files.len) + if (IS_BC || (option_mask32 & BC_FLAG_I)) s = bc_vm_stdin(); + if (!s && !BC_PARSE_CAN_EXEC(&G.prs)) s = bc_vm_process(""); @@ -7266,7 +7334,7 @@ static BcStatus bc_vm_exec(void) } #if ENABLE_FEATURE_CLEAN_UP -static void bc_program_free() +static void bc_program_free(void) { bc_num_free(&G.prog.ib); bc_num_free(&G.prog.ob); @@ -7298,7 +7366,7 @@ static void bc_vm_free(void) } #endif -static void bc_program_init(size_t line_len) +static void bc_program_init(void) { size_t idx; BcInstPtr ip; @@ -7307,32 +7375,30 @@ static void bc_program_init(size_t line_len) memset(&ip, 0, sizeof(BcInstPtr)); /* G.prog.nchars = G.prog.scale = 0; - already is */ - G.prog.len = line_len; - - bc_num_init(&G.prog.ib, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&G.prog.ib); bc_num_ten(&G.prog.ib); G.prog.ib_t = 10; - bc_num_init(&G.prog.ob, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&G.prog.ob); bc_num_ten(&G.prog.ob); G.prog.ob_t = 10; - bc_num_init(&G.prog.hexb, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&G.prog.hexb); bc_num_ten(&G.prog.hexb); G.prog.hexb.num[0] = 6; #if ENABLE_DC - bc_num_init(&G.prog.strmb, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&G.prog.strmb); bc_num_ulong2num(&G.prog.strmb, UCHAR_MAX + 1); #endif - bc_num_init(&G.prog.last, BC_NUM_DEF_SIZE); - bc_num_zero(&G.prog.last); + bc_num_init_DEF_SIZE(&G.prog.last); + //bc_num_zero(&G.prog.last); - already is - bc_num_init(&G.prog.zero, BC_NUM_DEF_SIZE); - bc_num_zero(&G.prog.zero); + bc_num_init_DEF_SIZE(&G.prog.zero); + //bc_num_zero(&G.prog.zero); - already is - bc_num_init(&G.prog.one, BC_NUM_DEF_SIZE); + bc_num_init_DEF_SIZE(&G.prog.one); bc_num_one(&G.prog.one); bc_vec_init(&G.prog.fns, sizeof(BcFunc), bc_func_free); @@ -7354,36 +7420,26 @@ static void bc_program_init(size_t line_len) bc_vec_push(&G.prog.stack, &ip); } -static void bc_vm_init(const char *env_len) +static int bc_vm_init(const char *env_len) { - size_t len = bc_vm_envLen(env_len); +#if ENABLE_FEATURE_EDITING + G.line_input_state = new_line_input_t(DO_HISTORY); +#endif + G.prog.len = bc_vm_envLen(env_len); bc_vec_init(&G.files, sizeof(char *), NULL); - - if (IS_BC) { - bc_vm_envArgs(); - } - - bc_program_init(len); + if (IS_BC) + IF_BC(bc_vm_envArgs();) + bc_program_init(); if (IS_BC) { - bc_parse_init(&G.prs, BC_PROG_MAIN); + IF_BC(bc_parse_init(&G.prs, BC_PROG_MAIN);) } else { - dc_parse_init(&G.prs, BC_PROG_MAIN); + IF_DC(dc_parse_init(&G.prs, BC_PROG_MAIN);) } -} - -static BcStatus bc_vm_run(int argc, char *argv[], - const char *env_len) -{ - BcStatus st; - - bc_vm_init(env_len); - bc_args(argc, argv); - G.ttyin = isatty(0); - - if (G.ttyin) { + if (isatty(0)) { #if ENABLE_FEATURE_BC_SIGNALS + G_ttyin = 1; // With SA_RESTART, most system calls will restart // (IOW: they won't fail with EINTR). // In particular, this means ^C won't cause @@ -7402,36 +7458,103 @@ static BcStatus bc_vm_run(int argc, char *argv[], // and exit. //signal_no_SA_RESTART_empty_mask(SIGINT, record_signo); #endif - if (!(option_mask32 & BC_FLAG_Q)) - bc_vm_info(); + return 1; // "tty" } - st = bc_vm_exec(); + return 0; // "not a tty" +} +static BcStatus bc_vm_run(void) +{ + BcStatus st = bc_vm_exec(); #if ENABLE_FEATURE_CLEAN_UP + if (G_exiting) // it was actually "halt" or "quit" + st = EXIT_SUCCESS; bc_vm_free(); +# if ENABLE_FEATURE_EDITING + free_line_input_t(G.line_input_state); +# endif + FREE_G(); #endif return st; } #if ENABLE_BC int bc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int bc_main(int argc, char **argv) +int bc_main(int argc UNUSED_PARAM, char **argv) { + int is_tty; + INIT_G(); G.sbgn = G.send = '"'; - return bc_vm_run(argc, argv, "BC_LINE_LENGTH"); + is_tty = bc_vm_init("BC_LINE_LENGTH"); + + bc_args(argv); + + if (is_tty && !(option_mask32 & BC_FLAG_Q)) + bc_vm_info(); + + return bc_vm_run(); } #endif #if ENABLE_DC int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int dc_main(int argc, char **argv) +int dc_main(int argc UNUSED_PARAM, char **argv) { + int noscript; + INIT_G(); G.sbgn = '['; G.send = ']'; + /* + * TODO: dc (GNU bc 1.07.1) 1.4.1 seems to use width + * 1 char wider than bc from the same package. + * Both default width, and xC_LINE_LENGTH=N are wider: + * "DC_LINE_LENGTH=5 dc -e'123456 p'" prints: + * 1234\ + * 56 + * "echo '123456' | BC_LINE_LENGTH=5 bc" prints: + * 123\ + * 456 + * Do the same, or it's a bug? + */ + bc_vm_init("DC_LINE_LENGTH"); + + // Run -e'SCRIPT' and -fFILE in order of appearance, then handle FILEs + noscript = BC_FLAG_I; + for (;;) { + int n = getopt(argc, argv, "e:f:x"); + if (n <= 0) + break; + switch (n) { + case 'e': + noscript = 0; + n = bc_vm_process(optarg); + if (n) return n; + break; + case 'f': + noscript = 0; + bc_vm_file(optarg); + break; + case 'x': + option_mask32 |= DC_FLAG_X; + break; + default: + bb_show_usage(); + } + } + argv += optind; - return bc_vm_run(argc, argv, "DC_LINE_LENGTH"); + while (*argv) { + noscript = 0; + bc_vec_push(&G.files, argv++); + } + + option_mask32 |= noscript; // set BC_FLAG_I if we need to interpret stdin + + return bc_vm_run(); } #endif + +#endif // not DC_SMALL