/*
* 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)"
//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.
//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"
//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;
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 {
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);
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 {
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)
| (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 {
#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)))
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;
-typedef BcStatus (*BcParseParse)(struct BcParse *);
+typedef BcStatus (*BcParseParse)(struct BcParse *) FAST_FUNC;
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;
#define BC_PROG_MAIN (0)
#define BC_PROG_READ (1)
-
#if ENABLE_DC
#define BC_PROG_REQ_FUNCS (2)
#endif
#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;
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)
+
+// 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,
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,
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)
{
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;
// 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)
{
{
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 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)
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;
}
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;
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)
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];
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;
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)
{
}
}
+#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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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 (!len) return true;
-
- small = base <= 10;
- b = (BcDig)(small ? base + '0' : base - 10 + 'A');
+ if (G.prog.nchars == G.prog.len - 1) {
+ bb_putchar('\\');
+ bb_putchar('\n');
+ G.prog.nchars = 0;
+ }
+}
- for (i = 0; i < len; ++i) {
+#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
- BcDig c = val[i];
+static FAST_FUNC void bc_num_printDigits(size_t num, size_t width, bool radix)
+{
+ size_t exp, pow;
- if (c == '.') {
+ bc_num_printNewline();
+ bb_putchar(radix ? '.' : ' ');
+ ++G.prog.nchars;
- if (radix) return false;
+ bc_num_printNewline();
+ for (exp = 0, pow = 1; exp < width - 1; ++exp, pow *= 10)
+ continue;
- 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) {
+ for (exp = 0; exp < width; pow /= 10, ++G.prog.nchars, ++exp) {
size_t dig;
- bc_num_printNewline(nchars, line_len);
+ bc_num_printNewline();
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)
+static FAST_FUNC void bc_num_printHex(size_t num, size_t width, bool radix)
{
if (radix) {
- bc_num_printNewline(nchars, line_len);
+ bc_num_printNewline();
bb_putchar('.');
- *nchars += 1;
+ G.prog.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;
bool radix;
if (n->len == 0) {
- print(0, width, false, nchars, len);
+ print(0, width, false);
return BC_STATUS_SUCCESS;
}
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;
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;
}
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,
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
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);
}
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;
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);
}
#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;
return BC_STATUS_SUCCESS;
}
+#endif
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);
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)
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);
}
}
}
}
-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));
}
}
#endif // ENABLE_DC
-static void bc_result_free(void *result)
+static FAST_FUNC void bc_result_free(void *result)
{
BcResult *r = (BcResult *) result;
}
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);
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.
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;
}
}
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;
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;
}
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;
}
}
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;
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;
}
#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);
{
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;
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)
}
#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));
{
BcStatus s;
BcLexType type;
- bool comma = false;
+ bool comma;
s = bc_lex_next(&p->l);
if (s) return s;
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);
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;
}
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;
// 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;
}
// "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:
return s;
}
-static BcStatus bc_parse_parse(BcParse *p)
+static FAST_FUNC BcStatus bc_parse_parse(BcParse *p)
{
BcStatus s;
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;
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:
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:
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;
}
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);
{
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;
return s;
}
-static BcStatus dc_parse_parse(BcParse *p)
+static FAST_FUNC BcStatus dc_parse_parse(BcParse *p)
{
BcStatus s;
{
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);)
}
}
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;
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);
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);
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);
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);
}
#endif
- for (i = 0; i < len; ++i, ++(*nchars)) {
+ for (i = 0; i < len; ++i, ++G.prog.nchars) {
int c = str[i];
case 'n':
{
bb_putchar('\n');
- *nchars = SIZE_MAX;
+ G.prog.nchars = SIZE_MAX;
break;
}
{
// Just print the backslash and following character.
bb_putchar('\\');
- ++(*nchars);
+ ++G.prog.nchars;
bb_putchar(c);
break;
}
{
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');
}
}
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);
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 {
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);
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)
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 {
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;
}
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");
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 {
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) {
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.
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;
}
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
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);
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);
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);
}
{
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;
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);
}
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];
}
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;
}
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);
}
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);
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();
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;
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;
}
}
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);
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);
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);
}
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;
}
}
-// 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
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:
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;
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;
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)
// 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.
- while (!G.eof && (s = bc_read_line(&buf, ">>> ")) == BC_STATUS_SUCCESS) {
+ while ((s = bc_read_line(&buf)) == BC_STATUS_SUCCESS) {
char *string = buf.v;
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");
static BcStatus bc_vm_exec(void)
{
- BcStatus s = BC_STATUS_SUCCESS;
+ BcStatus s;
size_t i;
#if ENABLE_BC
}
#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("");
}
#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);
}
#endif
-static void bc_program_init(size_t line_len)
+static void bc_program_init(void)
{
size_t idx;
BcInstPtr ip;
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);
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
// 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