X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=miscutils%2Fbc.c;h=0330c43e567e59c0dfd4a06d2355fe3a27c0e459;hb=b9c321d6d94fc8bbae5fe657e141cbd9f2397037;hp=866fa30273fccb745ebd8ee9d6506a427421fcf2;hpb=68cc0a676eeceffae84a91b73895b5c4d72c2021;p=oweals%2Fbusybox.git diff --git a/miscutils/bc.c b/miscutils/bc.c index 866fa3027..0330c43e5 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -94,17 +94,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 y +//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 +129,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,32 +155,37 @@ //usage: "obase = A\n" //usage: //usage:#define dc_trivial_usage -//usage: "EXPRESSION..." +//usage: "[-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+, -, *, /, %, ^, exp, ~, divmod, |, " +//usage: "modular exponentiation," +//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, @@ -186,9 +206,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,16 +216,17 @@ 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_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) +#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_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) \ @@ -217,14 +235,6 @@ typedef struct BcNum { 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); @@ -387,9 +397,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 +509,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,6 +565,7 @@ enum { | (1 << 18) | (1 << 19) }; +#define bc_lex_kws_POSIX(i) ((1 << (i)) & POSIX_KWORD_MASK) #endif struct BcLex; @@ -567,7 +576,6 @@ typedef struct BcLex { const char *buf; size_t i; size_t line; - const char *f; size_t len; bool newline; @@ -583,15 +591,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))) @@ -628,22 +632,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 BcOp { - char prec; - bool left; -} BcOp; - 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; @@ -671,34 +664,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; @@ -744,7 +709,6 @@ typedef struct BcProgram { #define BC_PROG_MAIN (0) #define BC_PROG_READ (1) - #if ENABLE_DC #define BC_PROG_REQ_FUNCS (2) #endif @@ -755,16 +719,13 @@ typedef struct BcProgram { 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)) @@ -779,7 +740,8 @@ static void bc_program_reset(void); #define BC_MAX_VARS ((unsigned long) SIZE_MAX - 1) struct globals { - smallint ttyin; + IF_FEATURE_BC_SIGNALS(smallint ttyin;) + IF_FEATURE_CLEAN_UP(smallint exiting;) smallint eof; char sbgn; char send; @@ -787,52 +749,97 @@ struct globals { BcParse prs; BcProgram prog; + // 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; + 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 an array that corresponds to token types. An entry is +// This is a bit array that corresponds to token types. An entry is // true if the token is valid in an expression, false otherwise. -static const bool bc_parse_exprs[] = { - false, false, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, false, false, true, true, false, false, false, false, - false, false, false, true, true, false, false, false, false, false, false, - false, true, false, true, true, true, true, false, false, true, false, true, - true, false, +enum { + BC_PARSE_EXPRS_BITS = 0 + + ((uint64_t)((0 << 0)+(0 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(1 << 5)+(1 << 6)+(1 << 7)) << (0*8)) + + ((uint64_t)((1 << 0)+(1 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(1 << 5)+(1 << 6)+(1 << 7)) << (1*8)) + + ((uint64_t)((1 << 0)+(1 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(1 << 5)+(1 << 6)+(1 << 7)) << (2*8)) + + ((uint64_t)((1 << 0)+(1 << 1)+(1 << 2)+(0 << 3)+(0 << 4)+(1 << 5)+(1 << 6)+(0 << 7)) << (3*8)) + + ((uint64_t)((0 << 0)+(0 << 1)+(0 << 2)+(0 << 3)+(0 << 4)+(0 << 5)+(1 << 6)+(1 << 7)) << (4*8)) + + ((uint64_t)((0 << 0)+(0 << 1)+(0 << 2)+(0 << 3)+(0 << 4)+(0 << 5)+(0 << 6)+(1 << 7)) << (5*8)) + + ((uint64_t)((0 << 0)+(1 << 1)+(1 << 2)+(1 << 3)+(1 << 4)+(0 << 5)+(0 << 6)+(1 << 7)) << (6*8)) + + ((uint64_t)((0 << 0)+(1 << 1)+(1 << 2)+(0 << 3) ) << (7*8)) }; +static ALWAYS_INLINE long bc_parse_exprs(unsigned i) +{ +#if ULONG_MAX > 0xffffffff + // 64-bit version (will not work correctly for 32-bit longs!) + return BC_PARSE_EXPRS_BITS & (1UL << i); +#else + // 32-bit version + unsigned long m = (uint32_t)BC_PARSE_EXPRS_BITS; + if (i >= 32) { + m = (uint32_t)(BC_PARSE_EXPRS_BITS >> 32); + i &= 31; + } + return m & (1UL << i); +#endif +} // This is an array of data for operators that correspond to token types. -static const BcOp bc_parse_ops[] = { - { 0, false }, { 0, false }, - { 1, false }, - { 2, false }, - { 3, true }, { 3, true }, { 3, true }, - { 4, true }, { 4, true }, - { 6, true }, { 6, true }, { 6, true }, { 6, true }, { 6, true }, { 6, true }, - { 1, false }, - { 7, true }, { 7, true }, - { 5, false }, { 5, false }, { 5, false }, { 5, false }, { 5, false }, - { 5, false }, { 5, false }, +static const uint8_t bc_parse_ops[] = { +#define OP(p,l) ((int)(l) * 0x10 + (p)) + OP(0, false), OP( 0, false ), // inc dec + OP(1, false), // neg + OP(2, false), + OP(3, true ), OP( 3, true ), OP( 3, true ), // pow mul div + OP(4, true ), OP( 4, true ), // mod + - + OP(6, true ), OP( 6, true ), OP( 6, true ), OP( 6, true ), OP( 6, true ), OP( 6, true ), // == <= >= != < > + OP(1, false), // not + OP(7, true ), OP( 7, true ), // or and + OP(5, false), OP( 5, false ), OP( 5, false ), OP( 5, false ), OP( 5, false ), // ^= *= /= %= += + OP(5, false), OP( 5, false ), // -= = +#undef OP }; +#define bc_parse_op_PREC(i) (bc_parse_ops[i] & 0x0f) +#define bc_parse_op_LEFT(i) (bc_parse_ops[i] & 0x10) // These identify what tokens can come after expressions in certain cases. +#define BC_PARSE_NEXT_TOKENS(...) .tokens = { __VA_ARGS__ } +#define BC_PARSE_NEXT(a, ...) \ + { \ + .len = (a), BC_PARSE_NEXT_TOKENS(__VA_ARGS__) \ + } 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 = @@ -913,187 +920,6 @@ static const BcNumBinaryOp bc_program_ops[] = { bc_num_pow, bc_num_mul, bc_num_div, bc_num_mod, bc_num_add, bc_num_sub, }; -static const char bc_program_stdin_name[] = ""; - -#if ENABLE_BC -static const char *bc_lib_name = "gen/lib.bc"; - -static const char bc_lib[] = { - "scale=20" -"\n" "define e(x){" -"\n" "auto b,s,n,r,d,i,p,f,v" -"\n" "b=ibase" -"\n" "ibase=A" -"\n" "if(x<0){" -"\n" "n=1" -"\n" "x=-x" -"\n" "}" -"\n" "s=scale" -"\n" "r=6+s+0.44*x" -"\n" "scale=scale(x)+1" -"\n" "while(x>1){" -"\n" "d+=1" -"\n" "x/=2" -"\n" "scale+=1" -"\n" "}" -"\n" "scale=r" -"\n" "r=x+1" -"\n" "p=x" -"\n" "f=v=1" -"\n" "for(i=2;v!=0;++i){" -"\n" "p*=x" -"\n" "f*=i" -"\n" "v=p/f" -"\n" "r+=v" -"\n" "}" -"\n" "while((d--)!=0)r*=r" -"\n" "scale=s" -"\n" "ibase=b" -"\n" "if(n!=0)return(1/r)" -"\n" "return(r/1)" -"\n" "}" -"\n" "define l(x){" -"\n" "auto b,s,r,p,a,q,i,v" -"\n" "b=ibase" -"\n" "ibase=A" -"\n" "if(x<=0){" -"\n" "r=(1-10^scale)/1" -"\n" "ibase=b" -"\n" "return(r)" -"\n" "}" -"\n" "s=scale" -"\n" "scale+=6" -"\n" "p=2" -"\n" "while(x>=2){" -"\n" "p*=2" -"\n" "x=sqrt(x)" -"\n" "}" -"\n" "while(x<=0.5){" -"\n" "p*=2" -"\n" "x=sqrt(x)" -"\n" "}" -"\n" "r=a=(x-1)/(x+1)" -"\n" "q=a*a" -"\n" "v=1" -"\n" "for(i=3;v!=0;i+=2){" -"\n" "a*=q" -"\n" "v=a/i" -"\n" "r+=v" -"\n" "}" -"\n" "r*=p" -"\n" "scale=s" -"\n" "ibase=b" -"\n" "return(r/1)" -"\n" "}" -"\n" "define s(x){" -"\n" "auto b,s,r,n,a,q,i" -"\n" "b=ibase" -"\n" "ibase=A" -"\n" "s=scale" -"\n" "scale=1.1*s+2" -"\n" "a=a(1)" -"\n" "if(x<0){" -"\n" "n=1" -"\n" "x=-x" -"\n" "}" -"\n" "scale=0" -"\n" "q=(x/a+2)/4" -"\n" "x=x-4*q*a" -"\n" "if(q%2!=0)x=-x" -"\n" "scale=s+2" -"\n" "r=a=x" -"\n" "q=-x*x" -"\n" "for(i=3;a!=0;i+=2){" -"\n" "a*=q/(i*(i-1))" -"\n" "r+=a" -"\n" "}" -"\n" "scale=s" -"\n" "ibase=b" -"\n" "if(n!=0)return(-r/1)" -"\n" "return(r/1)" -"\n" "}" -"\n" "define c(x){" -"\n" "auto b,s" -"\n" "b=ibase" -"\n" "ibase=A" -"\n" "s=scale" -"\n" "scale*=1.2" -"\n" "x=s(2*a(1)+x)" -"\n" "scale=s" -"\n" "ibase=b" -"\n" "return(x/1)" -"\n" "}" -"\n" "define a(x){" -"\n" "auto b,s,r,n,a,m,t,f,i,u" -"\n" "b=ibase" -"\n" "ibase=A" -"\n" "n=1" -"\n" "if(x<0){" -"\n" "n=-1" -"\n" "x=-x" -"\n" "}" -"\n" "if(x==1){" -"\n" "if(scale<65){" -"\n" "return(.7853981633974483096156608458198757210492923498437764552437361480/n)" -"\n" "}" -"\n" "}" -"\n" "if(x==.2){" -"\n" "if(scale<65){" -"\n" "return(.1973955598498807583700497651947902934475851037878521015176889402/n)" -"\n" "}" -"\n" "}" -"\n" "s=scale" -"\n" "if(x>.2){" -"\n" "scale+=5" -"\n" "a=a(.2)" -"\n" "}" -"\n" "scale=s+3" -"\n" "while(x>.2){" -"\n" "m+=1" -"\n" "x=(x-.2)/(1+.2*x)" -"\n" "}" -"\n" "r=u=x" -"\n" "f=-x*x" -"\n" "t=1" -"\n" "for(i=3;t!=0;i+=2){" -"\n" "u*=f" -"\n" "t=u/i" -"\n" "r+=t" -"\n" "}" -"\n" "scale=s" -"\n" "ibase=b" -"\n" "return((m*a+r)/n)" -"\n" "}" -"\n" "define j(n,x){" -"\n" "auto b,s,o,a,i,v,f" -"\n" "b=ibase" -"\n" "ibase=A" -"\n" "s=scale" -"\n" "scale=0" -"\n" "n/=1" -"\n" "if(n<0){" -"\n" "n=-n" -"\n" "if(n%2==1)o=1" -"\n" "}" -"\n" "a=1" -"\n" "for(i=2;i<=n;++i)a*=i" -"\n" "scale=1.5*s" -"\n" "a=(x^n)/2^n/a" -"\n" "r=v=1" -"\n" "f=-x*x/4" -"\n" "scale=scale+length(a)-scale(a)" -"\n" "for(i=1;v!=0;++i){" -"\n" "v=v*f/i/(n+i)" -"\n" "r+=v" -"\n" "}" -"\n" "scale=s" -"\n" "ibase=b" -"\n" "if(o!=0)a=-a" -"\n" "return(a*r/1)" -"\n" "}" -}; -#endif // ENABLE_BC - static void fflush_and_check(void) { fflush_all(); @@ -1101,6 +927,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) { @@ -1110,36 +947,106 @@ static void quit(void) exit(0); } -static int bc_error(const char *fmt, ...) +static void bc_verror_msg(const char *fmt, va_list p) +{ + const char *sv = sv; /* for compiler */ + if (G.prog.file) { + sv = applet_name; + applet_name = xasprintf("%s: %s:%u", applet_name, G.prog.file, G.err_line); + } + bb_verror_msg(fmt, p, NULL); + if (G.prog.file) { + free((char*)applet_name); + applet_name = sv; + } +} + +static NOINLINE int bc_error_fmt(const char *fmt, ...) { va_list p; va_start(p, fmt); - bb_verror_msg(fmt, p, NULL); + bc_verror_msg(fmt, p); va_end(p); - if (!G.ttyin) + + if (!ENABLE_FEATURE_CLEAN_UP && !G_ttyin) exit(1); return BC_STATUS_FAILURE; } -static int bc_posix_error(const char *fmt, ...) +#if ENABLE_BC +static NOINLINE int bc_posix_error_fmt(const char *fmt, ...) { va_list p; + // Are non-POSIX constructs totally ok? if (!(option_mask32 & (BC_FLAG_S|BC_FLAG_W))) - return BC_STATUS_SUCCESS; + return BC_STATUS_SUCCESS; // yes va_start(p, fmt); - bb_verror_msg(fmt, p, NULL); + bc_verror_msg(fmt, p); va_end(p); // 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 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); +} +#if ENABLE_BC +static int bc_POSIX_requires(const char *msg) +{ + return bc_posix_error_fmt("POSIX requires %s", msg); +} +static int bc_POSIX_does_not_allow(const char *msg) +{ + return bc_posix_error_fmt("%s%s", "POSIX does not allow ", msg); +} +static int bc_POSIX_does_not_allow_bool_ops_this_is_bad(const char *msg) +{ + return bc_posix_error_fmt("%s%s %s", "POSIX does not allow ", "boolean operators; the following is bad:", msg); +} +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); +} +static int bc_error_bad_expression(void) +{ + return bc_error("bad expression"); +} +static int bc_error_bad_token(void) +{ + return bc_error("bad token"); +} +static int bc_error_stack_has_too_few_elements(void) +{ + return bc_error("stack has too few elements"); +} +static int bc_error_variable_is_wrong_type(void) +{ + return bc_error("variable is wrong type"); +} +static int bc_error_nested_read_call(void) +{ + return bc_error("read() call inside of a read() call"); +} static void bc_vec_grow(BcVec *v, size_t n) { @@ -1171,6 +1078,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) @@ -1198,6 +1112,13 @@ static void bc_vec_pushByte(BcVec *v, char data) bc_vec_push(v, &data); } +static void bc_vec_pushZeroByte(BcVec *v) +{ + //bc_vec_pushByte(v, '\0'); + // better: + bc_vec_push(v, &const_int_0); +} + static void bc_vec_pushAt(BcVec *v, const void *data, size_t idx) { if (idx == v->len) @@ -1222,19 +1143,19 @@ static void bc_vec_string(BcVec *v, size_t len, const char *str) memcpy(v->v, str, len); v->len = len; - bc_vec_pushByte(v, '\0'); + bc_vec_pushZeroByte(v); } static void bc_vec_concat(BcVec *v, const char *str) { size_t len; - if (v->len == 0) bc_vec_pushByte(v, '\0'); + if (v->len == 0) bc_vec_pushZeroByte(v); len = v->len + strlen(str); if (v->cap < len) bc_vec_grow(v, len - v->len); - strcat(v->v, str); + strcpy(v->v + v->len - 1, str); v->len = len; } @@ -1249,6 +1170,11 @@ 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_top(const BcVec *v) +{ + return v->v + v->size * (v->len - 1); +} + static void bc_vec_free(void *vec) { BcVec *v = (BcVec *) vec; @@ -1256,6 +1182,16 @@ static void bc_vec_free(void *vec) 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 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; @@ -1290,74 +1226,101 @@ 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) { bool bad_chars; + if (G_posix) prompt = ""; + do { - int i; - char c; + 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 + if (G_interrupt) { // ^C was pressed intr: - bb_got_signal = 0; // resets G_interrupt to zero + G_interrupt = 0; fputs(IS_BC ? "\ninterrupt (type \"quit\" to exit)\n" : "\ninterrupt (type \"q\" to exit)\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, prompt, line_buf, COMMON_BUFSIZE); + if (n <= 0) { // read errors or EOF, or ^D, or ^C + if (n == 0) // ^C + goto intr; + G.eof = 1; + break; + } + i = 0; + for (;;) { + c = line_buf[i++]; + if (!c) break; + bad_chars |= push_input_byte(vec, c); + } +# undef line_buf + } else +# endif #endif - if (G.ttyin && !G_posix) - fputs(prompt, stderr); - -#if ENABLE_FEATURE_BC_SIGNALS - errno = 0; -#endif - do { - i = fgetc(stdin); - if (i == EOF) { -#if ENABLE_FEATURE_BC_SIGNALS + { + if (G_ttyin) + fputs(prompt, stderr); + 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("illegal character 0x%02x", i); - bad_chars = 1; - } - c = (char) i; - bc_vec_push(vec, &c); - } while (i != '\n'); + if (c == EOF) { + 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; + } + bad_chars |= push_input_byte(vec, c); + } while (c != '\n'); + } } while (bad_chars); - bc_vec_pushByte(vec, '\0'); + bc_vec_pushZeroByte(vec); return BC_STATUS_SUCCESS; } @@ -1368,7 +1331,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]; @@ -1384,36 +1348,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; @@ -1441,6 +1375,74 @@ static void bc_num_ten(BcNum *n) n->num[1] = 1; } +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)); + n->num = xmalloc(req); + n->cap = req; +} + +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 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) +{ + 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 void bc_num_subArrays(BcDig *restrict a, BcDig *restrict b, size_t len) { @@ -1746,16 +1748,16 @@ static BcStatus bc_num_k(BcNum *restrict a, BcNum *restrict b, BcNum *restrict c) { BcStatus s; - int carry; - size_t i, j, len, max = BC_MAX(a->len, b->len), max2 = (max + 1) / 2; + size_t max = BC_MAX(a->len, b->len), max2 = (max + 1) / 2; BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp; - bool aone = BC_NUM_ONE(a); + bool aone; if (a->len == 0 || b->len == 0) { bc_num_zero(c); return BC_STATUS_SUCCESS; } - else if (aone || BC_NUM_ONE(b)) { + aone = BC_NUM_ONE(a); + if (aone || BC_NUM_ONE(b)) { bc_num_copy(c, aone ? b : a); return BC_STATUS_SUCCESS; } @@ -1763,23 +1765,31 @@ static BcStatus bc_num_k(BcNum *restrict a, BcNum *restrict b, if (a->len + b->len < BC_NUM_KARATSUBA_LEN || a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN) { + size_t i, j, len; + unsigned carry; + bc_num_expand(c, a->len + b->len + 1); memset(c->num, 0, sizeof(BcDig) * c->cap); - c->len = carry = len = 0; + c->len = len = 0; for (i = 0; i < b->len; ++i) { + carry = 0; for (j = 0; j < a->len; ++j) { - int in = (int) c->num[i + j]; - in += ((int) a->num[j]) * ((int) b->num[i]) + carry; + unsigned in = c->num[i + j]; + in += ((unsigned) a->num[j]) * ((unsigned) b->num[i]) + carry; + // note: compilers prefer _unsigned_ div/const carry = in / 10; c->num[i + j] = (BcDig)(in % 10); } c->num[i + j] += (BcDig) carry; len = BC_MAX(len, i + j + !!carry); - carry = 0; + + // a=2^1000000 + // a*a <- without check below, this will not be interruptible + if (G_interrupt) return BC_STATUS_FAILURE; } c->len = len; @@ -1939,12 +1949,19 @@ static BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) for (q = 0; (!s && n[len] != 0) || bc_num_compare(n, p, len) >= 0; ++q) bc_num_subArrays(n, p, len); c->num[i] = q; + // a=2^100000 + // scale=40000 + // 1/a <- without check below, this will not be interruptible + if (G_interrupt) { + s = BC_STATUS_FAILURE; + break; + } } bc_num_retireMul(c, scale, a->neg, b->neg); bc_num_free(&cp); - return BC_STATUS_SUCCESS; // can't make void, see bc_num_binary() + return s; } static BcStatus bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c, @@ -1963,7 +1980,8 @@ static BcStatus bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c, } bc_num_init(&temp, d->cap); - bc_num_d(a, b, c, scale); + s = bc_num_d(a, b, c, scale); + if (s) goto err; if (scale != 0) scale = ts; @@ -2039,6 +2057,11 @@ static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) powrdx <<= 1; s = bc_num_mul(©, ©, ©, powrdx); if (s) goto err; + // Not needed: bc_num_mul() has a check for ^C: + //if (G_interrupt) { + // s = BC_STATUS_FAILURE; + // goto err; + //} } bc_num_copy(c, ©); @@ -2054,6 +2077,11 @@ static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) s = bc_num_mul(c, ©, c, resrdx); if (s) goto err; } + // Not needed: bc_num_mul() has a check for ^C: + //if (G_interrupt) { + // s = BC_STATUS_FAILURE; + // goto err; + //} } if (neg) { @@ -2407,39 +2435,6 @@ static BcStatus bc_num_stream(BcNum *n, BcNum *base, size_t *nchars, size_t len) } #endif -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)); - n->num = xmalloc(req); - n->cap = req; -} - -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 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_parse(BcNum *n, const char *val, BcNum *base, size_t base_t) { @@ -2478,62 +2473,27 @@ static BcStatus bc_num_print(BcNum *n, BcNum *base, size_t base_t, bool newline, return s; } -static BcStatus bc_num_ulong(BcNum *n, unsigned long *result) +static BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) { - 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; + 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)); +} - if (*result < prev || pow < powprev) - return bc_error("overflow"); - } +static 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)); +} - return BC_STATUS_SUCCESS; +static 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 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) -{ - 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) -{ - 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) -{ - 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 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); @@ -2732,16 +2692,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; @@ -2759,6 +2710,7 @@ static BcStatus bc_func_insert(BcFunc *f, char *name, bool var) return BC_STATUS_SUCCESS; } +#endif static void bc_func_init(BcFunc *f) { @@ -2776,6 +2728,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) @@ -2785,21 +2739,6 @@ 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; @@ -2818,6 +2757,21 @@ static void bc_array_expand(BcVec *a, size_t len) } } +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_string_free(void *string) { free(*((char **) string)); @@ -2953,7 +2907,7 @@ static BcStatus bc_lex_number(BcLex *l, char start) bc_vec_push(&l->t.v, &c); } - bc_vec_pushByte(&l->t.v, '\0'); + bc_vec_pushZeroByte(&l->t.v); l->i += i; return BC_STATUS_SUCCESS; @@ -2990,11 +2944,10 @@ static void bc_lex_free(BcLex *l) bc_vec_free(&l->t.v); } -static void bc_lex_file(BcLex *l, const char *file) +static void bc_lex_file(BcLex *l) { - l->line = 1; + G.err_line = l->line = 1; l->newline = false; - l->f = file; } static BcStatus bc_lex_next(BcLex *l) @@ -3005,6 +2958,7 @@ static BcStatus bc_lex_next(BcLex *l) if (l->t.last == BC_LEX_EOF) return bc_error("end of file"); l->line += l->newline; + G.err_line = l->line; l->t.t = BC_LEX_EOF; l->newline = (l->i == l->len); @@ -3047,8 +3001,8 @@ 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) { - s = bc_posix_error("POSIX does not allow the following keyword:"); // bc_lex_kws[i].name8 + 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; } @@ -3060,8 +3014,14 @@ static BcStatus bc_lex_identifier(BcLex *l) s = bc_lex_name(l); if (s) return s; - if (l->t.v.len > 2) - s = bc_posix_error("POSIX only allows one character names; the following is bad:"); // buf + if (l->t.v.len > 2) { + // Prevent this: + // >>> qwe=1 + // bc: POSIX only allows one character names; the following is bad: 'qwe=1 + // ' + unsigned len = strchrnul(buf, '\n') - buf; + s = bc_posix_error_fmt("POSIX only allows one character names; the following is bad: '%.*s'", len, buf); + } return s; } @@ -3087,6 +3047,7 @@ static BcStatus bc_lex_string(BcLex *l) l->i = i + 1; l->line += nls; + G.err_line = l->line; return BC_STATUS_SUCCESS; } @@ -3127,6 +3088,7 @@ static BcStatus bc_lex_comment(BcLex *l) l->i = i + 1; l->line += nls; + G.err_line = l->line; return BC_STATUS_SUCCESS; } @@ -3162,7 +3124,7 @@ static BcStatus bc_lex_token(BcLex *l) bc_lex_assign(l, BC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT); if (l->t.t == BC_LEX_OP_BOOL_NOT) { - s = bc_posix_error("POSIX does not allow boolean operators; the following is bad:"); // "!" + s = bc_POSIX_does_not_allow_bool_ops_this_is_bad("!"); if (s) return s; } @@ -3177,7 +3139,7 @@ static BcStatus bc_lex_token(BcLex *l) case '#': { - s = bc_posix_error("POSIX does not allow '#' script comments"); + s = bc_POSIX_does_not_allow("'#' script comments"); if (s) return s; bc_lex_lineComment(l); @@ -3196,7 +3158,7 @@ static BcStatus bc_lex_token(BcLex *l) c2 = l->buf[l->i]; if (c2 == '&') { - s = bc_posix_error("POSIX does not allow boolean operators; the following is bad:"); // "&&" + s = bc_POSIX_does_not_allow_bool_ops_this_is_bad("&&"); if (s) return s; ++l->i; @@ -3204,7 +3166,7 @@ static BcStatus bc_lex_token(BcLex *l) } else { l->t.t = BC_LEX_INVALID; - s = bc_error("bad character '%c'", '&'); + s = bc_error_bad_character('&'); } break; @@ -3259,7 +3221,7 @@ static BcStatus bc_lex_token(BcLex *l) s = bc_lex_number(l, c); else { l->t.t = BC_LEX_KEY_LAST; - s = bc_posix_error("POSIX does not allow a period ('.') as a shortcut for the last result"); + s = bc_POSIX_does_not_allow("a period ('.') as a shortcut for the last result"); } break; } @@ -3333,7 +3295,7 @@ static BcStatus bc_lex_token(BcLex *l) ++l->i; } else - s = bc_error("bad character '%c'", c); + s = bc_error_bad_character(c); break; } @@ -3386,7 +3348,7 @@ static BcStatus bc_lex_token(BcLex *l) c2 = l->buf[l->i]; if (c2 == '|') { - s = bc_posix_error("POSIX does not allow boolean operators; the following is bad:"); // "||" + s = bc_POSIX_does_not_allow_bool_ops_this_is_bad("||"); if (s) return s; ++l->i; @@ -3394,7 +3356,7 @@ static BcStatus bc_lex_token(BcLex *l) } else { l->t.t = BC_LEX_INVALID; - s = bc_error("bad character '%c'", c); + s = bc_error_bad_character(c); } break; @@ -3403,7 +3365,7 @@ static BcStatus bc_lex_token(BcLex *l) default: { l->t.t = BC_LEX_INVALID; - s = bc_error("bad character '%c'", c); + s = bc_error_bad_character(c); break; } } @@ -3427,8 +3389,8 @@ 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_pushByte(&l->t.v, '\0'); + bc_vec_push(&l->t.v, &l->buf[l->i - 1]); + bc_vec_pushZeroByte(&l->t.v); l->t.t = BC_LEX_NAME; } @@ -3457,12 +3419,13 @@ static BcStatus dc_lex_string(BcLex *l) return bc_error("string end could not be found"); } - bc_vec_pushByte(&l->t.v, '\0'); + 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]"); l->i = i; l->line += nls; + G.err_line = l->line; return BC_STATUS_SUCCESS; } @@ -3516,7 +3479,7 @@ static BcStatus dc_lex_token(BcLex *l) else if (c2 == '>') l->t.t = BC_LEX_OP_REL_GE; else - return bc_error("bad character '%c'", c); + return bc_error_bad_character(c); ++l->i; break; @@ -3533,7 +3496,7 @@ static BcStatus dc_lex_token(BcLex *l) if (isdigit(l->buf[l->i])) s = bc_lex_number(l, c); else - s = bc_error("bad character '%c'", c); + s = bc_error_bad_character(c); break; } @@ -3567,7 +3530,7 @@ static BcStatus dc_lex_token(BcLex *l) default: { l->t.t = BC_LEX_INVALID; - s = bc_error("bad character '%c'", c); + s = bc_error_bad_character(c); break; } } @@ -3576,12 +3539,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); } +#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); @@ -3636,6 +3603,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_vec_item(&G.prog.fns, 0); + ip = bc_vec_top(&G.prog.stack); + ip->idx = f->code.len; +} + +#define bc_parse_updateFunc(p, f) \ + ((p)->func = bc_vec_item(&G.prog.fns, ((p)->fidx = (f)))) + // Called when bc/dc_parse_parse() detects a failure, // resets parsing structures. static void bc_parse_reset(BcParse *p) @@ -3679,7 +3664,7 @@ static void bc_parse_create(BcParse *p, size_t func, bc_vec_init(&p->flags, sizeof(uint8_t), NULL); bc_vec_init(&p->exits, sizeof(BcInstPtr), NULL); bc_vec_init(&p->conds, sizeof(size_t), NULL); - bc_vec_pushByte(&p->flags, 0); + bc_vec_pushZeroByte(&p->flags); bc_vec_init(&p->ops, sizeof(BcLexType), NULL); p->parse = parse; @@ -3688,23 +3673,36 @@ static void bc_parse_create(BcParse *p, size_t func, } #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) { BcStatus s = BC_STATUS_SUCCESS; BcLexType t; - char l, r = bc_parse_ops[type - BC_LEX_OP_INC].prec; - bool left = bc_parse_ops[type - BC_LEX_OP_INC].left; + char l, r = bc_parse_op_PREC(type - BC_LEX_OP_INC); + bool left = bc_parse_op_LEFT(type - BC_LEX_OP_INC); while (p->ops.len > start) { t = BC_PARSE_TOP_OP(p); if (t == BC_LEX_LPAREN) break; - l = bc_parse_ops[t - BC_LEX_OP_INC].prec; + l = bc_parse_op_PREC(t - BC_LEX_OP_INC); if (l >= r && (l != r || !left)) break; bc_parse_push(p, BC_PARSE_TOKEN_INST(t)); @@ -3723,7 +3721,7 @@ static BcStatus bc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs) BcLexType top; if (p->ops.len <= ops_bgn) - return bc_error("bad expression"); + return bc_error_bad_expression(); top = BC_PARSE_TOP_OP(p); while (top != BC_LEX_LPAREN) { @@ -3734,7 +3732,7 @@ static BcStatus bc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs) *nexs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_NEG; if (p->ops.len <= ops_bgn) - return bc_error("bad expression"); + return bc_error_bad_expression(); top = BC_PARSE_TOP_OP(p); } @@ -3765,7 +3763,7 @@ static BcStatus bc_parse_params(BcParse *p, uint8_t flags) } } - if (comma) return bc_error("bad token"); + if (comma) return bc_error_bad_token(); bc_parse_push(p, BC_INST_CALL); bc_parse_pushIndex(p, nparams); @@ -3784,7 +3782,7 @@ static BcStatus bc_parse_call(BcParse *p, char *name, uint8_t flags) if (s) goto err; if (p->l.t.t != BC_LEX_RPAREN) { - s = bc_error("bad token"); + s = bc_error_bad_token(); goto err; } @@ -3826,7 +3824,7 @@ static BcStatus bc_parse_name(BcParse *p, BcInst *type, uint8_t flags) if (p->l.t.t == BC_LEX_RBRACKET) { if (!(flags & BC_PARSE_ARRAY)) { - s = bc_error("bad expression"); + s = bc_error_bad_expression(); goto err; } @@ -3849,7 +3847,7 @@ static BcStatus bc_parse_name(BcParse *p, BcInst *type, uint8_t flags) else if (p->l.t.t == BC_LEX_LPAREN) { if (flags & BC_PARSE_NOCALL) { - s = bc_error("bad token"); + s = bc_error_bad_token(); goto err; } @@ -3875,11 +3873,11 @@ static BcStatus bc_parse_read(BcParse *p) s = bc_lex_next(&p->l); if (s) return s; - if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; - if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token(); bc_parse_push(p, BC_INST_READ); @@ -3893,7 +3891,7 @@ static BcStatus bc_parse_builtin(BcParse *p, BcLexType type, uint8_t flags, s = bc_lex_next(&p->l); if (s) return s; - if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token(); flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY; @@ -3903,7 +3901,7 @@ static BcStatus bc_parse_builtin(BcParse *p, BcLexType type, uint8_t flags, s = bc_parse_expr(p, flags, bc_parse_next_rel); if (s) return s; - if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token(); *prev = (type == BC_LEX_KEY_LENGTH) ? BC_INST_LENGTH : BC_INST_SQRT; bc_parse_push(p, *prev); @@ -3932,7 +3930,7 @@ static BcStatus bc_parse_scale(BcParse *p, BcInst *type, uint8_t flags) s = bc_parse_expr(p, flags, bc_parse_next_rel); if (s) return s; - if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token(); bc_parse_push(p, BC_INST_SCALE_FUNC); return bc_lex_next(&p->l); @@ -3989,7 +3987,7 @@ static BcStatus bc_parse_incdec(BcParse *p, BcInst *prev, bool *paren_expr, s = bc_lex_next(&p->l); if (s) return s; if (p->l.t.t == BC_LEX_LPAREN) - s = bc_error("bad token"); + s = bc_error_bad_token(); else bc_parse_push(p, BC_INST_SCALE); break; @@ -3997,7 +3995,7 @@ static BcStatus bc_parse_incdec(BcParse *p, BcInst *prev, bool *paren_expr, default: { - s = bc_error("bad token"); + s = bc_error_bad_token(); break; } } @@ -4078,7 +4076,7 @@ static BcStatus bc_parse_print(BcParse *p) } if (s) return s; - if (comma) return bc_error("bad token"); + if (comma) return bc_error_bad_token(); return bc_lex_next(&p->l); } @@ -4089,7 +4087,7 @@ static BcStatus bc_parse_return(BcParse *p) BcLexType t; bool paren; - if (!BC_PARSE_FUNC(p)) return bc_error("bad token"); + if (!BC_PARSE_FUNC(p)) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; @@ -4101,18 +4099,15 @@ 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); - if (s && s != BC_STATUS_PARSE_EMPTY_EXP) - return s; - + 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); - if (s) return s; } + 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; } @@ -4127,18 +4122,18 @@ static BcStatus bc_parse_endBody(BcParse *p, bool brace) BcStatus s = BC_STATUS_SUCCESS; if (p->flags.len <= 1 || (brace && p->nbraces == 0)) - return bc_error("bad token"); + return bc_error_bad_token(); if (brace) { if (p->l.t.t == BC_LEX_RBRACE) { - if (!p->nbraces) return bc_error("bad token"); + if (!p->nbraces) return bc_error_bad_token(); --p->nbraces; s = bc_lex_next(&p->l); if (s) return s; } else - return bc_error("bad token"); + return bc_error_bad_token(); } if (BC_PARSE_IF(p)) { @@ -4224,13 +4219,13 @@ static BcStatus bc_parse_if(BcParse *p) s = bc_lex_next(&p->l); if (s) return s; - if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel); if (s) return s; - if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; @@ -4251,7 +4246,7 @@ static BcStatus bc_parse_else(BcParse *p) { BcInstPtr ip; - if (!BC_PARSE_IF_END(p)) return bc_error("bad token"); + if (!BC_PARSE_IF_END(p)) return bc_error_bad_token(); ip.idx = p->func->labels.len; ip.func = ip.len = 0; @@ -4275,7 +4270,7 @@ static BcStatus bc_parse_while(BcParse *p) s = bc_lex_next(&p->l); if (s) return s; - if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; @@ -4293,7 +4288,7 @@ static BcStatus bc_parse_while(BcParse *p) s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel); if (s) return s; - if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; @@ -4312,17 +4307,17 @@ static BcStatus bc_parse_for(BcParse *p) s = bc_lex_next(&p->l); if (s) return s; - if (p->l.t.t != BC_LEX_LPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_LPAREN) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; if (p->l.t.t != BC_LEX_SCOLON) s = bc_parse_expr(p, 0, bc_parse_next_for); else - s = bc_posix_error("POSIX does not allow an empty init expression in a for loop"); + s = bc_POSIX_does_not_allow_empty_X_expression_in_for("init"); if (s) return s; - if (p->l.t.t != BC_LEX_SCOLON) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_SCOLON) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; @@ -4336,10 +4331,10 @@ static BcStatus bc_parse_for(BcParse *p) if (p->l.t.t != BC_LEX_SCOLON) s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_for); else - s = bc_posix_error("POSIX does not allow an empty condition expression in a for loop"); + s = bc_POSIX_does_not_allow_empty_X_expression_in_for("condition"); if (s) return s; - if (p->l.t.t != BC_LEX_SCOLON) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_SCOLON) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; @@ -4357,11 +4352,11 @@ static BcStatus bc_parse_for(BcParse *p) if (p->l.t.t != BC_LEX_RPAREN) s = bc_parse_expr(p, 0, bc_parse_next_rel); else - s = bc_posix_error("POSIX does not allow an empty update expression in a for loop"); + s = bc_POSIX_does_not_allow_empty_X_expression_in_for("update"); if (s) return s; - if (p->l.t.t != BC_LEX_RPAREN) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_RPAREN) return bc_error_bad_token(); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, cond_idx); bc_vec_push(&p->func->labels, &p->func->code.len); @@ -4384,17 +4379,17 @@ static BcStatus bc_parse_loopExit(BcParse *p, BcLexType type) size_t i; BcInstPtr *ip; - if (!BC_PARSE_LOOP(p)) return bc_error("bad token"); + if (!BC_PARSE_LOOP(p)) return bc_error_bad_token(); if (type == BC_LEX_KEY_BREAK) { - if (p->exits.len == 0) return bc_error("bad token"); + if (p->exits.len == 0) return bc_error_bad_token(); i = p->exits.len - 1; ip = bc_vec_item(&p->exits, i); while (!ip->func && i < p->exits.len) ip = bc_vec_item(&p->exits, i--); - if (i >= p->exits.len && !ip->func) return bc_error("bad token"); + if (i >= p->exits.len && !ip->func) return bc_error_bad_token(); i = ip->idx; } @@ -4408,7 +4403,7 @@ static BcStatus bc_parse_loopExit(BcParse *p, BcLexType type) if (s) return s; if (p->l.t.t != BC_LEX_SCOLON && p->l.t.t != BC_LEX_NLINE) - return bc_error("bad token"); + return bc_error_bad_token(); return bc_lex_next(&p->l); } @@ -4481,7 +4476,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; @@ -4496,7 +4491,7 @@ static BcStatus bc_parse_auto(BcParse *p) bool comma, var, one; char *name; - if (!p->auto_part) return bc_error("bad token"); + if (!p->auto_part) return bc_error_bad_token(); s = bc_lex_next(&p->l); if (s) return s; @@ -4538,7 +4533,7 @@ static BcStatus bc_parse_auto(BcParse *p) if (!one) return bc_error("no auto variable found"); if (p->l.t.t != BC_LEX_NLINE && p->l.t.t != BC_LEX_SCOLON) - return bc_error("bad token"); + return bc_error_bad_token(); return bc_lex_next(&p->l); @@ -4556,7 +4551,7 @@ static BcStatus bc_parse_body(BcParse *p, bool brace) if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) { - if (!brace) return bc_error("bad token"); + if (!brace) return bc_error_bad_token(); p->auto_part = p->l.t.t != BC_LEX_KEY_AUTO; if (!p->auto_part) { @@ -4593,7 +4588,7 @@ static BcStatus bc_parse_stmt(BcParse *p) case BC_LEX_LBRACE: { - if (!BC_PARSE_BODY(p)) return bc_error("bad token"); + if (!BC_PARSE_BODY(p)) return bc_error_bad_token(); ++p->nbraces; s = bc_lex_next(&p->l); @@ -4721,7 +4716,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: @@ -4738,7 +4733,7 @@ static BcStatus bc_parse_stmt(BcParse *p) default: { - s = bc_error("bad token"); + s = bc_error_bad_token(); break; } } @@ -4753,7 +4748,7 @@ static BcStatus bc_parse_parse(BcParse *p) if (p->l.t.t == BC_LEX_EOF) s = p->flags.len > 0 ? bc_error("block end could not be found") : bc_error("end of file"); else if (p->l.t.t == BC_LEX_KEY_DEFINE) { - if (!BC_PARSE_CAN_EXEC(p)) return bc_error("bad token"); + if (!BC_PARSE_CAN_EXEC(p)) return bc_error_bad_token(); s = bc_parse_func(p); } else @@ -4767,7 +4762,7 @@ 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; @@ -4781,7 +4776,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) paren_expr = rprn = done = get_token = assign = false; bin_last = true; - for (; !G_interrupt && !s && !done && bc_parse_exprs[t]; t = p->l.t.t) { + for (; !G_interrupt && !s && !done && bc_parse_exprs(t); t = p->l.t.t) { switch (t) { case BC_LEX_OP_INC: @@ -4839,7 +4834,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) if (((t == BC_LEX_OP_BOOL_NOT) != bin_last) || (t != BC_LEX_OP_BOOL_NOT && prev == BC_INST_BOOL_NOT) ) { - return bc_error("bad expression"); + return bc_error_bad_expression(); } nrelops += t >= BC_LEX_OP_REL_EQ && t <= BC_LEX_OP_REL_GT; @@ -4854,7 +4849,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) case BC_LEX_LPAREN: { if (BC_PARSE_LEAF(prev, rprn)) - return bc_error("bad expression"); + return bc_error_bad_expression(); ++nparens; paren_expr = rprn = bin_last = false; get_token = true; @@ -4866,7 +4861,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) case BC_LEX_RPAREN: { if (bin_last || prev == BC_INST_BOOL_NOT) - return bc_error("bad expression"); + return bc_error_bad_expression(); if (nparens == 0) { s = BC_STATUS_SUCCESS; @@ -4889,7 +4884,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) case BC_LEX_NAME: { if (BC_PARSE_LEAF(prev, rprn)) - return bc_error("bad expression"); + return bc_error_bad_expression(); paren_expr = true; rprn = get_token = bin_last = false; s = bc_parse_name(p, &prev, flags & ~BC_PARSE_NOCALL); @@ -4901,7 +4896,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) case BC_LEX_NUMBER: { if (BC_PARSE_LEAF(prev, rprn)) - return bc_error("bad expression"); + return bc_error_bad_expression(); bc_parse_number(p, &prev, &nexprs); paren_expr = get_token = true; rprn = bin_last = false; @@ -4914,7 +4909,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) case BC_LEX_KEY_OBASE: { if (BC_PARSE_LEAF(prev, rprn)) - return bc_error("bad expression"); + return bc_error_bad_expression(); prev = (char) (t - BC_LEX_KEY_IBASE + BC_INST_IBASE); bc_parse_push(p, (char) prev); @@ -4929,7 +4924,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) case BC_LEX_KEY_SQRT: { if (BC_PARSE_LEAF(prev, rprn)) - return bc_error("bad expression"); + return bc_error_bad_expression(); s = bc_parse_builtin(p, t, flags, &prev); paren_expr = true; rprn = get_token = bin_last = false; @@ -4941,9 +4936,9 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) case BC_LEX_KEY_READ: { if (BC_PARSE_LEAF(prev, rprn)) - return bc_error("bad expression"); + return bc_error_bad_expression(); else if (flags & BC_PARSE_NOREAD) - s = bc_error("read() call inside of a read() call"); + s = bc_error_nested_read_call(); else s = bc_parse_read(p); @@ -4958,7 +4953,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) case BC_LEX_KEY_SCALE: { if (BC_PARSE_LEAF(prev, rprn)) - return bc_error("bad expression"); + return bc_error_bad_expression(); s = bc_parse_scale(p, &prev, flags); paren_expr = true; rprn = get_token = bin_last = false; @@ -4970,7 +4965,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) default: { - s = bc_error("bad token"); + s = bc_error_bad_token(); break; } } @@ -4987,7 +4982,7 @@ static BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN; if (top == BC_LEX_LPAREN || top == BC_LEX_RPAREN) - return bc_error("bad expression"); + return bc_error_bad_expression(); bc_parse_push(p, BC_PARSE_TOKEN_INST(top)); @@ -4996,20 +4991,20 @@ 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"); + return bc_error_bad_expression(); for (i = 0; i < next.len; ++i) if (t == next.tokens[i]) goto ok; - return bc_error("bad expression"); + return bc_error_bad_expression(); ok: if (!(flags & BC_PARSE_REL) && nrelops) { - s = bc_posix_error("POSIX does not allow comparison operators outside if or loops"); + s = bc_POSIX_does_not_allow("comparison operators outside if or loops"); 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; } @@ -5021,6 +5016,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); @@ -5030,9 +5035,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; @@ -5040,7 +5049,7 @@ static BcStatus dc_parse_register(BcParse *p) s = bc_lex_next(&p->l); if (s) return s; - if (p->l.t.t != BC_LEX_NAME) return bc_error("bad token"); + if (p->l.t.t != BC_LEX_NAME) return bc_error_bad_token(); name = xstrdup(p->l.t.v.v); bc_parse_pushName(p, name); @@ -5148,7 +5157,7 @@ static BcStatus dc_parse_token(BcParse *p, BcLexType t, uint8_t flags) s = bc_lex_next(&p->l); if (s) return s; if (p->l.t.t != BC_LEX_NUMBER) - return bc_error("bad token"); + return bc_error_bad_token(); } bc_parse_number(p, &prev, &p->nbraces); @@ -5162,7 +5171,7 @@ static BcStatus dc_parse_token(BcParse *p, BcLexType t, uint8_t flags) case BC_LEX_KEY_READ: { if (flags & BC_PARSE_NOREAD) - s = bc_error("read() call inside of a read() call"); + s = bc_error_nested_read_call(); else bc_parse_push(p, BC_INST_READ); get_token = true; @@ -5197,7 +5206,7 @@ static BcStatus dc_parse_token(BcParse *p, BcLexType t, uint8_t flags) default: { - s = bc_error("bad token"); + s = bc_error_bad_token(); get_token = true; break; } @@ -5255,23 +5264,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);) } } @@ -5383,7 +5393,7 @@ static BcStatus bc_program_binOpPrep(BcResult **l, BcNum **ln, BcResultType lt, rt; if (!BC_PROG_STACK(&G.prog.results, 2)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); *r = bc_vec_item_rev(&G.prog.results, 0); *l = bc_vec_item_rev(&G.prog.results, 1); @@ -5405,9 +5415,9 @@ static BcStatus bc_program_binOpPrep(BcResult **l, BcNum **ln, } if (!BC_PROG_NUM((*l), (*ln)) && (!assign || (*l)->t != BC_RESULT_VAR)) - return bc_error("variable is wrong type"); + return bc_error_variable_is_wrong_type(); if (!assign && !BC_PROG_NUM((*r), (*ln))) - return bc_error("variable is wrong type"); + return bc_error_variable_is_wrong_type(); return s; } @@ -5425,14 +5435,14 @@ static BcStatus bc_program_prep(BcResult **r, BcNum **n) BcStatus s; if (!BC_PROG_STACK(&G.prog.results, 1)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); *r = bc_vec_top(&G.prog.results); s = bc_program_num(*r, n, false); if (s) return s; if (!BC_PROG_NUM((*r), (*n))) - return bc_error("variable is wrong type"); + return bc_error_variable_is_wrong_type(); return s; } @@ -5467,6 +5477,7 @@ err: static BcStatus bc_program_read(void) { + const char *sv_file; BcStatus s; BcParse parse; BcVec buf; @@ -5477,17 +5488,20 @@ static BcStatus bc_program_read(void) for (i = 0; i < G.prog.stack.len; ++i) { BcInstPtr *ip_ptr = bc_vec_item(&G.prog.stack, i); if (ip_ptr->func == BC_PROG_READ) - return bc_error("read() call inside of a read() call"); + return bc_error_nested_read_call(); } bc_vec_pop_all(&f->code); bc_char_vec_init(&buf); + sv_file = G.prog.file; + G.prog.file = NULL; + s = bc_read_line(&buf, "read> "); if (s) goto io_err; common_parse_init(&parse, BC_PROG_READ); - bc_lex_file(&parse.l, bc_program_stdin_name); + bc_lex_file(&parse.l); s = bc_parse_text(&parse, buf.v); if (s) goto exec_err; @@ -5510,6 +5524,7 @@ static BcStatus bc_program_read(void) bc_vec_push(&G.prog.stack, &ip); exec_err: + G.prog.file = sv_file; bc_parse_free(&parse); io_err: bc_vec_free(&buf); @@ -5639,7 +5654,7 @@ static BcStatus bc_program_print(char inst, size_t idx) bool pop = inst != BC_INST_PRINT; if (!BC_PROG_STACK(&G.prog.results, idx + 1)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); r = bc_vec_item_rev(&G.prog.results, idx); s = bc_program_num(r, &num, false); @@ -5771,7 +5786,7 @@ static BcStatus bc_program_assignStr(BcResult *r, BcVec *v, if (!push) { if (!BC_PROG_STACK(&G.prog.results, 2)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); bc_vec_pop(v); bc_vec_pop(&G.prog.results); } @@ -5793,16 +5808,16 @@ static BcStatus bc_program_copyToVar(char *name, bool var) BcNum *n; if (!BC_PROG_STACK(&G.prog.results, 1)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); ptr = bc_vec_top(&G.prog.results); if ((ptr->t == BC_RESULT_ARRAY) != !var) - return bc_error("variable is wrong type"); + return bc_error_variable_is_wrong_type(); v = bc_program_search(name, var); #if ENABLE_DC if (ptr->t == BC_RESULT_STR && !var) - return bc_error("variable is wrong type"); + return bc_error_variable_is_wrong_type(); if (ptr->t == BC_RESULT_STR) return bc_program_assignStr(ptr, v, true); #endif @@ -5848,7 +5863,7 @@ static BcStatus bc_program_assign(char inst) BcVec *v; if (left->t != BC_RESULT_VAR) - return bc_error("variable is wrong type"); + return bc_error_variable_is_wrong_type(); v = bc_program_search(left->d.id.name, true); return bc_program_assignStr(right, v, false); @@ -5880,9 +5895,9 @@ static BcStatus bc_program_assign(char inst) 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 + 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_BASE_MAX]", //BC_RESULT_OBASE }; size_t *ptr; @@ -5942,7 +5957,7 @@ static BcStatus bc_program_pushVar(char *code, size_t *bgn, if (!BC_PROG_STACK(v, 2 - copy)) { free(name); - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); } free(name); @@ -6058,7 +6073,7 @@ static BcStatus bc_program_call(char *code, size_t *idx) return bc_error("undefined function"); } if (nparams != func->nparams) { - return bc_error("function has %u parameters, but called with %u", func->nparams, nparams); + return bc_error_fmt("function has %u parameters, but called with %u", func->nparams, nparams); } ip.len = G.prog.results.len - nparams; @@ -6068,7 +6083,7 @@ static BcStatus bc_program_call(char *code, size_t *idx) arg = bc_vec_top(&G.prog.results); if ((!a->idx) != (arg->t == BC_RESULT_ARRAY) || arg->t == BC_RESULT_STR) - return bc_error("variable is wrong type"); + return bc_error_variable_is_wrong_type(); s = bc_program_copyToVar(a->name, a->idx); if (s) return s; @@ -6104,7 +6119,7 @@ static BcStatus bc_program_return(char inst) BcInstPtr *ip = bc_vec_top(&G.prog.stack); if (!BC_PROG_STACK(&G.prog.results, ip->len + inst == BC_INST_RET)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); f = bc_vec_item(&G.prog.fns, ip->func); res.t = BC_RESULT_TEMP; @@ -6167,7 +6182,7 @@ static BcStatus bc_program_builtin(char inst) bool len = inst == BC_INST_LENGTH; if (!BC_PROG_STACK(&G.prog.results, 1)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); opnd = bc_vec_top(&G.prog.results); s = bc_program_num(opnd, &num, false); @@ -6175,7 +6190,7 @@ static BcStatus bc_program_builtin(char inst) #if ENABLE_DC if (!BC_PROG_NUM(opnd, num) && !len) - return bc_error("variable is wrong type"); + return bc_error_variable_is_wrong_type(); #endif bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); @@ -6241,7 +6256,7 @@ static BcStatus bc_program_modexp(void) BcNum *n1, *n2, *n3; if (!BC_PROG_STACK(&G.prog.results, 3)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); s = bc_program_binOpPrep(&r2, &n2, &r3, &n3, false); if (s) return s; @@ -6249,7 +6264,7 @@ static BcStatus bc_program_modexp(void) s = bc_program_num(r1, &n1, false); if (s) return s; if (!BC_PROG_NUM(r1, n1)) - return bc_error("variable is wrong type"); + return bc_error_variable_is_wrong_type(); // Make sure that the values have their pointers updated, if necessary. if (r1->t == BC_RESULT_VAR || r1->t == BC_RESULT_ARRAY_ELEM) { @@ -6301,7 +6316,7 @@ static BcStatus bc_program_asciify(void) unsigned long val; if (!BC_PROG_STACK(&G.prog.results, 1)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); r = bc_vec_top(&G.prog.results); s = bc_program_num(r, &num, false); @@ -6370,7 +6385,7 @@ static BcStatus bc_program_printStream(void) char *str; if (!BC_PROG_STACK(&G.prog.results, 1)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); r = bc_vec_top(&G.prog.results); s = bc_program_num(r, &n, false); @@ -6402,9 +6417,10 @@ static BcStatus bc_program_nquit(void) bc_vec_pop(&G.prog.results); if (G.prog.stack.len < val) - return bc_error("stack has too few elements"); - if (G.prog.stack.len == val) - quit(); + return bc_error_stack_has_too_few_elements(); + if (G.prog.stack.len == val) { + QUIT_OR_RETURN_TO_MAIN; + } bc_vec_npop(&G.prog.stack, val); @@ -6425,7 +6441,7 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn, bool exec; if (!BC_PROG_STACK(&G.prog.results, 1)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); r = bc_vec_top(&G.prog.results); @@ -6458,7 +6474,7 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn, if (!exec) goto exit; if (!BC_PROG_STR(n)) { - s = bc_error("variable is wrong type"); + s = bc_error_variable_is_wrong_type(); goto exit; } @@ -6490,7 +6506,7 @@ static BcStatus bc_program_execStr(char *code, size_t *bgn, if (s) goto err; if (prs.l.t.t != BC_LEX_EOF) { - s = bc_error("bad expression"); + s = bc_error_bad_expression(); goto err; } @@ -6565,24 +6581,6 @@ 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; @@ -6635,7 +6633,7 @@ static BcStatus bc_program_exec(void) case BC_INST_HALT: { - quit(); + QUIT_OR_RETURN_TO_MAIN; break; } @@ -6713,7 +6711,7 @@ static BcStatus bc_program_exec(void) case BC_INST_POP: { if (!BC_PROG_STACK(&G.prog.results, 1)) - s = bc_error("stack has too few elements"); + s = bc_error_stack_has_too_few_elements(); else bc_vec_pop(&G.prog.results); break; @@ -6826,7 +6824,7 @@ static BcStatus bc_program_exec(void) case BC_INST_DUPLICATE: { if (!BC_PROG_STACK(&G.prog.results, 1)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); ptr = bc_vec_top(&G.prog.results); bc_result_copy(&r, ptr); bc_vec_push(&G.prog.results, &r); @@ -6838,7 +6836,7 @@ static BcStatus bc_program_exec(void) BcResult *ptr2; if (!BC_PROG_STACK(&G.prog.results, 2)) - return bc_error("stack has too few elements"); + return bc_error_stack_has_too_few_elements(); ptr = bc_vec_item_rev(&G.prog.results, 0); ptr2 = bc_vec_item_rev(&G.prog.results, 1); @@ -6880,7 +6878,7 @@ static BcStatus bc_program_exec(void) case BC_INST_QUIT: { if (G.prog.stack.len <= 2) - quit(); + QUIT_OR_RETURN_TO_MAIN; bc_vec_npop(&G.prog.stack, 2); break; } @@ -6907,6 +6905,7 @@ static BcStatus bc_program_exec(void) return s; } +#if ENABLE_BC static void bc_vm_info(void) { printf("%s "BB_VER"\n" @@ -6916,13 +6915,42 @@ static void bc_vm_info(void) , 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; + +///should be in bc_vm_run() instead?? + 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; @@ -6930,40 +6958,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; @@ -6992,16 +7019,18 @@ static BcStatus bc_vm_process(const char *text) static BcStatus bc_vm_file(const char *file) { - BcStatus s; + const char *sv_file; char *data; + BcStatus s; BcFunc *main_func; BcInstPtr *ip; - G.prog.file = file; data = bc_read_file(file); - if (!data) return bc_error("file '%s' is not text", file); + if (!data) return bc_error_fmt("file '%s' is not text", file); - bc_lex_file(&G.prs.l, file); + sv_file = G.prog.file; + G.prog.file = file; + bc_lex_file(&G.prs.l); s = bc_vm_process(data); if (s) goto err; @@ -7009,9 +7038,10 @@ static BcStatus bc_vm_file(const char *file) ip = bc_vec_item(&G.prog.stack, 0); if (main_func->code.len < ip->idx) - s = bc_error("file '%s' is not executable", file); + s = bc_error_fmt("file '%s' is not executable", file); err: + G.prog.file = sv_file; free(data); return s; } @@ -7023,17 +7053,18 @@ static BcStatus bc_vm_stdin(void) size_t len, i, str = 0; bool comment = false; - G.prog.file = bc_program_stdin_name; - bc_lex_file(&G.prs.l, bc_program_stdin_name); + G.prog.file = NULL; + bc_lex_file(&G.prs.l); bc_char_vec_init(&buffer); bc_char_vec_init(&buf); - bc_vec_pushByte(&buffer, '\0'); + bc_vec_pushZeroByte(&buffer); // This loop is complex because the vm tries not to send any lines that end // 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) { char *string = buf.v; @@ -7079,6 +7110,12 @@ static BcStatus bc_vm_stdin(void) bc_vec_concat(&buffer, buf.v); s = bc_vm_process(buffer.v); if (s) { + 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; + } fflush_and_check(); fputs("ready for more input\n", stderr); } @@ -7098,35 +7135,225 @@ static BcStatus bc_vm_stdin(void) return s; } +#if ENABLE_BC +static const char bc_lib[] = { + "scale=20" +"\n" "define e(x){" +"\n" "auto b,s,n,r,d,i,p,f,v" +"\n" "b=ibase" +"\n" "ibase=A" +"\n" "if(x<0){" +"\n" "n=1" +"\n" "x=-x" +"\n" "}" +"\n" "s=scale" +"\n" "r=6+s+0.44*x" +"\n" "scale=scale(x)+1" +"\n" "while(x>1){" +"\n" "d+=1" +"\n" "x/=2" +"\n" "scale+=1" +"\n" "}" +"\n" "scale=r" +"\n" "r=x+1" +"\n" "p=x" +"\n" "f=v=1" +"\n" "for(i=2;v!=0;++i){" +"\n" "p*=x" +"\n" "f*=i" +"\n" "v=p/f" +"\n" "r+=v" +"\n" "}" +"\n" "while((d--)!=0)r*=r" +"\n" "scale=s" +"\n" "ibase=b" +"\n" "if(n!=0)return(1/r)" +"\n" "return(r/1)" +"\n" "}" +"\n" "define l(x){" +"\n" "auto b,s,r,p,a,q,i,v" +"\n" "b=ibase" +"\n" "ibase=A" +"\n" "if(x<=0){" +"\n" "r=(1-10^scale)/1" +"\n" "ibase=b" +"\n" "return(r)" +"\n" "}" +"\n" "s=scale" +"\n" "scale+=6" +"\n" "p=2" +"\n" "while(x>=2){" +"\n" "p*=2" +"\n" "x=sqrt(x)" +"\n" "}" +"\n" "while(x<=0.5){" +"\n" "p*=2" +"\n" "x=sqrt(x)" +"\n" "}" +"\n" "r=a=(x-1)/(x+1)" +"\n" "q=a*a" +"\n" "v=1" +"\n" "for(i=3;v!=0;i+=2){" +"\n" "a*=q" +"\n" "v=a/i" +"\n" "r+=v" +"\n" "}" +"\n" "r*=p" +"\n" "scale=s" +"\n" "ibase=b" +"\n" "return(r/1)" +"\n" "}" +"\n" "define s(x){" +"\n" "auto b,s,r,n,a,q,i" +"\n" "b=ibase" +"\n" "ibase=A" +"\n" "s=scale" +"\n" "scale=1.1*s+2" +"\n" "a=a(1)" +"\n" "if(x<0){" +"\n" "n=1" +"\n" "x=-x" +"\n" "}" +"\n" "scale=0" +"\n" "q=(x/a+2)/4" +"\n" "x=x-4*q*a" +"\n" "if(q%2!=0)x=-x" +"\n" "scale=s+2" +"\n" "r=a=x" +"\n" "q=-x*x" +"\n" "for(i=3;a!=0;i+=2){" +"\n" "a*=q/(i*(i-1))" +"\n" "r+=a" +"\n" "}" +"\n" "scale=s" +"\n" "ibase=b" +"\n" "if(n!=0)return(-r/1)" +"\n" "return(r/1)" +"\n" "}" +"\n" "define c(x){" +"\n" "auto b,s" +"\n" "b=ibase" +"\n" "ibase=A" +"\n" "s=scale" +"\n" "scale*=1.2" +"\n" "x=s(2*a(1)+x)" +"\n" "scale=s" +"\n" "ibase=b" +"\n" "return(x/1)" +"\n" "}" +"\n" "define a(x){" +"\n" "auto b,s,r,n,a,m,t,f,i,u" +"\n" "b=ibase" +"\n" "ibase=A" +"\n" "n=1" +"\n" "if(x<0){" +"\n" "n=-1" +"\n" "x=-x" +"\n" "}" +"\n" "if(x==1){" +"\n" "if(scale<65){" +"\n" "return(.7853981633974483096156608458198757210492923498437764552437361480/n)" +"\n" "}" +"\n" "}" +"\n" "if(x==.2){" +"\n" "if(scale<65){" +"\n" "return(.1973955598498807583700497651947902934475851037878521015176889402/n)" +"\n" "}" +"\n" "}" +"\n" "s=scale" +"\n" "if(x>.2){" +"\n" "scale+=5" +"\n" "a=a(.2)" +"\n" "}" +"\n" "scale=s+3" +"\n" "while(x>.2){" +"\n" "m+=1" +"\n" "x=(x-.2)/(1+.2*x)" +"\n" "}" +"\n" "r=u=x" +"\n" "f=-x*x" +"\n" "t=1" +"\n" "for(i=3;t!=0;i+=2){" +"\n" "u*=f" +"\n" "t=u/i" +"\n" "r+=t" +"\n" "}" +"\n" "scale=s" +"\n" "ibase=b" +"\n" "return((m*a+r)/n)" +"\n" "}" +"\n" "define j(n,x){" +"\n" "auto b,s,o,a,i,v,f" +"\n" "b=ibase" +"\n" "ibase=A" +"\n" "s=scale" +"\n" "scale=0" +"\n" "n/=1" +"\n" "if(n<0){" +"\n" "n=-n" +"\n" "if(n%2==1)o=1" +"\n" "}" +"\n" "a=1" +"\n" "for(i=2;i<=n;++i)a*=i" +"\n" "scale=1.5*s" +"\n" "a=(x^n)/2^n/a" +"\n" "r=v=1" +"\n" "f=-x*x/4" +"\n" "scale=scale+length(a)-scale(a)" +"\n" "for(i=1;v!=0;++i){" +"\n" "v=v*f/i/(n+i)" +"\n" "r+=v" +"\n" "}" +"\n" "scale=s" +"\n" "ibase=b" +"\n" "if(o!=0)a=-a" +"\n" "return(a*r/1)" +"\n" "}" +}; +#endif // ENABLE_BC + static BcStatus bc_vm_exec(void) { - BcStatus s = BC_STATUS_SUCCESS; + BcStatus s; size_t i; #if ENABLE_BC if (option_mask32 & BC_FLAG_L) { - bc_lex_file(&G.prs.l, bc_lib_name); + // We know that internal library is not buggy, + // thus error checking is normally disabled. +# define DEBUG_LIB 0 + bc_lex_file(&G.prs.l); s = bc_parse_text(&G.prs, bc_lib); + if (DEBUG_LIB && s) return s; - while (!s && G.prs.l.t.t != BC_LEX_EOF) + while (G.prs.l.t.t != BC_LEX_EOF) { s = G.prs.parse(&G.prs); - - if (s) return s; + if (DEBUG_LIB && s) return s; + } s = bc_program_exec(); - if (s) return s; + if (DEBUG_LIB && s) return s; } #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)) { + if (s) { + fflush_and_check(); + fputs("ready for more input\n", stderr); + } s = bc_vm_stdin(); + } + if (!s && !BC_PARSE_CAN_EXEC(&G.prs)) s = bc_vm_process(""); @@ -7134,7 +7361,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); @@ -7166,7 +7393,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; @@ -7175,7 +7402,6 @@ 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_ten(&G.prog.ib); @@ -7222,36 +7448,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) + IF_BC(bc_vm_envArgs();) + bc_program_init(); if (IS_BC) { - bc_vm_envArgs(); - } - - bc_program_init(len); - 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 @@ -7270,36 +7486,93 @@ 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 default width + // 1 char narrower than bc from the same package. Do the same? + bc_vm_init("DC_LINE_LENGTH"); - return bc_vm_run(argc, argv, "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; + + 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