/* * ***************************************************************************** * * Copyright (c) 2018-2019 Gavin D. Howard and contributors. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * ***************************************************************************** * * Adapted from the following: * * linenoise.c -- guerrilla line editing library against the idea that a * line editing lib needs to be 20,000 lines of C code. * * You can find the original source code at: * http://github.com/antirez/linenoise * * You can find the fork that this code is based on at: * https://github.com/rain-1/linenoise-mob * * ------------------------------------------------------------------------ * * This code is also under the following license: * * Copyright (c) 2010-2016, Salvatore Sanfilippo * Copyright (c) 2010-2013, Pieter Noordhuis * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ------------------------------------------------------------------------ * * Does a number of crazy assumptions that happen to be true in 99.9999% of * the 2010 UNIX computers around. * * References: * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html * * Todo list: * - Filter bogus Ctrl+ combinations. * - Win32 support * * Bloat: * - History search like Ctrl+r in readline? * * List of escape sequences used by this program, we do everything just * with three sequences. In order to be so cheap we may have some * flickering effect with some slow terminal, but the lesser sequences * the more compatible. * * EL (Erase Line) * Sequence: ESC [ n K * Effect: if n is 0 or missing, clear from cursor to end of line * Effect: if n is 1, clear from beginning of line to cursor * Effect: if n is 2, clear entire line * * CUF (CUrsor Forward) * Sequence: ESC [ n C * Effect: moves cursor forward n chars * * CUB (CUrsor Backward) * Sequence: ESC [ n D * Effect: moves cursor backward n chars * * The following is used to get the terminal width if getting * the width with the TIOCGWINSZ ioctl fails * * DSR (Device Status Report) * Sequence: ESC [ 6 n * Effect: reports the current cusor position as ESC [ n ; m R * where n is the row and m is the column * * When multi line mode is enabled, we also use two additional escape * sequences. However multi line editing is disabled by default. * * CUU (CUrsor Up) * Sequence: ESC [ n A * Effect: moves cursor up of n chars. * * CUD (CUrsor Down) * Sequence: ESC [ n B * Effect: moves cursor down of n chars. * * When bc_history_clearScreen() is called, two additional escape sequences * are used in order to clear the screen and position the cursor at home * position. * * CUP (CUrsor Position) * Sequence: ESC [ H * Effect: moves the cursor to upper left corner * * ED (Erase Display) * Sequence: ESC [ 2 J * Effect: clear the whole screen * * ***************************************************************************** * * Code for line history. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #include #else // _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #endif // _WIN32 #if BC_ENABLE_NLS # ifdef _WIN32 # error NLS is not supported on Windows. # endif // _WIN32 #include #endif // BC_ENABLE_NLS #ifndef BC_STATUS_H #define BC_STATUS_H #ifndef BC_ENABLED #define BC_ENABLED (1) #endif // BC_ENABLED #ifndef DC_ENABLED #define DC_ENABLED (1) #endif // DC_ENABLED typedef enum BcStatus { BC_STATUS_SUCCESS = 0, BC_STATUS_ERROR_MATH, BC_STATUS_ERROR_PARSE, BC_STATUS_ERROR_EXEC, BC_STATUS_ERROR_FATAL, BC_STATUS_EOF, BC_STATUS_EMPTY_EXPR, BC_STATUS_QUIT, BC_STATUS_SIGNAL, } BcStatus; typedef enum BcError { BC_ERROR_MATH_NEGATIVE, BC_ERROR_MATH_NON_INTEGER, BC_ERROR_MATH_OVERFLOW, BC_ERROR_MATH_DIVIDE_BY_ZERO, BC_ERROR_FATAL_ALLOC_ERR, BC_ERROR_FATAL_IO_ERR, BC_ERROR_FATAL_FILE_ERR, BC_ERROR_FATAL_BIN_FILE, BC_ERROR_FATAL_PATH_DIR, BC_ERROR_FATAL_OPTION, BC_ERROR_EXEC_IBASE, BC_ERROR_EXEC_OBASE, BC_ERROR_EXEC_SCALE, BC_ERROR_EXEC_READ_EXPR, BC_ERROR_EXEC_REC_READ, BC_ERROR_EXEC_TYPE, BC_ERROR_EXEC_STACK, BC_ERROR_EXEC_PARAMS, BC_ERROR_EXEC_UNDEF_FUNC, BC_ERROR_EXEC_VOID_VAL, BC_ERROR_PARSE_EOF, BC_ERROR_PARSE_CHAR, BC_ERROR_PARSE_STRING, BC_ERROR_PARSE_COMMENT, BC_ERROR_PARSE_TOKEN, #if BC_ENABLED BC_ERROR_PARSE_EXPR, BC_ERROR_PARSE_EMPTY_EXPR, BC_ERROR_PARSE_PRINT, BC_ERROR_PARSE_FUNC, BC_ERROR_PARSE_ASSIGN, BC_ERROR_PARSE_NO_AUTO, BC_ERROR_PARSE_DUP_LOCAL, BC_ERROR_PARSE_BLOCK, BC_ERROR_PARSE_RET_VOID, BC_ERROR_PARSE_REF_VAR, BC_ERROR_POSIX_NAME_LEN, BC_ERROR_POSIX_COMMENT, BC_ERROR_POSIX_KW, BC_ERROR_POSIX_DOT, BC_ERROR_POSIX_RET, BC_ERROR_POSIX_BOOL, BC_ERROR_POSIX_REL_POS, BC_ERROR_POSIX_MULTIREL, BC_ERROR_POSIX_FOR, BC_ERROR_POSIX_EXP_NUM, BC_ERROR_POSIX_REF, BC_ERROR_POSIX_VOID, BC_ERROR_POSIX_BRACE, #endif // BC_ENABLED BC_ERROR_NELEMS, #if BC_ENABLED BC_ERROR_POSIX_START = BC_ERROR_POSIX_NAME_LEN, BC_ERROR_POSIX_END = BC_ERROR_POSIX_BRACE, #endif // BC_ENABLED } BcError; #define BC_ERR_IDX_MATH (0) #define BC_ERR_IDX_PARSE (1) #define BC_ERR_IDX_EXEC (2) #define BC_ERR_IDX_FATAL (3) #define BC_ERR_IDX_NELEMS (4) #if BC_ENABLED #define BC_ERR_IDX_WARN (BC_ERR_IDX_NELEMS) #endif // BC_ENABLED #define BC_UNUSED(e) ((void) (e)) #ifndef BC_LIKELY #define BC_LIKELY(e) (e) #endif // BC_LIKELY #ifndef BC_UNLIKELY #define BC_UNLIKELY(e) (e) #endif // BC_UNLIKELY #define BC_ERR(e) BC_UNLIKELY(e) #define BC_NO_ERR(s) BC_LIKELY(s) #ifndef BC_DEBUG_CODE #define BC_DEBUG_CODE (0) #endif // BC_DEBUG_CODE #endif // BC_STATUS_H #ifndef BC_VECTOR_H #define BC_VECTOR_H #define BC_VEC_INVALID_IDX (SIZE_MAX) #define BC_VEC_START_CAP (UINTMAX_C(1)<<5) typedef unsigned char uchar; typedef void (*BcVecFree)(void*); // Forward declaration. struct BcId; typedef struct BcVec { char *v; size_t len; size_t cap; size_t size; BcVecFree dtor; } BcVec; void bc_vec_init(BcVec *restrict v, size_t esize, BcVecFree dtor); void bc_vec_expand(BcVec *restrict v, size_t req); void bc_vec_npop(BcVec *restrict v, size_t n); void bc_vec_push(BcVec *restrict v, const void *data); void bc_vec_npush(BcVec *restrict v, size_t n, const void *data); void bc_vec_pushByte(BcVec *restrict v, uchar data); void bc_vec_pushIndex(BcVec *restrict v, size_t idx); void bc_vec_string(BcVec *restrict v, size_t len, const char *restrict str); void bc_vec_concat(BcVec *restrict v, const char *restrict str); void bc_vec_empty(BcVec *restrict v); #if BC_ENABLE_HISTORY void bc_vec_popAt(BcVec *restrict v, size_t idx); void bc_vec_replaceAt(BcVec *restrict v, size_t idx, const void *data); #endif // BC_ENABLE_HISTORY void* bc_vec_item(const BcVec *restrict v, size_t idx); void* bc_vec_item_rev(const BcVec *restrict v, size_t idx); void bc_vec_free(void *vec); bool bc_map_insert(BcVec *restrict v, const struct BcId *restrict ptr, size_t *restrict i); size_t bc_map_index(const BcVec *restrict v, const struct BcId *restrict ptr); #define bc_vec_pop(v) (bc_vec_npop((v), 1)) #define bc_vec_top(v) (bc_vec_item_rev((v), 0)) #ifndef NDEBUG #define bc_map_init(v) (bc_vec_init((v), sizeof(BcId), bc_id_free)) #else // NDEBUG #define bc_map_init(v) (bc_vec_init((v), sizeof(BcId), NULL)) #endif // NDEBUG #endif // BC_VECTOR_H #ifndef BC_ARGS_H #define BC_ARGS_H BcStatus bc_args(int argc, char *argv[]); extern const char* const bc_args_env_name; #endif // BC_ARGS_H #ifndef BC_NUM_H #define BC_NUM_H #ifndef BC_ENABLE_EXTRA_MATH #define BC_ENABLE_EXTRA_MATH (1) #endif // BC_ENABLE_EXTRA_MATH #define BC_BASE (10) typedef unsigned long ulong; // For some reason, LONG_BIT is not defined in some versions of gcc. // I define it here to the minimum accepted value in the POSIX standard. #ifndef LONG_BIT #define LONG_BIT (32) #endif // LONG_BIT #ifndef BC_LONG_BIT #define BC_LONG_BIT LONG_BIT #endif // BC_LONG_BIT #if BC_LONG_BIT > LONG_BIT #error BC_LONG_BIT cannot be greater than LONG_BIT #endif // BC_LONG_BIT > LONG_BIT #if BC_LONG_BIT >= 64 typedef int_least32_t BcDig; typedef uint_fast64_t BcBigDig; #define BC_NUM_BIGDIG_MAX (UINT_FAST64_MAX) #define BC_BASE_DIGS (9) #define BC_BASE_POW (1000000000) #define BC_NUM_DEF_SIZE (2) #define BC_NUM_BIGDIG_C UINT64_C #elif BC_LONG_BIT >= 32 typedef int_least16_t BcDig; typedef uint_fast32_t BcBigDig; #define BC_NUM_BIGDIG_MAX (UINT_FAST32_MAX) #define BC_BASE_DIGS (4) #define BC_BASE_POW (10000) #define BC_NUM_DEF_SIZE (4) #define BC_NUM_BIGDIG_C UINT32_C #else #error BC_LONG_BIT must be at least 32 #endif // BC_LONG_BIT >= 64 typedef struct BcNum { BcDig *restrict num; size_t rdx; size_t scale; size_t len; size_t cap; bool neg; } BcNum; #define BC_NUM_MIN_BASE (BC_NUM_BIGDIG_C(2)) #define BC_NUM_MAX_POSIX_IBASE (BC_NUM_BIGDIG_C(16)) #define BC_NUM_MAX_IBASE (BC_NUM_BIGDIG_C(36)) // This is the max base allowed by bc_num_parseChar(). #define BC_NUM_MAX_LBASE (BC_NUM_BIGDIG_C('Z' + BC_BASE + 1)) #define BC_NUM_PRINT_WIDTH (BC_NUM_BIGDIG_C(69)) #ifndef BC_NUM_KARATSUBA_LEN #define BC_NUM_KARATSUBA_LEN (BC_NUM_BIGDIG_C(64)) #elif BC_NUM_KARATSUBA_LEN < 16 #error BC_NUM_KARATSUBA_LEN must be at least 16. #endif // BC_NUM_KARATSUBA_LEN // A crude, but always big enough, calculation of // the size required for ibase and obase BcNum's. #define BC_NUM_BIGDIG_LOG10 ((CHAR_BIT * sizeof(BcBigDig) + 1) / 2 + 1) #define BC_NUM_NONZERO(n) ((n)->len) #define BC_NUM_ZERO(n) (!BC_NUM_NONZERO(n)) #define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1) #define BC_NUM_NUM_LETTER(c) ((c) - 'A' + BC_BASE) #define BC_NUM_KARATSUBA_ALLOCS (6) #define BC_NUM_CMP_SIGNAL_VAL (~((ssize_t) ((size_t) SSIZE_MAX))) #define BC_NUM_CMP_SIGNAL(cmp) (cmp == BC_NUM_CMP_SIGNAL_VAL) #define BC_NUM_ROUND_POW(s) (bc_vm_growSize((s), BC_BASE_DIGS - 1)) #define BC_NUM_RDX(s) (BC_NUM_ROUND_POW(s) / BC_BASE_DIGS) #define BC_NUM_SIZE(n) ((n) * sizeof(BcDig)) #if BC_DEBUG_CODE #define BC_NUM_PRINT(x) fprintf(stderr, "%s = %lu\n", #x, (unsigned long)(x)) #define DUMP_NUM bc_num_dump #else // BC_DEBUG_CODE #undef DUMP_NUM #define DUMP_NUM(x,y) #define BC_NUM_PRINT(x) #endif // BC_DEBUG_CODE typedef BcStatus (*BcNumBinaryOp)(BcNum*, BcNum*, BcNum*, size_t); typedef size_t (*BcNumBinaryOpReq)(const BcNum*, const BcNum*, size_t); typedef void (*BcNumDigitOp)(size_t, size_t, bool); typedef BcStatus (*BcNumShiftAddOp)(BcDig*, const BcDig*, size_t); void bc_num_init(BcNum *restrict n, size_t req); void bc_num_setup(BcNum *restrict n, BcDig *restrict num, size_t cap); void bc_num_copy(BcNum *d, const BcNum *s); void bc_num_createCopy(BcNum *d, const BcNum *s); void bc_num_createFromBigdig(BcNum *n, BcBigDig val); void bc_num_free(void *num); size_t bc_num_scale(const BcNum *restrict n); size_t bc_num_len(const BcNum *restrict n); BcStatus bc_num_bigdig(const BcNum *restrict n, BcBigDig *result); void bc_num_bigdig2num(BcNum *restrict n, BcBigDig val); BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale); BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale); BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale); BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale); BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale); BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale); #if BC_ENABLE_EXTRA_MATH BcStatus bc_num_places(BcNum *a, BcNum *b, BcNum *c, size_t scale); BcStatus bc_num_lshift(BcNum *a, BcNum *b, BcNum *c, size_t scale); BcStatus bc_num_rshift(BcNum *a, BcNum *b, BcNum *c, size_t scale); #endif // BC_ENABLE_EXTRA_MATH BcStatus bc_num_sqrt(BcNum *restrict a, BcNum *restrict b, size_t scale); BcStatus bc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, size_t scale); size_t bc_num_addReq(const BcNum* a, const BcNum* b, size_t scale); size_t bc_num_mulReq(const BcNum *a, const BcNum *b, size_t scale); size_t bc_num_powReq(const BcNum *a, const BcNum *b, size_t scale); #if BC_ENABLE_EXTRA_MATH size_t bc_num_placesReq(const BcNum *a, const BcNum *b, size_t scale); #endif // BC_ENABLE_EXTRA_MATH void bc_num_truncate(BcNum *restrict n, size_t places); ssize_t bc_num_cmp(const BcNum *a, const BcNum *b); #if DC_ENABLED BcStatus bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d); #endif // DC_ENABLED void bc_num_one(BcNum *restrict n); ssize_t bc_num_cmpZero(const BcNum *n); BcStatus bc_num_parse(BcNum *restrict n, const char *restrict val, BcBigDig base, bool letter); BcStatus bc_num_print(BcNum *restrict n, BcBigDig base, bool newline); #if DC_ENABLED BcStatus bc_num_stream(BcNum *restrict n, BcBigDig base); #endif // DC_ENABLED #if BC_DEBUG_CODE void bc_num_printDebug(const BcNum *n, const char *name, bool emptyline); void bc_num_printDigs(const BcDig* n, size_t len, bool emptyline); void bc_num_printWithDigs(const BcNum *n, const char *name, bool emptyline); void bc_num_dump(const char *varname, const BcNum *n); #endif // BC_DEBUG_CODE extern const char bc_num_hex_digits[]; extern const BcBigDig bc_num_pow10[BC_BASE_DIGS + 1]; #endif // BC_NUM_H #ifndef BC_LANG_H #define BC_LANG_H #if BC_ENABLED #define BC_INST_IS_ASSIGN(i) \ ((i) == BC_INST_ASSIGN || (i) == BC_INST_ASSIGN_NO_VAL) #define BC_INST_USE_VAL(i) ((i) <= BC_INST_ASSIGN) #else // BC_ENABLED #define BC_INST_IS_ASSIGN(i) ((i) == BC_INST_ASSIGN_NO_VAL) #define BC_INST_USE_VAL(i) (false) #endif // BC_ENABLED typedef enum BcInst { #if BC_ENABLED BC_INST_INC_POST = 0, BC_INST_DEC_POST, BC_INST_INC_PRE, BC_INST_DEC_PRE, #endif // BC_ENABLED BC_INST_NEG, BC_INST_BOOL_NOT, #if BC_ENABLE_EXTRA_MATH BC_INST_TRUNC, #endif // BC_ENABLE_EXTRA_MATH BC_INST_POWER, BC_INST_MULTIPLY, BC_INST_DIVIDE, BC_INST_MODULUS, BC_INST_PLUS, BC_INST_MINUS, #if BC_ENABLE_EXTRA_MATH BC_INST_PLACES, BC_INST_LSHIFT, BC_INST_RSHIFT, #endif // BC_ENABLE_EXTRA_MATH BC_INST_REL_EQ, BC_INST_REL_LE, BC_INST_REL_GE, BC_INST_REL_NE, BC_INST_REL_LT, BC_INST_REL_GT, BC_INST_BOOL_OR, BC_INST_BOOL_AND, #if BC_ENABLED BC_INST_ASSIGN_POWER, BC_INST_ASSIGN_MULTIPLY, BC_INST_ASSIGN_DIVIDE, BC_INST_ASSIGN_MODULUS, BC_INST_ASSIGN_PLUS, BC_INST_ASSIGN_MINUS, #if BC_ENABLE_EXTRA_MATH BC_INST_ASSIGN_PLACES, BC_INST_ASSIGN_LSHIFT, BC_INST_ASSIGN_RSHIFT, #endif // BC_ENABLE_EXTRA_MATH BC_INST_ASSIGN, BC_INST_INC_NO_VAL, BC_INST_DEC_NO_VAL, BC_INST_ASSIGN_POWER_NO_VAL, BC_INST_ASSIGN_MULTIPLY_NO_VAL, BC_INST_ASSIGN_DIVIDE_NO_VAL, BC_INST_ASSIGN_MODULUS_NO_VAL, BC_INST_ASSIGN_PLUS_NO_VAL, BC_INST_ASSIGN_MINUS_NO_VAL, #if BC_ENABLE_EXTRA_MATH BC_INST_ASSIGN_PLACES_NO_VAL, BC_INST_ASSIGN_LSHIFT_NO_VAL, BC_INST_ASSIGN_RSHIFT_NO_VAL, #endif // BC_ENABLE_EXTRA_MATH #endif // BC_ENABLED BC_INST_ASSIGN_NO_VAL, BC_INST_NUM, BC_INST_VAR, BC_INST_ARRAY_ELEM, #if BC_ENABLED BC_INST_ARRAY, #endif // BC_ENABLED BC_INST_ONE, #if BC_ENABLED BC_INST_LAST, #endif // BC_ENABLED BC_INST_IBASE, BC_INST_OBASE, BC_INST_SCALE, BC_INST_LENGTH, BC_INST_SCALE_FUNC, BC_INST_SQRT, BC_INST_ABS, BC_INST_READ, BC_INST_MAXIBASE, BC_INST_MAXOBASE, BC_INST_MAXSCALE, BC_INST_PRINT, BC_INST_PRINT_POP, BC_INST_STR, BC_INST_PRINT_STR, #if BC_ENABLED BC_INST_JUMP, BC_INST_JUMP_ZERO, BC_INST_CALL, BC_INST_RET, BC_INST_RET0, BC_INST_RET_VOID, BC_INST_HALT, #endif // BC_ENABLED BC_INST_POP, #if DC_ENABLED BC_INST_POP_EXEC, BC_INST_MODEXP, BC_INST_DIVMOD, BC_INST_EXECUTE, BC_INST_EXEC_COND, BC_INST_ASCIIFY, BC_INST_PRINT_STREAM, BC_INST_PRINT_STACK, BC_INST_CLEAR_STACK, BC_INST_STACK_LEN, BC_INST_DUPLICATE, BC_INST_SWAP, BC_INST_LOAD, BC_INST_PUSH_VAR, BC_INST_PUSH_TO_VAR, BC_INST_QUIT, BC_INST_NQUIT, #endif // DC_ENABLED BC_INST_INVALID = UCHAR_MAX, } BcInst; typedef struct BcId { char *name; size_t idx; } BcId; typedef struct BcLoc { size_t loc; size_t idx; } BcLoc; typedef struct BcConst { char *val; BcBigDig base; BcNum num; } BcConst; typedef struct BcFunc { BcVec code; #if BC_ENABLED BcVec labels; BcVec autos; size_t nparams; #endif // BC_ENABLED BcVec strs; BcVec consts; const char *name; #if BC_ENABLED bool voidfn; #endif // BC_ENABLED } BcFunc; typedef enum BcResultType { BC_RESULT_VAR, BC_RESULT_ARRAY_ELEM, #if BC_ENABLED BC_RESULT_ARRAY, #endif // BC_ENABLED BC_RESULT_STR, BC_RESULT_CONSTANT, BC_RESULT_TEMP, BC_RESULT_ONE, #if BC_ENABLED BC_RESULT_LAST, BC_RESULT_VOID, #endif // BC_ENABLED BC_RESULT_IBASE, BC_RESULT_OBASE, BC_RESULT_SCALE, } BcResultType; typedef union BcResultData { BcNum n; BcVec v; BcLoc loc; } BcResultData; typedef struct BcResult { BcResultType t; BcResultData d; } BcResult; typedef struct BcInstPtr { size_t func; size_t idx; size_t len; } BcInstPtr; typedef enum BcType { BC_TYPE_VAR, BC_TYPE_ARRAY, #if BC_ENABLED BC_TYPE_REF, #endif // BC_ENABLED } BcType; struct BcProgram; void bc_func_init(BcFunc *f, const char* name); BcStatus bc_func_insert(BcFunc *f, struct BcProgram* p, char* name, BcType type, size_t line); void bc_func_reset(BcFunc *f); void bc_func_free(void *func); void bc_array_init(BcVec *a, bool nums); void bc_array_copy(BcVec *d, const BcVec *s); void bc_string_free(void *string); void bc_const_free(void *constant); void bc_id_free(void *id); void bc_result_copy(BcResult *d, BcResult *src); void bc_result_free(void *result); void bc_array_expand(BcVec *a, size_t len); int bc_id_cmp(const BcId *e1, const BcId *e2); #if BC_DEBUG_CODE extern const char* bc_inst_names[]; #endif // BC_DEBUG_CODE extern const char bc_func_main[]; extern const char bc_func_read[]; #endif // BC_LANG_H #ifndef BC_LEX_H #define BC_LEX_H #define bc_lex_err(l, e) (bc_vm_error((e), (l)->line)) #define bc_lex_verr(l, e, ...) (bc_vm_error((e), (l)->line, __VA_ARGS__)) #define BC_LEX_NEG_CHAR (BC_IS_BC ? '-' : '_') #define BC_LEX_LAST_NUM_CHAR (BC_IS_BC ? 'Z' : 'F') #define BC_LEX_NUM_CHAR(c, pt, int_only) \ (isdigit(c) || ((c) >= 'A' && (c) <= BC_LEX_LAST_NUM_CHAR) || \ ((c) == '.' && !(pt) && !(int_only))) // BC_LEX_NEG is not used in lexing; it is only for parsing. typedef enum BcLexType { BC_LEX_EOF, BC_LEX_INVALID, #if BC_ENABLED BC_LEX_OP_INC, BC_LEX_OP_DEC, #endif // BC_ENABLED BC_LEX_NEG, BC_LEX_OP_BOOL_NOT, #if BC_ENABLE_EXTRA_MATH BC_LEX_OP_TRUNC, #endif // BC_ENABLE_EXTRA_MATH BC_LEX_OP_POWER, BC_LEX_OP_MULTIPLY, BC_LEX_OP_DIVIDE, BC_LEX_OP_MODULUS, BC_LEX_OP_PLUS, BC_LEX_OP_MINUS, #if BC_ENABLE_EXTRA_MATH BC_LEX_OP_PLACES, BC_LEX_OP_LSHIFT, BC_LEX_OP_RSHIFT, #endif // BC_ENABLE_EXTRA_MATH 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_OP_BOOL_OR, BC_LEX_OP_BOOL_AND, #if BC_ENABLED BC_LEX_OP_ASSIGN_POWER, BC_LEX_OP_ASSIGN_MULTIPLY, BC_LEX_OP_ASSIGN_DIVIDE, BC_LEX_OP_ASSIGN_MODULUS, BC_LEX_OP_ASSIGN_PLUS, BC_LEX_OP_ASSIGN_MINUS, #if BC_ENABLE_EXTRA_MATH BC_LEX_OP_ASSIGN_PLACES, BC_LEX_OP_ASSIGN_LSHIFT, BC_LEX_OP_ASSIGN_RSHIFT, #endif // BC_ENABLE_EXTRA_MATH #endif // BC_ENABLED BC_LEX_OP_ASSIGN, BC_LEX_NLINE, BC_LEX_WHITESPACE, BC_LEX_LPAREN, BC_LEX_RPAREN, BC_LEX_LBRACKET, BC_LEX_COMMA, BC_LEX_RBRACKET, BC_LEX_LBRACE, BC_LEX_SCOLON, BC_LEX_RBRACE, BC_LEX_STR, BC_LEX_NAME, BC_LEX_NUMBER, #if BC_ENABLED BC_LEX_KW_AUTO, BC_LEX_KW_BREAK, BC_LEX_KW_CONTINUE, BC_LEX_KW_DEFINE, BC_LEX_KW_FOR, BC_LEX_KW_IF, BC_LEX_KW_LIMITS, BC_LEX_KW_RETURN, BC_LEX_KW_WHILE, BC_LEX_KW_HALT, BC_LEX_KW_LAST, #endif // BC_ENABLED BC_LEX_KW_IBASE, BC_LEX_KW_OBASE, BC_LEX_KW_SCALE, BC_LEX_KW_LENGTH, BC_LEX_KW_PRINT, BC_LEX_KW_SQRT, BC_LEX_KW_ABS, BC_LEX_KW_QUIT, BC_LEX_KW_READ, BC_LEX_KW_MAXIBASE, BC_LEX_KW_MAXOBASE, BC_LEX_KW_MAXSCALE, BC_LEX_KW_ELSE, #if DC_ENABLED BC_LEX_EQ_NO_REG, BC_LEX_OP_MODEXP, BC_LEX_OP_DIVMOD, BC_LEX_COLON, BC_LEX_EXECUTE, BC_LEX_PRINT_STACK, BC_LEX_CLEAR_STACK, BC_LEX_STACK_LEVEL, BC_LEX_DUPLICATE, BC_LEX_SWAP, BC_LEX_POP, BC_LEX_ASCIIFY, BC_LEX_PRINT_STREAM, BC_LEX_STORE_IBASE, BC_LEX_STORE_OBASE, BC_LEX_STORE_SCALE, BC_LEX_LOAD, BC_LEX_LOAD_POP, BC_LEX_STORE_PUSH, BC_LEX_PRINT_POP, BC_LEX_NQUIT, BC_LEX_SCALE_FACTOR, #endif // DC_ENABLED } BcLexType; struct BcLex; typedef BcStatus (*BcLexNext)(struct BcLex*); typedef struct BcLex { const char *buf; size_t i; size_t line; size_t len; BcLexType t; BcLexType last; BcVec str; } BcLex; void bc_lex_init(BcLex *l); void bc_lex_free(BcLex *l); void bc_lex_file(BcLex *l, const char *file); BcStatus bc_lex_text(BcLex *l, const char *text); BcStatus bc_lex_next(BcLex *l); void bc_lex_lineComment(BcLex *l); BcStatus bc_lex_comment(BcLex *l); void bc_lex_whitespace(BcLex *l); BcStatus bc_lex_number(BcLex *l, char start); void bc_lex_name(BcLex *l); void bc_lex_commonTokens(BcLex *l, char c); BcStatus bc_lex_invalidChar(BcLex *l, char c); #endif // BC_LEX_H #ifndef BC_PARSE_H #define BC_PARSE_H #define BC_PARSE_REL (UINTMAX_C(1)<<0) #define BC_PARSE_PRINT (UINTMAX_C(1)<<1) #define BC_PARSE_NOCALL (UINTMAX_C(1)<<2) #define BC_PARSE_NOREAD (UINTMAX_C(1)<<3) #define BC_PARSE_ARRAY (UINTMAX_C(1)<<4) #define BC_PARSE_NEEDVAL (UINTMAX_C(1)<<5) #define bc_parse_push(p, i) (bc_vec_pushByte(&(p)->func->code, (uchar) (i))) #define bc_parse_string(p)(bc_parse_addId((p), (p)->l.str.v, BC_INST_STR)) #define bc_parse_pushIndex(p, idx) (bc_vec_pushIndex(&(p)->func->code, (idx))) #define bc_parse_err(p, e) (bc_vm_error((e), (p)->l.line)) #define bc_parse_verr(p, e, ...) (bc_vm_error((e), (p)->l.line, __VA_ARGS__)) typedef struct BcParseNext { uchar len; uchar tokens[4]; } BcParseNext; #define BC_PARSE_NEXT_TOKENS(...) .tokens = { __VA_ARGS__ } #define BC_PARSE_NEXT(a, ...) \ { .len = (uchar) (a), BC_PARSE_NEXT_TOKENS(__VA_ARGS__) } struct BcParse; struct BcProgram; typedef BcStatus (*BcParseParse)(struct BcParse*); typedef BcStatus (*BcParseExpr)(struct BcParse*, uint8_t); typedef struct BcParse { BcLex l; #if BC_ENABLED BcVec flags; BcVec exits; BcVec conds; BcVec ops; BcVec buf; #endif // BC_ENABLED struct BcProgram *prog; BcFunc *func; size_t fidx; bool auto_part; } BcParse; void bc_parse_init(BcParse *p, struct BcProgram *prog, size_t func); void bc_parse_free(BcParse *p); BcStatus bc_parse_reset(BcParse *p, BcStatus s); void bc_parse_addId(BcParse *p, const char *string, uchar inst); void bc_parse_number(BcParse *p); void bc_parse_updateFunc(BcParse *p, size_t fidx); void bc_parse_pushName(const BcParse* p, char *name, bool var); BcStatus bc_parse_text(BcParse *p, const char *text); extern const char bc_parse_one[]; #endif // BC_PARSE_H #ifndef BC_PROGRAM_H #define BC_PROGRAM_H #define BC_PROG_GLOBALS_IBASE (0) #define BC_PROG_GLOBALS_OBASE (1) #define BC_PROG_GLOBALS_SCALE (2) #define BC_PROG_GLOBALS_LEN (3) #define BC_PROG_ONE_CAP (1) typedef struct BcProgram { BcBigDig globals[BC_PROG_GLOBALS_LEN]; BcVec globals_v[BC_PROG_GLOBALS_LEN]; #if DC_ENABLED BcBigDig strm; BcNum strmb; #endif // DC_ENABLED BcVec results; BcVec stack; BcVec fns; #if BC_ENABLED BcVec fn_map; #endif // BC_ENABLED BcVec vars; BcVec var_map; BcVec arrs; BcVec arr_map; BcNum one; #if BC_ENABLED BcNum last; #endif // BC_ENABLED #if DC_ENABLED // This uses BC_NUM_LONG_LOG10 because it is used in bc_num_ulong2num(), // which attempts to realloc, unless it is big enough. This is big enough. BcDig strmb_num[BC_NUM_BIGDIG_LOG10]; #endif // DC_ENABLED BcDig one_num[BC_PROG_ONE_CAP]; } BcProgram; #define BC_PROG_STACK(s, n) ((s)->len >= ((size_t) (n))) #define BC_PROG_GLOBAL_PTR(v) (bc_vec_top(v)) #define BC_PROG_GLOBAL(v) (*((BcBigDig*) BC_PROG_GLOBAL_PTR(v))) #define BC_PROG_IBASE(p) ((p)->globals[BC_PROG_GLOBALS_IBASE]) #define BC_PROG_OBASE(p) ((p)->globals[BC_PROG_GLOBALS_OBASE]) #define BC_PROG_SCALE(p) ((p)->globals[BC_PROG_GLOBALS_SCALE]) #define BC_PROG_MAIN (0) #define BC_PROG_READ (1) #if DC_ENABLED #define BC_PROG_REQ_FUNCS (2) #if !BC_ENABLED // For dc only, last is always true. #define bc_program_copyToVar(p, name, t, last) \ bc_program_copyToVar(p, name, t) #endif // !BC_ENABLED #else // DC_ENABLED // For bc, 'pop' and 'copy' are always false. #define bc_program_pushVar(p, code, bgn, pop, copy) \ bc_program_pushVar(p, code, bgn) #ifdef NDEBUG #define BC_PROG_NO_STACK_CHECK #endif // NDEBUG #endif // DC_ENABLED #define BC_PROG_STR(n) ((n)->num == NULL && !(n)->cap) #if BC_ENABLED #define BC_PROG_NUM(r, n) \ ((r)->t != BC_RESULT_ARRAY && (r)->t != BC_RESULT_STR && !BC_PROG_STR(n)) #else // BC_ENABLED #define BC_PROG_NUM(r, n) ((r)->t != BC_RESULT_STR && !BC_PROG_STR(n)) // For dc, inst is always BC_INST_ARRAY_ELEM. #define bc_program_pushArray(p, code, bgn, inst) \ bc_program_pushArray(p, code, bgn) #endif // BC_ENABLED typedef void (*BcProgramUnary)(BcResult*, BcNum*); void bc_program_init(BcProgram *p); void bc_program_free(BcProgram *p); #if BC_DEBUG_CODE #if BC_ENABLED && DC_ENABLED void bc_program_code(const BcProgram *p); void bc_program_printInst(const BcProgram *p, const char *code, size_t *restrict bgn); BcStatus bc_program_printStackDebug(BcProgram* p); #endif // BC_ENABLED && DC_ENABLED #endif // BC_DEBUG_CODE size_t bc_program_search(BcProgram *p, char* id, bool var); void bc_program_addFunc(BcProgram *p, BcFunc *f, const char* name); size_t bc_program_insertFunc(BcProgram *p, char *name); BcStatus bc_program_reset(BcProgram *p, BcStatus s); BcStatus bc_program_exec(BcProgram *p); void bc_program_negate(BcResult *r, BcNum *n); void bc_program_not(BcResult *r, BcNum *n); #if BC_ENABLE_EXTRA_MATH void bc_program_trunc(BcResult *r, BcNum *n); #endif // BC_ENABLE_EXTRA_MATH extern const BcNumBinaryOp bc_program_ops[]; extern const BcNumBinaryOpReq bc_program_opReqs[]; extern const BcProgramUnary bc_program_unarys[]; extern const char bc_program_exprs_name[]; extern const char bc_program_stdin_name[]; #if BC_ENABLE_SIGNALS extern const char bc_program_ready_msg[]; extern const size_t bc_program_ready_msg_len; #endif // BC_ENABLE_SIGNALS extern const char bc_program_esc_chars[]; extern const char bc_program_esc_seqs[]; #endif // BC_PROGRAM_H #ifndef BC_IO_H #define BC_IO_H #ifndef BC_ENABLE_PROMPT #define BC_ENABLE_PROMPT (1) #endif // BC_ENABLE_PROMPT #if !BC_ENABLE_PROMPT #define bc_read_line(vec, prompt) bc_read_line(vec) #define bc_read_chars(vec, prompt) bc_read_chars(vec) #endif // BC_ENABLE_PROMPT #define BC_READ_BIN_CHAR(c) (((c) < ' ' && !isspace((c))) || ((uchar) c) > '~') BcStatus bc_read_line(BcVec *vec, const char *prompt); BcStatus bc_read_file(const char *path, char **buf); BcStatus bc_read_chars(BcVec *vec, const char *prompt); #endif // BC_IO_H #ifndef BC_HISTORY_H #define BC_HISTORY_H #ifndef BC_ENABLE_HISTORY #define BC_ENABLE_HISTORY (1) #endif // BC_ENABLE_HISTORY #if BC_ENABLE_HISTORY #ifdef _WIN32 #error History is not supported on Windows. #endif #define BC_HIST_DEF_COLS (80) #define BC_HIST_MAX_LEN (128) #define BC_HIST_MAX_LINE (4095) #define BC_HIST_SEQ_SIZE (64) #define BC_HIST_BUF_LEN(h) ((h)->buf.len - 1) #define BC_HIST_WRITE(s, n) (write(STDERR_FILENO, (s), (n)) != (ssize_t) (n)) #define BC_HIST_READ(s, n) (read(STDIN_FILENO, (s), (n)) == -1) #define BC_HIST_NEXT (false) #define BC_HIST_PREV (true) #if BC_DEBUG_CODE #define lndebug(...) \ do { \ if (bc_history_debug_fp == NULL) { \ bc_history_debug_fp = fopen("/tmp/lndebug.txt","a"); \ fprintf(bc_history_debug_fp, \ "[%zu %zu %zu] p: %d, rows: %d, " \ "rpos: %d, max: %zu, oldmax: %d\n", \ l->len, l->pos, l->oldcolpos, plen, rows, rpos, \ l->maxrows, old_rows); \ } \ fprintf(bc_history_debug_fp, ", " __VA_ARGS__); \ fflush(bc_history_debug_fp); \ } while (0) #else // BC_DEBUG_CODE #define lndebug(fmt, ...) #endif // BC_DEBUG_CODE #if !BC_ENABLE_PROMPT #define bc_history_line(h, vec, prompt) bc_history_line(h, vec) #define bc_history_raw(h, prompt) bc_history_raw(h) #define bc_history_edit(h, prompt) bc_history_edit(h) #endif // BC_ENABLE_PROMPT typedef enum BcHistoryAction { BC_ACTION_NULL = 0, BC_ACTION_CTRL_A = 1, BC_ACTION_CTRL_B = 2, BC_ACTION_CTRL_C = 3, BC_ACTION_CTRL_D = 4, BC_ACTION_CTRL_E = 5, BC_ACTION_CTRL_F = 6, BC_ACTION_CTRL_H = 8, BC_ACTION_TAB = 9, BC_ACTION_LINE_FEED = 10, BC_ACTION_CTRL_K = 11, BC_ACTION_CTRL_L = 12, BC_ACTION_ENTER = 13, BC_ACTION_CTRL_N = 14, BC_ACTION_CTRL_P = 16, BC_ACTION_CTRL_T = 20, BC_ACTION_CTRL_U = 21, BC_ACTION_CTRL_W = 23, BC_ACTION_CTRL_Z = 26, BC_ACTION_ESC = 27, BC_ACTION_BACKSPACE = 127 } BcHistoryAction; /** * This represents the state during line editing. We pass this state * to functions implementing specific editing functionalities. */ typedef struct BcHistory { /// Edited line buffer. BcVec buf; /// The history. BcVec history; /// A temporary buffer for refresh. Using this /// prevents an allocation on every refresh. BcVec tmp; #if BC_ENABLE_PROMPT /// Prompt to display. const char *prompt; /// Prompt length. size_t plen; #endif // BC_ENABLE_PROMPT /// Prompt column length. size_t pcol; /// Current cursor position. size_t pos; /// Previous refresh cursor column position. size_t oldcolpos; /// Number of columns in terminal. size_t cols; /// The history index we are currently editing. size_t idx; /// The original terminal state. struct termios orig_termios; /// This is to check if stdin has more data. fd_set rdset; /// This is to check if stdin has more data. struct timespec ts; /// This is to check if stdin has more data. sigset_t sigmask; /// This is to signal that there is more, so we don't process yet. bool stdin_has_data; /// Whether we are in rawmode. bool rawMode; /// Whether the terminal is bad. bool badTerm; } BcHistory; BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt); void bc_history_add(BcHistory *h, char *line); void bc_history_init(BcHistory *h); void bc_history_free(BcHistory *h); extern const char *bc_history_bad_terms[]; extern const char bc_history_tab[]; extern const size_t bc_history_tab_len; extern const char bc_history_ctrlc[]; extern const uint32_t bc_history_wchars[][2]; extern const size_t bc_history_wchars_len; extern const uint32_t bc_history_combo_chars[]; extern const size_t bc_history_combo_chars_len; #if BC_DEBUG_CODE extern FILE *bc_history_debug_fp; BcStatus bc_history_printKeyCodes(BcHistory* l); #endif // BC_DEBUG_CODE #endif // BC_ENABLE_HISTORY #endif // BC_HISTORY_H #ifndef BC_VM_H #define BC_VM_H #if !BC_ENABLED && !DC_ENABLED #error Must define BC_ENABLED, DC_ENABLED, or both #endif // CHAR_BIT must be at least 6. #if CHAR_BIT < 6 #error CHAR_BIT must be at least 6. #endif #ifndef BC_ENABLE_NLS #define BC_ENABLE_NLS (0) #endif // BC_ENABLE_NLS #ifndef BC_ENABLE_SIGNALS #define BC_ENABLE_SIGNALS (1) #endif // BC_ENABLE_SIGNALS #ifndef MAINEXEC #define MAINEXEC bc #endif #ifndef EXECPREFIX #define EXECPREFIX #endif #define GEN_STR(V) #V #define GEN_STR2(V) GEN_STR(V) #define BC_VERSION GEN_STR2(VERSION) #define BC_EXECPREFIX GEN_STR2(EXECPREFIX) #define BC_MAINEXEC GEN_STR2(MAINEXEC) // Windows has deprecated isatty(). #ifdef _WIN32 #define isatty _isatty #endif // _WIN32 #define DC_FLAG_X (UINTMAX_C(1)<<0) #define BC_FLAG_W (UINTMAX_C(1)<<1) #define BC_FLAG_S (UINTMAX_C(1)<<2) #define BC_FLAG_Q (UINTMAX_C(1)<<3) #define BC_FLAG_L (UINTMAX_C(1)<<4) #define BC_FLAG_I (UINTMAX_C(1)<<5) #define BC_FLAG_G (UINTMAX_C(1)<<6) #define BC_FLAG_P (UINTMAX_C(1)<<7) #define BC_FLAG_TTYIN (UINTMAX_C(1)<<8) #define BC_TTYIN (vm->flags & BC_FLAG_TTYIN) #define BC_TTY (vm->tty) #define BC_S (BC_ENABLED && (vm->flags & BC_FLAG_S)) #define BC_W (BC_ENABLED && (vm->flags & BC_FLAG_W)) #define BC_L (BC_ENABLED && (vm->flags & BC_FLAG_L)) #define BC_I (vm->flags & BC_FLAG_I) #define BC_G (BC_ENABLED && (vm->flags & BC_FLAG_G)) #define DC_X (DC_ENABLED && (vm->flags & DC_FLAG_X)) #define BC_P (vm->flags & BC_FLAG_P) #define BC_USE_PROMPT (!BC_P && BC_TTY && !BC_IS_POSIX) #define BC_MAX(a, b) ((a) > (b) ? (a) : (b)) #define BC_MIN(a, b) ((a) < (b) ? (a) : (b)) #define BC_MAX_OBASE ((ulong) (BC_BASE_POW)) #define BC_MAX_DIM ((ulong) (SIZE_MAX - 1)) #define BC_MAX_SCALE ((ulong) (BC_NUM_BIGDIG_MAX - 1)) #define BC_MAX_STRING ((ulong) (BC_NUM_BIGDIG_MAX - 1)) #define BC_MAX_NAME BC_MAX_STRING #define BC_MAX_NUM BC_MAX_SCALE #define BC_MAX_EXP ((ulong) (BC_NUM_BIGDIG_MAX)) #define BC_MAX_VARS ((ulong) (SIZE_MAX - 1)) #define BC_IS_BC (BC_ENABLED && (!DC_ENABLED || vm->name[0] != 'd')) #define BC_IS_POSIX (BC_S || BC_W) #if BC_ENABLE_SIGNALS #define BC_SIG BC_UNLIKELY(vm->sig != vm->sig_chk) #define BC_NO_SIG BC_LIKELY(vm->sig == vm->sig_chk) #define BC_SIGTERM_VAL (SIG_ATOMIC_MAX) #define BC_SIGTERM (vm->sig == BC_SIGTERM_VAL) #define BC_SIGINT (vm->sig && vm->sig != BC_SIGTERM_VAL) #else // BC_ENABLE_SIGNALS #define BC_SIG (0) #define BC_NO_SIG (1) #endif // BC_ENABLE_SIGNALS #define bc_vm_err(e) (bc_vm_error((e), 0)) #define bc_vm_verr(e, ...) (bc_vm_error((e), 0, __VA_ARGS__)) #define BC_IO_ERR(e, f) (BC_ERR((e) < 0 || ferror(f))) #define BC_STATUS_IS_ERROR(s) \ ((s) >= BC_STATUS_ERROR_MATH && (s) <= BC_STATUS_ERROR_FATAL) #define BC_ERROR_SIGNAL_ONLY(s) (BC_ENABLE_SIGNALS && BC_ERR(s)) #define BC_VM_INVALID_CATALOG ((nl_catd) -1) typedef struct BcVm { BcParse prs; BcProgram prog; size_t nchars; const char* file; #if BC_ENABLE_SIGNALS const char *sigmsg; volatile sig_atomic_t sig; sig_atomic_t sig_chk; uchar siglen; #endif // BC_ENABLE_SIGNALS uint16_t flags; uchar read_ret; bool tty; uint16_t line_len; BcBigDig maxes[BC_PROG_GLOBALS_LEN]; BcVec files; BcVec exprs; const char *name; const char *help; #if BC_ENABLE_HISTORY BcHistory history; #endif // BC_ENABLE_HISTORY BcLexNext next; BcParseParse parse; BcParseExpr expr; const char *func_header; const char *err_ids[BC_ERR_IDX_NELEMS + BC_ENABLED]; const char *err_msgs[BC_ERROR_NELEMS]; const char *locale; BcBigDig last_base; BcBigDig last_pow; BcBigDig last_exp; BcBigDig last_rem; #if BC_ENABLE_NLS nl_catd catalog; #endif // BC_ENABLE_NLS } BcVm; #if BC_ENABLED BcStatus bc_vm_posixError(BcError e, size_t line, ...); #endif // BC_ENABLED void bc_vm_info(const char* const help); BcStatus bc_vm_boot(int argc, char *argv[], const char *env_len, const char* const env_args, const char* env_exp_quit); void bc_vm_shutdown(void); size_t bc_vm_printf(const char *fmt, ...); void bc_vm_puts(const char *str, FILE *restrict f); void bc_vm_putchar(int c); void bc_vm_fflush(FILE *restrict f); size_t bc_vm_arraySize(size_t n, size_t size); size_t bc_vm_growSize(size_t a, size_t b); void* bc_vm_malloc(size_t n); void* bc_vm_realloc(void *ptr, size_t n); char* bc_vm_strdup(const char *str); BcStatus bc_vm_error(BcError e, size_t line, ...); extern const char bc_copyright[]; extern const char* const bc_err_line; extern const char* const bc_err_func_header; extern const char *bc_errs[]; extern const uchar bc_err_ids[]; extern const char* const bc_err_msgs[]; extern BcVm *vm; #endif // BC_VM_H #ifndef BC_BC_H #define BC_BC_H #if BC_ENABLED int bc_main(int argc, char **argv); extern const char bc_help[]; extern const char bc_lib[]; extern const char* bc_lib_name; #if BC_ENABLE_EXTRA_MATH extern const char bc_lib2[]; extern const char* bc_lib2_name; #endif // BC_ENABLE_EXTRA_MATH typedef struct BcLexKeyword { uchar data; const char name[9]; } BcLexKeyword; #define BC_LEX_CHAR_MSB(bit) ((bit) << (CHAR_BIT - 1)) #define BC_LEX_KW_POSIX(kw) ((kw)->data & (BC_LEX_CHAR_MSB(1))) #define BC_LEX_KW_LEN(kw) ((size_t) ((kw)->data & ~(BC_LEX_CHAR_MSB(1)))) #define BC_LEX_KW_ENTRY(a, b, c) \ { .data = ((b) & ~(BC_LEX_CHAR_MSB(1))) | BC_LEX_CHAR_MSB(c),.name = a } extern const BcLexKeyword bc_lex_kws[]; extern const size_t bc_lex_kws_len; BcStatus bc_lex_token(BcLex *l); #define BC_PARSE_TOP_FLAG_PTR(p) ((uint16_t*) bc_vec_top(&(p)->flags)) #define BC_PARSE_TOP_FLAG(p) (*(BC_PARSE_TOP_FLAG_PTR(p))) #define BC_PARSE_FLAG_BRACE (UINTMAX_C(1)<<0) #define BC_PARSE_BRACE(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_BRACE) #define BC_PARSE_FLAG_FUNC_INNER (UINTMAX_C(1)<<1) #define BC_PARSE_FUNC_INNER(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_FUNC_INNER) #define BC_PARSE_FLAG_FUNC (UINTMAX_C(1)<<2) #define BC_PARSE_FUNC(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_FUNC) #define BC_PARSE_FLAG_BODY (UINTMAX_C(1)<<3) #define BC_PARSE_BODY(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_BODY) #define BC_PARSE_FLAG_LOOP (UINTMAX_C(1)<<4) #define BC_PARSE_LOOP(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_LOOP) #define BC_PARSE_FLAG_LOOP_INNER (UINTMAX_C(1)<<5) #define BC_PARSE_LOOP_INNER(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_LOOP_INNER) #define BC_PARSE_FLAG_IF (UINTMAX_C(1)<<6) #define BC_PARSE_IF(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_IF) #define BC_PARSE_FLAG_ELSE (UINTMAX_C(1)<<7) #define BC_PARSE_ELSE(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_ELSE) #define BC_PARSE_FLAG_IF_END (UINTMAX_C(1)<<8) #define BC_PARSE_IF_END(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_IF_END) #define BC_PARSE_NO_EXEC(p) ((p)->flags.len != 1 || BC_PARSE_TOP_FLAG(p) != 0) #define BC_PARSE_DELIMITER(t) \ ((t) == BC_LEX_SCOLON || (t) == BC_LEX_NLINE || (t) == BC_LEX_EOF) #define BC_PARSE_BLOCK_STMT(f) \ ((f) & (BC_PARSE_FLAG_ELSE | BC_PARSE_FLAG_LOOP_INNER)) #define BC_PARSE_OP(p, l) (((p) & ~(BC_LEX_CHAR_MSB(1))) | (BC_LEX_CHAR_MSB(l))) #define BC_PARSE_OP_DATA(t) bc_parse_ops[((t) - BC_LEX_OP_INC)] #define BC_PARSE_OP_LEFT(op) (BC_PARSE_OP_DATA(op) & BC_LEX_CHAR_MSB(1)) #define BC_PARSE_OP_PREC(op) (BC_PARSE_OP_DATA(op) & ~(BC_LEX_CHAR_MSB(1))) #define BC_PARSE_EXPR_ENTRY(e1, e2, e3, e4, e5, e6, e7, e8) \ ((UINTMAX_C(e1) << 7) | (UINTMAX_C(e2) << 6) | (UINTMAX_C(e3) << 5) | \ (UINTMAX_C(e4) << 4) | (UINTMAX_C(e5) << 3) | (UINTMAX_C(e6) << 2) | \ (UINTMAX_C(e7) << 1) | (UINTMAX_C(e8) << 0)) #define BC_PARSE_EXPR(i) \ (bc_parse_exprs[(((i) & (uchar) ~(0x07)) >> 3)] & (1 << (7 - ((i) & 0x07)))) #define BC_PARSE_TOP_OP(p) (*((BcLexType*) bc_vec_top(&(p)->ops))) #define BC_PARSE_LEAF(prev, bin_last, rparen) \ (!(bin_last) && ((rparen) || bc_parse_inst_isLeaf(prev))) #define BC_PARSE_INST_VAR(t) \ ((t) >= BC_INST_VAR && (t) <= BC_INST_SCALE && (t) != BC_INST_ARRAY) #define BC_PARSE_PREV_PREFIX(p) \ ((p) >= BC_INST_INC_PRE && (p) <= BC_INST_BOOL_NOT) #define BC_PARSE_OP_PREFIX(t) ((t) == BC_LEX_OP_BOOL_NOT || (t) == BC_LEX_NEG) // 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) ((uchar) ((t) - BC_LEX_NEG + BC_INST_NEG)) BcStatus bc_parse_expr(BcParse *p, uint8_t flags); BcStatus bc_parse_parse(BcParse *p); BcStatus bc_parse_expr_status(BcParse *p, uint8_t flags, BcParseNext next); // This is necessary to clear up for if statements at the end of files. void bc_parse_noElse(BcParse *p); #if BC_ENABLE_SIGNALS extern const char bc_sig_msg[]; #endif // BC_ENABLE_SIGNALS extern const char* const bc_parse_const1; extern const uint8_t bc_parse_exprs[]; extern const uchar bc_parse_ops[]; extern const BcParseNext bc_parse_next_expr; extern const BcParseNext bc_parse_next_param; extern const BcParseNext bc_parse_next_print; extern const BcParseNext bc_parse_next_rel; extern const BcParseNext bc_parse_next_elem; extern const BcParseNext bc_parse_next_for; extern const BcParseNext bc_parse_next_read; #endif // BC_ENABLED #endif // BC_BC_H #ifndef BC_DC_H #define BC_DC_H #if DC_ENABLED int dc_main(int argc, char **argv); extern const char dc_help[]; BcStatus dc_lex_token(BcLex *l); bool dc_lex_negCommand(BcLex *l); #if BC_ENABLE_SIGNALS extern const char dc_sig_msg[]; #endif // BC_ENABLE_SIGNALS extern const uint8_t dc_lex_regs[]; extern const size_t dc_lex_regs_len; extern const uint8_t dc_lex_tokens[]; extern const uint8_t dc_parse_insts[]; BcStatus dc_parse_parse(BcParse *p); BcStatus dc_parse_expr(BcParse *p, uint8_t flags); #endif // DC_ENABLED #endif // BC_DC_H #if BC_ENABLED const char bc_help[] = { 117,115,97,103,101,58,32,37,115,32,91,111,112,116,105,111,110,115,93,32,91, 102,105,108,101,46,46,46,93,10,10,98,99,32,105,115,32,97,32,99,111,109,109, 97,110,100,45,108,105,110,101,44,32,97,114,98,105,116,114,97,114,121,45,112, 114,101,99,105,115,105,111,110,32,99,97,108,99,117,108,97,116,111,114,32,119, 105,116,104,32,97,32,84,117,114,105,110,103,45,99,111,109,112,108,101,116,101, 10,108,97,110,103,117,97,103,101,46,32,70,111,114,32,100,101,116,97,105,108, 115,44,32,117,115,101,32,96,109,97,110,32,37,115,96,46,10,10,84,104,105,115, 32,98,99,32,105,115,32,99,111,109,112,97,116,105,98,108,101,32,119,105,116, 104,32,98,111,116,104,32,116,104,101,32,71,78,85,32,98,99,32,97,110,100,32, 116,104,101,32,80,79,83,73,88,32,98,99,32,115,112,101,99,46,32,83,101,101,32, 116,104,101,32,71,78,85,32,98,99,10,109,97,110,117,97,108,32,40,104,116,116, 112,115,58,47,47,119,119,119,46,103,110,117,46,111,114,103,47,115,111,102,116, 119,97,114,101,47,98,99,47,109,97,110,117,97,108,47,98,99,46,104,116,109,108, 41,32,97,110,100,32,98,99,32,115,112,101,99,10,40,104,116,116,112,58,47,47, 112,117,98,115,46,111,112,101,110,103,114,111,117,112,46,111,114,103,47,111, 110,108,105,110,101,112,117,98,115,47,57,54,57,57,57,49,57,55,57,57,47,117, 116,105,108,105,116,105,101,115,47,98,99,46,104,116,109,108,41,10,102,111,114, 32,100,101,116,97,105,108,115,46,10,10,84,104,105,115,32,98,99,32,104,97,115, 32,116,104,114,101,101,32,100,105,102,102,101,114,101,110,99,101,115,32,116, 111,32,116,104,101,32,71,78,85,32,98,99,58,10,10,32,32,49,41,32,65,114,114, 97,121,115,32,99,97,110,32,98,101,32,112,97,115,115,101,100,32,116,111,32,116, 104,101,32,98,117,105,108,116,105,110,32,34,108,101,110,103,116,104,34,32,102, 117,110,99,116,105,111,110,32,116,111,32,103,101,116,32,116,104,101,32,110, 117,109,98,101,114,32,111,102,10,32,32,32,32,32,101,108,101,109,101,110,116, 115,32,99,117,114,114,101,110,116,108,121,32,105,110,32,116,104,101,32,97,114, 114,97,121,46,32,84,104,101,32,102,111,108,108,111,119,105,110,103,32,101,120, 97,109,112,108,101,32,112,114,105,110,116,115,32,34,49,34,58,10,10,32,32,32, 32,32,32,32,97,91,48,93,32,61,32,48,10,32,32,32,32,32,32,32,108,101,110,103, 116,104,40,97,91,93,41,10,10,32,32,50,41,32,84,104,101,32,112,114,101,99,101, 100,101,110,99,101,32,111,102,32,116,104,101,32,98,111,111,108,101,97,110,32, 34,110,111,116,34,32,111,112,101,114,97,116,111,114,32,40,33,41,32,105,115, 32,101,113,117,97,108,32,116,111,32,116,104,97,116,32,111,102,32,116,104,101, 10,32,32,32,32,32,117,110,97,114,121,32,109,105,110,117,115,32,40,45,41,44, 32,111,114,32,110,101,103,97,116,105,111,110,44,32,111,112,101,114,97,116,111, 114,46,32,84,104,105,115,32,115,116,105,108,108,32,97,108,108,111,119,115,32, 80,79,83,73,88,45,99,111,109,112,108,105,97,110,116,10,32,32,32,32,32,115,99, 114,105,112,116,115,32,116,111,32,119,111,114,107,32,119,104,105,108,101,32, 115,111,109,101,119,104,97,116,32,112,114,101,115,101,114,118,105,110,103,32, 101,120,112,101,99,116,101,100,32,98,101,104,97,118,105,111,114,32,40,118,101, 114,115,117,115,32,67,41,32,97,110,100,10,32,32,32,32,32,109,97,107,105,110, 103,32,112,97,114,115,105,110,103,32,101,97,115,105,101,114,46,10,32,32,51, 41,32,84,104,105,115,32,98,99,32,104,97,115,32,109,97,110,121,32,109,111,114, 101,32,101,120,116,101,110,115,105,111,110,115,32,116,104,97,110,32,116,104, 101,32,71,78,85,32,98,99,32,100,111,101,115,46,32,70,111,114,32,100,101,116, 97,105,108,115,44,32,115,101,101,32,116,104,101,10,32,32,32,32,32,109,97,110, 32,112,97,103,101,46,10,10,79,112,116,105,111,110,115,58,10,10,32,32,45,101, 32,32,101,120,112,114,32,32,45,45,101,120,112,114,101,115,115,105,111,110,61, 101,120,112,114,10,10,32,32,32,32,32,32,82,117,110,32,34,101,120,112,114,34, 32,97,110,100,32,113,117,105,116,46,32,73,102,32,109,117,108,116,105,112,108, 101,32,101,120,112,114,101,115,115,105,111,110,115,32,111,114,32,102,105,108, 101,115,32,40,115,101,101,32,98,101,108,111,119,41,32,97,114,101,10,32,32,32, 32,32,32,103,105,118,101,110,44,32,116,104,101,121,32,97,114,101,32,97,108, 108,32,114,117,110,32,98,101,102,111,114,101,32,101,120,101,99,117,116,105, 110,103,32,102,114,111,109,32,115,116,100,105,110,46,10,10,32,32,45,102,32, 32,102,105,108,101,32,32,45,45,102,105,108,101,61,102,105,108,101,10,10,32, 32,32,32,32,32,82,117,110,32,116,104,101,32,98,99,32,99,111,100,101,32,105, 110,32,34,102,105,108,101,34,32,97,110,100,32,101,120,105,116,46,32,83,101, 101,32,97,98,111,118,101,32,97,115,32,119,101,108,108,46,10,10,32,32,45,103, 32,32,45,45,103,108,111,98,97,108,45,115,116,97,99,107,115,10,10,32,32,32,32, 32,32,84,117,114,110,32,115,99,97,108,101,44,32,105,98,97,115,101,44,32,97, 110,100,32,111,98,97,115,101,32,105,110,116,111,32,115,116,97,99,107,115,46, 32,84,104,105,115,32,109,97,107,101,115,32,116,104,101,32,118,97,108,117,101, 32,111,102,32,101,97,99,104,32,98,101,10,32,32,32,32,32,32,98,101,32,114,101, 115,116,111,114,101,100,32,111,110,32,114,101,116,117,114,110,105,110,103,32, 102,114,111,109,32,102,117,110,99,116,105,111,110,115,46,32,83,101,101,32,116, 104,101,32,109,97,110,32,112,97,103,101,32,102,111,114,32,109,111,114,101,10, 32,32,32,32,32,32,100,101,116,97,105,108,115,46,10,10,32,32,45,104,32,32,45, 45,104,101,108,112,10,10,32,32,32,32,32,32,80,114,105,110,116,32,116,104,105, 115,32,117,115,97,103,101,32,109,101,115,115,97,103,101,32,97,110,100,32,101, 120,105,116,46,10,10,32,32,45,105,32,32,45,45,105,110,116,101,114,97,99,116, 105,118,101,10,10,32,32,32,32,32,32,70,111,114,99,101,32,105,110,116,101,114, 97,99,116,105,118,101,32,109,111,100,101,46,10,10,32,32,45,108,32,32,45,45, 109,97,116,104,108,105,98,10,10,32,32,32,32,32,32,85,115,101,32,112,114,101, 100,101,102,105,110,101,100,32,109,97,116,104,32,114,111,117,116,105,110,101, 115,58,10,10,32,32,32,32,32,32,32,32,32,32,115,40,101,120,112,114,41,32,32, 61,32,32,115,105,110,101,32,111,102,32,101,120,112,114,32,105,110,32,114,97, 100,105,97,110,115,10,32,32,32,32,32,32,32,32,32,32,99,40,101,120,112,114,41, 32,32,61,32,32,99,111,115,105,110,101,32,111,102,32,101,120,112,114,32,105, 110,32,114,97,100,105,97,110,115,10,32,32,32,32,32,32,32,32,32,32,97,40,101, 120,112,114,41,32,32,61,32,32,97,114,99,116,97,110,103,101,110,116,32,111,102, 32,101,120,112,114,44,32,114,101,116,117,114,110,105,110,103,32,114,97,100, 105,97,110,115,10,32,32,32,32,32,32,32,32,32,32,108,40,101,120,112,114,41,32, 32,61,32,32,110,97,116,117,114,97,108,32,108,111,103,32,111,102,32,101,120, 112,114,10,32,32,32,32,32,32,32,32,32,32,101,40,101,120,112,114,41,32,32,61, 32,32,114,97,105,115,101,115,32,101,32,116,111,32,116,104,101,32,112,111,119, 101,114,32,111,102,32,101,120,112,114,10,32,32,32,32,32,32,32,32,32,32,106, 40,110,44,32,120,41,32,32,61,32,32,66,101,115,115,101,108,32,102,117,110,99, 116,105,111,110,32,111,102,32,105,110,116,101,103,101,114,32,111,114,100,101, 114,32,110,32,111,102,32,120,10,10,32,32,45,80,32,32,45,45,110,111,45,112,114, 111,109,112,116,10,10,32,32,32,32,32,32,68,105,115,97,98,108,101,32,116,104, 101,32,112,114,111,109,112,116,32,105,110,32,105,110,116,101,114,97,99,116, 105,118,101,32,109,111,100,101,46,10,10,32,32,45,113,32,32,45,45,113,117,105, 101,116,10,10,32,32,32,32,32,32,68,111,110,39,116,32,112,114,105,110,116,32, 118,101,114,115,105,111,110,32,97,110,100,32,99,111,112,121,114,105,103,104, 116,46,10,10,32,32,45,115,32,32,45,45,115,116,97,110,100,97,114,100,10,10,32, 32,32,32,32,32,69,114,114,111,114,32,105,102,32,97,110,121,32,110,111,110,45, 80,79,83,73,88,32,101,120,116,101,110,115,105,111,110,115,32,97,114,101,32, 117,115,101,100,46,10,10,32,32,45,119,32,32,45,45,119,97,114,110,10,10,32,32, 32,32,32,32,87,97,114,110,32,105,102,32,97,110,121,32,110,111,110,45,80,79, 83,73,88,32,101,120,116,101,110,115,105,111,110,115,32,97,114,101,32,117,115, 101,100,46,10,10,32,32,45,118,32,32,45,45,118,101,114,115,105,111,110,10,10, 32,32,32,32,32,32,80,114,105,110,116,32,118,101,114,115,105,111,110,32,105, 110,102,111,114,109,97,116,105,111,110,32,97,110,100,32,99,111,112,121,114, 105,103,104,116,32,97,110,100,32,101,120,105,116,46,10,0 }; #endif // BC_ENABLED #if DC_ENABLED const char dc_help[] = { 117,115,97,103,101,58,32,37,115,32,91,111,112,116,105,111,110,115,93,32,91, 102,105,108,101,46,46,46,93,10,10,100,99,32,105,115,32,97,32,114,101,118,101, 114,115,101,45,112,111,108,105,115,104,32,110,111,116,97,116,105,111,110,32, 99,111,109,109,97,110,100,45,108,105,110,101,32,99,97,108,99,117,108,97,116, 111,114,32,119,104,105,99,104,32,115,117,112,112,111,114,116,115,32,117,110, 108,105,109,105,116,101,100,10,112,114,101,99,105,115,105,111,110,32,97,114, 105,116,104,109,101,116,105,99,46,32,70,111,114,32,100,101,116,97,105,108,115, 44,32,117,115,101,32,96,109,97,110,32,37,115,96,46,10,10,84,104,105,115,32, 100,99,32,105,115,32,40,109,111,115,116,108,121,41,32,99,111,109,112,97,116, 105,98,108,101,32,119,105,116,104,32,116,104,101,32,70,114,101,101,66,83,68, 32,100,99,32,97,110,100,32,116,104,101,32,71,78,85,32,100,99,46,32,83,101,101, 32,116,104,101,10,70,114,101,101,66,83,68,32,109,97,110,32,112,97,103,101,32, 40,104,116,116,112,115,58,47,47,119,119,119,46,117,110,105,120,46,99,111,109, 47,109,97,110,45,112,97,103,101,47,70,114,101,101,66,83,68,47,49,47,100,99, 47,41,32,97,110,100,32,116,104,101,32,71,78,85,32,100,99,10,109,97,110,117, 97,108,32,40,104,116,116,112,115,58,47,47,119,119,119,46,103,110,117,46,111, 114,103,47,115,111,102,116,119,97,114,101,47,98,99,47,109,97,110,117,97,108, 47,100,99,45,49,46,48,53,47,104,116,109,108,95,109,111,110,111,47,100,99,46, 104,116,109,108,41,32,102,111,114,10,100,101,116,97,105,108,115,46,10,10,84, 104,105,115,32,100,99,32,104,97,115,32,97,32,102,101,119,32,100,105,102,102, 101,114,101,110,99,101,115,32,102,114,111,109,32,116,104,101,32,116,119,111, 32,97,98,111,118,101,58,10,10,32,32,49,41,32,87,104,101,110,32,112,114,105, 110,116,105,110,103,32,97,32,98,121,116,101,32,115,116,114,101,97,109,32,40, 99,111,109,109,97,110,100,32,34,80,34,41,44,32,116,104,105,115,32,98,99,32, 102,111,108,108,111,119,115,32,119,104,97,116,32,116,104,101,32,70,114,101, 101,66,83,68,10,32,32,32,32,32,100,99,32,100,111,101,115,46,10,32,32,50,41, 32,84,104,105,115,32,100,99,32,105,109,112,108,101,109,101,110,116,115,32,116, 104,101,32,71,78,85,32,101,120,116,101,110,115,105,111,110,115,32,102,111,114, 32,100,105,118,109,111,100,32,40,34,126,34,41,32,97,110,100,32,109,111,100, 117,108,97,114,10,32,32,32,32,32,101,120,112,111,110,101,110,116,105,97,116, 105,111,110,32,40,34,124,34,41,46,10,32,32,51,41,32,84,104,105,115,32,100,99, 32,105,109,112,108,101,109,101,110,116,115,32,97,108,108,32,70,114,101,101, 66,83,68,32,101,120,116,101,110,115,105,111,110,115,44,32,101,120,99,101,112, 116,32,102,111,114,32,34,74,34,32,97,110,100,32,34,77,34,46,10,32,32,52,41, 32,84,104,105,115,32,100,99,32,100,111,101,115,32,110,111,116,32,105,109,112, 108,101,109,101,110,116,32,116,104,101,32,114,117,110,32,99,111,109,109,97, 110,100,32,40,34,33,34,41,44,32,102,111,114,32,115,101,99,117,114,105,116,121, 32,114,101,97,115,111,110,115,46,10,32,32,53,41,32,76,105,107,101,32,116,104, 101,32,70,114,101,101,66,83,68,32,100,99,44,32,116,104,105,115,32,100,99,32, 115,117,112,112,111,114,116,115,32,101,120,116,101,110,100,101,100,32,114,101, 103,105,115,116,101,114,115,46,32,72,111,119,101,118,101,114,44,32,116,104, 101,121,32,97,114,101,10,32,32,32,32,32,105,109,112,108,101,109,101,110,116, 101,100,32,100,105,102,102,101,114,101,110,116,108,121,46,32,87,104,101,110, 32,105,116,32,101,110,99,111,117,110,116,101,114,115,32,119,104,105,116,101, 115,112,97,99,101,32,119,104,101,114,101,32,97,32,114,101,103,105,115,116,101, 114,10,32,32,32,32,32,115,104,111,117,108,100,32,98,101,44,32,105,116,32,115, 107,105,112,115,32,116,104,101,32,119,104,105,116,101,115,112,97,99,101,46, 32,73,102,32,116,104,101,32,99,104,97,114,97,99,116,101,114,32,102,111,108, 108,111,119,105,110,103,32,105,115,32,110,111,116,10,32,32,32,32,32,97,32,108, 111,119,101,114,99,97,115,101,32,108,101,116,116,101,114,44,32,97,110,32,101, 114,114,111,114,32,105,115,32,105,115,115,117,101,100,46,32,79,116,104,101, 114,119,105,115,101,44,32,116,104,101,32,114,101,103,105,115,116,101,114,32, 110,97,109,101,32,105,115,10,32,32,32,32,32,112,97,114,115,101,100,32,98,121, 32,116,104,101,32,102,111,108,108,111,119,105,110,103,32,114,101,103,101,120, 58,10,10,32,32,32,32,32,32,32,91,97,45,122,93,91,97,45,122,48,45,57,95,93,42, 10,10,32,32,32,32,32,84,104,105,115,32,103,101,110,101,114,97,108,108,121,32, 109,101,97,110,115,32,116,104,97,116,32,114,101,103,105,115,116,101,114,32, 110,97,109,101,115,32,119,105,108,108,32,98,101,32,115,117,114,114,111,117, 110,100,101,100,32,98,121,32,119,104,105,116,101,115,112,97,99,101,46,10,10, 32,32,32,32,32,69,120,97,109,112,108,101,115,58,10,10,32,32,32,32,32,32,32, 108,32,105,100,120,32,115,32,116,101,109,112,32,76,32,105,110,100,101,120,32, 83,32,116,101,109,112,50,32,60,32,100,111,95,116,104,105,110,103,10,10,32,32, 32,32,32,65,108,115,111,32,110,111,116,101,32,116,104,97,116,44,32,117,110, 108,105,107,101,32,116,104,101,32,70,114,101,101,66,83,68,32,100,99,44,32,101, 120,116,101,110,100,101,100,32,114,101,103,105,115,116,101,114,115,32,97,114, 101,32,110,111,116,32,101,118,101,110,10,32,32,32,32,32,112,97,114,115,101, 100,32,117,110,108,101,115,115,32,116,104,101,32,34,45,120,34,32,111,112,116, 105,111,110,32,105,115,32,103,105,118,101,110,46,32,73,110,115,116,101,97,100, 44,32,116,104,101,32,115,112,97,99,101,32,97,102,116,101,114,32,97,32,99,111, 109,109,97,110,100,10,32,32,32,32,32,116,104,97,116,32,114,101,113,117,105, 114,101,115,32,97,32,114,101,103,105,115,116,101,114,32,110,97,109,101,32,105, 115,32,116,97,107,101,110,32,97,115,32,116,104,101,32,114,101,103,105,115,116, 101,114,32,110,97,109,101,46,10,10,79,112,116,105,111,110,115,58,10,10,32,32, 45,101,32,32,101,120,112,114,32,32,45,45,101,120,112,114,101,115,115,105,111, 110,61,101,120,112,114,10,10,32,32,32,32,32,32,82,117,110,32,34,101,120,112, 114,34,32,97,110,100,32,113,117,105,116,46,32,73,102,32,109,117,108,116,105, 112,108,101,32,101,120,112,114,101,115,115,105,111,110,115,32,111,114,32,102, 105,108,101,115,32,40,115,101,101,32,98,101,108,111,119,41,32,97,114,101,10, 32,32,32,32,32,32,103,105,118,101,110,44,32,116,104,101,121,32,97,114,101,32, 97,108,108,32,114,117,110,46,32,65,102,116,101,114,32,114,117,110,110,105,110, 103,44,32,100,99,32,119,105,108,108,32,101,120,105,116,46,10,10,32,32,45,102, 32,32,102,105,108,101,32,32,45,45,102,105,108,101,61,102,105,108,101,10,10, 32,32,32,32,32,32,82,117,110,32,116,104,101,32,100,99,32,99,111,100,101,32, 105,110,32,34,102,105,108,101,34,32,97,110,100,32,101,120,105,116,46,32,83, 101,101,32,97,98,111,118,101,46,10,10,32,32,45,104,32,32,45,45,104,101,108, 112,10,10,32,32,32,32,32,32,80,114,105,110,116,32,116,104,105,115,32,117,115, 97,103,101,32,109,101,115,115,97,103,101,32,97,110,100,32,101,120,105,116,46, 10,10,32,32,45,105,32,32,45,45,105,110,116,101,114,97,99,116,105,118,101,10, 10,32,32,32,32,32,32,80,117,116,32,100,99,32,105,110,116,111,32,105,110,116, 101,114,97,99,116,105,118,101,32,109,111,100,101,46,32,83,101,101,32,116,104, 101,32,109,97,110,32,112,97,103,101,32,102,111,114,32,109,111,114,101,32,100, 101,116,97,105,108,115,46,10,10,32,32,45,80,32,32,45,45,110,111,45,112,114, 111,109,112,116,10,10,32,32,32,32,32,32,68,105,115,97,98,108,101,32,116,104, 101,32,112,114,111,109,112,116,32,105,110,32,105,110,116,101,114,97,99,116, 105,118,101,32,109,111,100,101,46,10,10,32,32,45,86,32,32,45,45,118,101,114, 115,105,111,110,10,10,32,32,32,32,32,32,80,114,105,110,116,32,118,101,114,115, 105,111,110,32,97,110,100,32,99,111,112,121,114,105,103,104,116,32,97,110,100, 32,101,120,105,116,46,10,10,32,32,45,120,32,32,45,45,101,120,116,101,110,100, 101,100,45,114,101,103,105,115,116,101,114,10,10,32,32,32,32,32,32,69,110,97, 98,108,101,32,101,120,116,101,110,100,101,100,32,114,101,103,105,115,116,101, 114,32,109,111,100,101,46,10,0 }; #endif // DC_ENABLED #if BC_ENABLED const char *bc_lib_name = "gen/lib.bc"; const char bc_lib[] = { 115,99,97,108,101,61,50,48,10,100,101,102,105,110,101,32,101,40,120,41,123, 10,97,117,116,111,32,98,44,115,44,110,44,114,44,100,44,105,44,112,44,102,44, 118,10,98,61,105,98,97,115,101,10,105,98,97,115,101,61,65,10,105,102,40,120, 60,48,41,123,10,110,61,49,10,120,61,45,120,10,125,10,115,61,115,99,97,108,101, 10,114,61,54,43,115,43,46,52,52,42,120,10,115,99,97,108,101,61,115,99,97,108, 101,40,120,41,43,49,10,119,104,105,108,101,40,120,62,49,41,123,10,100,43,61, 49,10,120,47,61,50,10,115,99,97,108,101,43,61,49,10,125,10,115,99,97,108,101, 61,114,10,114,61,120,43,49,10,112,61,120,10,102,61,118,61,49,10,102,111,114, 40,105,61,50,59,118,59,43,43,105,41,123,10,112,42,61,120,10,102,42,61,105,10, 118,61,112,47,102,10,114,43,61,118,10,125,10,119,104,105,108,101,40,100,45, 45,41,114,42,61,114,10,115,99,97,108,101,61,115,10,105,98,97,115,101,61,98, 10,105,102,40,110,41,114,101,116,117,114,110,40,49,47,114,41,10,114,101,116, 117,114,110,40,114,47,49,41,10,125,10,100,101,102,105,110,101,32,108,40,120, 41,123,10,97,117,116,111,32,98,44,115,44,114,44,112,44,97,44,113,44,105,44, 118,10,105,102,40,120,60,61,48,41,114,101,116,117,114,110,40,40,49,45,65,94, 115,99,97,108,101,41,47,49,41,10,98,61,105,98,97,115,101,10,105,98,97,115,101, 61,65,10,115,61,115,99,97,108,101,10,115,99,97,108,101,43,61,54,10,112,61,50, 10,119,104,105,108,101,40,120,62,61,50,41,123,10,112,42,61,50,10,120,61,115, 113,114,116,40,120,41,10,125,10,119,104,105,108,101,40,120,60,61,46,53,41,123, 10,112,42,61,50,10,120,61,115,113,114,116,40,120,41,10,125,10,114,61,97,61, 40,120,45,49,41,47,40,120,43,49,41,10,113,61,97,42,97,10,118,61,49,10,102,111, 114,40,105,61,51,59,118,59,105,43,61,50,41,123,10,97,42,61,113,10,118,61,97, 47,105,10,114,43,61,118,10,125,10,114,42,61,112,10,115,99,97,108,101,61,115, 10,105,98,97,115,101,61,98,10,114,101,116,117,114,110,40,114,47,49,41,10,125, 10,100,101,102,105,110,101,32,115,40,120,41,123,10,97,117,116,111,32,98,44, 115,44,114,44,97,44,113,44,105,10,105,102,40,120,60,48,41,114,101,116,117,114, 110,40,45,115,40,45,120,41,41,10,98,61,105,98,97,115,101,10,105,98,97,115,101, 61,65,10,115,61,115,99,97,108,101,10,115,99,97,108,101,61,49,46,49,42,115,43, 50,10,97,61,97,40,49,41,10,115,99,97,108,101,61,48,10,113,61,40,120,47,97,43, 50,41,47,52,10,120,45,61,52,42,113,42,97,10,105,102,40,113,37,50,41,120,61, 45,120,10,115,99,97,108,101,61,115,43,50,10,114,61,97,61,120,10,113,61,45,120, 42,120,10,102,111,114,40,105,61,51,59,97,59,105,43,61,50,41,123,10,97,42,61, 113,47,40,105,42,40,105,45,49,41,41,10,114,43,61,97,10,125,10,115,99,97,108, 101,61,115,10,105,98,97,115,101,61,98,10,114,101,116,117,114,110,40,114,47, 49,41,10,125,10,100,101,102,105,110,101,32,99,40,120,41,123,10,97,117,116,111, 32,98,44,115,10,98,61,105,98,97,115,101,10,105,98,97,115,101,61,65,10,115,61, 115,99,97,108,101,10,115,99,97,108,101,42,61,49,46,50,10,120,61,115,40,50,42, 97,40,49,41,43,120,41,10,115,99,97,108,101,61,115,10,105,98,97,115,101,61,98, 10,114,101,116,117,114,110,40,120,47,49,41,10,125,10,100,101,102,105,110,101, 32,97,40,120,41,123,10,97,117,116,111,32,98,44,115,44,114,44,110,44,97,44,109, 44,116,44,102,44,105,44,117,10,98,61,105,98,97,115,101,10,105,98,97,115,101, 61,65,10,110,61,49,10,105,102,40,120,60,48,41,123,10,110,61,45,49,10,120,61, 45,120,10,125,10,105,102,40,115,99,97,108,101,60,54,53,41,123,10,105,102,40, 120,61,61,49,41,123,10,114,61,46,55,56,53,51,57,56,49,54,51,51,57,55,52,52, 56,51,48,57,54,49,53,54,54,48,56,52,53,56,49,57,56,55,53,55,50,49,48,52,57, 50,57,50,51,52,57,56,52,51,55,55,54,52,53,53,50,52,51,55,51,54,49,52,56,48, 47,110,10,105,98,97,115,101,61,98,10,114,101,116,117,114,110,40,114,41,10,125, 10,105,102,40,120,61,61,46,50,41,123,10,114,61,46,49,57,55,51,57,53,53,53,57, 56,52,57,56,56,48,55,53,56,51,55,48,48,52,57,55,54,53,49,57,52,55,57,48,50, 57,51,52,52,55,53,56,53,49,48,51,55,56,55,56,53,50,49,48,49,53,49,55,54,56, 56,57,52,48,50,47,110,10,105,98,97,115,101,61,98,10,114,101,116,117,114,110, 40,114,41,10,125,10,125,10,115,61,115,99,97,108,101,10,105,102,40,120,62,46, 50,41,123,10,115,99,97,108,101,43,61,53,10,97,61,97,40,46,50,41,10,125,10,115, 99,97,108,101,61,115,43,51,10,119,104,105,108,101,40,120,62,46,50,41,123,10, 109,43,61,49,10,120,61,40,120,45,46,50,41,47,40,49,43,46,50,42,120,41,10,125, 10,114,61,117,61,120,10,102,61,45,120,42,120,10,116,61,49,10,102,111,114,40, 105,61,51,59,116,59,105,43,61,50,41,123,10,117,42,61,102,10,116,61,117,47,105, 10,114,43,61,116,10,125,10,115,99,97,108,101,61,115,10,105,98,97,115,101,61, 98,10,114,101,116,117,114,110,40,40,109,42,97,43,114,41,47,110,41,10,125,10, 100,101,102,105,110,101,32,106,40,110,44,120,41,123,10,97,117,116,111,32,98, 44,115,44,111,44,97,44,105,44,118,44,102,10,98,61,105,98,97,115,101,10,105, 98,97,115,101,61,65,10,115,61,115,99,97,108,101,10,115,99,97,108,101,61,48, 10,110,47,61,49,10,105,102,40,110,60,48,41,123,10,110,61,45,110,10,111,61,110, 37,50,10,125,10,97,61,49,10,102,111,114,40,105,61,50,59,105,60,61,110,59,43, 43,105,41,97,42,61,105,10,115,99,97,108,101,61,49,46,53,42,115,10,97,61,40, 120,94,110,41,47,50,94,110,47,97,10,114,61,118,61,49,10,102,61,45,120,42,120, 47,52,10,115,99,97,108,101,43,61,108,101,110,103,116,104,40,97,41,45,115,99, 97,108,101,40,97,41,10,102,111,114,40,105,61,49,59,118,59,43,43,105,41,123, 10,118,61,118,42,102,47,105,47,40,110,43,105,41,10,114,43,61,118,10,125,10, 115,99,97,108,101,61,115,10,105,98,97,115,101,61,98,10,105,102,40,111,41,97, 61,45,97,10,114,101,116,117,114,110,40,97,42,114,47,49,41,10,125,10,0 }; #endif // BC_ENABLED #if BC_ENABLED && BC_ENABLE_EXTRA_MATH const char *bc_lib2_name = "gen/lib2.bc"; const char bc_lib2[] = { 100,101,102,105,110,101,32,114,40,120,44,112,41,123,10,97,117,116,111,32,116, 44,110,10,105,102,40,120,61,61,48,41,114,101,116,117,114,110,32,120,10,112, 61,97,98,115,40,112,41,36,10,110,61,40,120,60,48,41,10,120,61,97,98,115,40, 120,41,10,116,61,120,64,112,10,105,102,40,112,60,115,99,97,108,101,40,120,41, 38,38,120,45,116,62,61,53,62,62,112,43,49,41,116,43,61,49,62,62,112,10,105, 102,40,110,41,116,61,45,116,10,114,101,116,117,114,110,32,116,10,125,10,100, 101,102,105,110,101,32,99,101,105,108,40,120,44,112,41,123,10,97,117,116,111, 32,116,44,110,10,105,102,40,120,61,61,48,41,114,101,116,117,114,110,32,120, 10,112,61,97,98,115,40,112,41,36,10,110,61,40,120,60,48,41,10,120,61,97,98, 115,40,120,41,10,116,61,40,120,43,40,57,62,62,112,43,49,41,41,64,112,10,105, 102,40,110,41,116,61,45,116,10,114,101,116,117,114,110,32,116,10,125,10,100, 101,102,105,110,101,32,102,40,110,41,123,10,97,117,116,111,32,114,10,110,61, 97,98,115,40,110,41,36,10,102,111,114,40,114,61,49,59,110,62,49,59,45,45,110, 41,114,42,61,110,10,114,101,116,117,114,110,32,114,10,125,10,100,101,102,105, 110,101,32,112,101,114,109,40,110,44,107,41,123,10,97,117,116,111,32,102,44, 103,44,115,10,105,102,40,107,62,110,41,114,101,116,117,114,110,32,48,10,110, 61,97,98,115,40,110,41,36,10,107,61,97,98,115,40,107,41,36,10,102,61,102,40, 110,41,10,103,61,102,40,110,45,107,41,10,115,61,115,99,97,108,101,10,115,99, 97,108,101,61,48,10,102,47,61,103,10,115,99,97,108,101,61,115,10,114,101,116, 117,114,110,32,102,10,125,10,100,101,102,105,110,101,32,99,111,109,98,40,110, 44,114,41,123,10,97,117,116,111,32,115,44,102,44,103,44,104,10,105,102,40,114, 62,110,41,114,101,116,117,114,110,32,48,10,110,61,97,98,115,40,110,41,36,10, 114,61,97,98,115,40,114,41,36,10,115,61,115,99,97,108,101,10,115,99,97,108, 101,61,48,10,102,61,102,40,110,41,10,104,61,102,40,114,41,10,103,61,102,40, 110,45,114,41,10,102,47,61,104,42,103,10,115,99,97,108,101,61,115,10,114,101, 116,117,114,110,32,102,10,125,10,100,101,102,105,110,101,32,108,111,103,40, 120,44,98,41,123,10,97,117,116,111,32,112,44,115,10,115,61,115,99,97,108,101, 10,105,102,40,115,99,97,108,101,60,75,41,115,99,97,108,101,61,75,10,105,102, 40,115,99,97,108,101,40,120,41,62,115,99,97,108,101,41,115,99,97,108,101,61, 115,99,97,108,101,40,120,41,10,115,99,97,108,101,42,61,50,10,112,61,108,40, 120,41,47,108,40,98,41,10,115,99,97,108,101,61,115,10,114,101,116,117,114,110, 32,112,10,125,10,100,101,102,105,110,101,32,108,50,40,120,41,123,114,101,116, 117,114,110,32,108,111,103,40,120,44,50,41,125,10,100,101,102,105,110,101,32, 108,49,48,40,120,41,123,114,101,116,117,114,110,32,108,111,103,40,120,44,65, 41,125,10,100,101,102,105,110,101,32,112,105,40,115,41,123,10,97,117,116,111, 32,116,44,118,10,105,102,40,115,61,61,48,41,114,101,116,117,114,110,32,51,10, 115,61,97,98,115,40,115,41,36,10,116,61,115,99,97,108,101,10,115,99,97,108, 101,61,115,43,49,10,118,61,52,42,97,40,49,41,10,115,99,97,108,101,61,116,10, 114,101,116,117,114,110,32,118,64,115,10,125,10,100,101,102,105,110,101,32, 116,40,120,41,123,10,97,117,116,111,32,115,44,99,44,108,10,108,61,115,99,97, 108,101,10,115,99,97,108,101,43,61,50,10,115,61,115,40,120,41,10,99,61,99,40, 120,41,10,115,99,97,108,101,61,108,10,114,101,116,117,114,110,32,115,47,99, 10,125,10,100,101,102,105,110,101,32,97,50,40,121,44,120,41,123,10,97,117,116, 111,32,97,44,112,10,105,102,40,33,120,38,38,33,121,41,121,47,120,10,105,102, 40,120,60,61,48,41,123,10,112,61,112,105,40,115,99,97,108,101,43,50,41,10,105, 102,40,121,60,48,41,112,61,45,112,10,125,10,105,102,40,120,61,61,48,41,97,61, 112,47,50,10,101,108,115,101,123,10,115,99,97,108,101,43,61,50,10,97,61,97, 40,121,47,120,41,43,112,10,115,99,97,108,101,45,61,50,10,125,10,114,101,116, 117,114,110,32,97,64,115,99,97,108,101,10,125,10,100,101,102,105,110,101,32, 115,105,110,40,120,41,123,114,101,116,117,114,110,32,115,40,120,41,125,10,100, 101,102,105,110,101,32,99,111,115,40,120,41,123,114,101,116,117,114,110,32, 99,40,120,41,125,10,100,101,102,105,110,101,32,97,116,97,110,40,120,41,123, 114,101,116,117,114,110,32,97,40,120,41,125,10,100,101,102,105,110,101,32,116, 97,110,40,120,41,123,114,101,116,117,114,110,32,116,40,120,41,125,10,100,101, 102,105,110,101,32,97,116,97,110,50,40,121,44,120,41,123,114,101,116,117,114, 110,32,97,50,40,121,44,120,41,125,10,100,101,102,105,110,101,32,114,50,100, 40,120,41,123,10,97,117,116,111,32,114,44,105,44,115,10,115,61,115,99,97,108, 101,10,115,99,97,108,101,43,61,53,10,105,61,105,98,97,115,101,10,105,98,97, 115,101,61,65,10,114,61,120,42,49,56,48,47,112,105,40,115,99,97,108,101,41, 10,105,98,97,115,101,61,105,10,115,99,97,108,101,61,115,10,114,101,116,117, 114,110,32,114,64,115,10,125,10,100,101,102,105,110,101,32,100,50,114,40,120, 41,123,10,97,117,116,111,32,114,44,105,44,115,10,115,61,115,99,97,108,101,10, 115,99,97,108,101,43,61,53,10,105,61,105,98,97,115,101,10,105,98,97,115,101, 61,65,10,114,61,120,42,112,105,40,115,99,97,108,101,41,47,49,56,48,10,105,98, 97,115,101,61,105,10,115,99,97,108,101,61,115,10,114,101,116,117,114,110,32, 114,64,115,10,125,10,100,101,102,105,110,101,32,118,111,105,100,32,111,117, 116,112,117,116,40,120,44,98,41,123,10,97,117,116,111,32,99,10,99,61,111,98, 97,115,101,10,111,98,97,115,101,61,98,10,120,10,111,98,97,115,101,61,99,10, 125,10,100,101,102,105,110,101,32,118,111,105,100,32,104,101,120,40,120,41, 123,111,117,116,112,117,116,40,120,44,71,41,125,10,100,101,102,105,110,101, 32,118,111,105,100,32,98,105,110,97,114,121,40,120,41,123,111,117,116,112,117, 116,40,120,44,50,41,125,10,100,101,102,105,110,101,32,117,98,121,116,101,115, 40,120,41,123,10,97,117,116,111,32,112,44,98,44,105,10,98,61,105,98,97,115, 101,10,105,98,97,115,101,61,65,10,120,61,97,98,115,40,120,41,36,10,105,61,50, 94,56,10,102,111,114,40,112,61,49,59,105,45,49,60,120,59,112,42,61,50,41,123, 105,42,61,105,125,10,105,98,97,115,101,61,98,10,114,101,116,117,114,110,32, 112,10,125,10,100,101,102,105,110,101,32,115,98,121,116,101,115,40,120,41,123, 10,97,117,116,111,32,112,44,98,44,110,44,122,10,122,61,40,120,60,48,41,10,120, 61,97,98,115,40,120,41,10,120,61,120,36,10,110,61,117,98,121,116,101,115,40, 120,41,10,98,61,105,98,97,115,101,10,105,98,97,115,101,61,65,10,112,61,50,94, 40,110,42,56,45,49,41,10,105,102,40,120,62,112,124,124,40,33,122,38,38,120, 61,61,112,41,41,110,42,61,50,10,105,98,97,115,101,61,98,10,114,101,116,117, 114,110,32,110,10,125,10,100,101,102,105,110,101,32,118,111,105,100,32,111, 117,116,112,117,116,95,98,121,116,101,40,120,44,105,41,123,10,97,117,116,111, 32,106,44,112,44,121,44,98,10,106,61,105,98,97,115,101,10,105,98,97,115,101, 61,65,10,115,61,115,99,97,108,101,10,115,99,97,108,101,61,48,10,120,61,97,98, 115,40,120,41,36,10,98,61,120,47,40,50,94,40,105,42,56,41,41,10,98,37,61,50, 94,56,10,121,61,108,111,103,40,50,53,54,44,111,98,97,115,101,41,36,10,105,102, 40,98,62,49,41,112,61,108,111,103,40,98,44,111,98,97,115,101,41,36,43,49,10, 101,108,115,101,32,112,61,98,10,102,111,114,40,105,61,121,45,112,59,105,62, 48,59,45,45,105,41,112,114,105,110,116,32,48,10,105,102,40,98,41,112,114,105, 110,116,32,98,10,115,99,97,108,101,61,115,10,105,98,97,115,101,61,106,10,125, 10,100,101,102,105,110,101,32,118,111,105,100,32,111,117,116,112,117,116,95, 117,105,110,116,40,120,44,110,41,123,10,97,117,116,111,32,105,44,98,10,98,61, 105,98,97,115,101,10,105,98,97,115,101,61,65,10,102,111,114,40,105,61,110,45, 49,59,105,62,61,48,59,45,45,105,41,123,10,111,117,116,112,117,116,95,98,121, 116,101,40,120,44,105,41,10,105,102,40,105,41,112,114,105,110,116,34,32,34, 10,101,108,115,101,32,112,114,105,110,116,34,92,110,34,10,125,10,105,98,97, 115,101,61,98,10,125,10,100,101,102,105,110,101,32,118,111,105,100,32,104,101, 120,95,117,105,110,116,40,120,44,110,41,123,10,97,117,116,111,32,111,10,111, 61,111,98,97,115,101,10,111,98,97,115,101,61,71,10,111,117,116,112,117,116, 95,117,105,110,116,40,120,44,110,41,10,111,98,97,115,101,61,111,10,125,10,100, 101,102,105,110,101,32,118,111,105,100,32,98,105,110,97,114,121,95,117,105, 110,116,40,120,44,110,41,123,10,97,117,116,111,32,111,10,111,61,111,98,97,115, 101,10,111,98,97,115,101,61,50,10,111,117,116,112,117,116,95,117,105,110,116, 40,120,44,110,41,10,111,98,97,115,101,61,111,10,125,10,100,101,102,105,110, 101,32,118,111,105,100,32,117,105,110,116,110,40,120,44,110,41,123,10,105,102, 40,115,99,97,108,101,40,120,41,41,123,10,112,114,105,110,116,34,69,114,114, 111,114,58,32,34,44,120,44,34,32,105,115,32,110,111,116,32,97,110,32,105,110, 116,101,103,101,114,46,92,110,34,10,114,101,116,117,114,110,10,125,10,105,102, 40,120,60,48,41,123,10,112,114,105,110,116,34,69,114,114,111,114,58,32,34,44, 120,44,34,32,105,115,32,110,101,103,97,116,105,118,101,46,92,110,34,10,114, 101,116,117,114,110,10,125,10,105,102,40,120,62,61,50,94,40,110,42,56,41,41, 123,10,112,114,105,110,116,34,69,114,114,111,114,58,32,34,44,120,44,34,32,99, 97,110,110,111,116,32,102,105,116,32,105,110,116,111,32,34,44,110,44,34,32, 117,110,115,105,103,110,101,100,32,98,121,116,101,40,115,41,46,92,110,34,10, 114,101,116,117,114,110,10,125,10,98,105,110,97,114,121,95,117,105,110,116, 40,120,44,110,41,10,104,101,120,95,117,105,110,116,40,120,44,110,41,10,125, 10,100,101,102,105,110,101,32,118,111,105,100,32,105,110,116,110,40,120,44, 110,41,123,10,97,117,116,111,32,116,10,105,102,40,115,99,97,108,101,40,120, 41,41,123,10,112,114,105,110,116,34,69,114,114,111,114,58,32,34,44,120,44,34, 32,105,115,32,110,111,116,32,97,110,32,105,110,116,101,103,101,114,46,92,110, 34,10,114,101,116,117,114,110,10,125,10,116,61,50,94,40,110,42,56,45,49,41, 10,105,102,40,97,98,115,40,120,41,62,61,116,38,38,40,120,62,48,124,124,120, 33,61,45,116,41,41,123,10,112,114,105,110,116,32,34,69,114,114,111,114,58,32, 34,44,120,44,34,32,99,97,110,110,111,116,32,102,105,116,32,105,110,116,111, 32,34,44,110,44,34,32,115,105,103,110,101,100,32,98,121,116,101,40,115,41,46, 92,110,34,10,114,101,116,117,114,110,10,125,10,105,102,40,120,60,48,41,120, 61,50,94,40,110,42,56,41,45,40,45,120,41,10,98,105,110,97,114,121,95,117,105, 110,116,40,120,44,110,41,10,104,101,120,95,117,105,110,116,40,120,44,110,41, 10,125,10,100,101,102,105,110,101,32,118,111,105,100,32,117,105,110,116,56, 40,120,41,123,117,105,110,116,110,40,120,44,49,41,125,10,100,101,102,105,110, 101,32,118,111,105,100,32,105,110,116,56,40,120,41,123,105,110,116,110,40,120, 44,49,41,125,10,100,101,102,105,110,101,32,118,111,105,100,32,117,105,110,116, 49,54,40,120,41,123,117,105,110,116,110,40,120,44,50,41,125,10,100,101,102, 105,110,101,32,118,111,105,100,32,105,110,116,49,54,40,120,41,123,105,110,116, 110,40,120,44,50,41,125,10,100,101,102,105,110,101,32,118,111,105,100,32,117, 105,110,116,51,50,40,120,41,123,117,105,110,116,110,40,120,44,52,41,125,10, 100,101,102,105,110,101,32,118,111,105,100,32,105,110,116,51,50,40,120,41,123, 105,110,116,110,40,120,44,52,41,125,10,100,101,102,105,110,101,32,118,111,105, 100,32,117,105,110,116,54,52,40,120,41,123,117,105,110,116,110,40,120,44,56, 41,125,10,100,101,102,105,110,101,32,118,111,105,100,32,105,110,116,54,52,40, 120,41,123,105,110,116,110,40,120,44,56,41,125,10,100,101,102,105,110,101,32, 118,111,105,100,32,117,105,110,116,40,120,41,123,117,105,110,116,110,40,120, 44,117,98,121,116,101,115,40,120,41,41,125,10,100,101,102,105,110,101,32,118, 111,105,100,32,105,110,116,40,120,41,123,105,110,116,110,40,120,44,115,98,121, 116,101,115,40,120,41,41,125,10,0 }; #endif // BC_ENABLED && BC_ENABLE_EXTRA_MATH #if BC_ENABLE_SIGNALS #if BC_ENABLED const char bc_sig_msg[] = "\ninterrupt (type \"quit\" to exit)\n"; #endif // BC_ENABLED #if DC_ENABLED const char dc_sig_msg[] = "\ninterrupt (type \"q\" to exit)\n"; #endif // DC_ENABLED #endif // BC_ENABLE_SIGNALS const char bc_copyright[] = "Copyright (c) 2018-2019 Gavin D. Howard and contributors\n" "Report bugs at: https://github.com/gavinhoward/bc\n\n" "This is free software with ABSOLUTELY NO WARRANTY.\n"; const char* const bc_err_func_header = "Function:"; const char* const bc_err_line = ":%zu"; const char *bc_errs[] = { "Math error:", "Parse error:", "Runtime error:", "Fatal error:", #if BC_ENABLED "Warning:", #endif // BC_ENABLED }; const uchar bc_err_ids[] = { BC_ERR_IDX_MATH, BC_ERR_IDX_MATH, BC_ERR_IDX_MATH, BC_ERR_IDX_MATH, BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL, BC_ERR_IDX_FATAL, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_EXEC, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, #if BC_ENABLED BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, BC_ERR_IDX_PARSE, #endif // BC_ENABLED }; const char* const bc_err_msgs[] = { "negative number", "non-integer number", "overflow: number cannot fit", "divide by 0", "memory allocation failed", "I/O error", "could not open file: %s", "file is not ASCII: %s", "path is a directory: %s", "bad command-line option: '%c' (\"%s\")", "bad ibase: must be [%lu, %lu]", "bad obase: must be [%lu, %lu]", "bad scale: must be [%lu, %lu]", "bad read() expression", "read() call inside of a read() call", "variable or array element is the wrong type", #if DC_ENABLED "stack has too few elements", #else // DC_ENABLED NULL, #endif // DC_ENABLED #if BC_ENABLED "wrong number of parameters; need %zu, have %zu", "undefined function: %s()", "cannot use a void value in an expression", #else NULL, NULL, NULL, #endif // BC_ENABLED "end of file", "bad character '%c'", "string end could not be found", "comment end could not be found", "bad token", #if BC_ENABLED "bad expression", "empty expression", "bad print statement", "bad function definition", "bad assignment: left side must be scale, ibase, " "obase, last, var, or array element", "no auto variable found", "function parameter or auto \"%s%s\" already exists", "block end could not be found", "cannot return a value from void function: %s()", "var cannot be a reference: %s", "POSIX does not allow names longer than 1 character: %s", "POSIX does not allow '#' script comments", "POSIX does not allow the following keyword: %s", "POSIX does not allow a period ('.') as a shortcut for the last result", "POSIX requires parentheses around return expressions", "POSIX does not allow the following operator: %s", "POSIX does not allow comparison operators outside if statements or loops", "POSIX requires 0 or 1 comparison operators per condition", "POSIX requires all 3 parts of a for loop to be non-empty", #if BC_ENABLE_EXTRA_MATH "POSIX does not allow exponential notation", #else NULL, #endif // BC_ENABLE_EXTRA_MATH "POSIX does not allow array references as function parameters", "POSIX does not allow void functions", "POSIX requires the left brace be on the same line as the function header", #endif // BC_ENABLED }; #if BC_ENABLE_HISTORY const char *bc_history_bad_terms[] = { "dumb", "cons25", "emacs", NULL }; const char bc_history_tab[] = " "; const size_t bc_history_tab_len = sizeof(bc_history_tab) - 1; // These are listed in ascending order for efficiency. const uint32_t bc_history_wchars[][2] = { { 0x1100, 0x115F }, { 0x231A, 0x231B }, { 0x2329, 0x232A }, { 0x23E9, 0x23EC }, { 0x23F0, 0x23F0 }, { 0x23F3, 0x23F3 }, { 0x25FD, 0x25FE }, { 0x2614, 0x2615 }, { 0x2648, 0x2653 }, { 0x267F, 0x267F }, { 0x2693, 0x2693 }, { 0x26A1, 0x26A1 }, { 0x26AA, 0x26AB }, { 0x26BD, 0x26BE }, { 0x26C4, 0x26C5 }, { 0x26CE, 0x26CE }, { 0x26D4, 0x26D4 }, { 0x26EA, 0x26EA }, { 0x26F2, 0x26F3 }, { 0x26F5, 0x26F5 }, { 0x26FA, 0x26FA }, { 0x26FD, 0x26FD }, { 0x2705, 0x2705 }, { 0x270A, 0x270B }, { 0x2728, 0x2728 }, { 0x274C, 0x274C }, { 0x274E, 0x274E }, { 0x2753, 0x2755 }, { 0x2757, 0x2757 }, { 0x2795, 0x2797 }, { 0x27B0, 0x27B0 }, { 0x27BF, 0x27BF }, { 0x2B1B, 0x2B1C }, { 0x2B50, 0x2B50 }, { 0x2B55, 0x2B55 }, { 0x2E80, 0x2E99 }, { 0x2E9B, 0x2EF3 }, { 0x2F00, 0x2FD5 }, { 0x2FF0, 0x2FFB }, { 0x3001, 0x303E }, { 0x3041, 0x3096 }, { 0x3099, 0x30FF }, { 0x3105, 0x312D }, { 0x3131, 0x318E }, { 0x3190, 0x31BA }, { 0x31C0, 0x31E3 }, { 0x31F0, 0x321E }, { 0x3220, 0x3247 }, { 0x3250, 0x32FE }, { 0x3300, 0x4DBF }, { 0x4E00, 0xA48C }, { 0xA490, 0xA4C6 }, { 0xA960, 0xA97C }, { 0xAC00, 0xD7A3 }, { 0xF900, 0xFAFF }, { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE52 }, { 0xFE54, 0xFE66 }, { 0xFE68, 0xFE6B }, { 0x16FE0, 0x16FE0 }, { 0x17000, 0x187EC }, { 0x18800, 0x18AF2 }, { 0x1B000, 0x1B001 }, { 0x1F004, 0x1F004 }, { 0x1F0CF, 0x1F0CF }, { 0x1F18E, 0x1F18E }, { 0x1F191, 0x1F19A }, { 0x1F200, 0x1F202 }, { 0x1F210, 0x1F23B }, { 0x1F240, 0x1F248 }, { 0x1F250, 0x1F251 }, { 0x1F300, 0x1F320 }, { 0x1F32D, 0x1F335 }, { 0x1F337, 0x1F37C }, { 0x1F37E, 0x1F393 }, { 0x1F3A0, 0x1F3CA }, { 0x1F3CF, 0x1F3D3 }, { 0x1F3E0, 0x1F3F0 }, { 0x1F3F4, 0x1F3F4 }, { 0x1F3F8, 0x1F43E }, { 0x1F440, 0x1F440 }, { 0x1F442, 0x1F4FC }, { 0x1F4FF, 0x1F53D }, { 0x1F54B, 0x1F54E }, { 0x1F550, 0x1F567 }, { 0x1F57A, 0x1F57A }, { 0x1F595, 0x1F596 }, { 0x1F5A4, 0x1F5A4 }, { 0x1F5FB, 0x1F64F }, { 0x1F680, 0x1F6C5 }, { 0x1F6CC, 0x1F6CC }, { 0x1F6D0, 0x1F6D2 }, { 0x1F6EB, 0x1F6EC }, { 0x1F6F4, 0x1F6F6 }, { 0x1F910, 0x1F91E }, { 0x1F920, 0x1F927 }, { 0x1F930, 0x1F930 }, { 0x1F933, 0x1F93E }, { 0x1F940, 0x1F94B }, { 0x1F950, 0x1F95E }, { 0x1F980, 0x1F991 }, { 0x1F9C0, 0x1F9C0 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD }, }; const size_t bc_history_wchars_len = sizeof(bc_history_wchars) / sizeof(bc_history_wchars[0]); // These are listed in ascending order for efficiency. const uint32_t bc_history_combo_chars[] = { 0x0300,0x0301,0x0302,0x0303,0x0304,0x0305,0x0306,0x0307, 0x0308,0x0309,0x030A,0x030B,0x030C,0x030D,0x030E,0x030F, 0x0310,0x0311,0x0312,0x0313,0x0314,0x0315,0x0316,0x0317, 0x0318,0x0319,0x031A,0x031B,0x031C,0x031D,0x031E,0x031F, 0x0320,0x0321,0x0322,0x0323,0x0324,0x0325,0x0326,0x0327, 0x0328,0x0329,0x032A,0x032B,0x032C,0x032D,0x032E,0x032F, 0x0330,0x0331,0x0332,0x0333,0x0334,0x0335,0x0336,0x0337, 0x0338,0x0339,0x033A,0x033B,0x033C,0x033D,0x033E,0x033F, 0x0340,0x0341,0x0342,0x0343,0x0344,0x0345,0x0346,0x0347, 0x0348,0x0349,0x034A,0x034B,0x034C,0x034D,0x034E,0x034F, 0x0350,0x0351,0x0352,0x0353,0x0354,0x0355,0x0356,0x0357, 0x0358,0x0359,0x035A,0x035B,0x035C,0x035D,0x035E,0x035F, 0x0360,0x0361,0x0362,0x0363,0x0364,0x0365,0x0366,0x0367, 0x0368,0x0369,0x036A,0x036B,0x036C,0x036D,0x036E,0x036F, 0x0483,0x0484,0x0485,0x0486,0x0487,0x0591,0x0592,0x0593, 0x0594,0x0595,0x0596,0x0597,0x0598,0x0599,0x059A,0x059B, 0x059C,0x059D,0x059E,0x059F,0x05A0,0x05A1,0x05A2,0x05A3, 0x05A4,0x05A5,0x05A6,0x05A7,0x05A8,0x05A9,0x05AA,0x05AB, 0x05AC,0x05AD,0x05AE,0x05AF,0x05B0,0x05B1,0x05B2,0x05B3, 0x05B4,0x05B5,0x05B6,0x05B7,0x05B8,0x05B9,0x05BA,0x05BB, 0x05BC,0x05BD,0x05BF,0x05C1,0x05C2,0x05C4,0x05C5,0x05C7, 0x0610,0x0611,0x0612,0x0613,0x0614,0x0615,0x0616,0x0617, 0x0618,0x0619,0x061A,0x064B,0x064C,0x064D,0x064E,0x064F, 0x0650,0x0651,0x0652,0x0653,0x0654,0x0655,0x0656,0x0657, 0x0658,0x0659,0x065A,0x065B,0x065C,0x065D,0x065E,0x065F, 0x0670,0x06D6,0x06D7,0x06D8,0x06D9,0x06DA,0x06DB,0x06DC, 0x06DF,0x06E0,0x06E1,0x06E2,0x06E3,0x06E4,0x06E7,0x06E8, 0x06EA,0x06EB,0x06EC,0x06ED,0x0711,0x0730,0x0731,0x0732, 0x0733,0x0734,0x0735,0x0736,0x0737,0x0738,0x0739,0x073A, 0x073B,0x073C,0x073D,0x073E,0x073F,0x0740,0x0741,0x0742, 0x0743,0x0744,0x0745,0x0746,0x0747,0x0748,0x0749,0x074A, 0x07A6,0x07A7,0x07A8,0x07A9,0x07AA,0x07AB,0x07AC,0x07AD, 0x07AE,0x07AF,0x07B0,0x07EB,0x07EC,0x07ED,0x07EE,0x07EF, 0x07F0,0x07F1,0x07F2,0x07F3,0x0816,0x0817,0x0818,0x0819, 0x081B,0x081C,0x081D,0x081E,0x081F,0x0820,0x0821,0x0822, 0x0823,0x0825,0x0826,0x0827,0x0829,0x082A,0x082B,0x082C, 0x082D,0x0859,0x085A,0x085B,0x08D4,0x08D5,0x08D6,0x08D7, 0x08D8,0x08D9,0x08DA,0x08DB,0x08DC,0x08DD,0x08DE,0x08DF, 0x08E0,0x08E1,0x08E3,0x08E4,0x08E5,0x08E6,0x08E7,0x08E8, 0x08E9,0x08EA,0x08EB,0x08EC,0x08ED,0x08EE,0x08EF,0x08F0, 0x08F1,0x08F2,0x08F3,0x08F4,0x08F5,0x08F6,0x08F7,0x08F8, 0x08F9,0x08FA,0x08FB,0x08FC,0x08FD,0x08FE,0x08FF,0x0900, 0x0901,0x0902,0x093A,0x093C,0x0941,0x0942,0x0943,0x0944, 0x0945,0x0946,0x0947,0x0948,0x094D,0x0951,0x0952,0x0953, 0x0954,0x0955,0x0956,0x0957,0x0962,0x0963,0x0981,0x09BC, 0x09C1,0x09C2,0x09C3,0x09C4,0x09CD,0x09E2,0x09E3,0x0A01, 0x0A02,0x0A3C,0x0A41,0x0A42,0x0A47,0x0A48,0x0A4B,0x0A4C, 0x0A4D,0x0A51,0x0A70,0x0A71,0x0A75,0x0A81,0x0A82,0x0ABC, 0x0AC1,0x0AC2,0x0AC3,0x0AC4,0x0AC5,0x0AC7,0x0AC8,0x0ACD, 0x0AE2,0x0AE3,0x0B01,0x0B3C,0x0B3F,0x0B41,0x0B42,0x0B43, 0x0B44,0x0B4D,0x0B56,0x0B62,0x0B63,0x0B82,0x0BC0,0x0BCD, 0x0C00,0x0C3E,0x0C3F,0x0C40,0x0C46,0x0C47,0x0C48,0x0C4A, 0x0C4B,0x0C4C,0x0C4D,0x0C55,0x0C56,0x0C62,0x0C63,0x0C81, 0x0CBC,0x0CBF,0x0CC6,0x0CCC,0x0CCD,0x0CE2,0x0CE3,0x0D01, 0x0D41,0x0D42,0x0D43,0x0D44,0x0D4D,0x0D62,0x0D63,0x0DCA, 0x0DD2,0x0DD3,0x0DD4,0x0DD6,0x0E31,0x0E34,0x0E35,0x0E36, 0x0E37,0x0E38,0x0E39,0x0E3A,0x0E47,0x0E48,0x0E49,0x0E4A, 0x0E4B,0x0E4C,0x0E4D,0x0E4E,0x0EB1,0x0EB4,0x0EB5,0x0EB6, 0x0EB7,0x0EB8,0x0EB9,0x0EBB,0x0EBC,0x0EC8,0x0EC9,0x0ECA, 0x0ECB,0x0ECC,0x0ECD,0x0F18,0x0F19,0x0F35,0x0F37,0x0F39, 0x0F71,0x0F72,0x0F73,0x0F74,0x0F75,0x0F76,0x0F77,0x0F78, 0x0F79,0x0F7A,0x0F7B,0x0F7C,0x0F7D,0x0F7E,0x0F80,0x0F81, 0x0F82,0x0F83,0x0F84,0x0F86,0x0F87,0x0F8D,0x0F8E,0x0F8F, 0x0F90,0x0F91,0x0F92,0x0F93,0x0F94,0x0F95,0x0F96,0x0F97, 0x0F99,0x0F9A,0x0F9B,0x0F9C,0x0F9D,0x0F9E,0x0F9F,0x0FA0, 0x0FA1,0x0FA2,0x0FA3,0x0FA4,0x0FA5,0x0FA6,0x0FA7,0x0FA8, 0x0FA9,0x0FAA,0x0FAB,0x0FAC,0x0FAD,0x0FAE,0x0FAF,0x0FB0, 0x0FB1,0x0FB2,0x0FB3,0x0FB4,0x0FB5,0x0FB6,0x0FB7,0x0FB8, 0x0FB9,0x0FBA,0x0FBB,0x0FBC,0x0FC6,0x102D,0x102E,0x102F, 0x1030,0x1032,0x1033,0x1034,0x1035,0x1036,0x1037,0x1039, 0x103A,0x103D,0x103E,0x1058,0x1059,0x105E,0x105F,0x1060, 0x1071,0x1072,0x1073,0x1074,0x1082,0x1085,0x1086,0x108D, 0x109D,0x135D,0x135E,0x135F,0x1712,0x1713,0x1714,0x1732, 0x1733,0x1734,0x1752,0x1753,0x1772,0x1773,0x17B4,0x17B5, 0x17B7,0x17B8,0x17B9,0x17BA,0x17BB,0x17BC,0x17BD,0x17C6, 0x17C9,0x17CA,0x17CB,0x17CC,0x17CD,0x17CE,0x17CF,0x17D0, 0x17D1,0x17D2,0x17D3,0x17DD,0x180B,0x180C,0x180D,0x1885, 0x1886,0x18A9,0x1920,0x1921,0x1922,0x1927,0x1928,0x1932, 0x1939,0x193A,0x193B,0x1A17,0x1A18,0x1A1B,0x1A56,0x1A58, 0x1A59,0x1A5A,0x1A5B,0x1A5C,0x1A5D,0x1A5E,0x1A60,0x1A62, 0x1A65,0x1A66,0x1A67,0x1A68,0x1A69,0x1A6A,0x1A6B,0x1A6C, 0x1A73,0x1A74,0x1A75,0x1A76,0x1A77,0x1A78,0x1A79,0x1A7A, 0x1A7B,0x1A7C,0x1A7F,0x1AB0,0x1AB1,0x1AB2,0x1AB3,0x1AB4, 0x1AB5,0x1AB6,0x1AB7,0x1AB8,0x1AB9,0x1ABA,0x1ABB,0x1ABC, 0x1ABD,0x1B00,0x1B01,0x1B02,0x1B03,0x1B34,0x1B36,0x1B37, 0x1B38,0x1B39,0x1B3A,0x1B3C,0x1B42,0x1B6B,0x1B6C,0x1B6D, 0x1B6E,0x1B6F,0x1B70,0x1B71,0x1B72,0x1B73,0x1B80,0x1B81, 0x1BA2,0x1BA3,0x1BA4,0x1BA5,0x1BA8,0x1BA9,0x1BAB,0x1BAC, 0x1BAD,0x1BE6,0x1BE8,0x1BE9,0x1BED,0x1BEF,0x1BF0,0x1BF1, 0x1C2C,0x1C2D,0x1C2E,0x1C2F,0x1C30,0x1C31,0x1C32,0x1C33, 0x1C36,0x1C37,0x1CD0,0x1CD1,0x1CD2,0x1CD4,0x1CD5,0x1CD6, 0x1CD7,0x1CD8,0x1CD9,0x1CDA,0x1CDB,0x1CDC,0x1CDD,0x1CDE, 0x1CDF,0x1CE0,0x1CE2,0x1CE3,0x1CE4,0x1CE5,0x1CE6,0x1CE7, 0x1CE8,0x1CED,0x1CF4,0x1CF8,0x1CF9,0x1DC0,0x1DC1,0x1DC2, 0x1DC3,0x1DC4,0x1DC5,0x1DC6,0x1DC7,0x1DC8,0x1DC9,0x1DCA, 0x1DCB,0x1DCC,0x1DCD,0x1DCE,0x1DCF,0x1DD0,0x1DD1,0x1DD2, 0x1DD3,0x1DD4,0x1DD5,0x1DD6,0x1DD7,0x1DD8,0x1DD9,0x1DDA, 0x1DDB,0x1DDC,0x1DDD,0x1DDE,0x1DDF,0x1DE0,0x1DE1,0x1DE2, 0x1DE3,0x1DE4,0x1DE5,0x1DE6,0x1DE7,0x1DE8,0x1DE9,0x1DEA, 0x1DEB,0x1DEC,0x1DED,0x1DEE,0x1DEF,0x1DF0,0x1DF1,0x1DF2, 0x1DF3,0x1DF4,0x1DF5,0x1DFB,0x1DFC,0x1DFD,0x1DFE,0x1DFF, 0x20D0,0x20D1,0x20D2,0x20D3,0x20D4,0x20D5,0x20D6,0x20D7, 0x20D8,0x20D9,0x20DA,0x20DB,0x20DC,0x20E1,0x20E5,0x20E6, 0x20E7,0x20E8,0x20E9,0x20EA,0x20EB,0x20EC,0x20ED,0x20EE, 0x20EF,0x20F0,0x2CEF,0x2CF0,0x2CF1,0x2D7F,0x2DE0,0x2DE1, 0x2DE2,0x2DE3,0x2DE4,0x2DE5,0x2DE6,0x2DE7,0x2DE8,0x2DE9, 0x2DEA,0x2DEB,0x2DEC,0x2DED,0x2DEE,0x2DEF,0x2DF0,0x2DF1, 0x2DF2,0x2DF3,0x2DF4,0x2DF5,0x2DF6,0x2DF7,0x2DF8,0x2DF9, 0x2DFA,0x2DFB,0x2DFC,0x2DFD,0x2DFE,0x2DFF,0x302A,0x302B, 0x302C,0x302D,0x3099,0x309A,0xA66F,0xA674,0xA675,0xA676, 0xA677,0xA678,0xA679,0xA67A,0xA67B,0xA67C,0xA67D,0xA69E, 0xA69F,0xA6F0,0xA6F1,0xA802,0xA806,0xA80B,0xA825,0xA826, 0xA8C4,0xA8C5,0xA8E0,0xA8E1,0xA8E2,0xA8E3,0xA8E4,0xA8E5, 0xA8E6,0xA8E7,0xA8E8,0xA8E9,0xA8EA,0xA8EB,0xA8EC,0xA8ED, 0xA8EE,0xA8EF,0xA8F0,0xA8F1,0xA926,0xA927,0xA928,0xA929, 0xA92A,0xA92B,0xA92C,0xA92D,0xA947,0xA948,0xA949,0xA94A, 0xA94B,0xA94C,0xA94D,0xA94E,0xA94F,0xA950,0xA951,0xA980, 0xA981,0xA982,0xA9B3,0xA9B6,0xA9B7,0xA9B8,0xA9B9,0xA9BC, 0xA9E5,0xAA29,0xAA2A,0xAA2B,0xAA2C,0xAA2D,0xAA2E,0xAA31, 0xAA32,0xAA35,0xAA36,0xAA43,0xAA4C,0xAA7C,0xAAB0,0xAAB2, 0xAAB3,0xAAB4,0xAAB7,0xAAB8,0xAABE,0xAABF,0xAAC1,0xAAEC, 0xAAED,0xAAF6,0xABE5,0xABE8,0xABED,0xFB1E,0xFE00,0xFE01, 0xFE02,0xFE03,0xFE04,0xFE05,0xFE06,0xFE07,0xFE08,0xFE09, 0xFE0A,0xFE0B,0xFE0C,0xFE0D,0xFE0E,0xFE0F,0xFE20,0xFE21, 0xFE22,0xFE23,0xFE24,0xFE25,0xFE26,0xFE27,0xFE28,0xFE29, 0xFE2A,0xFE2B,0xFE2C,0xFE2D,0xFE2E,0xFE2F, 0x101FD,0x102E0,0x10376,0x10377,0x10378,0x10379,0x1037A,0x10A01, 0x10A02,0x10A03,0x10A05,0x10A06,0x10A0C,0x10A0D,0x10A0E,0x10A0F, 0x10A38,0x10A39,0x10A3A,0x10A3F,0x10AE5,0x10AE6,0x11001,0x11038, 0x11039,0x1103A,0x1103B,0x1103C,0x1103D,0x1103E,0x1103F,0x11040, 0x11041,0x11042,0x11043,0x11044,0x11045,0x11046,0x1107F,0x11080, 0x11081,0x110B3,0x110B4,0x110B5,0x110B6,0x110B9,0x110BA,0x11100, 0x11101,0x11102,0x11127,0x11128,0x11129,0x1112A,0x1112B,0x1112D, 0x1112E,0x1112F,0x11130,0x11131,0x11132,0x11133,0x11134,0x11173, 0x11180,0x11181,0x111B6,0x111B7,0x111B8,0x111B9,0x111BA,0x111BB, 0x111BC,0x111BD,0x111BE,0x111CA,0x111CB,0x111CC,0x1122F,0x11230, 0x11231,0x11234,0x11236,0x11237,0x1123E,0x112DF,0x112E3,0x112E4, 0x112E5,0x112E6,0x112E7,0x112E8,0x112E9,0x112EA,0x11300,0x11301, 0x1133C,0x11340,0x11366,0x11367,0x11368,0x11369,0x1136A,0x1136B, 0x1136C,0x11370,0x11371,0x11372,0x11373,0x11374,0x11438,0x11439, 0x1143A,0x1143B,0x1143C,0x1143D,0x1143E,0x1143F,0x11442,0x11443, 0x11444,0x11446,0x114B3,0x114B4,0x114B5,0x114B6,0x114B7,0x114B8, 0x114BA,0x114BF,0x114C0,0x114C2,0x114C3,0x115B2,0x115B3,0x115B4, 0x115B5,0x115BC,0x115BD,0x115BF,0x115C0,0x115DC,0x115DD,0x11633, 0x11634,0x11635,0x11636,0x11637,0x11638,0x11639,0x1163A,0x1163D, 0x1163F,0x11640,0x116AB,0x116AD,0x116B0,0x116B1,0x116B2,0x116B3, 0x116B4,0x116B5,0x116B7,0x1171D,0x1171E,0x1171F,0x11722,0x11723, 0x11724,0x11725,0x11727,0x11728,0x11729,0x1172A,0x1172B,0x11C30, 0x11C31,0x11C32,0x11C33,0x11C34,0x11C35,0x11C36,0x11C38,0x11C39, 0x11C3A,0x11C3B,0x11C3C,0x11C3D,0x11C3F,0x11C92,0x11C93,0x11C94, 0x11C95,0x11C96,0x11C97,0x11C98,0x11C99,0x11C9A,0x11C9B,0x11C9C, 0x11C9D,0x11C9E,0x11C9F,0x11CA0,0x11CA1,0x11CA2,0x11CA3,0x11CA4, 0x11CA5,0x11CA6,0x11CA7,0x11CAA,0x11CAB,0x11CAC,0x11CAD,0x11CAE, 0x11CAF,0x11CB0,0x11CB2,0x11CB3,0x11CB5,0x11CB6,0x16AF0,0x16AF1, 0x16AF2,0x16AF3,0x16AF4,0x16B30,0x16B31,0x16B32,0x16B33,0x16B34, 0x16B35,0x16B36,0x16F8F,0x16F90,0x16F91,0x16F92,0x1BC9D,0x1BC9E, 0x1D167,0x1D168,0x1D169,0x1D17B,0x1D17C,0x1D17D,0x1D17E,0x1D17F, 0x1D180,0x1D181,0x1D182,0x1D185,0x1D186,0x1D187,0x1D188,0x1D189, 0x1D18A,0x1D18B,0x1D1AA,0x1D1AB,0x1D1AC,0x1D1AD,0x1D242,0x1D243, 0x1D244,0x1DA00,0x1DA01,0x1DA02,0x1DA03,0x1DA04,0x1DA05,0x1DA06, 0x1DA07,0x1DA08,0x1DA09,0x1DA0A,0x1DA0B,0x1DA0C,0x1DA0D,0x1DA0E, 0x1DA0F,0x1DA10,0x1DA11,0x1DA12,0x1DA13,0x1DA14,0x1DA15,0x1DA16, 0x1DA17,0x1DA18,0x1DA19,0x1DA1A,0x1DA1B,0x1DA1C,0x1DA1D,0x1DA1E, 0x1DA1F,0x1DA20,0x1DA21,0x1DA22,0x1DA23,0x1DA24,0x1DA25,0x1DA26, 0x1DA27,0x1DA28,0x1DA29,0x1DA2A,0x1DA2B,0x1DA2C,0x1DA2D,0x1DA2E, 0x1DA2F,0x1DA30,0x1DA31,0x1DA32,0x1DA33,0x1DA34,0x1DA35,0x1DA36, 0x1DA3B,0x1DA3C,0x1DA3D,0x1DA3E,0x1DA3F,0x1DA40,0x1DA41,0x1DA42, 0x1DA43,0x1DA44,0x1DA45,0x1DA46,0x1DA47,0x1DA48,0x1DA49,0x1DA4A, 0x1DA4B,0x1DA4C,0x1DA4D,0x1DA4E,0x1DA4F,0x1DA50,0x1DA51,0x1DA52, 0x1DA53,0x1DA54,0x1DA55,0x1DA56,0x1DA57,0x1DA58,0x1DA59,0x1DA5A, 0x1DA5B,0x1DA5C,0x1DA5D,0x1DA5E,0x1DA5F,0x1DA60,0x1DA61,0x1DA62, 0x1DA63,0x1DA64,0x1DA65,0x1DA66,0x1DA67,0x1DA68,0x1DA69,0x1DA6A, 0x1DA6B,0x1DA6C,0x1DA75,0x1DA84,0x1DA9B,0x1DA9C,0x1DA9D,0x1DA9E, 0x1DA9F,0x1DAA1,0x1DAA2,0x1DAA3,0x1DAA4,0x1DAA5,0x1DAA6,0x1DAA7, 0x1DAA8,0x1DAA9,0x1DAAA,0x1DAAB,0x1DAAC,0x1DAAD,0x1DAAE,0x1DAAF, 0x1E000,0x1E001,0x1E002,0x1E003,0x1E004,0x1E005,0x1E006,0x1E008, 0x1E009,0x1E00A,0x1E00B,0x1E00C,0x1E00D,0x1E00E,0x1E00F,0x1E010, 0x1E011,0x1E012,0x1E013,0x1E014,0x1E015,0x1E016,0x1E017,0x1E018, 0x1E01B,0x1E01C,0x1E01D,0x1E01E,0x1E01F,0x1E020,0x1E021,0x1E023, 0x1E024,0x1E026,0x1E027,0x1E028,0x1E029,0x1E02A,0x1E8D0,0x1E8D1, 0x1E8D2,0x1E8D3,0x1E8D4,0x1E8D5,0x1E8D6,0x1E944,0x1E945,0x1E946, 0x1E947,0x1E948,0x1E949,0x1E94A,0xE0100,0xE0101,0xE0102,0xE0103, 0xE0104,0xE0105,0xE0106,0xE0107,0xE0108,0xE0109,0xE010A,0xE010B, 0xE010C,0xE010D,0xE010E,0xE010F,0xE0110,0xE0111,0xE0112,0xE0113, 0xE0114,0xE0115,0xE0116,0xE0117,0xE0118,0xE0119,0xE011A,0xE011B, 0xE011C,0xE011D,0xE011E,0xE011F,0xE0120,0xE0121,0xE0122,0xE0123, 0xE0124,0xE0125,0xE0126,0xE0127,0xE0128,0xE0129,0xE012A,0xE012B, 0xE012C,0xE012D,0xE012E,0xE012F,0xE0130,0xE0131,0xE0132,0xE0133, 0xE0134,0xE0135,0xE0136,0xE0137,0xE0138,0xE0139,0xE013A,0xE013B, 0xE013C,0xE013D,0xE013E,0xE013F,0xE0140,0xE0141,0xE0142,0xE0143, 0xE0144,0xE0145,0xE0146,0xE0147,0xE0148,0xE0149,0xE014A,0xE014B, 0xE014C,0xE014D,0xE014E,0xE014F,0xE0150,0xE0151,0xE0152,0xE0153, 0xE0154,0xE0155,0xE0156,0xE0157,0xE0158,0xE0159,0xE015A,0xE015B, 0xE015C,0xE015D,0xE015E,0xE015F,0xE0160,0xE0161,0xE0162,0xE0163, 0xE0164,0xE0165,0xE0166,0xE0167,0xE0168,0xE0169,0xE016A,0xE016B, 0xE016C,0xE016D,0xE016E,0xE016F,0xE0170,0xE0171,0xE0172,0xE0173, 0xE0174,0xE0175,0xE0176,0xE0177,0xE0178,0xE0179,0xE017A,0xE017B, 0xE017C,0xE017D,0xE017E,0xE017F,0xE0180,0xE0181,0xE0182,0xE0183, 0xE0184,0xE0185,0xE0186,0xE0187,0xE0188,0xE0189,0xE018A,0xE018B, 0xE018C,0xE018D,0xE018E,0xE018F,0xE0190,0xE0191,0xE0192,0xE0193, 0xE0194,0xE0195,0xE0196,0xE0197,0xE0198,0xE0199,0xE019A,0xE019B, 0xE019C,0xE019D,0xE019E,0xE019F,0xE01A0,0xE01A1,0xE01A2,0xE01A3, 0xE01A4,0xE01A5,0xE01A6,0xE01A7,0xE01A8,0xE01A9,0xE01AA,0xE01AB, 0xE01AC,0xE01AD,0xE01AE,0xE01AF,0xE01B0,0xE01B1,0xE01B2,0xE01B3, 0xE01B4,0xE01B5,0xE01B6,0xE01B7,0xE01B8,0xE01B9,0xE01BA,0xE01BB, 0xE01BC,0xE01BD,0xE01BE,0xE01BF,0xE01C0,0xE01C1,0xE01C2,0xE01C3, 0xE01C4,0xE01C5,0xE01C6,0xE01C7,0xE01C8,0xE01C9,0xE01CA,0xE01CB, 0xE01CC,0xE01CD,0xE01CE,0xE01CF,0xE01D0,0xE01D1,0xE01D2,0xE01D3, 0xE01D4,0xE01D5,0xE01D6,0xE01D7,0xE01D8,0xE01D9,0xE01DA,0xE01DB, 0xE01DC,0xE01DD,0xE01DE,0xE01DF,0xE01E0,0xE01E1,0xE01E2,0xE01E3, 0xE01E4,0xE01E5,0xE01E6,0xE01E7,0xE01E8,0xE01E9,0xE01EA,0xE01EB, 0xE01EC,0xE01ED,0xE01EE,0xE01EF, }; const size_t bc_history_combo_chars_len = sizeof(bc_history_combo_chars) / sizeof(bc_history_combo_chars[0]); #if BC_DEBUG_CODE FILE *bc_history_debug_fp = NULL; #endif // BC_DEBUG_CODE #endif // BC_ENABLE_HISTORY const char bc_func_main[] = "(main)"; const char bc_func_read[] = "(read)"; #if BC_DEBUG_CODE const char* bc_inst_names[] = { #if BC_ENABLED "BC_INST_INC_POST", "BC_INST_DEC_POST", "BC_INST_INC_PRE", "BC_INST_DEC_PRE", #endif // BC_ENABLED "BC_INST_NEG", "BC_INST_BOOL_NOT", #if BC_ENABLE_EXTRA_MATH "BC_INST_TRUNC", #endif // BC_ENABLE_EXTRA_MATH "BC_INST_POWER", "BC_INST_MULTIPLY", "BC_INST_DIVIDE", "BC_INST_MODULUS", "BC_INST_PLUS", "BC_INST_MINUS", #if BC_ENABLE_EXTRA_MATH "BC_INST_PLACES", "BC_INST_LSHIFT", "BC_INST_RSHIFT", #endif // BC_ENABLE_EXTRA_MATH "BC_INST_REL_EQ", "BC_INST_REL_LE", "BC_INST_REL_GE", "BC_INST_REL_NE", "BC_INST_REL_LT", "BC_INST_REL_GT", "BC_INST_BOOL_OR", "BC_INST_BOOL_AND", #if BC_ENABLED "BC_INST_ASSIGN_POWER", "BC_INST_ASSIGN_MULTIPLY", "BC_INST_ASSIGN_DIVIDE", "BC_INST_ASSIGN_MODULUS", "BC_INST_ASSIGN_PLUS", "BC_INST_ASSIGN_MINUS", #if BC_ENABLE_EXTRA_MATH "BC_INST_ASSIGN_PLACES", "BC_INST_ASSIGN_LSHIFT", "BC_INST_ASSIGN_RSHIFT", #endif // BC_ENABLE_EXTRA_MATH "BC_INST_ASSIGN", "BC_INST_INC_NO_VAL", "BC_INST_DEC_NO_VAL", "BC_INST_ASSIGN_POWER_NO_VAL", "BC_INST_ASSIGN_MULTIPLY_NO_VAL", "BC_INST_ASSIGN_DIVIDE_NO_VAL", "BC_INST_ASSIGN_MODULUS_NO_VAL", "BC_INST_ASSIGN_PLUS_NO_VAL", "BC_INST_ASSIGN_MINUS_NO_VAL", #if BC_ENABLE_EXTRA_MATH "BC_INST_ASSIGN_PLACES_NO_VAL", "BC_INST_ASSIGN_LSHIFT_NO_VAL", "BC_INST_ASSIGN_RSHIFT_NO_VAL", #endif // BC_ENABLE_EXTRA_MATH #endif // BC_ENABLED "BC_INST_ASSIGN_NO_VAL", "BC_INST_NUM", "BC_INST_VAR", "BC_INST_ARRAY_ELEM", #if BC_ENABLED "BC_INST_ARRAY", #endif // BC_ENABLED "BC_INST_ONE", #if BC_ENABLED "BC_INST_LAST", #endif // BC_ENABLED "BC_INST_IBASE", "BC_INST_OBASE", "BC_INST_SCALE", "BC_INST_LENGTH", "BC_INST_SCALE_FUNC", "BC_INST_SQRT", "BC_INST_ABS", "BC_INST_READ", "BC_INST_MAXIBASE", "BC_INST_MAXOBASE", "BC_INST_MAXSCALE", "BC_INST_PRINT", "BC_INST_PRINT_POP", "BC_INST_STR", "BC_INST_PRINT_STR", #if BC_ENABLED "BC_INST_JUMP", "BC_INST_JUMP_ZERO", "BC_INST_CALL", "BC_INST_RET", "BC_INST_RET0", "BC_INST_RET_VOID", "BC_INST_HALT," #endif // BC_ENABLED #if DC_ENABLED "BC_INST_POP", "BC_INST_POP_EXEC", "BC_INST_MODEXP", "BC_INST_DIVMOD", "BC_INST_EXECUTE", "BC_INST_EXEC_COND", "BC_INST_ASCIIFY", "BC_INST_PRINT_STREAM", "BC_INST_PRINT_STACK", "BC_INST_CLEAR_STACK", "BC_INST_STACK_LEN", "BC_INST_DUPLICATE", "BC_INST_SWAP", "BC_INST_LOAD", "BC_INST_PUSH_VAR", "BC_INST_PUSH_TO_VAR", "BC_INST_QUIT", "BC_INST_NQUIT", #endif // DC_ENABLED }; #endif // BC_DEBUG_CODE #if BC_ENABLED const BcLexKeyword bc_lex_kws[] = { BC_LEX_KW_ENTRY("auto", 4, true), BC_LEX_KW_ENTRY("break", 5, true), BC_LEX_KW_ENTRY("continue", 8, false), BC_LEX_KW_ENTRY("define", 6, true), BC_LEX_KW_ENTRY("for", 3, true), BC_LEX_KW_ENTRY("if", 2, true), BC_LEX_KW_ENTRY("limits", 6, false), BC_LEX_KW_ENTRY("return", 6, true), BC_LEX_KW_ENTRY("while", 5, true), BC_LEX_KW_ENTRY("halt", 4, false), BC_LEX_KW_ENTRY("last", 4, false), BC_LEX_KW_ENTRY("ibase", 5, true), BC_LEX_KW_ENTRY("obase", 5, true), BC_LEX_KW_ENTRY("scale", 5, true), BC_LEX_KW_ENTRY("length", 6, true), BC_LEX_KW_ENTRY("print", 5, false), BC_LEX_KW_ENTRY("sqrt", 4, true), BC_LEX_KW_ENTRY("abs", 3, false), BC_LEX_KW_ENTRY("quit", 4, true), BC_LEX_KW_ENTRY("read", 4, false), BC_LEX_KW_ENTRY("maxibase", 8, false), BC_LEX_KW_ENTRY("maxobase", 8, false), BC_LEX_KW_ENTRY("maxscale", 8, false), BC_LEX_KW_ENTRY("else", 4, false), }; const size_t bc_lex_kws_len = sizeof(bc_lex_kws) / sizeof(BcLexKeyword); const char* const bc_parse_const1 = "1"; // This is an array that corresponds to token types. An entry is // true if the token is valid in an expression, false otherwise. const uint8_t bc_parse_exprs[] = { BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true), BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true), BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true), #if BC_ENABLE_EXTRA_MATH BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true), BC_PARSE_EXPR_ENTRY(true, true, false, false, true, true, false, false), BC_PARSE_EXPR_ENTRY(false, false, false, false, false, true, true, false), BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, false, false), BC_PARSE_EXPR_ENTRY(false, true, true, true, true, true, false, true), BC_PARSE_EXPR_ENTRY(true, false, true, true, true, true, false, 0), #else // BC_ENABLE_EXTRA_MATH BC_PARSE_EXPR_ENTRY(true, true, true, false, false, true, true, false), BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, true, true), BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, false, false), BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, false), BC_PARSE_EXPR_ENTRY(true, true, false, true, true, true, true, false) #endif // BC_ENABLE_EXTRA_MATH }; // This is an array of data for operators that correspond to token types. const uchar bc_parse_ops[] = { BC_PARSE_OP(0, false), BC_PARSE_OP(0, false), BC_PARSE_OP(1, false), BC_PARSE_OP(1, false), #if BC_ENABLE_EXTRA_MATH BC_PARSE_OP(2, false), #endif // BC_ENABLE_EXTRA_MATH BC_PARSE_OP(4, false), BC_PARSE_OP(5, true), BC_PARSE_OP(5, true), BC_PARSE_OP(5, true), BC_PARSE_OP(6, true), BC_PARSE_OP(6, true), #if BC_ENABLE_EXTRA_MATH BC_PARSE_OP(3, false), BC_PARSE_OP(7, true), BC_PARSE_OP(7, true), #endif // BC_ENABLE_EXTRA_MATH BC_PARSE_OP(9, true), BC_PARSE_OP(9, true), BC_PARSE_OP(9, true), BC_PARSE_OP(9, true), BC_PARSE_OP(9, true), BC_PARSE_OP(9, true), BC_PARSE_OP(11, true), BC_PARSE_OP(10, true), BC_PARSE_OP(8, false), BC_PARSE_OP(8, false), BC_PARSE_OP(8, false), BC_PARSE_OP(8, false), BC_PARSE_OP(8, false), BC_PARSE_OP(8, false), #if BC_ENABLE_EXTRA_MATH BC_PARSE_OP(8, false), BC_PARSE_OP(8, false), BC_PARSE_OP(8, false), #endif // BC_ENABLE_EXTRA_MATH BC_PARSE_OP(8, false), }; // These identify what tokens can come after expressions in certain cases. const BcParseNext bc_parse_next_expr = BC_PARSE_NEXT(4, BC_LEX_NLINE, BC_LEX_SCOLON, BC_LEX_RBRACE, BC_LEX_EOF); const BcParseNext bc_parse_next_param = BC_PARSE_NEXT(2, BC_LEX_RPAREN, BC_LEX_COMMA); const BcParseNext bc_parse_next_print = BC_PARSE_NEXT(4, BC_LEX_COMMA, BC_LEX_NLINE, BC_LEX_SCOLON, BC_LEX_EOF); const BcParseNext bc_parse_next_rel = BC_PARSE_NEXT(1, BC_LEX_RPAREN); const BcParseNext bc_parse_next_elem = BC_PARSE_NEXT(1, BC_LEX_RBRACKET); const BcParseNext bc_parse_next_for = BC_PARSE_NEXT(1, BC_LEX_SCOLON); const BcParseNext bc_parse_next_read = BC_PARSE_NEXT(2, BC_LEX_NLINE, BC_LEX_EOF); #endif // BC_ENABLED #if DC_ENABLED const 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_KW_ELSE, BC_LEX_LOAD, BC_LEX_LOAD_POP, BC_LEX_OP_ASSIGN, BC_LEX_STORE_PUSH, }; const size_t dc_lex_regs_len = sizeof(dc_lex_regs) / sizeof(uint8_t); const uchar dc_lex_tokens[] = { #if BC_ENABLE_EXTRA_MATH BC_LEX_OP_TRUNC, #else // BC_ENABLE_EXTRA_MATH BC_LEX_INVALID, #endif // BC_ENABLE_EXTRA_MATH BC_LEX_OP_MODULUS, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_LPAREN, BC_LEX_RPAREN, 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, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_COLON, BC_LEX_SCOLON, BC_LEX_OP_REL_GT, BC_LEX_OP_REL_EQ, BC_LEX_OP_REL_LT, BC_LEX_KW_READ, #if BC_ENABLE_EXTRA_MATH BC_LEX_OP_PLACES, #else // BC_ENABLE_EXTRA_MATH BC_LEX_INVALID, #endif // BC_ENABLE_EXTRA_MATH BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_EQ_NO_REG, #if BC_ENABLE_EXTRA_MATH BC_LEX_OP_LSHIFT, #else // BC_ENABLE_EXTRA_MATH BC_LEX_INVALID, #endif // BC_ENABLE_EXTRA_MATH BC_LEX_KW_IBASE, BC_LEX_INVALID, BC_LEX_KW_SCALE, BC_LEX_LOAD_POP, BC_LEX_INVALID, BC_LEX_OP_BOOL_NOT, BC_LEX_KW_OBASE, BC_LEX_PRINT_STREAM, BC_LEX_NQUIT, BC_LEX_POP, BC_LEX_STORE_PUSH, BC_LEX_KW_MAXIBASE, BC_LEX_KW_MAXOBASE, BC_LEX_KW_MAXSCALE, BC_LEX_INVALID, BC_LEX_SCALE_FACTOR, BC_LEX_INVALID, BC_LEX_KW_LENGTH, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_OP_POWER, BC_LEX_NEG, BC_LEX_INVALID, BC_LEX_ASCIIFY, BC_LEX_KW_ABS, BC_LEX_CLEAR_STACK, BC_LEX_DUPLICATE, BC_LEX_KW_ELSE, BC_LEX_PRINT_STACK, BC_LEX_INVALID, #if BC_ENABLE_EXTRA_MATH BC_LEX_OP_RSHIFT, #else // BC_ENABLE_EXTRA_MATH BC_LEX_INVALID, #endif // BC_ENABLE_EXTRA_MATH BC_LEX_STORE_IBASE, BC_LEX_INVALID, BC_LEX_STORE_SCALE, BC_LEX_LOAD, BC_LEX_INVALID, BC_LEX_PRINT_POP, BC_LEX_STORE_OBASE, BC_LEX_KW_PRINT, BC_LEX_KW_QUIT, BC_LEX_SWAP, BC_LEX_OP_ASSIGN, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_KW_SQRT, BC_LEX_INVALID, BC_LEX_EXECUTE, BC_LEX_INVALID, BC_LEX_STACK_LEVEL, BC_LEX_LBRACE, BC_LEX_OP_MODEXP, BC_LEX_RBRACE, BC_LEX_OP_DIVMOD, BC_LEX_INVALID }; const uchar dc_parse_insts[] = { BC_INST_INVALID, BC_INST_INVALID, #if BC_ENABLED BC_INST_INVALID, BC_INST_INVALID, #endif // BC_ENABLED BC_INST_INVALID, BC_INST_BOOL_NOT, #if BC_ENABLE_EXTRA_MATH BC_INST_TRUNC, #endif // BC_ENABLE_EXTRA_MATH BC_INST_POWER, BC_INST_MULTIPLY, BC_INST_DIVIDE, BC_INST_MODULUS, BC_INST_PLUS, BC_INST_MINUS, #if BC_ENABLE_EXTRA_MATH BC_INST_PLACES, BC_INST_LSHIFT, BC_INST_RSHIFT, #endif // BC_ENABLE_EXTRA_MATH BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, #if BC_ENABLED BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, #if BC_ENABLE_EXTRA_MATH BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, #endif // BC_ENABLE_EXTRA_MATH #endif // BC_ENABLED BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GT, BC_INST_REL_LT, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE, BC_INST_INVALID, BC_INST_REL_LE, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, #if BC_ENABLED BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, #endif // BC_ENABLED BC_INST_IBASE, BC_INST_OBASE, BC_INST_SCALE, BC_INST_LENGTH, BC_INST_PRINT, BC_INST_SQRT, BC_INST_ABS, BC_INST_QUIT, BC_INST_INVALID, BC_INST_MAXIBASE, BC_INST_MAXOBASE, BC_INST_MAXSCALE, BC_INST_INVALID, BC_INST_REL_EQ, BC_INST_MODEXP, BC_INST_DIVMOD, BC_INST_INVALID, BC_INST_EXECUTE, BC_INST_PRINT_STACK, BC_INST_CLEAR_STACK, BC_INST_STACK_LEN, BC_INST_DUPLICATE, BC_INST_SWAP, BC_INST_POP, BC_INST_ASCIIFY, BC_INST_PRINT_STREAM, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_PRINT_POP, BC_INST_NQUIT, BC_INST_SCALE_FUNC, }; #endif // DC_ENABLED const char bc_parse_one[] = "1"; const char bc_num_hex_digits[] = "0123456789ABCDEF"; const BcBigDig bc_num_pow10[BC_BASE_DIGS + 1] = { 1, 10, 100, 1000, 10000, #if BC_BASE_DIGS > 4 100000, 1000000, 10000000, 100000000, 1000000000, #endif // BC_BASE_DIGS > 4 }; const BcNumBinaryOp bc_program_ops[] = { bc_num_pow, bc_num_mul, bc_num_div, bc_num_mod, bc_num_add, bc_num_sub, #if BC_ENABLE_EXTRA_MATH bc_num_places, bc_num_lshift, bc_num_rshift, #endif // BC_ENABLE_EXTRA_MATH }; const BcNumBinaryOpReq bc_program_opReqs[] = { bc_num_powReq, bc_num_mulReq, bc_num_mulReq, bc_num_mulReq, bc_num_addReq, bc_num_addReq, #if BC_ENABLE_EXTRA_MATH bc_num_placesReq, bc_num_placesReq, bc_num_placesReq, #endif // BC_ENABLE_EXTRA_MATH }; const BcProgramUnary bc_program_unarys[] = { bc_program_negate, bc_program_not, #if BC_ENABLE_EXTRA_MATH bc_program_trunc, #endif // BC_ENABLE_EXTRA_MATH }; const char bc_program_exprs_name[] = ""; const char bc_program_stdin_name[] = ""; #if BC_ENABLE_SIGNALS const char bc_program_ready_msg[] = "ready for more input\n"; const size_t bc_program_ready_msg_len = sizeof(bc_program_ready_msg) - 1; #endif // BC_ENABLE_SIGNALS const char bc_program_esc_chars[] = "ab\\efnqrt"; const char bc_program_esc_seqs[] = "\a\b\\\\\f\n\"\r\t"; static void bc_vec_grow(BcVec *restrict v, size_t n) { size_t len, cap = v->cap; len = bc_vm_growSize(v->len, n); while (cap < len) cap = bc_vm_growSize(cap, cap); v->v = bc_vm_realloc(v->v, bc_vm_arraySize(cap, v->size)); v->cap = cap; } void bc_vec_init(BcVec *restrict v, size_t esize, BcVecFree dtor) { assert(v != NULL && esize); v->size = esize; v->cap = BC_VEC_START_CAP; v->len = 0; v->dtor = dtor; v->v = bc_vm_malloc(bc_vm_arraySize(BC_VEC_START_CAP, esize)); } void bc_vec_expand(BcVec *restrict v, size_t req) { assert(v != NULL); if (v->cap < req) { v->v = bc_vm_realloc(v->v, bc_vm_arraySize(req, v->size)); v->cap = req; } } void bc_vec_npop(BcVec *restrict v, size_t n) { assert(v != NULL && n <= v->len); if (v->dtor == NULL) v->len -= n; else { size_t len = v->len - n; while (v->len > len) v->dtor(v->v + (v->size * --v->len)); } } void bc_vec_npush(BcVec *restrict v, size_t n, const void *data) { assert(v != NULL && data != NULL); if (v->len + n > v->cap) bc_vec_grow(v, n); memcpy(v->v + (v->size * v->len), data, v->size * n); v->len += n; } void bc_vec_push(BcVec *restrict v, const void *data) { bc_vec_npush(v, 1, data); } void bc_vec_pushByte(BcVec *restrict v, uchar data) { assert(v != NULL && v->size == sizeof(uchar)); bc_vec_push(v, &data); } void bc_vec_pushIndex(BcVec *restrict v, size_t idx) { uchar amt, nums[sizeof(size_t)]; assert(v != NULL); assert(v->size == sizeof(uchar)); for (amt = 0; idx; ++amt) { nums[amt] = (uchar) idx; idx &= ((size_t) ~(UCHAR_MAX)); idx >>= sizeof(uchar) * CHAR_BIT; } bc_vec_push(v, &amt); bc_vec_npush(v, amt, nums); } static void bc_vec_pushAt(BcVec *restrict v, const void *data, size_t idx) { assert(v != NULL && data != NULL && idx <= v->len); if (idx == v->len) bc_vec_push(v, data); else { char *ptr; if (v->len == v->cap) bc_vec_grow(v, 1); ptr = v->v + v->size * idx; memmove(ptr + v->size, ptr, v->size * (v->len++ - idx)); memmove(ptr, data, v->size); } } void bc_vec_string(BcVec *restrict v, size_t len, const char *restrict str) { assert(v != NULL && v->size == sizeof(char)); assert(!v->len || !v->v[v->len - 1]); assert(v->v != str); bc_vec_npop(v, v->len); bc_vec_expand(v, bc_vm_growSize(len, 1)); memcpy(v->v, str, len); v->len = len; bc_vec_pushByte(v, '\0'); } void bc_vec_concat(BcVec *restrict v, const char *restrict str) { assert(v != NULL && v->size == sizeof(char)); assert(!v->len || !v->v[v->len - 1]); assert(v->v != str); if (v->len) bc_vec_pop(v); bc_vec_npush(v, strlen(str) + 1, str); } void bc_vec_empty(BcVec *restrict v) { assert(v != NULL && v->size == sizeof(char)); bc_vec_npop(v, v->len); bc_vec_pushByte(v, '\0'); } #if BC_ENABLE_HISTORY void bc_vec_popAt(BcVec *restrict v, size_t idx) { char* ptr, *data; assert(v != NULL); assert(idx + 1 < v->len); ptr = bc_vec_item(v, idx); data = bc_vec_item(v, idx + 1); if (v->dtor != NULL) v->dtor(ptr); v->len -= 1; memmove(ptr, data, v->len * v->size); } void bc_vec_replaceAt(BcVec *restrict v, size_t idx, const void *data) { char *ptr; assert(v != NULL); ptr = bc_vec_item(v, idx); if (v->dtor != NULL) v->dtor(ptr); memcpy(ptr, data, v->size); } #endif // BC_ENABLE_HISTORY void* bc_vec_item(const BcVec *restrict v, size_t idx) { assert(v != NULL && v->len && idx < v->len); return v->v + v->size * idx; } void* bc_vec_item_rev(const BcVec *restrict v, size_t idx) { assert(v != NULL && v->len && idx < v->len); return v->v + v->size * (v->len - idx - 1); } void bc_vec_free(void *vec) { BcVec *v = (BcVec*) vec; bc_vec_npop(v, v->len); free(v->v); } static size_t bc_map_find(const BcVec *restrict v, const BcId *restrict ptr) { size_t low = 0, high = v->len; while (low < high) { size_t mid = (low + high) / 2; BcId *id = bc_vec_item(v, mid); int result = bc_id_cmp(ptr, id); if (!result) return mid; else if (result < 0) high = mid; else low = mid + 1; } return low; } bool bc_map_insert(BcVec *restrict v, const BcId *restrict ptr, size_t *restrict i) { assert(v != NULL && ptr != NULL && i != NULL); *i = bc_map_find(v, ptr); assert(*i <= v->len); if (*i == v->len) bc_vec_push(v, ptr); else if (!bc_id_cmp(ptr, bc_vec_item(v, *i))) return false; else bc_vec_pushAt(v, ptr, *i); return true; } size_t bc_map_index(const BcVec *restrict v, const BcId *restrict ptr) { size_t i; assert(v != NULL && ptr != NULL); 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; } static const struct option bc_args_lopt[] = { { "expression", required_argument, NULL, 'e' }, { "file", required_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, { "interactive", no_argument, NULL, 'i' }, { "no-prompt", no_argument, NULL, 'P' }, #if BC_ENABLED { "global-stacks", no_argument, NULL, 'g' }, { "mathlib", no_argument, NULL, 'l' }, { "quiet", no_argument, NULL, 'q' }, { "standard", no_argument, NULL, 's' }, { "warn", no_argument, NULL, 'w' }, #endif // BC_ENABLED { "version", no_argument, NULL, 'v' }, #if DC_ENABLED { "extended-register", no_argument, NULL, 'x' }, #endif // DC_ENABLED { 0, 0, 0, 0 }, }; #if !BC_ENABLED static const char* const bc_args_opt = "e:f:hiPvVx"; #elif !DC_ENABLED static const char* const bc_args_opt = "e:f:ghilPqsvVw"; #else // BC_ENABLED && DC_ENABLED static const char* const bc_args_opt = "e:f:ghilPqsvVwx"; #endif // BC_ENABLED && DC_ENABLED static void bc_args_exprs(BcVec *exprs, const char *str) { bc_vec_concat(exprs, str); bc_vec_concat(exprs, "\n"); } static BcStatus bc_args_file(BcVec *exprs, const char *file) { BcStatus s; char *buf; vm->file = file; s = bc_read_file(file, &buf); if (BC_ERR(s)) return s; bc_args_exprs(exprs, buf); free(buf); return s; } BcStatus bc_args(int argc, char *argv[]) { BcStatus s = BC_STATUS_SUCCESS; int c, i, err = 0; bool do_exit = false, version = false; i = optind = 0; while ((c = getopt_long(argc, argv, bc_args_opt, bc_args_lopt, &i)) != -1) { switch (c) { case 0: { // This is the case when a long option is found. break; } case 'e': { bc_args_exprs(&vm->exprs, optarg); break; } case 'f': { s = bc_args_file(&vm->exprs, optarg); if (BC_ERR(s)) return s; break; } case 'h': { bc_vm_info(vm->help); do_exit = true; break; } case 'i': { vm->flags |= BC_FLAG_I; break; } case 'P': { vm->flags |= BC_FLAG_P; break; } #if BC_ENABLED case 'g': { if (BC_ERR(!BC_IS_BC)) err = c; vm->flags |= BC_FLAG_G; break; } case 'l': { if (BC_ERR(!BC_IS_BC)) err = c; vm->flags |= BC_FLAG_L; break; } case 'q': { if (BC_ERR(!BC_IS_BC)) err = c; vm->flags |= BC_FLAG_Q; break; } case 's': { if (BC_ERR(!BC_IS_BC)) err = c; vm->flags |= BC_FLAG_S; break; } case 'w': { if (BC_ERR(!BC_IS_BC)) err = c; vm->flags |= BC_FLAG_W; break; } #endif // BC_ENABLED case 'V': case 'v': { do_exit = version = true; break; } #if DC_ENABLED case 'x': { if (BC_ERR(BC_IS_BC)) err = c; vm->flags |= DC_FLAG_X; break; } #endif // DC_ENABLED // Getopt printed an error message, but we should exit. case '?': default: { return BC_STATUS_ERROR_FATAL; } } if (BC_ERR(err)) { for (i = 0; bc_args_lopt[i].name != NULL; ++i) { if (bc_args_lopt[i].val == err) break; } return bc_vm_verr(BC_ERROR_FATAL_OPTION, err, bc_args_lopt[i].name); } } if (version) bc_vm_info(NULL); if (do_exit) exit((int) s); if (vm->exprs.len > 1 || !BC_IS_BC) vm->flags |= BC_FLAG_Q; if (argv[optind] != NULL && !strcmp(argv[optind], "--")) ++optind; for (i = optind; i < argc; ++i) bc_vec_push(&vm->files, argv + i); return s; } static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); static ssize_t bc_num_neg(size_t n, bool neg) { return (((ssize_t) n) ^ -((ssize_t) neg)) + neg; } ssize_t bc_num_cmpZero(const BcNum *n) { return bc_num_neg((n)->len != 0, (n)->neg); } static size_t bc_num_int(const BcNum *n) { return n->len ? n->len - n->rdx : 0; } static void bc_num_expand(BcNum *restrict n, size_t req) { assert(n != NULL); req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; if (req > n->cap) { n->num = bc_vm_realloc(n->num, BC_NUM_SIZE(req)); n->cap = req; } } static void bc_num_setToZero(BcNum *restrict n, size_t scale) { assert(n != NULL); n->scale = scale; n->len = n->rdx = 0; n->neg = false; } static void bc_num_zero(BcNum *restrict n) { bc_num_setToZero(n, 0); } void bc_num_one(BcNum *restrict n) { bc_num_zero(n); n->len = 1; n->num[0] = 1; } static void bc_num_clean(BcNum *restrict n) { while (BC_NUM_NONZERO(n) && !n->num[n->len - 1]) n->len -= 1; if (BC_NUM_ZERO(n)) { n->neg = false; n->rdx = 0; } else if (n->len < n->rdx) n->len = n->rdx; } static size_t bc_num_log10(size_t i) { size_t len; for (len = 1; i; i /= BC_BASE, ++len); assert(len - 1 <= BC_BASE_DIGS + 1); return len - 1; } static size_t bc_num_zeroDigits(const BcDig *n) { return BC_BASE_DIGS - bc_num_log10((size_t) *n); } static size_t bc_num_intDigits(const BcNum *n) { size_t digits = bc_num_int(n) * BC_BASE_DIGS; if (digits > 0) digits -= bc_num_zeroDigits(n->num + n->len - 1); return digits; } static size_t bc_num_nonzeroLen(const BcNum *restrict n) { size_t i, len = n->len; assert(len == n->rdx); for (i = len - 1; i < len && !n->num[i]; --i); assert(i + 1 > 0); return i + 1; } static BcDig bc_num_addDigits(BcDig a, BcDig b, bool *carry) { assert(((BcBigDig) BC_BASE_POW) * 2 == ((BcDig) BC_BASE_POW) * 2); assert(a < BC_BASE_POW); assert(b < BC_BASE_POW); a += b + *carry; *carry = (a >= BC_BASE_POW); if (*carry) a -= BC_BASE_POW; assert(a >= 0); assert(a < BC_BASE_POW); return a; } static BcDig bc_num_subDigits(BcDig a, BcDig b, bool *carry) { assert(a < BC_BASE_POW); assert(b < BC_BASE_POW); b += *carry; *carry = (a < b); if (*carry) a += BC_BASE_POW; assert(a - b >= 0); assert(a - b < BC_BASE_POW); return a - b; } static BcStatus bc_num_addArrays(BcDig *restrict a, const BcDig *restrict b, size_t len) { size_t i; bool carry = false; for (i = 0; BC_NO_SIG && i < len; ++i) a[i] = bc_num_addDigits(a[i], b[i], &carry); for (; BC_NO_SIG && carry; ++i) a[i] = bc_num_addDigits(a[i], 0, &carry); return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } static BcStatus bc_num_subArrays(BcDig *restrict a, const BcDig *restrict b, size_t len) { size_t i; bool carry = false; for (i = 0; BC_NO_SIG && i < len; ++i) a[i] = bc_num_subDigits(a[i], b[i], &carry); for (; BC_NO_SIG && carry; ++i) a[i] = bc_num_subDigits(a[i], 0, &carry); return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } static BcStatus bc_num_mulArray(const BcNum *restrict a, BcBigDig b, BcNum *restrict c) { size_t i; BcBigDig carry = 0; assert(b <= BC_BASE_POW); if (a->len + 1 > c->cap) bc_num_expand(c, a->len + 1); memset(c->num, 0, BC_NUM_SIZE(c->cap)); for (i = 0; BC_NO_SIG && i < a->len; ++i) { BcBigDig in = ((BcBigDig) a->num[i]) * b + carry; c->num[i] = in % BC_BASE_POW; carry = in / BC_BASE_POW; } if (BC_NO_SIG) { assert(carry < BC_BASE_POW); c->num[i] = (BcDig) carry; c->len = a->len; c->len += (carry != 0); } bc_num_clean(c); assert(!c->neg || BC_NUM_NONZERO(c)); assert(c->rdx <= c->len || !c->len || BC_SIG); assert(!c->len || c->num[c->len - 1] || c->rdx == c->len); return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } static BcStatus bc_num_divArray(const BcNum *restrict a, BcBigDig b, BcNum *restrict c, BcBigDig *rem) { size_t i; BcBigDig carry = 0; assert(c->cap >= a->len); for (i = a->len - 1; BC_NO_SIG && i < a->len; --i) { BcBigDig in = ((BcBigDig) a->num[i]) + carry * BC_BASE_POW; assert(in / b < BC_BASE_POW); c->num[i] = (BcDig) (in / b); carry = in % b; } c->len = a->len; bc_num_clean(c); *rem = carry; assert(!c->neg || BC_NUM_NONZERO(c)); assert(c->rdx <= c->len || !c->len || BC_SIG); assert(!c->len || c->num[c->len - 1] || c->rdx == c->len); return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } static ssize_t bc_num_compare(const BcDig *restrict a, const BcDig *restrict b, size_t len) { size_t i; BcDig c = 0; for (i = len - 1; BC_NO_SIG && i < len && !(c = a[i] - b[i]); --i); return BC_SIG ? BC_NUM_CMP_SIGNAL_VAL : bc_num_neg(i + 1, c < 0); } ssize_t bc_num_cmp(const BcNum *a, const BcNum *b) { size_t i, min, a_int, b_int, diff; BcDig *max_num, *min_num; bool a_max, neg = false; ssize_t cmp; assert(a != NULL && b != NULL); if (a == b) return 0; if (BC_NUM_ZERO(a)) return bc_num_neg(b->len != 0, !b->neg); if (BC_NUM_ZERO(b)) return bc_num_cmpZero(a); if (a->neg) { if (b->neg) neg = true; else return -1; } else if (b->neg) return 1; a_int = bc_num_int(a); b_int = bc_num_int(b); a_int -= b_int; a_max = (a->rdx > b->rdx); if (a_int) return (ssize_t) a_int; if (a_max) { min = b->rdx; diff = a->rdx - b->rdx; max_num = a->num + diff; min_num = b->num; } else { min = a->rdx; diff = b->rdx - a->rdx; max_num = b->num + diff; min_num = a->num; } cmp = bc_num_compare(max_num, min_num, b_int + min); #if BC_ENABLE_SIGNALS if (BC_NUM_CMP_SIGNAL(cmp)) return cmp; #endif // BC_ENABLE_SIGNALS if (cmp) return bc_num_neg((size_t) cmp, !a_max == !neg); for (max_num -= diff, i = diff - 1; BC_NO_SIG && i < diff; --i) { if (max_num[i]) return bc_num_neg(1, !a_max == !neg); } return BC_SIG ? BC_NUM_CMP_SIGNAL_VAL : 0; } void bc_num_truncate(BcNum *restrict n, size_t places) { size_t places_rdx; if (!places) return; places_rdx = n->rdx ? n->rdx - BC_NUM_RDX(n->scale - places) : 0; assert(places <= n->scale && (BC_NUM_ZERO(n) || places_rdx <= n->len)); n->scale -= places; n->rdx -= places_rdx; if (BC_NUM_NONZERO(n)) { size_t pow; pow = n->scale % BC_BASE_DIGS; pow = pow ? BC_BASE_DIGS - pow : 0; pow = bc_num_pow10[pow]; n->len -= places_rdx; memmove(n->num, n->num + places_rdx, BC_NUM_SIZE(n->len)); // Clear the lower part of the last digit. if (BC_NUM_NONZERO(n)) n->num[0] -= n->num[0] % (BcDig) pow; bc_num_clean(n); } } static void bc_num_extend(BcNum *restrict n, size_t places) { size_t places_rdx; if (!places) return; if (BC_NUM_ZERO(n)) { n->scale += places; return; } places_rdx = BC_NUM_RDX(places + n->scale) - n->rdx; if (places_rdx) { bc_num_expand(n, bc_vm_growSize(n->len, places_rdx)); memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len)); memset(n->num, 0, BC_NUM_SIZE(places_rdx)); } n->rdx += places_rdx; n->scale += places; n->len += places_rdx; assert(n->rdx == BC_NUM_RDX(n->scale)); } static void bc_num_retireMul(BcNum *restrict n, size_t scale, bool neg1, bool neg2) { if (n->scale < scale) bc_num_extend(n, scale - n->scale); else bc_num_truncate(n, n->scale - scale); bc_num_clean(n); if (BC_NUM_NONZERO(n)) n->neg = (!neg1 != !neg2); } static void bc_num_split(const BcNum *restrict n, size_t idx, BcNum *restrict a, BcNum *restrict b) { assert(BC_NUM_ZERO(a)); assert(BC_NUM_ZERO(b)); if (idx < n->len) { b->len = n->len - idx; a->len = idx; a->scale = a->rdx = b->scale = b->rdx = 0; assert(a->cap >= a->len); assert(b->cap >= b->len); memcpy(b->num, n->num + idx, BC_NUM_SIZE(b->len)); memcpy(a->num, n->num, BC_NUM_SIZE(idx)); bc_num_clean(b); } else bc_num_copy(a, n); bc_num_clean(a); } static size_t bc_num_shiftZero(BcNum *restrict n) { size_t i; assert(!n->rdx || BC_NUM_ZERO(n)); for (i = 0; i < n->len && !n->num[i]; ++i); n->len -= i; n->num += i; return i; } static void bc_num_unshiftZero(BcNum *restrict n, size_t places_rdx) { n->len += places_rdx; n->num -= places_rdx; } static BcStatus bc_num_shift(BcNum *restrict n, BcBigDig dig) { size_t i, len = n->len; BcBigDig carry = 0, pow; BcDig *ptr = n->num; assert(dig < BC_BASE_DIGS); pow = bc_num_pow10[dig]; dig = bc_num_pow10[BC_BASE_DIGS - dig]; for (i = len - 1; BC_NO_SIG && i < len; --i) { BcBigDig in, temp; in = ((BcBigDig) ptr[i]); temp = carry * dig; carry = in % pow; ptr[i] = ((BcDig) (in / pow)) + (BcDig) temp; } assert(!carry); return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } static BcStatus bc_num_shiftLeft(BcNum *restrict n, size_t places) { BcStatus s = BC_STATUS_SUCCESS; BcBigDig dig; size_t places_rdx; bool shift; if (!places) return s; if (places > n->scale) { size_t size = bc_vm_growSize(BC_NUM_RDX(places - n->scale), n->len); if (size > SIZE_MAX - 1) return bc_vm_err(BC_ERROR_MATH_OVERFLOW); } if (BC_NUM_ZERO(n)) { if (n->scale >= places) n->scale -= places; else n->scale = 0; return s; } dig = (BcBigDig) (places % BC_BASE_DIGS); shift = (dig != 0); places_rdx = BC_NUM_RDX(places); if (n->scale) { if (n->rdx >= places_rdx) { size_t mod = n->scale % BC_BASE_DIGS, revdig; mod = mod ? mod : BC_BASE_DIGS; revdig = dig ? BC_BASE_DIGS - dig : 0; if (mod + revdig > BC_BASE_DIGS) places_rdx = 1; else places_rdx = 0; } else places_rdx -= n->rdx; } if (places_rdx) { bc_num_expand(n, bc_vm_growSize(n->len, places_rdx)); memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len)); memset(n->num, 0, BC_NUM_SIZE(places_rdx)); n->len += places_rdx; } if (places > n->scale) n->scale = n->rdx = 0; else { n->scale -= places; n->rdx = BC_NUM_RDX(n->scale); } if (shift) s = bc_num_shift(n, BC_BASE_DIGS - dig); bc_num_clean(n); return BC_SIG && !s ? BC_STATUS_SIGNAL : s; } static BcStatus bc_num_shiftRight(BcNum *restrict n, size_t places) { BcStatus s = BC_STATUS_SUCCESS; BcBigDig dig; size_t places_rdx, scale, scale_mod, int_len, expand; bool shift; if (!places) return s; if (BC_NUM_ZERO(n)) { n->scale += places; bc_num_expand(n, BC_NUM_RDX(n->scale)); return s; } dig = (BcBigDig) (places % BC_BASE_DIGS); shift = (dig != 0); scale = n->scale; scale_mod = scale % BC_BASE_DIGS; scale_mod = scale_mod ? scale_mod : BC_BASE_DIGS; int_len = bc_num_int(n); places_rdx = BC_NUM_RDX(places); if (scale_mod + dig > BC_BASE_DIGS) { expand = places_rdx - 1; places_rdx = 1; } else { expand = places_rdx; places_rdx = 0; } if (expand > int_len) expand -= int_len; else expand = 0; bc_num_extend(n, places_rdx * BC_BASE_DIGS); bc_num_expand(n, bc_vm_growSize(expand, n->len)); memset(n->num + n->len, 0, BC_NUM_SIZE(expand)); n->len += expand; n->scale = n->rdx = 0; if (shift) s = bc_num_shift(n, dig); n->scale = scale + places; n->rdx = BC_NUM_RDX(n->scale); bc_num_clean(n); assert(n->rdx <= n->len && n->len <= n->cap); assert(n->rdx == BC_NUM_RDX(n->scale)); return BC_SIG && !s ? BC_STATUS_SIGNAL : s; } static BcStatus bc_num_inv(BcNum *a, BcNum *b, size_t scale) { BcNum one; BcDig num[2]; assert(BC_NUM_NONZERO(a)); bc_num_setup(&one, num, sizeof(num) / sizeof(BcDig)); bc_num_one(&one); return bc_num_div(&one, a, b, scale); } #if BC_ENABLE_EXTRA_MATH static BcStatus bc_num_intop(const BcNum *a, const BcNum *b, BcNum *restrict c, BcBigDig *v) { if (BC_ERR(b->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER); bc_num_copy(c, a); return bc_num_bigdig(b, v); } #endif // BC_ENABLE_EXTRA_MATH static BcStatus bc_num_as(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) { BcDig *ptr_c, *ptr_l, *ptr_r; size_t i, min_rdx, max_rdx, diff, a_int, b_int, min_len, max_len, max_int; size_t len_l, len_r; bool b_neg, do_sub, do_rev_sub, carry; // Because this function doesn't need to use scale (per the bc spec), // I am hijacking it to say whether it's doing an add or a subtract. // Convert substraction to addition of negative second operand. if (BC_NUM_ZERO(b)) { bc_num_copy(c, a); return BC_STATUS_SUCCESS; } if (BC_NUM_ZERO(a)) { bc_num_copy(c, b); c->neg = (b->neg != sub); return BC_STATUS_SUCCESS; } b_neg = (b->neg != sub); do_sub = (a->neg != b_neg); a_int = bc_num_int(a); b_int = bc_num_int(b); max_int = BC_MAX(a_int, b_int); min_rdx = BC_MIN(a->rdx, b->rdx); max_rdx = BC_MAX(a->rdx, b->rdx); diff = max_rdx - min_rdx; max_len = max_int + max_rdx; if (do_sub) { if (a_int != b_int) do_rev_sub = (a_int < b_int); else if (a->rdx > b->rdx) do_rev_sub = (bc_num_compare(a->num + diff, b->num, b->len) < 0); else do_rev_sub = (bc_num_compare(a->num, b->num + diff, a->len) <= 0); } else { max_len += 1; do_rev_sub = false; } assert(max_len <= c->cap); if (do_rev_sub) { ptr_l = b->num; ptr_r = a->num; len_l = b->len; len_r = a->len; } else { ptr_l = a->num; ptr_r = b->num; len_l = a->len; len_r = b->len; } ptr_c = c->num; carry = false; if (diff) { if ((a->rdx > b->rdx) != do_rev_sub) { memcpy(ptr_c, ptr_l, BC_NUM_SIZE(diff)); ptr_l += diff; len_l -= diff; } else { if (do_sub) { for (i = 0; BC_NO_SIG && i < diff; i++) ptr_c[i] = bc_num_subDigits(0, ptr_r[i], &carry); if (BC_SIG) return BC_STATUS_SIGNAL; } else memcpy(ptr_c, ptr_r, BC_NUM_SIZE(diff)); ptr_r += diff; len_r -= diff; } ptr_c += diff; } min_len = BC_MIN(len_l, len_r); if (do_sub) { for (i = 0; BC_NO_SIG && i < min_len; i++) ptr_c[i] = bc_num_subDigits(ptr_l[i], ptr_r[i], &carry); for (; BC_NO_SIG && i < len_l; i++) ptr_c[i] = bc_num_subDigits(ptr_l[i], 0, &carry); for (; BC_NO_SIG && i < len_r; i++) ptr_c[i] = bc_num_subDigits(0, ptr_r[i], &carry); for (; BC_NO_SIG && i < max_len - diff; i++) ptr_c[i] = bc_num_subDigits(0, 0, &carry); } else { for (i = 0; BC_NO_SIG && i < min_len; i++) ptr_c[i] = bc_num_addDigits(ptr_l[i], ptr_r[i], &carry); for (; BC_NO_SIG && i < len_l; i++) ptr_c[i] = bc_num_addDigits(ptr_l[i], 0, &carry); for (; BC_NO_SIG && i < len_r; i++) ptr_c[i] = bc_num_addDigits(0, ptr_r[i], &carry); for (; BC_NO_SIG && i < max_len - diff; i++) ptr_c[i] = bc_num_addDigits(0, 0, &carry); } if (BC_NO_SIG) { assert(carry == false); // The result has the same sign as a, unless the operation was a // reverse subtraction (b - a). c->neg = (a->neg != do_rev_sub); c->len = max_len; c->rdx = max_rdx; c->scale = BC_MAX(a->scale, b->scale); bc_num_clean(c); } return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } static BcStatus bc_num_m_simp(const BcNum *a, const BcNum *b, BcNum *restrict c) { size_t i, alen = a->len, blen = b->len, clen; BcDig *ptr_a = a->num, *ptr_b = b->num, *ptr_c; BcBigDig sum = 0, carry = 0; assert(sizeof(sum) >= sizeof(BcDig) * 2); assert(!a->rdx && !b->rdx); clen = bc_vm_growSize(alen, blen); bc_num_expand(c, bc_vm_growSize(clen, 1)); ptr_c = c->num; memset(ptr_c, 0, BC_NUM_SIZE(c->cap)); for (i = 0; BC_NO_SIG && i < clen; ++i) { ssize_t sidx = (ssize_t) (i - blen + 1); size_t j = (size_t) BC_MAX(0, sidx), k = BC_MIN(i, blen - 1); for (; BC_NO_SIG && j < alen && k < blen; ++j, --k) { sum += ((BcBigDig) ptr_a[j]) * ((BcBigDig) ptr_b[k]); if (sum >= ((BcBigDig) BC_BASE_POW) * BC_BASE_POW) { carry += sum / BC_BASE_POW; sum %= BC_BASE_POW; } } if (sum >= BC_BASE_POW) { carry += sum / BC_BASE_POW; sum %= BC_BASE_POW; } ptr_c[i] = (BcDig) sum; assert(ptr_c[i] < BC_BASE_POW); sum = carry; carry = 0; } if (sum) { assert(sum < BC_BASE_POW); ptr_c[clen] = (BcDig) sum; clen += 1; } c->len = clen; return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } static BcStatus bc_num_shiftAddSub(BcNum *restrict n, const BcNum *restrict a, size_t shift, BcNumShiftAddOp op) { assert(n->len >= shift + a->len); assert(!n->rdx && !a->rdx); return op(n->num + shift, a->num, a->len); } static BcStatus bc_num_k(BcNum *a, BcNum *b, BcNum *restrict c) { BcStatus s; size_t max, max2, total; BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp; BcDig *digs, *dig_ptr; BcNumShiftAddOp op; bool aone = BC_NUM_ONE(a); assert(BC_NUM_ZERO(c)); // This is here because the function is recursive. if (BC_SIG) return BC_STATUS_SIGNAL; if (BC_NUM_ZERO(a) || BC_NUM_ZERO(b)) return BC_STATUS_SUCCESS; if (aone || BC_NUM_ONE(b)) { bc_num_copy(c, aone ? b : a); if ((aone && a->neg) || b->neg) c->neg = !c->neg; return BC_STATUS_SUCCESS; } if (a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN) return bc_num_m_simp(a, b, c); max = BC_MAX(a->len, b->len); max = BC_MAX(max, BC_NUM_DEF_SIZE); max2 = (max + 1) / 2; total = bc_vm_arraySize(BC_NUM_KARATSUBA_ALLOCS, max); digs = dig_ptr = bc_vm_malloc(BC_NUM_SIZE(total)); bc_num_setup(&l1, dig_ptr, max); dig_ptr += max; bc_num_setup(&h1, dig_ptr, max); dig_ptr += max; bc_num_setup(&l2, dig_ptr, max); dig_ptr += max; bc_num_setup(&h2, dig_ptr, max); dig_ptr += max; bc_num_setup(&m1, dig_ptr, max); dig_ptr += max; bc_num_setup(&m2, dig_ptr, max); max = bc_vm_growSize(max, 1); bc_num_init(&z0, max); bc_num_init(&z1, max); bc_num_init(&z2, max); max = bc_vm_growSize(max, max) + 1; bc_num_init(&temp, max); bc_num_split(a, max2, &l1, &h1); bc_num_split(b, max2, &l2, &h2); bc_num_expand(c, max); c->len = max; memset(c->num, 0, BC_NUM_SIZE(c->len)); s = bc_num_sub(&h1, &l1, &m1, 0); if (BC_ERR(s)) goto err; s = bc_num_sub(&l2, &h2, &m2, 0); if (BC_ERR(s)) goto err; if (BC_NUM_NONZERO(&h1) && BC_NUM_NONZERO(&h2)) { s = bc_num_m(&h1, &h2, &z2, 0); if (BC_ERR(s)) goto err; bc_num_clean(&z2); s = bc_num_shiftAddSub(c, &z2, max2 * 2, bc_num_addArrays); if (BC_ERR(s)) goto err; s = bc_num_shiftAddSub(c, &z2, max2, bc_num_addArrays); if (BC_ERR(s)) goto err; } if (BC_NUM_NONZERO(&l1) && BC_NUM_NONZERO(&l2)) { s = bc_num_m(&l1, &l2, &z0, 0); if (BC_ERR(s)) goto err; bc_num_clean(&z0); s = bc_num_shiftAddSub(c, &z0, max2, bc_num_addArrays); if (BC_ERR(s)) goto err; s = bc_num_shiftAddSub(c, &z0, 0, bc_num_addArrays); if (BC_ERR(s)) goto err; } if (BC_NUM_NONZERO(&m1) && BC_NUM_NONZERO(&m2)) { s = bc_num_m(&m1, &m2, &z1, 0); if (BC_ERR(s)) goto err; bc_num_clean(&z1); op = (m1.neg != m2.neg) ? bc_num_subArrays : bc_num_addArrays; s = bc_num_shiftAddSub(c, &z1, max2, op); if (BC_ERR(s)) goto err; } err: free(digs); bc_num_free(&temp); bc_num_free(&z2); bc_num_free(&z1); bc_num_free(&z0); return s; } static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s; BcNum cpa, cpb; size_t ascale, bscale, ardx, brdx, azero = 0, bzero = 0, zero, len, rscale; bc_num_zero(c); ascale = a->scale; bscale = b->scale; scale = BC_MAX(scale, ascale); scale = BC_MAX(scale, bscale); rscale = ascale + bscale; scale = BC_MIN(rscale, scale); if ((a->len == 1 || b->len == 1) && !a->rdx && !b->rdx) { BcNum *operand; BcBigDig dig; if (a->len == 1) { dig = (BcBigDig) a->num[0]; operand = b; } else { dig = (BcBigDig) b->num[0]; operand = a; } s = bc_num_mulArray(operand, dig, c); if (BC_ERROR_SIGNAL_ONLY(s)) return s; if (BC_NUM_NONZERO(c)) c->neg = (a->neg != b->neg); return s; } bc_num_init(&cpa, a->len + a->rdx); bc_num_init(&cpb, b->len + b->rdx); bc_num_copy(&cpa, a); bc_num_copy(&cpb, b); cpa.neg = cpb.neg = false; ardx = cpa.rdx * BC_BASE_DIGS; s = bc_num_shiftLeft(&cpa, ardx); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; bc_num_clean(&cpa); azero = bc_num_shiftZero(&cpa); brdx = cpb.rdx * BC_BASE_DIGS; s = bc_num_shiftLeft(&cpb, brdx); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; bzero = bc_num_shiftZero(&cpb); bc_num_clean(&cpb); s = bc_num_k(&cpa, &cpb, c); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; zero = bc_vm_growSize(azero, bzero); len = bc_vm_growSize(c->len, zero); bc_num_expand(c, len); s = bc_num_shiftLeft(c, (len - c->len) * BC_BASE_DIGS); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; s = bc_num_shiftRight(c, ardx + brdx); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; bc_num_retireMul(c, scale, a->neg, b->neg); err: bc_num_unshiftZero(&cpb, bzero); bc_num_free(&cpb); bc_num_unshiftZero(&cpa, azero); bc_num_free(&cpa); return s; } static bool bc_num_nonZeroDig(BcDig *restrict a, size_t len) { size_t i; bool nonzero = false; for (i = len - 1; !nonzero && i < len; --i) nonzero = (a[i] != 0); return nonzero; } static ssize_t bc_num_divCmp(const BcDig *a, const BcNum *b, size_t len) { ssize_t cmp; if (b->len > len && a[len]) cmp = bc_num_compare(a, b->num, len + 1); else if (b->len <= len) { if (a[len]) cmp = 1; else cmp = bc_num_compare(a, b->num, len); } else cmp = -1; return cmp; } static BcStatus bc_num_divExtend(BcNum *restrict a, BcNum *restrict b, BcBigDig divisor) { BcStatus s; size_t pow; pow = BC_BASE_DIGS - bc_num_log10((size_t) divisor); s = bc_num_shiftLeft(a, pow); if (BC_ERROR_SIGNAL_ONLY(s)) return s; return bc_num_shiftLeft(b, pow); } static BcStatus bc_num_d_long(BcNum *restrict a, BcNum *restrict b, BcNum *restrict c, size_t scale) { BcStatus s = BC_STATUS_SUCCESS; BcBigDig divisor; size_t len, end, i, rdx; BcNum cpb; bool nonzero = false; assert(b->len < a->len); len = b->len; end = a->len - len; assert(len >= 1); bc_num_expand(c, a->len); memset(c->num, 0, c->cap * sizeof(BcDig)); c->rdx = a->rdx; c->scale = a->scale; c->len = a->len; divisor = (BcBigDig) b->num[len - 1]; if (len > 1 && bc_num_nonZeroDig(b->num, len - 1)) { nonzero = (divisor > 1 << ((10 * BC_BASE_DIGS) / 6 + 1)); if (!nonzero) { s = bc_num_divExtend(a, b, divisor); if (BC_ERROR_SIGNAL_ONLY(s)) return s; len = BC_MAX(a->len, b->len); bc_num_expand(a, len + 1); if (len + 1 > a->len) a->len = len + 1; len = b->len; end = a->len - len; divisor = (BcBigDig) b->num[len - 1]; nonzero = bc_num_nonZeroDig(b->num, len - 1); } } divisor += nonzero; bc_num_expand(c, a->len); memset(c->num, 0, BC_NUM_SIZE(c->cap)); assert(c->scale >= scale); rdx = c->rdx - BC_NUM_RDX(scale); bc_num_init(&cpb, len + 1); i = end - 1; for (; BC_NO_SIG && i < end && i >= rdx && BC_NUM_NONZERO(a); --i) { ssize_t cmp; BcDig *n; BcBigDig q, result; n = a->num + i; assert(n >= a->num); result = q = 0; cmp = bc_num_divCmp(n, b, len); #if BC_ENABLE_SIGNALS if (BC_NUM_CMP_SIGNAL(cmp)) goto err; #endif // BC_ENABLE_SIGNALS while (cmp >= 0) { BcBigDig n1, dividend; n1 = (BcBigDig) n[len]; dividend = n1 * BC_BASE_POW + (BcBigDig) n[len - 1]; q = (dividend / divisor); if (q <= 1) { q = 1; s = bc_num_subArrays(n, b->num, len); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; } else { assert(q <= BC_BASE_POW); s = bc_num_mulArray(b, (BcBigDig) q, &cpb); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; s = bc_num_subArrays(n, cpb.num, cpb.len); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; } result += q; assert(result <= BC_BASE_POW); if (nonzero) { cmp = bc_num_divCmp(n, b, len); #if BC_ENABLE_SIGNALS if (BC_NUM_CMP_SIGNAL(cmp)) goto err; #endif // BC_ENABLE_SIGNALS } else cmp = -1; } assert(result < BC_BASE_POW); c->num[i] = (BcDig) result; } err: if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL; bc_num_free(&cpb); return s; } static BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s = BC_STATUS_SUCCESS; size_t len; BcNum cpa, cpb; if (BC_NUM_ZERO(b)) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO); if (BC_NUM_ZERO(a)) { bc_num_setToZero(c, scale); return BC_STATUS_SUCCESS; } if (BC_NUM_ONE(b)) { bc_num_copy(c, a); bc_num_retireMul(c, scale, a->neg, b->neg); return BC_STATUS_SUCCESS; } if (!a->rdx && !b->rdx && b->len == 1 && !scale) { BcBigDig rem; s = bc_num_divArray(a, (BcBigDig) b->num[0], c, &rem); bc_num_retireMul(c, scale, a->neg, b->neg); return s; } len = bc_num_mulReq(a, b, scale); bc_num_init(&cpa, len); bc_num_copy(&cpa, a); bc_num_createCopy(&cpb, b); len = b->len; if (len > cpa.len) { bc_num_expand(&cpa, bc_vm_growSize(len, 2)); bc_num_extend(&cpa, (len - cpa.len) * BC_BASE_DIGS); } cpa.scale = cpa.rdx * BC_BASE_DIGS; bc_num_extend(&cpa, b->scale); cpa.rdx -= BC_NUM_RDX(b->scale); cpa.scale = cpa.rdx * BC_BASE_DIGS; if (scale > cpa.scale) { bc_num_extend(&cpa, scale); cpa.scale = cpa.rdx * BC_BASE_DIGS; } if (cpa.cap == cpa.len) bc_num_expand(&cpa, bc_vm_growSize(cpa.len, 1)); // We want an extra zero in front to make things simpler. cpa.num[cpa.len++] = 0; if (cpa.rdx == cpa.len) cpa.len = bc_num_nonzeroLen(&cpa); if (cpb.rdx == cpb.len) cpb.len = bc_num_nonzeroLen(&cpb); cpb.scale = cpb.rdx = 0; s = bc_num_d_long(&cpa, &cpb, c, scale); if (BC_NO_ERR(!s)) { if (BC_SIG) s = BC_STATUS_SIGNAL; else bc_num_retireMul(c, scale, a->neg, b->neg); } bc_num_free(&cpb); bc_num_free(&cpa); return s; } static BcStatus bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c, BcNum *restrict d, size_t scale, size_t ts) { BcStatus s; BcNum temp; bool neg; if (BC_NUM_ZERO(b)) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO); if (BC_NUM_ZERO(a)) { bc_num_setToZero(c, ts); bc_num_setToZero(d, ts); return BC_STATUS_SUCCESS; } bc_num_init(&temp, d->cap); s = bc_num_d(a, b, c, scale); assert(!s || s == BC_STATUS_SIGNAL); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; if (scale) scale = ts + 1; s = bc_num_m(c, b, &temp, scale); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; s = bc_num_sub(a, &temp, d, scale); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; if (ts > d->scale && BC_NUM_NONZERO(d)) bc_num_extend(d, ts - d->scale); neg = d->neg; bc_num_retireMul(d, ts, a->neg, b->neg); d->neg = BC_NUM_NONZERO(d) ? neg : false; err: bc_num_free(&temp); return s; } static BcStatus bc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s; BcNum c1; size_t ts; ts = bc_vm_growSize(scale, b->scale); ts = BC_MAX(ts, a->scale); bc_num_init(&c1, bc_num_mulReq(a, b, ts)); s = bc_num_r(a, b, &c1, c, scale, ts); bc_num_free(&c1); return s; } static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s = BC_STATUS_SUCCESS; BcNum copy; BcBigDig pow = 0; size_t i, powrdx, resrdx; bool neg, zero; if (BC_ERR(b->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER); if (BC_NUM_ZERO(b)) { bc_num_one(c); return BC_STATUS_SUCCESS; } if (BC_NUM_ZERO(a)) { if (b->neg) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO); bc_num_setToZero(c, scale); return BC_STATUS_SUCCESS; } if (BC_NUM_ONE(b)) { if (!b->neg) bc_num_copy(c, a); else s = bc_num_inv(a, c, scale); return s; } neg = b->neg; b->neg = false; s = bc_num_bigdig(b, &pow); b->neg = neg; if (s) return s; bc_num_createCopy(©, a); if (!neg) { size_t max = BC_MAX(scale, a->scale), scalepow = a->scale * pow; scale = BC_MIN(scalepow, max); } for (powrdx = a->scale; BC_NO_SIG && !(pow & 1); pow >>= 1) { powrdx <<= 1; s = bc_num_mul(©, ©, ©, powrdx); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; } if (BC_SIG) goto sig_err; bc_num_copy(c, ©); resrdx = powrdx; while (BC_NO_SIG && (pow >>= 1)) { powrdx <<= 1; s = bc_num_mul(©, ©, ©, powrdx); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; if (pow & 1) { resrdx += powrdx; s = bc_num_mul(c, ©, c, resrdx); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; } } if (BC_SIG) goto sig_err; if (neg) { s = bc_num_inv(c, c, scale); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; } if (c->scale > scale) bc_num_truncate(c, c->scale - scale); // We can't use bc_num_clean() here. for (zero = true, i = 0; zero && i < c->len; ++i) zero = !c->num[i]; if (zero) bc_num_setToZero(c, scale); sig_err: if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL; err: bc_num_free(©); return s; } #if BC_ENABLE_EXTRA_MATH static BcStatus bc_num_place(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s = BC_STATUS_SUCCESS; BcBigDig val = 0; BC_UNUSED(scale); s = bc_num_intop(a, b, c, &val); if (BC_ERR(s)) return s; if (val < c->scale) bc_num_truncate(c, c->scale - val); else if (val > c->scale) bc_num_extend(c, val - c->scale); return s; } static BcStatus bc_num_left(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s = BC_STATUS_SUCCESS; BcBigDig val = 0; BC_UNUSED(scale); s = bc_num_intop(a, b, c, &val); if (BC_ERR(s)) return s; return bc_num_shiftLeft(c, (size_t) val); } static BcStatus bc_num_right(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { BcStatus s = BC_STATUS_SUCCESS; BcBigDig val = 0; BC_UNUSED(scale); s = bc_num_intop(a, b, c, &val); if (BC_ERR(s)) return s; if (BC_NUM_ZERO(c)) return s; return bc_num_shiftRight(c, (size_t) val); } #endif // BC_ENABLE_EXTRA_MATH static BcStatus bc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale, BcNumBinaryOp op, size_t req) { BcStatus s; BcNum num2, *ptr_a, *ptr_b; bool init = false; assert(a != NULL && b != NULL && c != NULL && op != NULL); if (c == a) { ptr_a = &num2; memcpy(ptr_a, c, sizeof(BcNum)); init = true; } else ptr_a = a; if (c == b) { ptr_b = &num2; if (c != a) { memcpy(ptr_b, c, sizeof(BcNum)); init = true; } } else ptr_b = b; if (init) bc_num_init(c, req); else bc_num_expand(c, req); s = op(ptr_a, ptr_b, c, scale); assert(!c->neg || BC_NUM_NONZERO(c)); assert(c->rdx <= c->len || !c->len || s); assert(!c->len || c->num[c->len - 1] || c->rdx == c->len); if (init) bc_num_free(&num2); return s; } #ifndef NDEBUG static bool bc_num_strValid(const char *val) { bool radix = false; size_t i, len = strlen(val); if (!len) return true; for (i = 0; i < len; ++i) { BcDig c = val[i]; if (c == '.') { if (radix) return false; radix = true; continue; } if (!(isdigit(c) || isupper(c))) return false; } return true; } #endif // NDEBUG static BcBigDig bc_num_parseChar(char c, size_t base_t) { if (isupper(c)) { c = BC_NUM_NUM_LETTER(c); c = ((size_t) c) >= base_t ? (char) base_t - 1 : c; } else c -= '0'; return (BcBigDig) (uchar) c; } static void bc_num_parseDecimal(BcNum *restrict n, const char *restrict val) { size_t len, i, temp, mod; const char *ptr; bool zero = true, rdx; for (i = 0; val[i] == '0'; ++i); val += i; assert(!val[0] || isalnum(val[0]) || val[0] == '.'); // All 0's. We can just return, since this // procedure expects a virgin (already 0) BcNum. if (!val[0]) return; len = strlen(val); ptr = strchr(val, '.'); rdx = (ptr != NULL); for (i = 0; i < len && (zero = (val[i] == '0' || val[i] == '.')); ++i); n->scale = (size_t) (rdx * ((val + len) - (ptr + 1))); n->rdx = BC_NUM_RDX(n->scale); i = len - (ptr == val ? 0 : i) - rdx; temp = BC_NUM_ROUND_POW(i); mod = n->scale % BC_BASE_DIGS; i = mod ? BC_BASE_DIGS - mod : 0; n->len = ((temp + i) / BC_BASE_DIGS); bc_num_expand(n, n->len); memset(n->num, 0, BC_NUM_SIZE(n->len)); if (zero) n->len = n->rdx = 0; else { BcBigDig exp, pow; assert(i <= BC_NUM_BIGDIG_MAX); exp = (BcBigDig) i; pow = bc_num_pow10[exp]; for (i = len - 1; i < len; --i, ++exp) { char c = val[i]; if (c == '.') exp -= 1; else { size_t idx = exp / BC_BASE_DIGS; if (isupper(c)) c = '9'; n->num[idx] += (((BcBigDig) c) - '0') * pow; if ((exp + 1) % BC_BASE_DIGS == 0) pow = 1; else pow *= BC_BASE; } } } } static BcStatus bc_num_parseBase(BcNum *restrict n, const char *restrict val, BcBigDig base) { BcStatus s = BC_STATUS_SUCCESS; BcNum temp, mult1, mult2, result1, result2, *m1, *m2, *ptr; char c = 0; bool zero = true; BcBigDig v; size_t i, digs, len = strlen(val); for (i = 0; zero && i < len; ++i) zero = (val[i] == '.' || val[i] == '0'); if (zero) return BC_STATUS_SUCCESS; bc_num_init(&temp, BC_NUM_BIGDIG_LOG10); bc_num_init(&mult1, BC_NUM_BIGDIG_LOG10); for (i = 0; i < len && (c = val[i]) && c != '.'; ++i) { v = bc_num_parseChar(c, base); s = bc_num_mulArray(n, base, &mult1); if (BC_ERROR_SIGNAL_ONLY(s)) goto int_err; bc_num_bigdig2num(&temp, v); s = bc_num_add(&mult1, &temp, n, 0); if (BC_ERROR_SIGNAL_ONLY(s)) goto int_err; } if (i == len && !(c = val[i])) goto int_err; assert(c == '.'); bc_num_init(&mult2, BC_NUM_BIGDIG_LOG10); bc_num_init(&result1, BC_NUM_DEF_SIZE); bc_num_init(&result2, BC_NUM_DEF_SIZE); bc_num_one(&mult1); m1 = &mult1; m2 = &mult2; for (i += 1, digs = 0; BC_NO_SIG && i < len && (c = val[i]); ++i, ++digs) { v = bc_num_parseChar(c, base); s = bc_num_mulArray(&result1, base, &result2); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; bc_num_bigdig2num(&temp, v); s = bc_num_add(&result2, &temp, &result1, 0); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; s = bc_num_mulArray(m1, base, m2); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; if (m2->len < m2->rdx) m2->len = m2->rdx; ptr = m1; m1 = m2; m2 = ptr; } if (BC_SIG) { s = BC_STATUS_SIGNAL; goto err; } // This one cannot be a divide by 0 because mult starts out at 1, then is // multiplied by base, and base cannot be 0, so mult cannot be 0. s = bc_num_div(&result1, m1, &result2, digs * 2); assert(!s || s == BC_STATUS_SIGNAL); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; bc_num_truncate(&result2, digs); s = bc_num_add(n, &result2, n, digs); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; if (BC_NUM_NONZERO(n)) { if (n->scale < digs) bc_num_extend(n, digs - n->scale); } else bc_num_zero(n); err: bc_num_free(&result2); bc_num_free(&result1); bc_num_free(&mult2); int_err: bc_num_free(&mult1); bc_num_free(&temp); return s; } static void bc_num_printNewline(void) { if (vm->nchars >= (size_t) (vm->line_len - 1)) { bc_vm_putchar('\\'); bc_vm_putchar('\n'); } } static void bc_num_putchar(int c) { if (c != '\n') bc_num_printNewline(); bc_vm_putchar(c); } #if DC_ENABLED static void bc_num_printChar(size_t n, size_t len, bool rdx) { BC_UNUSED(rdx); BC_UNUSED(len); assert(len == 1); bc_vm_putchar((uchar) n); } #endif // DC_ENABLED static void bc_num_printDigits(size_t n, size_t len, bool rdx) { size_t exp, pow; bc_num_putchar(rdx ? '.' : ' '); for (exp = 0, pow = 1; exp < len - 1; ++exp, pow *= BC_BASE); for (exp = 0; exp < len; pow /= BC_BASE, ++exp) { size_t dig = n / pow; n -= dig * pow; bc_num_putchar(((uchar) dig) + '0'); } } static void bc_num_printHex(size_t n, size_t len, bool rdx) { BC_UNUSED(len); assert(len == 1); if (rdx) bc_num_putchar('.'); bc_num_putchar(bc_num_hex_digits[n]); } static void bc_num_printDecimal(const BcNum *restrict n) { size_t i, j, rdx = n->rdx; bool zero = true; size_t buffer[BC_BASE_DIGS]; if (n->neg) bc_num_putchar('-'); for (i = n->len - 1; i < n->len; --i) { BcDig n9 = n->num[i]; size_t temp; bool irdx = (i == rdx - 1); zero = (zero & !irdx); temp = n->scale % BC_BASE_DIGS; temp = i || !temp ? 0 : BC_BASE_DIGS - temp; memset(buffer, 0, BC_BASE_DIGS * sizeof(size_t)); for (j = 0; n9 && j < BC_BASE_DIGS; ++j) { buffer[j] = n9 % BC_BASE; n9 /= BC_BASE; } for (j = BC_BASE_DIGS - 1; j < BC_BASE_DIGS && j >= temp; --j) { bool print_rdx = (irdx & (j == BC_BASE_DIGS - 1)); zero = (zero && buffer[j] == 0); if (!zero) bc_num_printHex(buffer[j], 1, print_rdx); } } } #if BC_ENABLE_EXTRA_MATH static BcStatus bc_num_printExponent(const BcNum *restrict n, bool eng) { BcStatus s = BC_STATUS_SUCCESS; bool neg = (n->len <= n->rdx); BcNum temp, exp; size_t places, mod; BcDig digs[BC_NUM_BIGDIG_LOG10]; bc_num_createCopy(&temp, n); if (neg) { size_t i, idx = bc_num_nonzeroLen(n) - 1; places = 1; for (i = BC_BASE_DIGS - 1; i < BC_BASE_DIGS; --i) { if (bc_num_pow10[i] > (BcBigDig) n->num[idx]) places += 1; else break; } places += (n->rdx - (idx + 1)) * BC_BASE_DIGS; mod = places % 3; if (eng && mod != 0) places += 3 - mod; s = bc_num_shiftLeft(&temp, places); if (BC_ERROR_SIGNAL_ONLY(s)) goto exit; } else { places = bc_num_intDigits(n) - 1; mod = places % 3; if (eng && mod != 0) places -= 3 - (3 - mod); s = bc_num_shiftRight(&temp, places); if (BC_ERROR_SIGNAL_ONLY(s)) goto exit; } bc_num_printDecimal(&temp); bc_num_putchar('e'); if (!places) { bc_num_printHex(0, 1, false); goto exit; } if (neg) bc_num_putchar('-'); bc_num_setup(&exp, digs, BC_NUM_BIGDIG_LOG10); bc_num_bigdig2num(&exp, (BcBigDig) places); bc_num_printDecimal(&exp); exit: bc_num_free(&temp); return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } #endif // BC_ENABLE_EXTRA_MATH static BcStatus bc_num_printFixup(BcNum *restrict n, BcBigDig rem, BcBigDig pow, size_t idx) { size_t i, len = n->len - idx; BcBigDig acc; BcDig *a = n->num + idx; if (len < 2) return BC_STATUS_SUCCESS; for (i = len - 1; BC_NO_SIG && i > 0; --i) { acc = ((BcBigDig) a[i]) * rem + ((BcBigDig) a[i - 1]); a[i - 1] = (BcDig) (acc % pow); acc /= pow; acc += (BcBigDig) a[i]; if (acc >= BC_BASE_POW) { if (i == len - 1) { len = bc_vm_growSize(len, 1); bc_num_expand(n, bc_vm_growSize(len, idx)); a = n->num + idx; a[len - 1] = 0; } a[i + 1] += acc / BC_BASE_POW; acc %= BC_BASE_POW; } assert(acc < BC_BASE_POW); a[i] = (BcDig) acc; } n->len = len + idx; return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } static BcStatus bc_num_printPrepare(BcNum *restrict n, BcBigDig rem, BcBigDig pow) { BcStatus s = BC_STATUS_SUCCESS; size_t i; for (i = 0; BC_NO_SIG && BC_NO_ERR(!s) && i < n->len; ++i) s = bc_num_printFixup(n, rem, pow, i); if (BC_ERR(s)) return s; for (i = 0; BC_NO_SIG && i < n->len; ++i) { assert(pow == ((BcBigDig) ((BcDig) pow))); if (n->num[i] >= (BcDig) pow) { if (i + 1 == n->len) { n->len = bc_vm_growSize(n->len, 1); bc_num_expand(n, n->len); n->num[i + 1] = 0; } assert(pow < BC_BASE_POW); n->num[i + 1] += n->num[i] / ((BcDig) pow); n->num[i] %= (BcDig) pow; } } return BC_NO_ERR(!s) && BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } static BcStatus bc_num_printNum(BcNum *restrict n, BcBigDig base, size_t len, BcNumDigitOp print) { BcStatus s; BcVec stack; BcNum intp, fracp1, fracp2, digit, flen1, flen2, *n1, *n2, *temp; BcBigDig dig = 0, *ptr, acc, exp; size_t i, j; bool radix; BcDig digit_digs[BC_NUM_BIGDIG_LOG10 + 1]; assert(base > 1); if (BC_NUM_ZERO(n)) { print(0, len, false); return BC_STATUS_SUCCESS; } // This function uses an algorithm that Stefan Esser came // up with to print the integer part of a number. What it does is convert // intp into a number of the specified base, but it does it directly, // instead of just doing a series of divisions and printing the remainders // in reverse order. // // Let me explain in a bit more detail: // // The algorithm takes the current least significant digit (after intp has // been converted to an integer) and the next to least significant digit, // and it converts the least significant digit into one of the specified // base, putting any overflow into the next to least significant digit. It // iterates through the whole number, from least significant to most // significant, doing this conversion. At the end of that iteration, the // least significant digit is converted, but the others are not, so it // iterates again, starting at the next to least significant digit. It keeps // doing that conversion, skipping one more digit than the last time, until // all digits have been converted. Then it prints them in reverse order. // // That is the gist of the algorithm. It leaves out several things, such as // the fact that digits are not always converted into the specified base, // but into something close, basically a power of the specified base. In // Stefan's words, "You could consider BcDigs to be of base 10^BC_BASE_DIGS // in the normal case and obase^N for the largest value of N that satisfies // obase^N <= 10^BC_BASE_DIGS. [This means that] the result is not in base // "obase", but in base "obase^N", which happens to be printable as a number // of base "obase" without consideration for neighbouring BcDigs." This fact // is what necessitates the existence of the loop later in this function. // // The conversion happens in bc_num_printPrepare() where the outer loop // happens and bc_num_printFixup() where the inner loop, or actual // conversion, happens. bc_vec_init(&stack, sizeof(BcBigDig), NULL); bc_num_init(&fracp1, n->rdx); bc_num_createCopy(&intp, n); bc_num_truncate(&intp, intp.scale); s = bc_num_sub(n, &intp, &fracp1, 0); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; if (base != vm->last_base) { vm->last_pow = 1; vm->last_exp = 0; while (vm->last_pow * base <= BC_BASE_POW) { vm->last_pow *= base; vm->last_exp += 1; } vm->last_rem = BC_BASE_POW - vm->last_pow; vm->last_base = base; } exp = vm->last_exp; if (vm->last_rem != 0) { s = bc_num_printPrepare(&intp, vm->last_rem, vm->last_pow); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; } for (i = 0; BC_NO_SIG && i < intp.len; ++i) { acc = (BcBigDig) intp.num[i]; for (j = 0; BC_NO_SIG && j < exp && (i < intp.len - 1 || acc != 0); ++j) { if (j != exp - 1) { dig = acc % base; acc /= base; } else { dig = acc; acc = 0; } assert(dig < base); bc_vec_push(&stack, &dig); } assert(acc == 0 || BC_SIG); } if (BC_SIG) goto sig_err; for (i = 0; BC_NO_SIG && i < stack.len; ++i) { ptr = bc_vec_item_rev(&stack, i); assert(ptr != NULL); print(*ptr, len, false); } if (BC_SIG) goto sig_err; if (!n->scale) goto err; bc_num_init(&fracp2, n->rdx); bc_num_setup(&digit, digit_digs, sizeof(digit_digs) / sizeof(BcDig)); bc_num_init(&flen1, BC_NUM_BIGDIG_LOG10 + 1); bc_num_init(&flen2, BC_NUM_BIGDIG_LOG10 + 1); bc_num_one(&flen1); radix = true; n1 = &flen1; n2 = &flen2; fracp2.scale = n->scale; fracp2.rdx = BC_NUM_RDX(fracp2.scale); while (BC_NO_SIG && bc_num_intDigits(n1) < n->scale + 1) { bc_num_expand(&fracp2, fracp1.len + 1); s = bc_num_mulArray(&fracp1, base, &fracp2); if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err; if (fracp2.len < fracp2.rdx) fracp2.len = fracp2.rdx; // Will never fail (except for signals) because fracp is // guaranteed to be non-negative and small enough. s = bc_num_bigdig(&fracp2, &dig); if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err; bc_num_bigdig2num(&digit, dig); s = bc_num_sub(&fracp2, &digit, &fracp1, 0); if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err; print(dig, len, radix); s = bc_num_mulArray(n1, base, n2); if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err; radix = false; temp = n1; n1 = n2; n2 = temp; } frac_err: bc_num_free(&flen2); bc_num_free(&flen1); bc_num_free(&fracp2); sig_err: if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL; err: bc_num_free(&fracp1); bc_num_free(&intp); bc_vec_free(&stack); return s; } static BcStatus bc_num_printBase(BcNum *restrict n, BcBigDig base) { BcStatus s; size_t width; BcNumDigitOp print; bool neg = n->neg; if (neg) bc_num_putchar('-'); n->neg = false; if (base <= BC_NUM_MAX_POSIX_IBASE) { width = 1; print = bc_num_printHex; } else { width = bc_num_log10(base - 1); print = bc_num_printDigits; } s = bc_num_printNum(n, base, width, print); n->neg = neg; return s; } #if DC_ENABLED BcStatus bc_num_stream(BcNum *restrict n, BcBigDig base) { return bc_num_printNum(n, base, 1, bc_num_printChar); } #endif // DC_ENABLED void bc_num_setup(BcNum *restrict n, BcDig *restrict num, size_t cap) { assert(n != NULL); n->num = num; n->cap = cap; bc_num_zero(n); } void bc_num_init(BcNum *restrict n, size_t req) { assert(n != NULL); req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; bc_num_setup(n, bc_vm_malloc(BC_NUM_SIZE(req)), req); } void bc_num_free(void *num) { assert(num != NULL); free(((BcNum*) num)->num); } void bc_num_copy(BcNum *d, const BcNum *s) { assert(d != NULL && s != NULL); if (d == s) return; bc_num_expand(d, s->len); d->len = s->len; d->neg = s->neg; d->rdx = s->rdx; d->scale = s->scale; memcpy(d->num, s->num, BC_NUM_SIZE(d->len)); } void bc_num_createCopy(BcNum *d, const BcNum *s) { bc_num_init(d, s->len); bc_num_copy(d, s); } void bc_num_createFromBigdig(BcNum *n, BcBigDig val) { bc_num_init(n, (BC_NUM_BIGDIG_LOG10 - 1) / BC_BASE_DIGS + 1); bc_num_bigdig2num(n, val); } size_t bc_num_scale(const BcNum *restrict n) { return n->scale; } size_t bc_num_len(const BcNum *restrict n) { size_t len = n->len; if (BC_NUM_ZERO(n)) return 0; if (n->rdx == len) { size_t zero, scale; len = bc_num_nonzeroLen(n); scale = n->scale % BC_BASE_DIGS; scale = scale ? scale : BC_BASE_DIGS; zero = bc_num_zeroDigits(n->num + len - 1); len = len * BC_BASE_DIGS - zero - (BC_BASE_DIGS - scale); } else len = bc_num_intDigits(n) + n->scale; return len; } BcStatus bc_num_parse(BcNum *restrict n, const char *restrict val, BcBigDig base, bool letter) { BcStatus s = BC_STATUS_SUCCESS; assert(n != NULL && val != NULL && base); assert(base >= BC_NUM_MIN_BASE && base <= vm->maxes[BC_PROG_GLOBALS_IBASE]); assert(bc_num_strValid(val)); if (letter) { BcBigDig dig = bc_num_parseChar(val[0], BC_NUM_MAX_LBASE); bc_num_bigdig2num(n, dig); } else if (base == BC_BASE) bc_num_parseDecimal(n, val); else s = bc_num_parseBase(n, val, base); return s; } BcStatus bc_num_print(BcNum *restrict n, BcBigDig base, bool newline) { BcStatus s = BC_STATUS_SUCCESS; assert(n != NULL); assert(BC_ENABLE_EXTRA_MATH || base >= BC_NUM_MIN_BASE); bc_num_printNewline(); if (BC_NUM_ZERO(n)) bc_num_printHex(0, 1, false); else if (base == BC_BASE) bc_num_printDecimal(n); #if BC_ENABLE_EXTRA_MATH else if (base == 0 || base == 1) s = bc_num_printExponent(n, base != 0); #endif // BC_ENABLE_EXTRA_MATH else s = bc_num_printBase(n, base); if (BC_NO_ERR(!s) && newline) bc_num_putchar('\n'); return s; } BcStatus bc_num_bigdig(const BcNum *restrict n, BcBigDig *result) { size_t i; BcBigDig r; assert(n != NULL && result != NULL); if (BC_ERR(n->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE); for (r = 0, i = n->len; i > n->rdx;) { BcBigDig prev = r * BC_BASE_POW; if (BC_ERR(prev / BC_BASE_POW != r)) return bc_vm_err(BC_ERROR_MATH_OVERFLOW); r = prev + (BcBigDig) n->num[--i]; if (BC_ERR(r < prev)) return bc_vm_err(BC_ERROR_MATH_OVERFLOW); } *result = r; return BC_STATUS_SUCCESS; } void bc_num_bigdig2num(BcNum *restrict n, BcBigDig val) { BcDig *ptr; size_t i; assert(n != NULL); bc_num_zero(n); if (!val) return; bc_num_expand(n, BC_NUM_BIGDIG_LOG10); for (ptr = n->num, i = 0; val; ++i, val /= BC_BASE_POW) ptr[i] = val % BC_BASE_POW; n->len = i; } size_t bc_num_addReq(const BcNum *a, const BcNum *b, size_t scale) { size_t aint, bint, ardx, brdx; BC_UNUSED(scale); ardx = a->rdx; aint = bc_num_int(a); assert(aint <= a->len && ardx <= a->len); brdx = b->rdx; bint = bc_num_int(b); assert(bint <= b->len && brdx <= b->len); ardx = BC_MAX(ardx, brdx); aint = BC_MAX(aint, bint); return bc_vm_growSize(bc_vm_growSize(ardx, aint), 1); } size_t bc_num_mulReq(const BcNum *a, const BcNum *b, size_t scale) { size_t max, rdx; rdx = bc_vm_growSize(a->rdx, b->rdx); max = BC_NUM_RDX(scale); max = bc_vm_growSize(BC_MAX(max, rdx), 1); rdx = bc_vm_growSize(bc_vm_growSize(bc_num_int(a), bc_num_int(b)), max); return rdx; } size_t bc_num_powReq(const BcNum *a, const BcNum *b, size_t scale) { BC_UNUSED(scale); return bc_vm_growSize(bc_vm_growSize(a->len, b->len), 1); } #if BC_ENABLE_EXTRA_MATH size_t bc_num_placesReq(const BcNum *a, const BcNum *b, size_t scale) { BC_UNUSED(scale); return a->len + b->len - a->rdx - b->rdx; } #endif // BC_ENABLE_EXTRA_MATH BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) { return bc_num_binary(a, b, c, false, bc_num_as, bc_num_addReq(a, b, scale)); } BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) { return bc_num_binary(a, b, c, true, bc_num_as, bc_num_addReq(a, b, scale)); } BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) { return bc_num_binary(a, b, c, scale, bc_num_m, bc_num_mulReq(a, b, scale)); } BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) { return bc_num_binary(a, b, c, scale, bc_num_d, bc_num_mulReq(a, b, scale)); } BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) { size_t req = bc_num_mulReq(a, b, scale); return bc_num_binary(a, b, c, scale, bc_num_rem, req); } BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) { return bc_num_binary(a, b, c, scale, bc_num_p, bc_num_powReq(a, b, scale)); } #if BC_ENABLE_EXTRA_MATH BcStatus bc_num_places(BcNum *a, BcNum *b, BcNum *c, size_t scale) { size_t req = bc_num_placesReq(a, b, scale); return bc_num_binary(a, b, c, scale, bc_num_place, req); } BcStatus bc_num_lshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) { size_t req = bc_num_placesReq(a, b, scale); return bc_num_binary(a, b, c, scale, bc_num_left, req); } BcStatus bc_num_rshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) { size_t req = bc_num_placesReq(a, b, scale); return bc_num_binary(a, b, c, scale, bc_num_right, req); } #endif // BC_ENABLE_EXTRA_MATH BcStatus bc_num_sqrt(BcNum *restrict a, BcNum *restrict b, size_t scale) { BcStatus s = BC_STATUS_SUCCESS; BcNum num1, num2, half, f, fprime, *x0, *x1, *temp; size_t pow, len, rdx, req, digs, digs1, digs2, resscale, times = 0; ssize_t cmp = 1, cmp1 = SSIZE_MAX, cmp2 = SSIZE_MAX; BcDig half_digs[1]; assert(a != NULL && b != NULL && a != b); if (BC_ERR(a->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE); if (a->scale > scale) scale = a->scale; len = bc_vm_growSize(bc_num_intDigits(a), 1); rdx = BC_NUM_RDX(scale); req = bc_vm_growSize(BC_MAX(rdx, a->rdx), len >> 1); bc_num_init(b, bc_vm_growSize(req, 1)); if (BC_NUM_ZERO(a)) { bc_num_setToZero(b, scale); return BC_STATUS_SUCCESS; } if (BC_NUM_ONE(a)) { bc_num_one(b); bc_num_extend(b, scale); return BC_STATUS_SUCCESS; } rdx = BC_NUM_RDX(scale); rdx = BC_MAX(rdx, a->rdx); len = bc_vm_growSize(a->len, rdx); bc_num_init(&num1, len); bc_num_init(&num2, len); bc_num_setup(&half, half_digs, sizeof(half_digs) / sizeof(BcDig)); bc_num_one(&half); half.num[0] = BC_BASE_POW / 2; half.len = 1; half.rdx = 1; half.scale = 1; bc_num_init(&f, len); bc_num_init(&fprime, len); x0 = &num1; x1 = &num2; bc_num_one(x0); pow = bc_num_intDigits(a); if (pow) { if (pow & 1) x0->num[0] = 2; else x0->num[0] = 6; pow -= 2 - (pow & 1); s = bc_num_shiftLeft(x0, pow / 2); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; } x0->scale = x0->rdx = digs = digs1 = digs2 = 0; resscale = (scale + BC_BASE_DIGS) * 2; len = BC_NUM_RDX(bc_num_intDigits(x0) + resscale - 1); while (BC_NO_SIG && (cmp || digs < len)) { assert(BC_NUM_NONZERO(x0)); s = bc_num_div(a, x0, &f, resscale); assert(!s || s == BC_STATUS_SIGNAL); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; s = bc_num_add(x0, &f, &fprime, resscale); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; s = bc_num_mul(&fprime, &half, x1, resscale); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; cmp = bc_num_cmp(x1, x0); #if BC_ENABLE_SIGNALS if (BC_NUM_CMP_SIGNAL(cmp)) { s = BC_STATUS_SIGNAL; break; } #endif // BC_ENABLE_SIGNALS digs = x1->len - (size_t) labs(cmp); if (cmp == cmp2 && digs == digs1) times += 1; else times = 0; resscale += (times > 2); cmp2 = cmp1; cmp1 = cmp; digs1 = digs; temp = x0; x0 = x1; x1 = temp; } if (BC_SIG) { s = BC_STATUS_SIGNAL; goto err; } bc_num_copy(b, x0); if (b->scale > scale) bc_num_truncate(b, b->scale - scale); assert(!b->neg || BC_NUM_NONZERO(b)); assert(b->rdx <= b->len || !b->len); assert(!b->len || b->num[b->len - 1] || b->rdx == b->len); err: if (BC_ERR(s)) bc_num_free(b); bc_num_free(&fprime); bc_num_free(&f); bc_num_free(&num2); bc_num_free(&num1); return s; } BcStatus bc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, size_t scale) { BcStatus s; BcNum num2, *ptr_a; bool init = false; size_t ts, len; ts = BC_MAX(scale + b->scale, a->scale); len = bc_num_mulReq(a, b, ts); assert(a != NULL && b != NULL && c != NULL && d != NULL); assert(c != d && a != d && b != d && b != c); if (c == a) { memcpy(&num2, c, sizeof(BcNum)); ptr_a = &num2; bc_num_init(c, len); init = true; } else { ptr_a = a; bc_num_expand(c, len); } if (BC_NUM_NONZERO(a) && !a->rdx && !b->rdx && b->len == 1 && !scale) { BcBigDig rem; s = bc_num_divArray(ptr_a, (BcBigDig) b->num[0], c, &rem); assert(rem < BC_BASE_POW); d->num[0] = (BcDig) rem; d->len = (rem != 0); } else s = bc_num_r(ptr_a, b, c, d, scale, ts); assert(!c->neg || BC_NUM_NONZERO(c)); assert(c->rdx <= c->len || !c->len); assert(!c->len || c->num[c->len - 1] || c->rdx == c->len); assert(!d->neg || BC_NUM_NONZERO(d)); assert(d->rdx <= d->len || !d->len); assert(!d->len || d->num[d->len - 1] || d->rdx == d->len); if (init) bc_num_free(&num2); return s; } #if DC_ENABLED BcStatus bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) { BcStatus s; BcNum base, exp, two, temp; BcDig two_digs[2]; assert(a != NULL && b != NULL && c != NULL && d != NULL); assert(a != d && b != d && c != d); if (BC_ERR(BC_NUM_ZERO(c))) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO); if (BC_ERR(b->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE); if (BC_ERR(a->rdx || b->rdx || c->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER); bc_num_expand(d, c->len); bc_num_init(&base, c->len); bc_num_setup(&two, two_digs, sizeof(two_digs) / sizeof(BcDig)); bc_num_init(&temp, b->len + 1); bc_num_one(&two); two.num[0] = 2; bc_num_one(d); // We already checked for 0. s = bc_num_rem(a, c, &base, 0); assert(!s || s == BC_STATUS_SIGNAL); if (BC_ERROR_SIGNAL_ONLY(s)) goto rem_err; bc_num_createCopy(&exp, b); while (BC_NO_SIG && BC_NUM_NONZERO(&exp)) { // Num two cannot be 0, so no errors. s = bc_num_divmod(&exp, &two, &exp, &temp, 0); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; if (BC_NUM_ONE(&temp) && !temp.neg) { s = bc_num_mul(d, &base, &temp, 0); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; // We already checked for 0. s = bc_num_rem(&temp, c, d, 0); assert(!s || s == BC_STATUS_SIGNAL); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; } s = bc_num_mul(&base, &base, &temp, 0); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; // We already checked for 0. s = bc_num_rem(&temp, c, &base, 0); assert(!s || s == BC_STATUS_SIGNAL); if (BC_ERROR_SIGNAL_ONLY(s)) goto err; } if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL; err: bc_num_free(&exp); rem_err: bc_num_free(&temp); bc_num_free(&base); assert(!d->neg || d->len); assert(!d->len || d->num[d->len - 1] || d->rdx == d->len); return s; } #endif // DC_ENABLED #if BC_DEBUG_CODE void bc_num_printDebug(const BcNum *n, const char *name, bool emptyline) { printf("%s: ", name); bc_num_printDecimal(n); printf("\n"); if (emptyline) printf("\n"); vm->nchars = 0; } void bc_num_printDigs(const BcDig *n, size_t len, bool emptyline) { size_t i; for (i = len - 1; i < len; --i) printf(" %0*d", BC_BASE_DIGS, n[i]); printf("\n"); if (emptyline) printf("\n"); vm->nchars = 0; } void bc_num_printWithDigs(const BcNum *n, const char *name, bool emptyline) { printf("%s len: %zu, rdx: %zu, scale: %zu\n", name, n->len, n->rdx, n->scale); bc_num_printDigs(n->num, n->len, emptyline); } void bc_num_dump(const char *varname, const BcNum *n) { ulong i, scale = n->scale; fprintf(stderr, "\n%s = %s", varname, n->len ? (n->neg ? "-" : "+") : "0 "); for (i = n->len - 1; i < n->len; --i) { if (i + 1 == n->rdx) fprintf(stderr, ". "); if (scale / BC_BASE_DIGS != n->rdx - i - 1) fprintf(stderr, "%0*d ", BC_BASE_DIGS, n->num[i]); else { int mod = scale % BC_BASE_DIGS; int d = BC_BASE_DIGS - mod; BcDig div; if (mod != 0) { div = n->num[i] / ((BcDig) bc_num_pow10[(ulong) d]); fprintf(stderr, "%0*d", (int) mod, div); } div = n->num[i] % ((BcDig) bc_num_pow10[(ulong) d]); fprintf(stderr, " ' %0*d ", d, div); } } fprintf(stderr, "(%zu | %zu.%zu / %zu) %p\n", n->scale, n->len, n->rdx, n->cap, (void*) n->num); } #endif // BC_DEBUG_CODE int bc_id_cmp(const BcId *e1, const BcId *e2) { return strcmp(e1->name, e2->name); } #ifndef NDEBUG void bc_id_free(void *id) { assert(id != NULL); free(((BcId*) id)->name); } #endif // NDEBUG void bc_string_free(void *string) { assert(string != NULL && (*((char**) string)) != NULL); free(*((char**) string)); } void bc_const_free(void *constant) { BcConst *c = constant; assert(c->val != NULL); free(c->val); bc_num_free(&c->num); } #if BC_ENABLED BcStatus bc_func_insert(BcFunc *f, BcProgram *p, char *name, BcType type, size_t line) { BcLoc a; size_t i, idx; assert(f != NULL); idx = bc_program_search(p, name, type == BC_TYPE_VAR); for (i = 0; i < f->autos.len; ++i) { BcLoc *id = bc_vec_item(&f->autos, i); if (BC_ERR(idx == id->loc && type == (BcType) id->idx)) { const char *array = type == BC_TYPE_ARRAY ? "[]" : ""; return bc_vm_error(BC_ERROR_PARSE_DUP_LOCAL, line, name, array); } } a.loc = idx; a.idx = type; bc_vec_push(&f->autos, &a); return BC_STATUS_SUCCESS; } #endif // BC_ENABLED void bc_func_init(BcFunc *f, const char *name) { assert(f != NULL && name != NULL); bc_vec_init(&f->code, sizeof(uchar), NULL); bc_vec_init(&f->strs, sizeof(char*), bc_string_free); bc_vec_init(&f->consts, sizeof(BcConst), bc_const_free); #if BC_ENABLED bc_vec_init(&f->autos, sizeof(BcLoc), NULL); bc_vec_init(&f->labels, sizeof(size_t), NULL); f->nparams = 0; f->voidfn = false; #endif // BC_ENABLED f->name = name; } void bc_func_reset(BcFunc *f) { assert(f != NULL); bc_vec_npop(&f->code, f->code.len); bc_vec_npop(&f->strs, f->strs.len); bc_vec_npop(&f->consts, f->consts.len); #if BC_ENABLED bc_vec_npop(&f->autos, f->autos.len); bc_vec_npop(&f->labels, f->labels.len); f->nparams = 0; f->voidfn = false; #endif // BC_ENABLED } void bc_func_free(void *func) { BcFunc *f = (BcFunc*) func; assert(f != NULL); bc_vec_free(&f->code); bc_vec_free(&f->strs); bc_vec_free(&f->consts); #if BC_ENABLED bc_vec_free(&f->autos); bc_vec_free(&f->labels); #endif // BC_ENABLED } void bc_array_init(BcVec *a, bool nums) { if (nums) bc_vec_init(a, sizeof(BcNum), bc_num_free); else bc_vec_init(a, sizeof(BcVec), bc_vec_free); bc_array_expand(a, 1); } void bc_array_copy(BcVec *d, const BcVec *s) { size_t i; assert(d != NULL && s != NULL); assert(d != s && d->size == s->size && d->dtor == s->dtor); bc_vec_npop(d, d->len); 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_createCopy(dnum, snum); } } void bc_array_expand(BcVec *a, size_t len) { assert(a != NULL); bc_vec_expand(a, len); if (a->size == sizeof(BcNum) && a->dtor == bc_num_free) { BcNum n; while (len > a->len) { bc_num_init(&n, BC_NUM_DEF_SIZE); bc_vec_push(a, &n); } } else { BcVec v; assert(a->size == sizeof(BcVec) && a->dtor == bc_vec_free); while (len > a->len) { bc_array_init(&v, true); bc_vec_push(a, &v); } } } #if DC_ENABLED void bc_result_copy(BcResult *d, BcResult *src) { assert(d != NULL && src != NULL); d->t = src->t; switch (d->t) { case BC_RESULT_TEMP: case BC_RESULT_IBASE: case BC_RESULT_SCALE: case BC_RESULT_OBASE: { bc_num_createCopy(&d->d.n, &src->d.n); break; } case BC_RESULT_VAR: #if BC_ENABLED case BC_RESULT_ARRAY: #endif // BC_ENABLED case BC_RESULT_ARRAY_ELEM: { memcpy(&d->d.loc, &src->d.loc, sizeof(BcLoc)); break; } case BC_RESULT_CONSTANT: case BC_RESULT_STR: { memcpy(&d->d.n, &src->d.n, sizeof(BcNum)); break; } case BC_RESULT_ONE: { // Do nothing. break; } #if BC_ENABLED case BC_RESULT_VOID: case BC_RESULT_LAST: { #ifndef NDEBUG assert(false); break; #endif // NDEBUG } #endif // BC_ENABLED } } #endif // DC_ENABLED void bc_result_free(void *result) { BcResult *r = (BcResult*) result; assert(r != NULL); switch (r->t) { case BC_RESULT_TEMP: case BC_RESULT_IBASE: case BC_RESULT_SCALE: case BC_RESULT_OBASE: { bc_num_free(&r->d.n); break; } case BC_RESULT_VAR: #if BC_ENABLED case BC_RESULT_ARRAY: #endif // BC_ENABLED case BC_RESULT_ARRAY_ELEM: case BC_RESULT_STR: case BC_RESULT_CONSTANT: case BC_RESULT_ONE: #if BC_ENABLED case BC_RESULT_VOID: case BC_RESULT_LAST: #endif // BC_ENABLED { // Do nothing. break; } } } BcStatus bc_lex_invalidChar(BcLex *l, char c) { l->t = BC_LEX_INVALID; return bc_lex_verr(l, BC_ERROR_PARSE_CHAR, c); } void bc_lex_lineComment(BcLex *l) { l->t = BC_LEX_WHITESPACE; while (l->i < l->len && l->buf[l->i] != '\n') l->i += 1; } BcStatus bc_lex_comment(BcLex *l) { size_t i, nlines = 0; const char *buf = l->buf; bool end = false; char c; l->i += 1; l->t = BC_LEX_WHITESPACE; for (i = l->i; !end; i += !end) { for (; (c = buf[i]) && c != '*'; ++i) nlines += (c == '\n'); if (BC_ERR(!c || buf[i + 1] == '\0')) { l->i = i; return bc_lex_err(l, BC_ERROR_PARSE_COMMENT); } end = buf[i + 1] == '/'; } l->i = i + 2; l->line += nlines; return BC_STATUS_SUCCESS; } void bc_lex_whitespace(BcLex *l) { char c; l->t = BC_LEX_WHITESPACE; for (c = l->buf[l->i]; c != '\n' && isspace(c); c = l->buf[++l->i]); } void bc_lex_commonTokens(BcLex *l, char c) { if (!c) l->t = BC_LEX_EOF; else if (c == '\n') l->t = BC_LEX_NLINE; else bc_lex_whitespace(l); } static size_t bc_lex_num(BcLex *l, char start, bool int_only) { const char *buf = l->buf + l->i; size_t i; char c; bool last_pt, pt = (start == '.'); for (i = 0; (c = buf[i]) && (BC_LEX_NUM_CHAR(c, pt, int_only) || (c == '\\' && buf[i + 1] == '\n')); ++i) { if (c == '\\') { if (buf[i + 1] == '\n') { i += 2; // Make sure to eat whitespace at the beginning of the line. while(isspace(buf[i]) && buf[i] != '\n') i += 1; c = buf[i]; if (!BC_LEX_NUM_CHAR(c, pt, int_only)) break; } else break; } last_pt = (c == '.'); if (pt && last_pt) break; pt = pt || last_pt; bc_vec_push(&l->str, &c); } return i; } BcStatus bc_lex_number(BcLex *l, char start) { l->t = BC_LEX_NUMBER; bc_vec_npop(&l->str, l->str.len); bc_vec_push(&l->str, &start); l->i += bc_lex_num(l, start, false); #if BC_ENABLE_EXTRA_MATH { char c = l->buf[l->i]; if (c == 'e') { #if BC_ENABLED if (BC_IS_POSIX) { BcStatus s = bc_lex_err(l, BC_ERROR_POSIX_EXP_NUM); if (BC_ERR(s)) return s; } #endif // BC_ENABLED bc_vec_push(&l->str, &c); l->i += 1; c = l->buf[l->i]; if (c == BC_LEX_NEG_CHAR) { bc_vec_push(&l->str, &c); l->i += 1; c = l->buf[l->i]; } if (BC_ERR(!BC_LEX_NUM_CHAR(c, false, true))) return bc_lex_verr(l, BC_ERROR_PARSE_CHAR, c); l->i += bc_lex_num(l, 0, true); } } #endif // BC_ENABLE_EXTRA_MATH bc_vec_pushByte(&l->str, '\0'); return BC_STATUS_SUCCESS; } void bc_lex_name(BcLex *l) { size_t i = 0; const char *buf = l->buf + l->i - 1; char c = buf[i]; l->t = BC_LEX_NAME; while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_') c = buf[++i]; bc_vec_string(&l->str, i, buf); // Increment the index. We minus 1 because it has already been incremented. l->i += i - 1; } void bc_lex_init(BcLex *l) { assert(l != NULL); bc_vec_init(&l->str, sizeof(char), NULL); } void bc_lex_free(BcLex *l) { assert(l != NULL); bc_vec_free(&l->str); } void bc_lex_file(BcLex *l, const char *file) { assert(l != NULL && file != NULL); l->line = 1; vm->file = file; } BcStatus bc_lex_next(BcLex *l) { BcStatus s; assert(l != NULL); l->last = l->t; l->line += (l->i != 0 && l->buf[l->i - 1] == '\n'); if (BC_ERR(l->last == BC_LEX_EOF)) return bc_lex_err(l, BC_ERROR_PARSE_EOF); l->t = BC_LEX_EOF; if (l->i == l->len) return BC_STATUS_SUCCESS; // Loop until failure or we don't have whitespace. This // is so the parser doesn't get inundated with whitespace. do { s = vm->next(l); } while (BC_NO_ERR(!s) && l->t == BC_LEX_WHITESPACE); return s; } BcStatus bc_lex_text(BcLex *l, const char *text) { assert(l != NULL && text != NULL); l->buf = text; l->i = 0; l->len = strlen(text); l->t = l->last = BC_LEX_INVALID; return bc_lex_next(l); } void bc_parse_updateFunc(BcParse *p, size_t fidx) { p->fidx = fidx; p->func = bc_vec_item(&p->prog->fns, fidx); } void bc_parse_pushName(const BcParse *p, char *name, bool var) { size_t idx = bc_program_search(p->prog, name, var); bc_parse_pushIndex(p, idx); } void bc_parse_addId(BcParse *p, const char *string, uchar inst) { BcFunc *f = BC_IS_BC ? p->func : bc_vec_item(&p->prog->fns, BC_PROG_MAIN); BcVec *v = inst == BC_INST_NUM ? &f->consts : &f->strs; size_t idx = v->len; if (inst == BC_INST_NUM) { if (bc_parse_one[0] == string[0] && bc_parse_one[1] == string[1]) { bc_parse_push(p, BC_INST_ONE); return; } else { BcConst c; char *str = bc_vm_strdup(string); c.val = str; c.base = BC_NUM_BIGDIG_MAX; memset(&c.num, 0, sizeof(BcNum)); bc_vec_push(v, &c); } } else { char *str = bc_vm_strdup(string); bc_vec_push(v, &str); } bc_parse_updateFunc(p, p->fidx); bc_parse_push(p, inst); bc_parse_pushIndex(p, idx); } void bc_parse_number(BcParse *p) { #if BC_ENABLE_EXTRA_MATH char *exp = strchr(p->l.str.v, 'e'); size_t idx = SIZE_MAX; if (exp != NULL) { idx = ((size_t) (exp - p->l.str.v)); *exp = 0; } #endif // BC_ENABLE_EXTRA_MATH bc_parse_addId(p, p->l.str.v, BC_INST_NUM); #if BC_ENABLE_EXTRA_MATH if (exp != NULL) { bool neg; neg = (*((char*) bc_vec_item(&p->l.str, idx + 1)) == BC_LEX_NEG_CHAR); bc_parse_addId(p, bc_vec_item(&p->l.str, idx + 1 + neg), BC_INST_NUM); bc_parse_push(p, BC_INST_LSHIFT + neg); } #endif // BC_ENABLE_EXTRA_MATH } BcStatus bc_parse_text(BcParse *p, const char *text) { // Make sure the pointer isn't invalidated. p->func = bc_vec_item(&p->prog->fns, p->fidx); return bc_lex_text(&p->l, text); } BcStatus bc_parse_reset(BcParse *p, BcStatus s) { if (p->fidx != BC_PROG_MAIN) { bc_func_reset(p->func); bc_parse_updateFunc(p, BC_PROG_MAIN); } p->l.i = p->l.len; p->l.t = BC_LEX_EOF; p->auto_part = false; #if BC_ENABLED bc_vec_npop(&p->flags, p->flags.len - 1); bc_vec_npop(&p->exits, p->exits.len); bc_vec_npop(&p->conds, p->conds.len); bc_vec_npop(&p->ops, p->ops.len); #endif // BC_ENABLED return bc_program_reset(p->prog, s); } void bc_parse_free(BcParse *p) { assert(p != NULL); #if BC_ENABLED bc_vec_free(&p->flags); bc_vec_free(&p->exits); bc_vec_free(&p->conds); bc_vec_free(&p->ops); bc_vec_free(&p->buf); #endif // BC_ENABLED bc_lex_free(&p->l); } void bc_parse_init(BcParse *p, BcProgram *prog, size_t func) { #if BC_ENABLED uint16_t flag = 0; #endif // BC_ENABLED assert(p != NULL && prog != NULL); #if BC_ENABLED bc_vec_init(&p->flags, sizeof(uint16_t), NULL); bc_vec_push(&p->flags, &flag); bc_vec_init(&p->exits, sizeof(BcInstPtr), NULL); bc_vec_init(&p->conds, sizeof(size_t), NULL); bc_vec_init(&p->ops, sizeof(BcLexType), NULL); bc_vec_init(&p->buf, sizeof(char), NULL); #endif // BC_ENABLED bc_lex_init(&p->l); p->prog = prog; p->auto_part = false; bc_parse_updateFunc(p, func); } #ifndef BC_PROG_NO_STACK_CHECK static BcStatus bc_program_checkStack(const BcVec *v, size_t n) { #if DC_ENABLED if (!BC_IS_BC && BC_ERR(!BC_PROG_STACK(v, n))) return bc_vm_err(BC_ERROR_EXEC_STACK); #endif // DC_ENABLED assert(BC_PROG_STACK(v, n)); return BC_STATUS_SUCCESS; } #endif // BC_PROG_NO_STACK_CHECK static BcStatus bc_program_type_num(BcResult *r, BcNum *n) { #if BC_ENABLED assert(r->t != BC_RESULT_VOID); #endif // BC_ENABLED if (BC_ERR(!BC_PROG_NUM(r, n))) return bc_vm_err(BC_ERROR_EXEC_TYPE); return BC_STATUS_SUCCESS; } #if BC_ENABLED static BcStatus bc_program_type_match(BcResult *r, BcType t) { #if DC_ENABLED assert(!BC_IS_BC || BC_NO_ERR(r->t != BC_RESULT_STR)); #endif // DC_ENABLED if (BC_ERR((r->t != BC_RESULT_ARRAY) != (!t))) return bc_vm_err(BC_ERROR_EXEC_TYPE); return BC_STATUS_SUCCESS; } #endif // BC_ENABLED static BcFunc* bc_program_func(const BcProgram *p) { size_t i; if (BC_IS_BC) { BcInstPtr *ip = bc_vec_item_rev(&p->stack, 0); i = ip->func; } else i = BC_PROG_MAIN; return bc_vec_item(&p->fns, i); } static BcConst* bc_program_const(const BcProgram *p, size_t idx) { BcFunc *f = bc_program_func(p); return bc_vec_item(&f->consts, idx); } static char* bc_program_str(const BcProgram *p, size_t idx) { BcFunc *f = bc_program_func(p); return *((char**) bc_vec_item(&f->strs, idx)); } static size_t bc_program_index(const char *restrict code, size_t *restrict bgn) { uchar amt = (uchar) code[(*bgn)++], i = 0; size_t res = 0; for (; i < amt; ++i, ++(*bgn)) { size_t temp = ((size_t) ((int) (uchar) code[*bgn]) & UCHAR_MAX); res |= (temp << (i * CHAR_BIT)); } return res; } static void bc_program_prepGlobals(BcProgram *p) { size_t i; for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) bc_vec_push(p->globals_v + i, p->globals + i); } #if BC_ENABLED static BcVec* bc_program_dereference(const BcProgram *p, BcVec *vec) { BcVec *v; size_t vidx, nidx, i = 0; assert(vec->size == sizeof(uchar)); vidx = bc_program_index(vec->v, &i); nidx = bc_program_index(vec->v, &i); v = bc_vec_item(bc_vec_item(&p->arrs, vidx), nidx); assert(v->size != sizeof(uchar)); return v; } #endif // BC_ENABLED size_t bc_program_search(BcProgram *p, char *id, bool var) { BcId e, *ptr; BcVec *v, *map; size_t i; BcResultData data; bool new; v = var ? &p->vars : &p->arrs; map = var ? &p->var_map : &p->arr_map; e.name = id; e.idx = v->len; new = bc_map_insert(map, &e, &i); if (new) { bc_array_init(&data.v, var); bc_vec_push(v, &data.v); } ptr = bc_vec_item(map, i); if (new) ptr->name = bc_vm_strdup(e.name); return ptr->idx; } static BcVec* bc_program_vec(const BcProgram *p, size_t idx, BcType type) { const BcVec *v = (type == BC_TYPE_VAR) ? &p->vars : &p->arrs; return bc_vec_item(v, idx); } static BcStatus bc_program_num(BcProgram *p, BcResult *r, BcNum **num) { BcStatus s = BC_STATUS_SUCCESS; BcNum *n; switch (r->t) { case BC_RESULT_CONSTANT: { BcConst *c = bc_program_const(p, r->d.loc.loc); BcBigDig base = BC_PROG_IBASE(p); if (c->base != base) { if (c->num.num == NULL) bc_num_init(&c->num, BC_NUM_RDX(strlen(c->val))); s = bc_num_parse(&c->num, c->val, base, !c->val[1]); assert(!s || s == BC_STATUS_SIGNAL); #if BC_ENABLE_SIGNALS // bc_num_parse() should only do operations that can // only fail when signals happen. Thus, if signals // are not enabled, we don't need this check. if (BC_ERROR_SIGNAL_ONLY(s)) return s; #endif // BC_ENABLE_SIGNALS c->base = base; } n = &r->d.n; bc_num_createCopy(n, &c->num); r->t = BC_RESULT_TEMP; break; } case BC_RESULT_STR: case BC_RESULT_TEMP: case BC_RESULT_IBASE: case BC_RESULT_SCALE: case BC_RESULT_OBASE: { n = &r->d.n; break; } case BC_RESULT_VAR: #if BC_ENABLED case BC_RESULT_ARRAY: #endif // BC_ENABLED case BC_RESULT_ARRAY_ELEM: { BcVec *v; BcType type = (r->t == BC_RESULT_VAR) ? BC_TYPE_VAR : BC_TYPE_ARRAY; v = bc_program_vec(p, r->d.loc.loc, type); if (r->t == BC_RESULT_ARRAY_ELEM) { size_t idx = r->d.loc.idx; v = bc_vec_top(v); #if BC_ENABLED if (v->size == sizeof(uchar)) v = bc_program_dereference(p, v); #endif // BC_ENABLED assert(v->size == sizeof(BcNum)); if (v->len <= idx) bc_array_expand(v, bc_vm_growSize(idx, 1)); n = bc_vec_item(v, idx); } else n = bc_vec_top(v); break; } case BC_RESULT_ONE: { n = &p->one; break; } #if BC_ENABLED case BC_RESULT_LAST: { n = &p->last; break; } case BC_RESULT_VOID: { #ifndef NDEBUG assert(false); #endif // NDEBUG n = &r->d.n; break; } #endif // BC_ENABLED } *num = n; return s; } static BcStatus bc_program_operand(BcProgram *p, BcResult **r, BcNum **n, size_t idx) { #ifndef BC_PROG_NO_STACK_CHECK BcStatus s = bc_program_checkStack(&p->results, idx + 1); if (BC_ERR(s)) return s; #endif // BC_PROG_NO_STACK_CHECK *r = bc_vec_item_rev(&p->results, idx); #if BC_ENABLED if (BC_ERR((*r)->t == BC_RESULT_VOID)) return bc_vm_err(BC_ERROR_EXEC_VOID_VAL); #endif // BC_ENABLED return bc_program_num(p, *r, n); } static BcStatus bc_program_binPrep(BcProgram *p, BcResult **l, BcNum **ln, BcResult **r, BcNum **rn) { BcStatus s; BcResultType lt; assert(p != NULL && l != NULL && ln != NULL && r != NULL && rn != NULL); s = bc_program_operand(p, l, ln, 1); if (BC_ERR(s)) return s; s = bc_program_operand(p, r, rn, 0); if (BC_ERR(s)) return s; lt = (*l)->t; #if BC_ENABLED assert(lt != BC_RESULT_VOID && (*r)->t != BC_RESULT_VOID); #endif // BC_ENABLED // We run this again under these conditions in case any vector has been // reallocated out from under the BcNums or arrays we had. if (lt == (*r)->t && (lt == BC_RESULT_VAR || lt == BC_RESULT_ARRAY_ELEM)) s = bc_program_num(p, *l, ln); if (BC_NO_ERR(!s) && BC_ERR(lt == BC_RESULT_STR)) return bc_vm_err(BC_ERROR_EXEC_TYPE); return s; } static BcStatus bc_program_binOpPrep(BcProgram *p, BcResult **l, BcNum **ln, BcResult **r, BcNum **rn) { BcStatus s; s = bc_program_binPrep(p, l, ln, r, rn); if (BC_ERR(s)) return s; s = bc_program_type_num(*l, *ln); if (BC_ERR(s)) return s; return bc_program_type_num(*r, *rn); } static BcStatus bc_program_assignPrep(BcProgram *p, BcResult **l, BcNum **ln, BcResult **r, BcNum **rn) { BcStatus s; bool good = false; BcResultType lt; s = bc_program_binPrep(p, l, ln, r, rn); if (BC_ERR(s)) return s; lt = (*l)->t; if (BC_ERR(lt == BC_RESULT_CONSTANT || lt == BC_RESULT_TEMP || lt == BC_RESULT_ONE)) { return bc_vm_err(BC_ERROR_EXEC_TYPE); } #if BC_ENABLED if (BC_ERR(lt == BC_RESULT_ARRAY)) return bc_vm_err(BC_ERROR_EXEC_TYPE); #endif // BC_ENABLED #if DC_ENABLED if(!BC_IS_BC) good = (((*r)->t == BC_RESULT_STR || BC_PROG_STR(*rn)) && (lt == BC_RESULT_VAR || lt == BC_RESULT_ARRAY_ELEM)); #else assert((*r)->t != BC_RESULT_STR); #endif // DC_ENABLED if (!good) s = bc_program_type_num(*r, *rn); return s; } static void bc_program_binOpRetire(BcProgram *p, BcResult *r) { r->t = BC_RESULT_TEMP; bc_vec_pop(&p->results); bc_vec_pop(&p->results); bc_vec_push(&p->results, r); } static BcStatus bc_program_prep(BcProgram *p, BcResult **r, BcNum **n) { BcStatus s; assert(p != NULL && r != NULL && n != NULL); s = bc_program_operand(p, r, n, 0); if (BC_ERR(s)) return s; #if DC_ENABLED assert((*r)->t != BC_RESULT_VAR || !BC_PROG_STR(*n)); #endif // DC_ENABLED return bc_program_type_num(*r, *n); } static void bc_program_retire(BcProgram *p, BcResult *r, BcResultType t) { r->t = t; bc_vec_pop(&p->results); bc_vec_push(&p->results, r); } static BcStatus bc_program_op(BcProgram *p, uchar inst) { BcStatus s; BcResult *opd1, *opd2, res; BcNum *n1, *n2; size_t idx = inst - BC_INST_POWER; s = bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2); if (BC_ERR(s)) return s; bc_num_init(&res.d.n, bc_program_opReqs[idx](n1, n2, BC_PROG_SCALE(p))); s = bc_program_ops[idx](n1, n2, &res.d.n, BC_PROG_SCALE(p)); if (BC_ERR(s)) goto err; bc_program_binOpRetire(p, &res); return s; err: bc_num_free(&res.d.n); return s; } static BcStatus bc_program_read(BcProgram *p) { BcStatus s; BcParse parse; BcVec buf; BcInstPtr ip; size_t i; const char* file; BcFunc *f = bc_vec_item(&p->fns, BC_PROG_READ); for (i = 0; i < p->stack.len; ++i) { BcInstPtr *ip_ptr = bc_vec_item(&p->stack, i); if (ip_ptr->func == BC_PROG_READ) return bc_vm_err(BC_ERROR_EXEC_REC_READ); } file = vm->file; bc_lex_file(&parse.l, bc_program_stdin_name); bc_vec_npop(&f->code, f->code.len); bc_vec_init(&buf, sizeof(char), NULL); s = bc_read_line(&buf, BC_IS_BC ? "read> " : "?> "); if (BC_ERR(s)) { if (s == BC_STATUS_EOF) s = bc_vm_err(BC_ERROR_EXEC_READ_EXPR); goto io_err; } bc_parse_init(&parse, p, BC_PROG_READ); s = bc_parse_text(&parse, buf.v); if (BC_ERR(s)) goto exec_err; s = vm->expr(&parse, BC_PARSE_NOREAD | BC_PARSE_NEEDVAL); if (BC_ERR(s)) goto exec_err; if (BC_ERR(parse.l.t != BC_LEX_NLINE && parse.l.t != BC_LEX_EOF)) { s = bc_vm_err(BC_ERROR_EXEC_READ_EXPR); goto exec_err; } if (BC_G) bc_program_prepGlobals(p); ip.func = BC_PROG_READ; ip.idx = 0; ip.len = p->results.len; // Update this pointer, just in case. f = bc_vec_item(&p->fns, BC_PROG_READ); bc_vec_pushByte(&f->code, vm->read_ret); bc_vec_push(&p->stack, &ip); exec_err: bc_parse_free(&parse); io_err: bc_vec_free(&buf); vm->file = file; return s; } static void bc_program_printChars(const char *str) { const char *nl; vm->nchars += bc_vm_printf("%s", str); nl = strrchr(str, '\n'); if (nl) vm->nchars = strlen(nl + 1); } static void bc_program_printString(const char *restrict str) { size_t i, len = strlen(str); #if DC_ENABLED if (!len && !BC_IS_BC) { bc_vm_putchar('\0'); return; } #endif // DC_ENABLED for (i = 0; i < len; ++i) { int c = str[i]; if (c == '\\' && i != len - 1) { const char *ptr; c = str[++i]; ptr = strchr(bc_program_esc_chars, c); if (ptr != NULL) { if (c == 'n') vm->nchars = SIZE_MAX; c = bc_program_esc_seqs[(size_t) (ptr - bc_program_esc_chars)]; } else { // Just print the backslash. The following // character will be printed later. bc_vm_putchar('\\'); } } bc_vm_putchar(c); } } static BcStatus bc_program_print(BcProgram *p, uchar inst, size_t idx) { BcStatus s = BC_STATUS_SUCCESS; BcResult *r; char *str; BcNum *n; bool pop = (inst != BC_INST_PRINT); assert(p != NULL); #ifndef BC_PROG_NO_STACK_CHECK s = bc_program_checkStack(&p->results, idx + 1); if (BC_ERR(s)) return s; #endif // BC_PROG_NO_STACK_CHECK r = bc_vec_item_rev(&p->results, idx); #if BC_ENABLED if (r->t == BC_RESULT_VOID) { if (BC_ERR(pop)) return bc_vm_err(BC_ERROR_EXEC_VOID_VAL); bc_vec_pop(&p->results); return s; } #endif // BC_ENABLED s = bc_program_num(p, r, &n); if (BC_ERR(s)) return s; if (BC_PROG_NUM(r, n)) { assert(inst != BC_INST_PRINT_STR); s = bc_num_print(n, BC_PROG_OBASE(p), !pop); #if BC_ENABLED if (BC_NO_ERR(!s)) bc_num_copy(&p->last, n); #endif // BC_ENABLED } else { size_t i = (r->t == BC_RESULT_STR) ? r->d.loc.loc : n->scale; str = bc_program_str(p, i); if (inst == BC_INST_PRINT_STR) bc_program_printChars(str); else { bc_program_printString(str); if (inst == BC_INST_PRINT) bc_vm_putchar('\n'); } } if (BC_NO_ERR(!s) && (BC_IS_BC || pop)) bc_vec_pop(&p->results); return s; } void bc_program_negate(BcResult *r, BcNum *n) { BcNum *rn = &r->d.n; bc_num_copy(rn, n); if (BC_NUM_NONZERO(rn)) rn->neg = !rn->neg; } void bc_program_not(BcResult *r, BcNum *n) { if (!bc_num_cmpZero(n)) bc_num_one(&r->d.n); } #if BC_ENABLE_EXTRA_MATH void bc_program_trunc(BcResult *r, BcNum *n) { BcNum *rn = &r->d.n; bc_num_copy(rn, n); bc_num_truncate(rn, n->scale); } #endif // BC_ENABLE_EXTRA_MATH static BcStatus bc_program_unary(BcProgram *p, uchar inst) { BcStatus s; BcResult res, *ptr; BcNum *num; s = bc_program_prep(p, &ptr, &num); if (BC_ERR(s)) return s; bc_num_init(&res.d.n, num->len); bc_program_unarys[inst - BC_INST_NEG](&res, num); bc_program_retire(p, &res, BC_RESULT_TEMP); return s; } static BcStatus bc_program_logical(BcProgram *p, uchar inst) { BcStatus s; BcResult *opd1, *opd2, res; BcNum *n1, *n2; bool cond = 0; ssize_t cmp; s = bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2); if (BC_ERR(s)) return s; if (inst == BC_INST_BOOL_AND) cond = (bc_num_cmpZero(n1) && bc_num_cmpZero(n2)); else if (inst == BC_INST_BOOL_OR) cond = (bc_num_cmpZero(n1) || bc_num_cmpZero(n2)); else { cmp = bc_num_cmp(n1, n2); #if BC_ENABLE_SIGNALS if (BC_NUM_CMP_SIGNAL(cmp)) return BC_STATUS_SIGNAL; #endif // BC_ENABLE_SIGNALS switch (inst) { case BC_INST_REL_EQ: { cond = (cmp == 0); break; } case BC_INST_REL_LE: { cond = (cmp <= 0); break; } case BC_INST_REL_GE: { cond = (cmp >= 0); break; } case BC_INST_REL_NE: { cond = (cmp != 0); break; } case BC_INST_REL_LT: { cond = (cmp < 0); break; } case BC_INST_REL_GT: { cond = (cmp > 0); break; } #ifndef NDEBUG default: { assert(false); break; } #endif // NDEBUG } } bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); if (cond) bc_num_one(&res.d.n); bc_program_binOpRetire(p, &res); return s; } #if DC_ENABLED static BcStatus bc_program_assignStr(BcProgram *p, BcResult *r, BcVec *v, bool push) { BcNum n2; memset(&n2, 0, sizeof(BcNum)); n2.num = NULL; n2.scale = r->d.loc.loc; if (!push) { #ifndef BC_PROG_NO_STACK_CHECK BcStatus s = bc_program_checkStack(&p->results, 2); if (BC_ERR(s)) return s; #endif // BC_PROG_NO_STACK_CHECK bc_vec_pop(v); bc_vec_pop(&p->results); } bc_vec_pop(&p->results); bc_vec_push(v, &n2); return BC_STATUS_SUCCESS; } #endif // DC_ENABLED static BcStatus bc_program_copyToVar(BcProgram *p, size_t idx, BcType t, bool last) { BcStatus s = BC_STATUS_SUCCESS; BcResult *ptr, r; BcVec *vec; BcNum *n; bool var = (t == BC_TYPE_VAR); #if BC_ENABLED #if DC_ENABLED if (!BC_IS_BC) s = bc_program_operand(p, &ptr, &n, 0); else #endif // DC_ENABLED { ptr = bc_vec_top(&p->results); s = bc_program_type_match(ptr, t); if (BC_ERR(s)) return s; if (last) s = bc_program_num(p, ptr, &n); else if (var) n = bc_vec_item_rev(bc_program_vec(p, ptr->d.loc.loc, t), 1); } #else // BC_ENABLED s = bc_program_operand(p, &ptr, &n, 0); #endif // BC_ENABLED if (BC_ERR(s)) return s; vec = bc_program_vec(p, idx, t); #if DC_ENABLED if (ptr->t == BC_RESULT_STR) { if (BC_ERR(!var)) return bc_vm_err(BC_ERROR_EXEC_TYPE); return bc_program_assignStr(p, ptr, vec, true); } #endif // DC_ENABLED if (var) bc_num_createCopy(&r.d.n, n); else { BcVec *v = (BcVec*) n, *rv = &r.d.v; #if BC_ENABLED BcVec *parent; bool ref, ref_size; parent = bc_program_vec(p, ptr->d.loc.loc, t); assert(parent != NULL); if (!last) v = bc_vec_item_rev(parent, !last); assert(v != NULL); ref = (v->size == sizeof(BcNum) && t == BC_TYPE_REF); ref_size = (v->size == sizeof(uchar)); if (ref || (ref_size && t == BC_TYPE_REF)) { bc_vec_init(rv, sizeof(uchar), NULL); if (ref) { assert(parent->len >= (size_t) (!last + 1)); // Make sure the pointer was not invalidated. vec = bc_program_vec(p, idx, t); bc_vec_pushIndex(rv, ptr->d.loc.loc); bc_vec_pushIndex(rv, parent->len - !last - 1); } // If we get here, we are copying a ref to a ref. else bc_vec_npush(rv, v->len * sizeof(uchar), v->v); // We need to return early. bc_vec_push(vec, &r.d); bc_vec_pop(&p->results); return s; } else if (ref_size && t != BC_TYPE_REF) v = bc_program_dereference(p, v); #endif // BC_ENABLED bc_array_init(rv, true); bc_array_copy(rv, v); } bc_vec_push(vec, &r.d); bc_vec_pop(&p->results); return s; } static BcStatus bc_program_assign(BcProgram *p, uchar inst) { BcStatus s; BcResult *left, *right, res; BcNum *l, *r; bool ob, sc, use_val = BC_INST_USE_VAL(inst); s = bc_program_assignPrep(p, &left, &l, &right, &r); if (BC_ERR(s)) return s; #if DC_ENABLED assert(left->t != BC_RESULT_STR); if (right->t == BC_RESULT_STR) { size_t idx = right->d.loc.loc; if (left->t == BC_RESULT_ARRAY_ELEM) { bc_num_free(l); memset(l, 0, sizeof(BcNum)); l->num = NULL; l->scale = idx; } else { BcVec *v = bc_program_vec(p, left->d.loc.loc, BC_TYPE_VAR); s = bc_program_assignStr(p, right, v, false); } return s; } #endif // DC_ENABLED if (BC_INST_IS_ASSIGN(inst)) bc_num_copy(l, r); #if BC_ENABLED else { BcBigDig scale = BC_PROG_SCALE(p); if (!use_val) inst -= (BC_INST_ASSIGN_POWER_NO_VAL - BC_INST_ASSIGN_POWER); s = bc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, scale); if (BC_ERR(s)) return s; } #endif // BC_ENABLED ob = (left->t == BC_RESULT_OBASE); sc = (left->t == BC_RESULT_SCALE); if (ob || sc || left->t == BC_RESULT_IBASE) { BcVec *v; BcBigDig *ptr, *ptr_t, val, max, min; BcError e; s = bc_num_bigdig(l, &val); if (BC_ERR(s)) return s; e = left->t - BC_RESULT_IBASE + BC_ERROR_EXEC_IBASE; if (sc) { min = 0; max = vm->maxes[BC_PROG_GLOBALS_SCALE]; v = p->globals_v + BC_PROG_GLOBALS_SCALE; ptr_t = p->globals + BC_PROG_GLOBALS_SCALE; } else { min = BC_NUM_MIN_BASE; if (BC_ENABLE_EXTRA_MATH && ob && (!BC_IS_BC || !BC_IS_POSIX)) min = 0; max = vm->maxes[ob + BC_PROG_GLOBALS_IBASE]; v = p->globals_v + BC_PROG_GLOBALS_IBASE + ob; ptr_t = p->globals + BC_PROG_GLOBALS_IBASE + ob; } if (BC_ERR(val > max || val < min)) return bc_vm_verr(e, min, max); ptr = bc_vec_top(v); *ptr = val; *ptr_t = val; } if (use_val) { bc_num_createCopy(&res.d.n, l); bc_program_binOpRetire(p, &res); } else { bc_vec_pop(&p->results); bc_vec_pop(&p->results); } return s; } static BcStatus bc_program_pushVar(BcProgram *p, const char *restrict code, size_t *restrict bgn, bool pop, bool copy) { BcStatus s = BC_STATUS_SUCCESS; BcResult r; size_t idx = bc_program_index(code, bgn); r.t = BC_RESULT_VAR; r.d.loc.loc = idx; #if DC_ENABLED if (pop || copy) { BcVec *v = bc_program_vec(p, idx, BC_TYPE_VAR); BcNum *num = bc_vec_top(v); s = bc_program_checkStack(v, 2 - copy); if (BC_ERR(s)) return s; if (!BC_PROG_STR(num)) { r.t = BC_RESULT_TEMP; bc_num_createCopy(&r.d.n, num); } else { r.t = BC_RESULT_STR; r.d.loc.loc = num->scale; } if (!copy) bc_vec_pop(v); } #endif // DC_ENABLED bc_vec_push(&p->results, &r); return s; } static BcStatus bc_program_pushArray(BcProgram *p, const char *restrict code, size_t *restrict bgn, uchar inst) { BcStatus s = BC_STATUS_SUCCESS; BcResult r, *operand; BcNum *num; BcBigDig temp; r.d.loc.loc = bc_program_index(code, bgn); #if BC_ENABLED if (inst == BC_INST_ARRAY) { r.t = BC_RESULT_ARRAY; bc_vec_push(&p->results, &r); return s; } #endif // BC_ENABLED s = bc_program_prep(p, &operand, &num); if (BC_ERR(s)) return s; s = bc_num_bigdig(num, &temp); if (BC_ERR(s)) return s; r.d.loc.idx = (size_t) temp; bc_program_retire(p, &r, BC_RESULT_ARRAY_ELEM); return s; } #if BC_ENABLED static BcStatus bc_program_incdec(BcProgram *p, uchar inst) { BcStatus s; BcResult *ptr, res, copy; BcNum *num; uchar inst2; bool post, use_val; s = bc_program_prep(p, &ptr, &num); if (BC_ERR(s)) return s; use_val = (inst != BC_INST_INC_NO_VAL && inst != BC_INST_DEC_NO_VAL); post = use_val && (inst == BC_INST_INC_POST || inst == BC_INST_DEC_POST); if (post) { copy.t = BC_RESULT_TEMP; bc_num_createCopy(©.d.n, num); } res.t = BC_RESULT_ONE; inst2 = BC_INST_ASSIGN_PLUS; if (!use_val) { inst2 += (inst == BC_INST_DEC_NO_VAL); inst2 += (BC_INST_ASSIGN_PLUS_NO_VAL - BC_INST_ASSIGN_PLUS); } else inst2 += (inst & 0x01); bc_vec_push(&p->results, &res); s = bc_program_assign(p, inst2); if (post) { if (BC_ERR(s)) { bc_num_free(©.d.n); return s; } bc_vec_pop(&p->results); bc_vec_push(&p->results, ©); } return s; } static BcStatus bc_program_call(BcProgram *p, const char *restrict code, size_t *restrict idx) { BcStatus s = BC_STATUS_SUCCESS; BcInstPtr ip; size_t i, nparams = bc_program_index(code, idx); BcFunc *f; BcVec *v; BcLoc *a; BcResultData param; BcResult *arg; ip.idx = 0; ip.func = bc_program_index(code, idx); f = bc_vec_item(&p->fns, ip.func); if (BC_ERR(!f->code.len)) return bc_vm_verr(BC_ERROR_EXEC_UNDEF_FUNC, f->name); if (BC_ERR(nparams != f->nparams)) return bc_vm_verr(BC_ERROR_EXEC_PARAMS, f->nparams, nparams); ip.len = p->results.len - nparams; assert(BC_PROG_STACK(&p->results, nparams)); if (BC_G) bc_program_prepGlobals(p); for (i = 0; i < nparams; ++i) { size_t j; bool last = true; arg = bc_vec_top(&p->results); if (BC_ERR(arg->t == BC_RESULT_VOID)) return bc_vm_err(BC_ERROR_EXEC_VOID_VAL); a = bc_vec_item(&f->autos, nparams - 1 - i); // If I have already pushed to a var, I need to make sure I // get the previous version, not the already pushed one. if (arg->t == BC_RESULT_VAR || arg->t == BC_RESULT_ARRAY) { for (j = 0; j < i && last; ++j) { BcLoc *loc = bc_vec_item(&f->autos, nparams - 1 - j); last = (arg->d.loc.loc != loc->loc || (!loc->idx) != (arg->t == BC_RESULT_VAR)); } } s = bc_program_copyToVar(p, a->loc, (BcType) a->idx, last); if (BC_ERR(s)) return s; } for (; i < f->autos.len; ++i) { a = bc_vec_item(&f->autos, i); v = bc_program_vec(p, a->loc, (BcType) a->idx); if (a->idx == BC_TYPE_VAR) { bc_num_init(¶m.n, BC_NUM_DEF_SIZE); bc_vec_push(v, ¶m.n); } else { assert(a->idx == BC_TYPE_ARRAY); bc_array_init(¶m.v, true); bc_vec_push(v, ¶m.v); } } bc_vec_push(&p->stack, &ip); return BC_STATUS_SUCCESS; } static BcStatus bc_program_return(BcProgram *p, uchar inst) { BcStatus s; BcResult res; BcFunc *f; size_t i; BcInstPtr *ip = bc_vec_top(&p->stack); assert(BC_PROG_STACK(&p->stack, 2)); #ifndef BC_PROG_NO_STACK_CHECK s = bc_program_checkStack(&p->results, ip->len + (inst == BC_INST_RET)); if (BC_ERR(s)) return s; #endif // BC_PROG_NO_STACK_CHECK f = bc_vec_item(&p->fns, ip->func); res.t = BC_RESULT_TEMP; if (inst == BC_INST_RET) { BcNum *num; BcResult *operand; s = bc_program_operand(p, &operand, &num, 0); if (BC_ERR(s)) return s; bc_num_createCopy(&res.d.n, num); } else if (inst == BC_INST_RET_VOID) res.t = BC_RESULT_VOID; else bc_num_init(&res.d.n, BC_NUM_DEF_SIZE); // We need to pop arguments as well, so this takes that into account. for (i = 0; i < f->autos.len; ++i) { BcLoc *a = bc_vec_item(&f->autos, i); BcVec *v = bc_program_vec(p, a->loc, (BcType) a->idx); bc_vec_pop(v); } bc_vec_npop(&p->results, p->results.len - ip->len); if (BC_G) { for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) { BcVec *v = p->globals_v + i; bc_vec_pop(v); p->globals[i] = BC_PROG_GLOBAL(v); } } bc_vec_push(&p->results, &res); bc_vec_pop(&p->stack); return BC_STATUS_SUCCESS; } #endif // BC_ENABLED static BcStatus bc_program_builtin(BcProgram *p, uchar inst) { BcStatus s; BcResult *opd; BcResult res; BcNum *num, *resn = &res.d.n; bool len = (inst == BC_INST_LENGTH); assert(inst >= BC_INST_LENGTH && inst <= BC_INST_ABS); s = bc_program_operand(p, &opd, &num, 0); if (BC_ERR(s)) return s; assert(num != NULL); #if DC_ENABLED if (!len && inst != BC_INST_SCALE_FUNC) { s = bc_program_type_num(opd, num); if (BC_ERR(s)) return s; } #endif // DC_ENABLED if (inst == BC_INST_SQRT) { s = bc_num_sqrt(num, resn, BC_PROG_SCALE(p)); if (BC_ERR(s)) return s; } else if (inst == BC_INST_ABS) { bc_num_createCopy(resn, num); resn->neg = false; } else { BcBigDig val = 0; if (len) { #if BC_ENABLED if (BC_IS_BC && opd->t == BC_RESULT_ARRAY) val = (BcBigDig) ((BcVec*) num)->len; else #endif // BC_ENABLED { #if DC_ENABLED if (!BC_PROG_NUM(opd, num)) { size_t idx; idx = opd->t == BC_RESULT_STR ? opd->d.loc.loc : num->scale; val = (BcBigDig) strlen(bc_program_str(p, idx)); } else #endif // DC_ENABLED { val = (BcBigDig) bc_num_len(num); } } } else if (BC_IS_BC || BC_PROG_NUM(opd, num)) val = (BcBigDig) bc_num_scale(num); bc_num_createFromBigdig(resn, val); } bc_program_retire(p, &res, BC_RESULT_TEMP); return s; } #if DC_ENABLED static BcStatus bc_program_divmod(BcProgram *p) { BcStatus s; BcResult *opd1, *opd2, res, res2; BcNum *n1, *n2, *resn = &res.d.n, *resn2 = &res2.d.n; size_t req; s = bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2); if (BC_ERR(s)) return s; req = bc_num_mulReq(n1, n2, BC_PROG_SCALE(p)); bc_num_init(resn, req); bc_num_init(resn2, req); s = bc_num_divmod(n1, n2, resn2, resn, BC_PROG_SCALE(p)); if (BC_ERR(s)) goto err; bc_program_binOpRetire(p, &res2); res.t = BC_RESULT_TEMP; bc_vec_push(&p->results, &res); return s; err: bc_num_free(resn2); bc_num_free(resn); return s; } static BcStatus bc_program_modexp(BcProgram *p) { BcStatus s; BcResult *r1, *r2, *r3, res; BcNum *n1, *n2, *n3, *resn = &res.d.n; s = bc_program_operand(p, &r1, &n1, 2); if (BC_ERR(s)) return s; s = bc_program_type_num(r1, n1); if (BC_ERR(s)) return s; s = bc_program_binOpPrep(p, &r2, &n2, &r3, &n3); if (BC_ERR(s)) return s; // Make sure that the values have their pointers updated, if necessary. // Only array elements are possible. if (r1->t == BC_RESULT_ARRAY_ELEM && (r1->t == r2->t || r1->t == r3->t)) { s = bc_program_num(p, r1, &n1); if (BC_ERR(s)) return s; } bc_num_init(resn, n3->len); s = bc_num_modexp(n1, n2, n3, resn); if (BC_ERR(s)) goto err; bc_vec_pop(&p->results); bc_program_binOpRetire(p, &res); return s; err: bc_num_free(resn); return s; } static void bc_program_stackLen(BcProgram *p) { BcResult res; res.t = BC_RESULT_TEMP; bc_num_createFromBigdig(&res.d.n, (BcBigDig) p->results.len); bc_vec_push(&p->results, &res); } static BcStatus bc_program_asciify(BcProgram *p) { BcStatus s; BcResult *r, res; BcNum *n, num; char str[2], *str2, c; size_t len; BcBigDig val; BcFunc f, *func; s = bc_program_operand(p, &r, &n, 0); if (BC_ERR(s)) return s; assert(n != NULL); func = bc_vec_item(&p->fns, BC_PROG_MAIN); len = func->strs.len; assert(len + BC_PROG_REQ_FUNCS == p->fns.len); if (BC_PROG_NUM(r, n)) { bc_num_createCopy(&num, n); bc_num_truncate(&num, num.scale); num.neg = false; // This is guaranteed to not have a divide by 0 // because strmb is equal to UCHAR_MAX + 1. s = bc_num_mod(&num, &p->strmb, &num, 0); assert(!s || s == BC_STATUS_SIGNAL); #if BC_ENABLE_SIGNALS if (BC_ERROR_SIGNAL_ONLY(s)) goto num_err; #endif // BC_ENABLE_SIGNALS // This is also guaranteed to not error because num is in the range // [0, UCHAR_MAX], which is definitely in range for a BcBigDig. And // it is not negative. s = bc_num_bigdig(&num, &val); assert(!s || s == BC_STATUS_SIGNAL); #if BC_ENABLE_SIGNALS if (BC_ERROR_SIGNAL_ONLY(s)) goto num_err; #endif // BC_ENABLE_SIGNALS c = (char) val; bc_num_free(&num); } else { size_t idx = r->t == BC_RESULT_STR ? r->d.loc.loc : n->scale; str2 = *((char**) bc_vec_item(&func->strs, idx)); c = str2[0]; } str[0] = c; str[1] = '\0'; bc_program_addFunc(p, &f, bc_func_main); str2 = bc_vm_strdup(str); // Make sure the pointer is updated. func = bc_vec_item(&p->fns, BC_PROG_MAIN); bc_vec_push(&func->strs, &str2); res.t = BC_RESULT_STR; res.d.loc.loc = len; bc_vec_pop(&p->results); bc_vec_push(&p->results, &res); return BC_STATUS_SUCCESS; #if BC_ENABLE_SIGNALS num_err: bc_num_free(&num); return s; #endif // BC_ENABLE_SIGNALS } static BcStatus bc_program_printStream(BcProgram *p) { BcStatus s; BcResult *r; BcNum *n; s = bc_program_operand(p, &r, &n, 0); if (BC_ERR(s)) return s; assert(n != NULL); if (BC_PROG_NUM(r, n)) s = bc_num_stream(n, p->strm); else { size_t idx = (r->t == BC_RESULT_STR) ? r->d.loc.loc : n->scale; bc_program_printChars(bc_program_str(p, idx)); } return s; } static BcStatus bc_program_nquit(BcProgram *p) { BcStatus s; BcResult *opnd; BcNum *num; BcBigDig val; s = bc_program_prep(p, &opnd, &num); if (BC_ERR(s)) return s; s = bc_num_bigdig(num, &val); if (BC_ERR(s)) return s; bc_vec_pop(&p->results); s = bc_program_checkStack(&p->stack, val); if (BC_ERR(s)) return s; else if (p->stack.len == val) return BC_STATUS_QUIT; bc_vec_npop(&p->stack, val); return s; } static BcStatus bc_program_execStr(BcProgram *p, const char *restrict code, size_t *restrict bgn, bool cond) { BcStatus s = BC_STATUS_SUCCESS; BcResult *r; char *str; BcFunc *f; BcParse prs; BcInstPtr ip; size_t fidx, sidx; BcNum *n; bool exec; s = bc_program_operand(p, &r, &n, 0); if (BC_ERR(s)) return s; if (cond) { size_t idx = SIZE_MAX, then_idx, else_idx; then_idx = bc_program_index(code, bgn); else_idx = bc_program_index(code, bgn); exec = (r->d.n.len != 0); if (exec) idx = then_idx; else { exec = (else_idx != SIZE_MAX); idx = else_idx; } if (exec) n = bc_vec_top(bc_program_vec(p, idx, BC_TYPE_VAR)); else goto exit; if (BC_ERR(!BC_PROG_STR(n))) { s = bc_vm_err(BC_ERROR_EXEC_TYPE); goto exit; } sidx = n->scale; } else { // In non-conditional situations, only the top of stack can be executed, // and in those cases, variables are not allowed to be "on the stack"; // they are only put on the stack to be assigned to. assert(r->t != BC_RESULT_VAR); if (r->t == BC_RESULT_STR) sidx = r->d.loc.loc; else goto no_exec; } fidx = sidx + BC_PROG_REQ_FUNCS; str = bc_program_str(p, sidx); f = bc_vec_item(&p->fns, fidx); if (!f->code.len) { bc_parse_init(&prs, p, fidx); bc_lex_file(&prs.l, vm->file); s = bc_parse_text(&prs, str); if (BC_ERR(s)) goto err; s = vm->expr(&prs, BC_PARSE_NOCALL); if (BC_ERR(s)) goto err; // We can just assert this here because // dc should parse everything until EOF. assert(prs.l.t == BC_LEX_EOF); bc_parse_free(&prs); } ip.idx = 0; ip.len = p->results.len; ip.func = fidx; bc_vec_pop(&p->results); bc_vec_push(&p->stack, &ip); return BC_STATUS_SUCCESS; err: bc_parse_free(&prs); f = bc_vec_item(&p->fns, fidx); bc_vec_npop(&f->code, f->code.len); exit: bc_vec_pop(&p->results); no_exec: return s; } static BcStatus bc_program_printStack(BcProgram *p) { BcStatus s = BC_STATUS_SUCCESS; size_t idx; for (idx = 0; BC_NO_ERR(!s) && idx < p->results.len; ++idx) s = bc_program_print(p, BC_INST_PRINT, idx); return s; } #endif // DC_ENABLED static void bc_program_pushBigDig(BcProgram *p, BcBigDig dig, BcResultType type) { BcResult res; bc_num_createFromBigdig(&res.d.n, dig); res.t = type; bc_vec_push(&p->results, &res); } static void bc_program_pushGlobal(BcProgram *p, uchar inst) { BcResultType t; assert(inst >= BC_INST_IBASE && inst <= BC_INST_SCALE); t = inst - BC_INST_IBASE + BC_RESULT_IBASE; bc_program_pushBigDig(p, p->globals[inst - BC_INST_IBASE], t); } #ifndef NDEBUG void bc_program_free(BcProgram *p) { size_t i; assert(p != NULL); for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) bc_vec_free(p->globals_v + i); bc_vec_free(&p->fns); #if BC_ENABLED bc_vec_free(&p->fn_map); #endif // BC_ENABLED bc_vec_free(&p->vars); bc_vec_free(&p->var_map); bc_vec_free(&p->arrs); bc_vec_free(&p->arr_map); bc_vec_free(&p->results); bc_vec_free(&p->stack); #if BC_ENABLED bc_num_free(&p->last); #endif // BC_ENABLED } #endif // NDEBUG void bc_program_init(BcProgram *p) { BcInstPtr ip; size_t i; BcBigDig val = BC_BASE; assert(p != NULL); memset(p, 0, sizeof(BcProgram)); memset(&ip, 0, sizeof(BcInstPtr)); for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i) { bc_vec_init(p->globals_v + i, sizeof(BcBigDig), NULL); val = i == BC_PROG_GLOBALS_SCALE ? 0 : val; bc_vec_push(p->globals_v + i, &val); p->globals[i] = val; } #if DC_ENABLED p->strm = UCHAR_MAX + 1; bc_num_setup(&p->strmb, p->strmb_num, BC_NUM_BIGDIG_LOG10); bc_num_bigdig2num(&p->strmb, p->strm); #endif // DC_ENABLED bc_num_setup(&p->one, p->one_num, BC_PROG_ONE_CAP); bc_num_one(&p->one); #if BC_ENABLED bc_num_init(&p->last, BC_NUM_DEF_SIZE); #endif // BC_ENABLED bc_vec_init(&p->fns, sizeof(BcFunc), bc_func_free); #if BC_ENABLED bc_map_init(&p->fn_map); bc_program_insertFunc(p, bc_vm_strdup(bc_func_main)); bc_program_insertFunc(p, bc_vm_strdup(bc_func_read)); #else // BC_ENABLED { BcFunc f; bc_program_addFunc(p, &f, bc_func_main); bc_program_addFunc(p, &f, bc_func_read); } #endif // BC_ENABLED bc_vec_init(&p->vars, sizeof(BcVec), bc_vec_free); bc_map_init(&p->var_map); bc_vec_init(&p->arrs, sizeof(BcVec), bc_vec_free); bc_map_init(&p->arr_map); bc_vec_init(&p->results, sizeof(BcResult), bc_result_free); bc_vec_init(&p->stack, sizeof(BcInstPtr), NULL); bc_vec_push(&p->stack, &ip); } void bc_program_addFunc(BcProgram *p, BcFunc *f, const char *name) { bc_func_init(f, name); bc_vec_push(&p->fns, f); } #if BC_ENABLED size_t bc_program_insertFunc(BcProgram *p, char *name) { BcId id; BcFunc f; bool new; size_t idx; assert(p != NULL && name != NULL); id.name = name; id.idx = p->fns.len; new = bc_map_insert(&p->fn_map, &id, &idx); idx = ((BcId*) bc_vec_item(&p->fn_map, idx))->idx; if (!new) { BcFunc *func = bc_vec_item(&p->fns, idx); bc_func_reset(func); free(name); } else bc_program_addFunc(p, &f, name); return idx; } #endif // BC_ENABLED BcStatus bc_program_reset(BcProgram *p, BcStatus s) { BcFunc *f; BcInstPtr *ip; bc_vec_npop(&p->stack, p->stack.len - 1); bc_vec_npop(&p->results, p->results.len); f = bc_vec_item(&p->fns, 0); ip = bc_vec_top(&p->stack); ip->idx = f->code.len; #if BC_ENABLE_SIGNALS if (BC_SIGTERM || (!s && BC_SIGINT && BC_I)) return BC_STATUS_QUIT; vm->sig_chk = vm->sig; if (!s || s == BC_STATUS_SIGNAL) { if (BC_TTYIN || BC_I) { bc_vm_puts(bc_program_ready_msg, stderr); bc_vm_fflush(stderr); s = BC_STATUS_SUCCESS; } else s = BC_STATUS_QUIT; } #endif // BC_ENABLE_SIGNALS return s; } BcStatus bc_program_exec(BcProgram *p) { BcStatus s = BC_STATUS_SUCCESS; size_t idx; BcResult r, *ptr; BcInstPtr *ip = bc_vec_top(&p->stack); BcFunc *func = bc_vec_item(&p->fns, ip->func); char *code = func->code.v; bool cond = false; #if BC_ENABLED BcNum *num; #endif // BC_ENABLED while (BC_NO_SIG && BC_NO_ERR(!s) && ip->idx < func->code.len) { uchar inst = (uchar) code[(ip->idx)++]; switch (inst) { #if BC_ENABLED case BC_INST_JUMP_ZERO: { s = bc_program_prep(p, &ptr, &num); if (BC_ERR(s)) return s; cond = !bc_num_cmpZero(num); bc_vec_pop(&p->results); } // Fallthrough. case BC_INST_JUMP: { idx = bc_program_index(code, &ip->idx); if (inst == BC_INST_JUMP || cond) { size_t *addr = bc_vec_item(&func->labels, idx); assert(*addr != SIZE_MAX); ip->idx = *addr; } break; } case BC_INST_CALL: { s = bc_program_call(p, code, &ip->idx); ip = bc_vec_top(&p->stack); func = bc_vec_item(&p->fns, ip->func); code = func->code.v; break; } case BC_INST_INC_PRE: case BC_INST_DEC_PRE: case BC_INST_INC_POST: case BC_INST_DEC_POST: case BC_INST_INC_NO_VAL: case BC_INST_DEC_NO_VAL: { s = bc_program_incdec(p, inst); break; } case BC_INST_HALT: { s = BC_STATUS_QUIT; break; } case BC_INST_RET: case BC_INST_RET0: case BC_INST_RET_VOID: { s = bc_program_return(p, inst); ip = bc_vec_top(&p->stack); func = bc_vec_item(&p->fns, ip->func); code = func->code.v; break; } case BC_INST_BOOL_OR: case BC_INST_BOOL_AND: #endif // BC_ENABLED case BC_INST_REL_EQ: case BC_INST_REL_LE: case BC_INST_REL_GE: case BC_INST_REL_NE: case BC_INST_REL_LT: case BC_INST_REL_GT: { s = bc_program_logical(p, inst); break; } case BC_INST_READ: { s = bc_program_read(p); ip = bc_vec_top(&p->stack); func = bc_vec_item(&p->fns, ip->func); code = func->code.v; break; } case BC_INST_MAXIBASE: case BC_INST_MAXOBASE: case BC_INST_MAXSCALE: { BcBigDig dig = vm->maxes[inst - BC_INST_MAXIBASE]; bc_program_pushBigDig(p, dig, BC_RESULT_TEMP); break; } case BC_INST_VAR: { s = bc_program_pushVar(p, code, &ip->idx, false, false); break; } case BC_INST_ARRAY_ELEM: #if BC_ENABLED case BC_INST_ARRAY: #endif // BC_ENABLED { s = bc_program_pushArray(p, code, &ip->idx, inst); break; } case BC_INST_IBASE: case BC_INST_SCALE: case BC_INST_OBASE: { bc_program_pushGlobal(p, inst); break; } case BC_INST_LENGTH: case BC_INST_SCALE_FUNC: case BC_INST_SQRT: case BC_INST_ABS: { s = bc_program_builtin(p, inst); break; } case BC_INST_NUM: { r.t = BC_RESULT_CONSTANT; r.d.loc.loc = bc_program_index(code, &ip->idx); bc_vec_push(&p->results, &r); break; } case BC_INST_ONE: #if BC_ENABLED case BC_INST_LAST: #endif // BC_ENABLED { r.t = BC_RESULT_ONE + (inst - BC_INST_ONE); bc_vec_push(&p->results, &r); break; } case BC_INST_PRINT: case BC_INST_PRINT_POP: case BC_INST_PRINT_STR: { s = bc_program_print(p, inst, 0); break; } case BC_INST_STR: { r.t = BC_RESULT_STR; r.d.loc.loc = bc_program_index(code, &ip->idx); bc_vec_push(&p->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: #if BC_ENABLE_EXTRA_MATH case BC_INST_PLACES: case BC_INST_LSHIFT: case BC_INST_RSHIFT: #endif // BC_ENABLE_EXTRA_MATH { s = bc_program_op(p, inst); break; } case BC_INST_NEG: case BC_INST_BOOL_NOT: #if BC_ENABLE_EXTRA_MATH case BC_INST_TRUNC: #endif // BC_ENABLE_EXTRA_MATH { s = bc_program_unary(p, inst); break; } #if BC_ENABLED case BC_INST_ASSIGN_POWER: case BC_INST_ASSIGN_MULTIPLY: case BC_INST_ASSIGN_DIVIDE: case BC_INST_ASSIGN_MODULUS: case BC_INST_ASSIGN_PLUS: case BC_INST_ASSIGN_MINUS: #if BC_ENABLE_EXTRA_MATH case BC_INST_ASSIGN_PLACES: case BC_INST_ASSIGN_LSHIFT: case BC_INST_ASSIGN_RSHIFT: #endif // BC_ENABLE_EXTRA_MATH case BC_INST_ASSIGN: case BC_INST_ASSIGN_POWER_NO_VAL: case BC_INST_ASSIGN_MULTIPLY_NO_VAL: case BC_INST_ASSIGN_DIVIDE_NO_VAL: case BC_INST_ASSIGN_MODULUS_NO_VAL: case BC_INST_ASSIGN_PLUS_NO_VAL: case BC_INST_ASSIGN_MINUS_NO_VAL: #if BC_ENABLE_EXTRA_MATH case BC_INST_ASSIGN_PLACES_NO_VAL: case BC_INST_ASSIGN_LSHIFT_NO_VAL: case BC_INST_ASSIGN_RSHIFT_NO_VAL: #endif // BC_ENABLE_EXTRA_MATH #endif // BC_ENABLED case BC_INST_ASSIGN_NO_VAL: { s = bc_program_assign(p, inst); break; } case BC_INST_POP: { #ifndef BC_PROG_NO_STACK_CHECK s = bc_program_checkStack(&p->results, 1); if (BC_ERR(s)) return s; #endif // BC_PROG_NO_STACK_CHECK bc_vec_pop(&p->results); break; } #if DC_ENABLED case BC_INST_POP_EXEC: { assert(BC_PROG_STACK(&p->stack, 2)); bc_vec_pop(&p->stack); ip = bc_vec_top(&p->stack); func = bc_vec_item(&p->fns, ip->func); code = func->code.v; break; } case BC_INST_MODEXP: { s = bc_program_modexp(p); break; } case BC_INST_DIVMOD: { s = bc_program_divmod(p); break; } case BC_INST_EXECUTE: case BC_INST_EXEC_COND: { cond = (inst == BC_INST_EXEC_COND); s = bc_program_execStr(p, code, &ip->idx, cond); ip = bc_vec_top(&p->stack); func = bc_vec_item(&p->fns, ip->func); code = func->code.v; break; } case BC_INST_PRINT_STACK: { s = bc_program_printStack(p); break; } case BC_INST_CLEAR_STACK: { bc_vec_npop(&p->results, p->results.len); break; } case BC_INST_STACK_LEN: { bc_program_stackLen(p); break; } case BC_INST_DUPLICATE: { s = bc_program_checkStack(&p->results, 1); if (BC_ERR(s)) break; ptr = bc_vec_top(&p->results); bc_result_copy(&r, ptr); bc_vec_push(&p->results, &r); break; } case BC_INST_SWAP: { BcResult *ptr2; s = bc_program_checkStack(&p->results, 2); if (BC_ERR(s)) break; ptr = bc_vec_item_rev(&p->results, 0); ptr2 = bc_vec_item_rev(&p->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(p); ip = bc_vec_top(&p->stack); func = bc_vec_item(&p->fns, ip->func); code = func->code.v; break; } case BC_INST_PRINT_STREAM: { s = bc_program_printStream(p); break; } case BC_INST_LOAD: case BC_INST_PUSH_VAR: { bool copy = (inst == BC_INST_LOAD); s = bc_program_pushVar(p, code, &ip->idx, true, copy); break; } case BC_INST_PUSH_TO_VAR: { idx = bc_program_index(code, &ip->idx); s = bc_program_copyToVar(p, idx, BC_TYPE_VAR, true); break; } case BC_INST_QUIT: { if (p->stack.len <= 2) s = BC_STATUS_QUIT; else bc_vec_npop(&p->stack, 2); ip = bc_vec_top(&p->stack); func = bc_vec_item(&p->fns, ip->func); code = func->code.v; break; } case BC_INST_NQUIT: { s = bc_program_nquit(p); ip = bc_vec_top(&p->stack); func = bc_vec_item(&p->fns, ip->func); code = func->code.v; break; } #endif // DC_ENABLED #ifndef NDEBUG default: { assert(false); break; } #endif // NDEBUG } } if (BC_ERR(s && s != BC_STATUS_QUIT) || BC_SIG) s = bc_program_reset(p, s); return s; } #if BC_DEBUG_CODE #if BC_ENABLED && DC_ENABLED BcStatus bc_program_printStackDebug(BcProgram *p) { BcStatus s; printf("-------------- Stack ----------\n"); s = bc_program_printStack(p); printf("-------------- Stack End ------\n"); return s; } static void bc_program_printIndex(const char *restrict code, size_t *restrict bgn) { uchar byte, i, bytes = (uchar) code[(*bgn)++]; ulong val = 0; for (byte = 1, i = 0; byte && i < bytes; ++i) { byte = (uchar) code[(*bgn)++]; if (byte) val |= ((ulong) byte) << (CHAR_BIT * i); } bc_vm_printf(" (%lu) ", val); } static void bc_program_printStr(const BcProgram *p, const char *restrict code, size_t *restrict bgn) { size_t idx = bc_program_index(code, bgn); char *s; s = bc_program_str(p, idx); bc_vm_printf(" (\"%s\") ", s); } void bc_program_printInst(const BcProgram *p, const char *restrict code, size_t *restrict bgn) { uchar inst = (uchar) code[(*bgn)++]; bc_vm_printf("Inst: %s [%u]; ", bc_inst_names[inst], inst); if (inst == BC_INST_VAR || inst == BC_INST_ARRAY_ELEM || inst == BC_INST_ARRAY) { bc_program_printIndex(code, bgn); } else if (inst == BC_INST_STR) bc_program_printStr(p, code, bgn); else if (inst == BC_INST_NUM) { size_t idx = bc_program_index(code, bgn); BcConst *c = bc_program_const(p, idx); bc_vm_printf("(%s)", c->val); } else if (inst == BC_INST_CALL || (inst > BC_INST_STR && inst <= BC_INST_JUMP_ZERO)) { bc_program_printIndex(code, bgn); if (inst == BC_INST_CALL) bc_program_printIndex(code, bgn); } bc_vm_putchar('\n'); } void bc_program_code(const BcProgram* p) { BcFunc *f; char *code; BcInstPtr ip; size_t i; for (i = 0; i < p->fns.len; ++i) { ip.idx = ip.len = 0; ip.func = i; f = bc_vec_item(&p->fns, ip.func); code = f->code.v; bc_vm_printf("func[%zu]:\n", ip.func); while (ip.idx < f->code.len) bc_program_printInst(p, code, &ip.idx); bc_vm_printf("\n\n"); } } #endif // BC_ENABLED && DC_ENABLED #endif // BC_DEBUG_CODE static bool bc_read_binary(const char *buf, size_t size) { size_t i; for (i = 0; i < size; ++i) { if (BC_ERR(BC_READ_BIN_CHAR(buf[i]))) return true; } return false; } BcStatus bc_read_chars(BcVec *vec, const char *prompt) { int i; signed char c = 0; assert(vec != NULL && vec->size == sizeof(char)); bc_vec_npop(vec, vec->len); #if BC_ENABLE_PROMPT if (BC_USE_PROMPT) { bc_vm_puts(prompt, stderr); bc_vm_fflush(stderr); } #endif // BC_ENABLE_PROMPT while (BC_NO_SIG && c != '\n') { i = fgetc(stdin); if (BC_UNLIKELY(i == EOF)) { #if BC_ENABLE_SIGNALS if (errno == EINTR) { if (BC_SIGTERM) return BC_STATUS_QUIT; vm->sig_chk = vm->sig; if (BC_TTYIN || BC_I) { bc_vm_puts(bc_program_ready_msg, stderr); #if BC_ENABLE_PROMPT if (BC_USE_PROMPT) bc_vm_puts(prompt, stderr); #endif // BC_ENABLE_PROMPT bc_vm_fflush(stderr); } else return BC_STATUS_SIGNAL; continue; } #endif // BC_ENABLE_SIGNALS bc_vec_pushByte(vec, '\0'); return BC_STATUS_EOF; } c = (signed char) i; bc_vec_push(vec, &c); } bc_vec_pushByte(vec, '\0'); return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS; } BcStatus bc_read_line(BcVec *vec, const char *prompt) { BcStatus s; // We are about to output to stderr, so flush stdout to // make sure that we don't get the outputs mixed up. bc_vm_fflush(stdout); #if BC_ENABLE_HISTORY s = bc_history_line(&vm->history, vec, prompt); #else // BC_ENABLE_HISTORY s = bc_read_chars(vec, prompt); #endif // BC_ENABLE_HISTORY if (BC_ERR(s && s != BC_STATUS_EOF)) return s; if (BC_ERR(bc_read_binary(vec->v, vec->len - 1))) return bc_vm_verr(BC_ERROR_FATAL_BIN_FILE, bc_program_stdin_name); return s; } BcStatus bc_read_file(const char *path, char **buf) { BcError e = BC_ERROR_FATAL_IO_ERR; FILE *f; size_t size, read; long res; struct stat pstat; assert(path != NULL); f = fopen(path, "r"); if (BC_ERR(f == NULL)) return bc_vm_verr(BC_ERROR_FATAL_FILE_ERR, path); if (BC_ERR(fstat(fileno(f), &pstat) == -1)) goto malloc_err; if (BC_ERR(S_ISDIR(pstat.st_mode))) { e = BC_ERROR_FATAL_PATH_DIR; goto malloc_err; } if (BC_ERR(fseek(f, 0, SEEK_END) == -1)) goto malloc_err; res = ftell(f); if (BC_ERR(res < 0)) goto malloc_err; if (BC_ERR(fseek(f, 0, SEEK_SET) == -1)) goto malloc_err; size = (size_t) res; *buf = bc_vm_malloc(size + 1); read = fread(*buf, 1, size, f); if (BC_ERR(read != size)) goto read_err; (*buf)[size] = '\0'; if (BC_ERR(bc_read_binary(*buf, size))) { e = BC_ERROR_FATAL_BIN_FILE; goto read_err; } fclose(f); return BC_STATUS_SUCCESS; read_err: free(*buf); malloc_err: fclose(f); return bc_vm_verr(e, path); } #if BC_ENABLE_HISTORY /** * Check if the code is a wide character. */ static bool bc_history_wchar(uint32_t cp) { size_t i; for (i = 0; i < bc_history_wchars_len; ++i) { // Ranges are listed in ascending order. Therefore, once the // whole range is higher than the codepoint we're testing, the // codepoint won't be found in any remaining range => bail early. if (bc_history_wchars[i][0] > cp) return false; // Test this range. if (bc_history_wchars[i][0] <= cp && cp <= bc_history_wchars[i][1]) return true; } return false; } /** * Check if the code is a combining character. */ static bool bc_history_comboChar(uint32_t cp) { size_t i; for (i = 0; i < bc_history_combo_chars_len; ++i) { // Combining chars are listed in ascending order, so once we pass // the codepoint of interest, we know it's not a combining char. if (bc_history_combo_chars[i] > cp) return false; if (bc_history_combo_chars[i] == cp) return true; } return false; } /** * Get length of previous UTF8 character. */ static size_t bc_history_prevCharLen(const char *buf, size_t pos) { size_t end = pos; for (pos -= 1; pos < end && (buf[pos] & 0xC0) == 0x80; --pos); return end - (pos >= end ? 0 : pos); } /** * Convert UTF-8 to Unicode code point. */ static size_t bc_history_codePoint(const char *s, size_t len, uint32_t *cp) { if (len) { uchar byte = (uchar) s[0]; if ((byte & 0x80) == 0) { *cp = byte; return 1; } else if ((byte & 0xE0) == 0xC0) { if (len >= 2) { *cp = (((uint32_t) (s[0] & 0x1F)) << 6) | ((uint32_t) (s[1] & 0x3F)); return 2; } } else if ((byte & 0xF0) == 0xE0) { if (len >= 3) { *cp = (((uint32_t) (s[0] & 0x0F)) << 12) | (((uint32_t) (s[1] & 0x3F)) << 6) | ((uint32_t) (s[2] & 0x3F)); return 3; } } else if ((byte & 0xF8) == 0xF0) { if (len >= 4) { *cp = (((uint32_t) (s[0] & 0x07)) << 18) | (((uint32_t) (s[1] & 0x3F)) << 12) | (((uint32_t) (s[2] & 0x3F)) << 6) | ((uint32_t) (s[3] & 0x3F)); return 4; } } else { *cp = 0xFFFD; return 1; } } *cp = 0; return 1; } /** * Get length of next grapheme. */ static size_t bc_history_nextLen(const char *buf, size_t buf_len, size_t pos, size_t *col_len) { uint32_t cp; size_t beg = pos; size_t len = bc_history_codePoint(buf + pos, buf_len - pos, &cp); if (bc_history_comboChar(cp)) { // Currently unreachable? return 0; } if (col_len != NULL) *col_len = bc_history_wchar(cp) ? 2 : 1; pos += len; while (pos < buf_len) { len = bc_history_codePoint(buf + pos, buf_len - pos, &cp); if (!bc_history_comboChar(cp)) return pos - beg; pos += len; } return pos - beg; } /** * Get length of previous grapheme. */ static size_t bc_history_prevLen(const char *buf, size_t pos, size_t *col_len) { size_t end = pos; while (pos > 0) { uint32_t cp; size_t len = bc_history_prevCharLen(buf, pos); pos -= len; bc_history_codePoint(buf + pos, len, &cp); if (!bc_history_comboChar(cp)) { if (col_len != NULL) *col_len = 1 + (bc_history_wchar(cp) != 0); return end - pos; } } // Currently unreachable? return 0; } /** * Read a Unicode code point from a file. */ static BcStatus bc_history_readCode(char *buf, size_t buf_len, uint32_t *cp, size_t *nread) { BcStatus s = BC_STATUS_EOF; ssize_t n; assert(buf_len >= 1); n = read(STDIN_FILENO, buf, 1); if (BC_ERR(n <= 0)) goto err; uchar byte = (uchar) buf[0]; if ((byte & 0x80) != 0) { if ((byte & 0xE0) == 0xC0) { assert(buf_len >= 2); n = read(STDIN_FILENO, buf + 1, 1); if (BC_ERR(n <= 0)) goto err; } else if ((byte & 0xF0) == 0xE0) { assert(buf_len >= 3); n = read(STDIN_FILENO, buf + 1, 2); if (BC_ERR(n <= 0)) goto err; } else if ((byte & 0xF8) == 0xF0) { assert(buf_len >= 3); n = read(STDIN_FILENO, buf + 1, 3); if (BC_ERR(n <= 0)) goto err; } else { n = -1; goto err; } } *nread = bc_history_codePoint(buf, buf_len, cp); return BC_STATUS_SUCCESS; err: if (BC_ERR(n < 0)) s = bc_vm_err(BC_ERROR_FATAL_IO_ERR); else *nread = (size_t) n; return s; } /** * Get column length from begining of buffer to current byte position. */ static size_t bc_history_colPos(const char *buf, size_t buf_len, size_t pos) { size_t ret = 0, off = 0; while (off < pos) { size_t col_len, len; len = bc_history_nextLen(buf, buf_len, off, &col_len); off += len; ret += col_len; } return ret; } /** * Return true if the terminal name is in the list of terminals we know are * not able to understand basic escape sequences. */ static bool bc_history_isBadTerm(void) { size_t i; char *term = getenv("TERM"); if (term == NULL) return false; for (i = 0; bc_history_bad_terms[i]; ++i) { if (!strcasecmp(term, bc_history_bad_terms[i])) return true; } return false; } /** * Raw mode: 1960's black magic. */ static BcStatus bc_history_enableRaw(BcHistory *h) { struct termios raw; assert(BC_TTYIN); if (h->rawMode) return BC_STATUS_SUCCESS; if (BC_ERR(tcgetattr(STDIN_FILENO, &h->orig_termios) == -1)) return bc_vm_err(BC_ERROR_FATAL_IO_ERR); // Modify the original mode. raw = h->orig_termios; // Input modes: no break, no CR to NL, no parity check, no strip char, // no start/stop output control. raw.c_iflag &= (unsigned int) (~(BRKINT | ICRNL | INPCK | ISTRIP | IXON)); // Control modes - set 8 bit chars. raw.c_cflag |= (CS8); // Local modes - choing off, canonical off, no extended functions, // no signal chars (^Z,^C). raw.c_lflag &= (unsigned int) (~(ECHO | ICANON | IEXTEN | ISIG)); // Control chars - set return condition: min number of bytes and timer. // We want read to give every single byte, w/o timeout (1 byte, no timer). raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; // Put terminal in raw mode after flushing. if (BC_ERR(tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0)) return bc_vm_err(BC_ERROR_FATAL_IO_ERR); h->rawMode = true; return BC_STATUS_SUCCESS; } static void bc_history_disableRaw(BcHistory *h) { // Don't even check the return value as it's too late. if (!h->rawMode) return; if (BC_ERR(tcsetattr(STDIN_FILENO, TCSAFLUSH, &h->orig_termios) != -1)) h->rawMode = false; } /** * Use the ESC [6n escape sequence to query the horizontal cursor position * and return it. On error -1 is returned, on success the position of the * cursor. */ static size_t bc_history_cursorPos(void) { char buf[BC_HIST_SEQ_SIZE]; size_t cols, rows, i; // Report cursor location. if (BC_ERR(BC_HIST_WRITE("\x1b[6n", 4))) return SIZE_MAX; // Read the response: ESC [ rows ; cols R. for (i = 0; i < sizeof(buf) - 1; ++i) { if (read(STDIN_FILENO, buf + i, 1) != 1 || buf[i] == 'R') break; } buf[i] = '\0'; // Parse it. if (BC_ERR(buf[0] != BC_ACTION_ESC || buf[1] != '[')) return SIZE_MAX; if (BC_ERR(sscanf(buf + 2, "%zu;%zu", &rows, &cols) != 2)) return SIZE_MAX; return cols <= UINT16_MAX ? cols : 0; } /** * Try to get the number of columns in the current terminal, or assume 80 * if it fails. */ static size_t bc_history_columns(void) { struct winsize ws; if (BC_ERR(ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 || !ws.ws_col)) { // Calling ioctl() failed. Try to query the terminal itself. size_t start, cols; // Get the initial position so we can restore it later. start = bc_history_cursorPos(); if (BC_ERR(start == SIZE_MAX)) return BC_HIST_DEF_COLS; // Go to right margin and get position. if (BC_ERR(BC_HIST_WRITE("\x1b[999C", 6))) return BC_HIST_DEF_COLS; cols = bc_history_cursorPos(); if (BC_ERR(cols == SIZE_MAX)) return BC_HIST_DEF_COLS; // Restore position. if (cols > start) { char seq[BC_HIST_SEQ_SIZE]; size_t len; snprintf(seq, BC_HIST_SEQ_SIZE, "\x1b[%zuD", cols - start); len = strlen(seq); // If this fails, return a value that // callers will translate into an error. if (BC_ERR(BC_HIST_WRITE(seq, len))) return SIZE_MAX; } return cols; } return ws.ws_col; } #if BC_ENABLE_PROMPT /** * Check if text is an ANSI escape sequence. */ static bool bc_history_ansiEscape(const char *buf, size_t buf_len, size_t *len) { if (buf_len > 2 && !memcmp("\033[", buf, 2)) { size_t off = 2; while (off < buf_len) { char c = buf[off++]; if ((c >= 'A' && c <= 'K' && c != 'I') || c == 'S' || c == 'T' || c == 'f' || c == 'm') { *len = off; return true; } } } return false; } /** * Get column length of prompt text. */ static size_t bc_history_promptColLen(const char *prompt, size_t plen) { char buf[BC_HIST_MAX_LINE + 1]; size_t buf_len = 0, off = 0; while (off < plen) { size_t len; if (bc_history_ansiEscape(prompt + off, plen - off, &len)) { off += len; continue; } buf[buf_len++] = prompt[off++]; } return bc_history_colPos(buf, buf_len, buf_len); } #endif // BC_ENABLE_PROMPT /** * Rewrites the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static BcStatus bc_history_refresh(BcHistory *h) { char seq[BC_HIST_SEQ_SIZE]; char* buf = h->buf.v; size_t colpos, len = BC_HIST_BUF_LEN(h), pos = h->pos; while(h->pcol + bc_history_colPos(buf, len, pos) >= h->cols) { size_t chlen = bc_history_nextLen(buf, len, 0, NULL); buf += chlen; len -= chlen; pos -= chlen; } while (h->pcol + bc_history_colPos(buf, len, len) > h->cols) len -= bc_history_prevLen(buf, len, NULL); // Cursor to left edge. snprintf(seq, BC_HIST_SEQ_SIZE, "\r"); bc_vec_string(&h->tmp, strlen(seq), seq); // Write the prompt, if desired. #if BC_ENABLE_PROMPT if (BC_USE_PROMPT) bc_vec_concat(&h->tmp, h->prompt); #endif // BC_ENABLE_PROMPT bc_vec_concat(&h->tmp, buf); // Erase to right. snprintf(seq, BC_HIST_SEQ_SIZE, "\x1b[0K"); bc_vec_concat(&h->tmp, seq); // Move cursor to original position. colpos = bc_history_colPos(buf, len, pos) + h->pcol; if (colpos) { snprintf(seq, BC_HIST_SEQ_SIZE, "\r\x1b[%zuC", colpos); bc_vec_concat(&h->tmp, seq); } if (h->tmp.len && BC_ERR(BC_HIST_WRITE(h->tmp.v, h->tmp.len - 1))) return bc_vm_err(BC_ERROR_FATAL_IO_ERR); return BC_STATUS_SUCCESS; } /** * Insert the character 'c' at cursor current position. */ static BcStatus bc_history_edit_insert(BcHistory *h, const char *cbuf, size_t clen) { BcStatus s = BC_STATUS_SUCCESS; bc_vec_expand(&h->buf, bc_vm_growSize(h->buf.len, clen)); if (h->pos == BC_HIST_BUF_LEN(h)) { size_t colpos = 0, len; memcpy(bc_vec_item(&h->buf, h->pos), cbuf, clen); h->pos += clen; h->buf.len += clen - 1; bc_vec_pushByte(&h->buf, '\0'); len = BC_HIST_BUF_LEN(h); #if BC_ENABLE_PROMPT colpos = bc_history_promptColLen(h->prompt, h->plen); #endif // BC_ENABLE_PROMPT colpos += bc_history_colPos(h->buf.v, len, len); if (colpos < h->cols) { // Avoid a full update of the line in the trivial case. if (BC_ERR(BC_HIST_WRITE(cbuf, clen))) s = bc_vm_err(BC_ERROR_FATAL_IO_ERR); } else s = bc_history_refresh(h); } else { size_t amt = BC_HIST_BUF_LEN(h) - h->pos; memmove(h->buf.v + h->pos + clen, h->buf.v + h->pos, amt); memcpy(h->buf.v + h->pos, cbuf, clen); h->pos += clen; h->buf.len += clen; h->buf.v[BC_HIST_BUF_LEN(h)] = '\0'; s = bc_history_refresh(h); } return s; } /** * Move cursor to the left. */ static BcStatus bc_history_edit_left(BcHistory *h) { if (h->pos <= 0) return BC_STATUS_SUCCESS; h->pos -= bc_history_prevLen(h->buf.v, h->pos, NULL); return bc_history_refresh(h); } /** * Move cursor on the right. */ static BcStatus bc_history_edit_right(BcHistory *h) { if (h->pos == BC_HIST_BUF_LEN(h)) return BC_STATUS_SUCCESS; h->pos += bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL); return bc_history_refresh(h); } /** * Move cursor to the end of the current word. */ static BcStatus bc_history_edit_wordEnd(BcHistory *h) { size_t len = BC_HIST_BUF_LEN(h); if (!len || h->pos >= len) return BC_STATUS_SUCCESS; while (h->pos < len && isspace(h->buf.v[h->pos])) h->pos += 1; while (h->pos < len && !isspace(h->buf.v[h->pos])) h->pos += 1; return bc_history_refresh(h); } /** * Move cursor to the start of the current word. */ static BcStatus bc_history_edit_wordStart(BcHistory *h) { size_t len = BC_HIST_BUF_LEN(h); if (!len) return BC_STATUS_SUCCESS; while (h->pos > 0 && isspace(h->buf.v[h->pos - 1])) h->pos -= 1; while (h->pos > 0 && !isspace(h->buf.v[h->pos - 1])) h->pos -= 1; return bc_history_refresh(h); } /** * Move cursor to the start of the line. */ static BcStatus bc_history_edit_home(BcHistory *h) { if (!h->pos) return BC_STATUS_SUCCESS; h->pos = 0; return bc_history_refresh(h); } /** * Move cursor to the end of the line. */ static BcStatus bc_history_edit_end(BcHistory *h) { if (h->pos == BC_HIST_BUF_LEN(h)) return BC_STATUS_SUCCESS; h->pos = BC_HIST_BUF_LEN(h); return bc_history_refresh(h); } /** * Substitute the currently edited line with the next or previous history * entry as specified by 'dir' (direction). */ static BcStatus bc_history_edit_next(BcHistory *h, bool dir) { char *dup, *str; if (h->history.len <= 1) return BC_STATUS_SUCCESS; dup = bc_vm_strdup(h->buf.v); // Update the current history entry before // overwriting it with the next one. bc_vec_replaceAt(&h->history, h->history.len - 1 - h->idx, &dup); // Show the new entry. h->idx += (dir == BC_HIST_PREV ? 1 : SIZE_MAX); if (h->idx == SIZE_MAX) { h->idx = 0; return BC_STATUS_SUCCESS; } else if (h->idx >= h->history.len) { h->idx = h->history.len - 1; return BC_STATUS_SUCCESS; } str = *((char**) bc_vec_item(&h->history, h->history.len - 1 - h->idx)); bc_vec_string(&h->buf, strlen(str), str); assert(h->buf.len > 0); h->pos = BC_HIST_BUF_LEN(h); return bc_history_refresh(h); } /** * Delete the character at the right of the cursor without altering the cursor * position. Basically this is what happens with the "Delete" keyboard key. */ static BcStatus bc_history_edit_delete(BcHistory *h) { size_t chlen, len = BC_HIST_BUF_LEN(h); if (!len || h->pos >= len) return BC_STATUS_SUCCESS; chlen = bc_history_nextLen(h->buf.v, len, h->pos, NULL); memmove(h->buf.v + h->pos, h->buf.v + h->pos + chlen, len - h->pos - chlen); h->buf.len -= chlen; h->buf.v[BC_HIST_BUF_LEN(h)] = '\0'; return bc_history_refresh(h); } static BcStatus bc_history_edit_backspace(BcHistory *h) { size_t chlen, len = BC_HIST_BUF_LEN(h); if (!h->pos || !len) return BC_STATUS_SUCCESS; chlen = bc_history_prevLen(h->buf.v, h->pos, NULL); memmove(h->buf.v + h->pos - chlen, h->buf.v + h->pos, len - h->pos); h->pos -= chlen; h->buf.len -= chlen; h->buf.v[BC_HIST_BUF_LEN(h)] = '\0'; return bc_history_refresh(h); } /** * Delete the previous word, maintaining the cursor at the start of the * current word. */ static BcStatus bc_history_edit_deletePrevWord(BcHistory *h) { size_t diff, old_pos = h->pos; while (h->pos > 0 && h->buf.v[h->pos - 1] == ' ') --h->pos; while (h->pos > 0 && h->buf.v[h->pos - 1] != ' ') --h->pos; diff = old_pos - h->pos; memmove(h->buf.v + h->pos, h->buf.v + old_pos, BC_HIST_BUF_LEN(h) - old_pos + 1); h->buf.len -= diff; return bc_history_refresh(h); } /** * Delete the next word, maintaining the cursor at the same position. */ static BcStatus bc_history_edit_deleteNextWord(BcHistory *h) { size_t next_end = h->pos, len = BC_HIST_BUF_LEN(h); while (next_end < len && h->buf.v[next_end] == ' ') ++next_end; while (next_end < len && h->buf.v[next_end] != ' ') ++next_end; memmove(h->buf.v + h->pos, h->buf.v + next_end, len - next_end); h->buf.len -= next_end - h->pos; return bc_history_refresh(h); } static BcStatus bc_history_swap(BcHistory *h) { BcStatus s = BC_STATUS_SUCCESS; size_t pcl, ncl; char auxb[5]; pcl = bc_history_prevLen(h->buf.v, h->pos, NULL); ncl = bc_history_nextLen(h->buf.v, BC_HIST_BUF_LEN(h), h->pos, NULL); // To perform a swap we need: // * nonzero char length to the left // * not at the end of the line if (pcl && h->pos != BC_HIST_BUF_LEN(h) && pcl < 5 && ncl < 5) { memcpy(auxb, h->buf.v + h->pos - pcl, pcl); memcpy(h->buf.v + h->pos - pcl, h->buf.v + h->pos, ncl); memcpy(h->buf.v + h->pos - pcl + ncl, auxb, pcl); h->pos += -pcl + ncl; s = bc_history_refresh(h); } return s; } /** * Handle escape sequences. */ static BcStatus bc_history_escape(BcHistory *h) { BcStatus s = BC_STATUS_SUCCESS; char c, seq[3]; if (BC_ERR(BC_HIST_READ(seq, 1))) return s; c = seq[0]; // ESC ? sequences. if (c != '[' && c != 'O') { if (c == 'f') s = bc_history_edit_wordEnd(h); else if (c == 'b') s = bc_history_edit_wordStart(h); else if (c == 'd') s = bc_history_edit_deleteNextWord(h); } else { if (BC_ERR(BC_HIST_READ(seq + 1, 1))) return bc_vm_err(BC_ERROR_FATAL_IO_ERR); // ESC [ sequences. if (c == '[') { c = seq[1]; if (c >= '0' && c <= '9') { // Extended escape, read additional byte. if (BC_ERR(BC_HIST_READ(seq + 2, 1))) return bc_vm_err(BC_ERROR_FATAL_IO_ERR); if (seq[2] == '~' && c == '3') s = bc_history_edit_delete(h); else if(seq[2] == ';') { if (BC_ERR(BC_HIST_READ(seq, 2))) return bc_vm_err(BC_ERROR_FATAL_IO_ERR); if (seq[0] != '5') return s; else if (seq[1] == 'C') s = bc_history_edit_wordEnd(h); else if (seq[1] == 'D') s = bc_history_edit_wordStart(h); } } else { switch(c) { // Up. case 'A': { s = bc_history_edit_next(h, BC_HIST_PREV); break; } // Down. case 'B': { s = bc_history_edit_next(h, BC_HIST_NEXT); break; } // Right. case 'C': { s = bc_history_edit_right(h); break; } // Left. case 'D': { s = bc_history_edit_left(h); break; } // Home. case 'H': case '1': { s = bc_history_edit_home(h); break; } // End. case 'F': case '4': { s = bc_history_edit_end(h); break; } case 'd': { s = bc_history_edit_deleteNextWord(h); break; } } } } // ESC O sequences. else if (c == 'O') { switch (seq[1]) { case 'A': { s = bc_history_edit_next(h, BC_HIST_PREV); break; } case 'B': { s = bc_history_edit_next(h, BC_HIST_NEXT); break; } case 'C': { s = bc_history_edit_right(h); break; } case 'D': { s = bc_history_edit_left(h); break; } case 'F': { s = bc_history_edit_end(h); break; } case 'H': { s = bc_history_edit_home(h); break; } } } } return s; } static BcStatus bc_history_reset(BcHistory *h) { h->oldcolpos = h->pos = h->idx = 0; h->cols = bc_history_columns(); // Check for error from bc_history_columns. if (BC_ERR(h->cols == SIZE_MAX)) return bc_vm_err(BC_ERROR_FATAL_IO_ERR); // The latest history entry is always our current buffer, that // initially is just an empty string. bc_history_add(h, bc_vm_strdup("")); // Buffer starts empty. bc_vec_empty(&h->buf); return BC_STATUS_SUCCESS; } static BcStatus bc_history_printCtrl(BcHistory *h, unsigned int c) { BcStatus s; char str[3] = "^A"; const char newline[2] = "\n"; str[1] = (char) (c + 'A' - BC_ACTION_CTRL_A); bc_vec_concat(&h->buf, str); s = bc_history_refresh(h); if (BC_ERR(s)) return s; bc_vec_npop(&h->buf, sizeof(str)); bc_vec_pushByte(&h->buf, '\0'); if (c != BC_ACTION_CTRL_C && c != BC_ACTION_CTRL_D) { if (BC_ERR(BC_HIST_WRITE(newline, sizeof(newline) - 1))) return bc_vm_err(BC_ERROR_FATAL_IO_ERR); s = bc_history_refresh(h); } return s; } /** * This function is the core of the line editing capability of bc history. * It expects 'fd' to be already in "raw mode" so that every key pressed * will be returned ASAP to read(). */ static BcStatus bc_history_edit(BcHistory *h, const char *prompt) { BcStatus s = bc_history_reset(h); if (BC_ERR(s)) return s; #if BC_ENABLE_PROMPT if (BC_USE_PROMPT) { h->prompt = prompt; h->plen = strlen(prompt); h->pcol = bc_history_promptColLen(prompt, h->plen); if (BC_ERR(BC_HIST_WRITE(prompt, h->plen))) return bc_vm_err(BC_ERROR_FATAL_IO_ERR); } #endif // BC_ENABLE_PROMPT while (BC_NO_ERR(!s)) { // Large enough for any encoding? char cbuf[32]; unsigned int c = 0; size_t nread = 0; s = bc_history_readCode(cbuf, sizeof(cbuf), &c, &nread); if (BC_ERR(s)) return s; switch (c) { case BC_ACTION_LINE_FEED: case BC_ACTION_ENTER: { bc_vec_pop(&h->history); return s; } case BC_ACTION_TAB: { memcpy(cbuf, bc_history_tab, bc_history_tab_len + 1); s = bc_history_edit_insert(h, cbuf, bc_history_tab_len); break; } case BC_ACTION_CTRL_C: { s = bc_history_printCtrl(h, c); if (BC_ERR(s)) return s; #if BC_ENABLE_SIGNALS s = bc_history_reset(h); if (BC_ERR(s)) break; if (BC_ERR(BC_HIST_WRITE(vm->sigmsg, vm->siglen)) || BC_ERR(BC_HIST_WRITE(bc_program_ready_msg, bc_program_ready_msg_len))) { s = bc_vm_err(BC_ERROR_FATAL_IO_ERR); } else s = bc_history_refresh(h); break; #else // BC_ENABLE_SIGNALS if (BC_ERR(BC_HIST_WRITE("\n", 1))) bc_vm_err(BC_ERROR_FATAL_IO_ERR); // Make sure the terminal is back to normal before exiting. bc_vm_shutdown(); exit((((uchar) 1) << (CHAR_BIT - 1)) + SIGINT); #endif // BC_ENABLE_SIGNALS } case BC_ACTION_BACKSPACE: case BC_ACTION_CTRL_H: { s = bc_history_edit_backspace(h); break; } // Act as end-of-file. case BC_ACTION_CTRL_D: { s = BC_STATUS_EOF; break; } // Swaps current character with previous. case BC_ACTION_CTRL_T: { s = bc_history_swap(h); break; } case BC_ACTION_CTRL_B: { s = bc_history_edit_left(h); break; } case BC_ACTION_CTRL_F: { s = bc_history_edit_right(h); break; } case BC_ACTION_CTRL_P: { s = bc_history_edit_next(h, BC_HIST_PREV); break; } case BC_ACTION_CTRL_N: { s = bc_history_edit_next(h, BC_HIST_NEXT); break; } case BC_ACTION_ESC: { s = bc_history_escape(h); break; } // Delete the whole line. case BC_ACTION_CTRL_U: { bc_vec_string(&h->buf, 0, ""); h->pos = 0; s = bc_history_refresh(h); break; } // Delete from current to end of line. case BC_ACTION_CTRL_K: { bc_vec_npop(&h->buf, h->buf.len - h->pos); bc_vec_pushByte(&h->buf, '\0'); s = bc_history_refresh(h); break; } // Go to the start of the line. case BC_ACTION_CTRL_A: { s = bc_history_edit_home(h); break; } // Go to the end of the line. case BC_ACTION_CTRL_E: { s = bc_history_edit_end(h); break; } // Clear screen. case BC_ACTION_CTRL_L: { if (BC_ERR(BC_HIST_WRITE("\x1b[H\x1b[2J", 7))) s = bc_vm_err(BC_ERROR_FATAL_IO_ERR); if (BC_NO_ERR(!s)) s = bc_history_refresh(h); break; } // Delete previous word. case BC_ACTION_CTRL_W: { s = bc_history_edit_deletePrevWord(h); break; } default: { if (c >= BC_ACTION_CTRL_A && c <= BC_ACTION_CTRL_Z) s = bc_history_printCtrl(h, c); else s = bc_history_edit_insert(h, cbuf, nread); break; } } } return s; } static bool bc_history_stdinHasData(BcHistory *h) { int n; return pselect(1, &h->rdset, NULL, NULL, &h->ts, &h->sigmask) > 0 || (ioctl(STDIN_FILENO, FIONREAD, &n) >= 0 && n > 0); } /** * This function calls the line editing function bc_history_edit() * using the STDIN file descriptor set in raw mode. */ static BcStatus bc_history_raw(BcHistory *h, const char *prompt) { BcStatus s; s = bc_history_enableRaw(h); if (BC_ERR(s)) return s; s = bc_history_edit(h, prompt); h->stdin_has_data = bc_history_stdinHasData(h); if (!h->stdin_has_data) bc_history_disableRaw(h); if (BC_NO_ERR(!s) && BC_ERR(BC_HIST_WRITE("\n", 1))) s = bc_vm_err(BC_ERROR_FATAL_IO_ERR); return s; } BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt) { BcStatus s; char* line; if (BC_TTYIN && !vm->history.badTerm) { s = bc_history_raw(h, prompt); if (BC_ERR(s && s != BC_STATUS_EOF)) return s; bc_vec_string(vec, BC_HIST_BUF_LEN(h), h->buf.v); line = bc_vm_strdup(h->buf.v); bc_history_add(h, line); bc_vec_concat(vec, "\n"); } else s = bc_read_chars(vec, prompt); return s; } void bc_history_add(BcHistory *h, char *line) { if (h->history.len) { if (!strcmp(*((char**) bc_vec_item_rev(&h->history, 0)), line)) { free(line); return; } } if (h->history.len == BC_HIST_MAX_LEN) bc_vec_popAt(&h->history, 0); bc_vec_push(&h->history, &line); } void bc_history_init(BcHistory *h) { bc_vec_init(&h->buf, sizeof(char), NULL); bc_vec_init(&h->history, sizeof(char*), bc_string_free); bc_vec_init(&h->tmp, sizeof(char), NULL); FD_ZERO(&h->rdset); FD_SET(STDIN_FILENO, &h->rdset); h->ts.tv_sec = 0; h->ts.tv_nsec = 0; sigemptyset(&h->sigmask); sigaddset(&h->sigmask, SIGINT); h->rawMode = h->stdin_has_data = false; h->badTerm = bc_history_isBadTerm(); } void bc_history_free(BcHistory *h) { bc_history_disableRaw(h); #ifndef NDEBUG bc_vec_free(&h->buf); bc_vec_free(&h->history); bc_vec_free(&h->tmp); #endif // NDEBUG } /** * This special mode is used by bc history in order to print scan codes * on screen for debugging / development purposes. */ #if BC_DEBUG_CODE BcStatus bc_history_printKeyCodes(BcHistory *h) { BcStatus s; char quit[4]; bc_vm_printf("Linenoise key codes debugging mode.\n" "Press keys to see scan codes. " "Type 'quit' at any time to exit.\n"); s = bc_history_enableRaw(h); if (BC_ERR(s)) return s; memset(quit, ' ', 4); while(true) { char c; ssize_t nread; nread = read(STDIN_FILENO, &c, 1); if (nread <= 0) continue; // Shift string to left. memmove(quit, quit + 1, sizeof(quit) - 1); // Insert current char on the right. quit[sizeof(quit) - 1] = c; if (!memcmp(quit, "quit", sizeof(quit))) break; printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', (int) c, (int) c); // Go left edge manually, we are in raw mode. bc_vm_putchar('\r'); bc_vm_fflush(stdout); } bc_history_disableRaw(h); return s; } #endif // BC_DEBUG_CODE #endif // BC_ENABLE_HISTORY #if BC_ENABLE_SIGNALS #ifndef _WIN32 static void bc_vm_sig(int sig) { int err = errno; if (sig == SIGINT) { size_t n = vm->siglen; if (BC_NO_ERR(write(STDERR_FILENO, vm->sigmsg, n) == (ssize_t) n)) vm->sig += 1; if (vm->sig == BC_SIGTERM_VAL) vm->sig = 1; } else vm->sig = BC_SIGTERM_VAL; errno = err; } #else // _WIN32 static BOOL WINAPI bc_vm_sig(DWORD sig) { if (sig == CTRL_C_EVENT) { bc_vm_puts(vm->sigmsg, stderr); vm->sig += 1; if (vm->sig == BC_SIGTERM_VAL) vm->sig = 1; } else vm->sig = BC_SIGTERM_VAL; return TRUE; } #endif // _WIN32 #endif // BC_ENABLE_SIGNALS void bc_vm_info(const char* const help) { bc_vm_printf("%s %s\n", vm->name, BC_VERSION); bc_vm_puts(bc_copyright, stdout); if (help) { bc_vm_putchar('\n'); bc_vm_printf(help, vm->name, vm->name); } } BcStatus bc_vm_error(BcError e, size_t line, ...) { va_list args; uchar id = bc_err_ids[e]; const char* err_type = vm->err_ids[id]; assert(e < BC_ERROR_NELEMS); #if BC_ENABLED if (!BC_S && e >= BC_ERROR_POSIX_START) { if (BC_W) { // Make sure to not return an error. id = UCHAR_MAX; err_type = vm->err_ids[BC_ERR_IDX_WARN]; } else return BC_STATUS_SUCCESS; } #endif // BC_ENABLED // Make sure all of stdout is written first. fflush(stdout); va_start(args, line); fprintf(stderr, "\n%s ", err_type); vfprintf(stderr, vm->err_msgs[e], args); va_end(args); if (BC_NO_ERR(vm->file)) { // This is the condition for parsing vs runtime. // If line is not 0, it is parsing. if (line) { fprintf(stderr, "\n %s", vm->file); fprintf(stderr, bc_err_line, line); } else { BcInstPtr *ip = bc_vec_item_rev(&vm->prog.stack, 0); BcFunc *f = bc_vec_item(&vm->prog.fns, ip->func); fprintf(stderr, "\n %s %s", vm->func_header, f->name); if (BC_IS_BC && ip->func != BC_PROG_MAIN && ip->func != BC_PROG_READ) { fprintf(stderr, "()"); } } } fputs("\n\n", stderr); fflush(stderr); return (BcStatus) (id + 1); } static BcStatus bc_vm_envArgs(const char* const env_args_name) { BcStatus s; BcVec v; char *env_args = getenv(env_args_name), *buffer, *buf; if (env_args == NULL) return BC_STATUS_SUCCESS; buffer = bc_vm_strdup(env_args); buf = buffer; bc_vec_init(&v, sizeof(char*), NULL); bc_vec_push(&v, &env_args_name); while (*buf) { if (!isspace(*buf)) { bc_vec_push(&v, &buf); while (*buf && !isspace(*buf)) buf += 1; if (*buf) { *buf = '\0'; buf += 1; } } else buf += 1; } // Make sure to push a NULL pointer at the end. buf = NULL; bc_vec_push(&v, &buf); s = bc_args((int) v.len - 1, bc_vec_item(&v, 0)); bc_vec_free(&v); free(buffer); return s; } static size_t bc_vm_envLen(const char *var) { char *lenv = getenv(var); size_t i, len = BC_NUM_PRINT_WIDTH; int num; if (lenv == NULL) 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 >= UINT16_MAX) len = BC_NUM_PRINT_WIDTH; } else len = BC_NUM_PRINT_WIDTH; return len; } void bc_vm_shutdown(void) { #if BC_ENABLE_NLS if (vm->catalog != BC_VM_INVALID_CATALOG) catclose(vm->catalog); #endif // BC_ENABLE_NLS #if BC_ENABLE_HISTORY // This must always run to ensure that the terminal is back to normal. bc_history_free(&vm->history); #endif // BC_ENABLE_HISTORY #ifndef NDEBUG bc_vec_free(&vm->files); bc_vec_free(&vm->exprs); bc_program_free(&vm->prog); bc_parse_free(&vm->prs); free(vm); #endif // NDEBUG } static void bc_vm_exit(BcError e) { BcStatus s = bc_vm_err(e); bc_vm_shutdown(); exit((int) s); } size_t bc_vm_arraySize(size_t n, size_t size) { size_t res = n * size; if (BC_ERR(res >= SIZE_MAX || (n != 0 && res / n != size))) bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR); return res; } size_t bc_vm_growSize(size_t a, size_t b) { size_t res = a + b; if (BC_ERR(res >= SIZE_MAX || res < a || res < b)) bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR); return res; } void* bc_vm_malloc(size_t n) { void* ptr = malloc(n); if (BC_ERR(ptr == NULL)) bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR); return ptr; } void* bc_vm_realloc(void *ptr, size_t n) { void* temp = realloc(ptr, n); if (BC_ERR(temp == NULL)) bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR); return temp; } char* bc_vm_strdup(const char *str) { char *s = strdup(str); if (BC_ERR(!s)) bc_vm_exit(BC_ERROR_FATAL_ALLOC_ERR); return s; } size_t bc_vm_printf(const char *fmt, ...) { va_list args; int ret; va_start(args, fmt); ret = vfprintf(stdout, fmt, args); va_end(args); if (BC_IO_ERR(ret, stdout)) bc_vm_exit(BC_ERROR_FATAL_IO_ERR); vm->nchars = 0; return (size_t) ret; } void bc_vm_puts(const char *str, FILE *restrict f) { if (BC_IO_ERR(fputs(str, f), f)) bc_vm_exit(BC_ERROR_FATAL_IO_ERR); } void bc_vm_putchar(int c) { if (BC_IO_ERR(fputc(c, stdout), stdout)) bc_vm_exit(BC_ERROR_FATAL_IO_ERR); vm->nchars = (c == '\n' ? 0 : vm->nchars + 1); } void bc_vm_fflush(FILE *restrict f) { if (BC_IO_ERR(fflush(f), f)) bc_vm_exit(BC_ERROR_FATAL_IO_ERR); } static void bc_vm_clean(void) { BcProgram *prog = &vm->prog; BcVec *fns = &prog->fns; BcFunc *f = bc_vec_item(fns, BC_PROG_MAIN); BcInstPtr *ip = bc_vec_item(&prog->stack, 0); bool good = false; #if BC_ENABLED if (BC_IS_BC) good = !BC_PARSE_NO_EXEC(&vm->prs); #endif // BC_ENABLED #if DC_ENABLED if (!BC_IS_BC) { size_t i; for (i = 0; i < vm->prog.vars.len; ++i) { BcVec *arr = bc_vec_item(&vm->prog.vars, i); BcNum *n = bc_vec_top(arr); if (arr->len != 1 || BC_PROG_STR(n)) break; } if (i == vm->prog.vars.len) { for (i = 0; i < vm->prog.arrs.len; ++i) { BcVec *arr = bc_vec_item(&vm->prog.arrs, i); size_t j; assert(arr->len == 1); arr = bc_vec_top(arr); for (j = 0; j < arr->len; ++j) { BcNum *n = bc_vec_item(arr, j); if (BC_PROG_STR(n)) break; } if (j != arr->len) break; } good = (i == vm->prog.arrs.len); } } #endif // DC_ENABLED // If this condition is true, we can get rid of strings, // constants, and code. This is an idea from busybox. if (good && prog->stack.len == 1 && !prog->results.len && ip->idx == f->code.len) { #if BC_ENABLED bc_vec_npop(&f->labels, f->labels.len); #endif // BC_ENABLED bc_vec_npop(&f->strs, f->strs.len); bc_vec_npop(&f->consts, f->consts.len); bc_vec_npop(&f->code, f->code.len); ip->idx = 0; #if DC_ENABLED if (!BC_IS_BC) bc_vec_npop(fns, fns->len - BC_PROG_REQ_FUNCS); #endif // DC_ENABLED } } static BcStatus bc_vm_process(const char *text, bool is_stdin) { BcStatus s; s = bc_parse_text(&vm->prs, text); if (BC_ERR(s)) goto err; while (vm->prs.l.t != BC_LEX_EOF) { s = vm->parse(&vm->prs); if (BC_ERR(s)) goto err; } #if BC_ENABLED { uint16_t *flags = BC_PARSE_TOP_FLAG_PTR(&vm->prs); if (!is_stdin && vm->prs.flags.len == 1 && *flags == BC_PARSE_FLAG_IF_END) { bc_parse_noElse(&vm->prs); } if (BC_PARSE_NO_EXEC(&vm->prs)) goto err; } #endif // BC_ENABLED s = bc_program_exec(&vm->prog); if (BC_I) bc_vm_fflush(stdout); err: bc_vm_clean(); return s == BC_STATUS_QUIT || !BC_I || !is_stdin ? s : BC_STATUS_SUCCESS; } static BcStatus bc_vm_file(const char *file) { BcStatus s; char *data; bc_lex_file(&vm->prs.l, file); s = bc_read_file(file, &data); if (BC_ERR(s)) return s; s = bc_vm_process(data, false); if (BC_ERR(s)) goto err; #if BC_ENABLED if (BC_ERR(BC_PARSE_NO_EXEC(&vm->prs))) s = bc_parse_err(&vm->prs, BC_ERROR_PARSE_BLOCK); #endif // BC_ENABLED err: free(data); return s; } static BcStatus bc_vm_stdin(void) { BcStatus s = BC_STATUS_SUCCESS; BcVec buf, buffer; size_t string = 0; bool comment = false, done = false; bc_lex_file(&vm->prs.l, bc_program_stdin_name); bc_vec_init(&buffer, sizeof(uchar), NULL); bc_vec_init(&buf, sizeof(uchar), NULL); bc_vec_pushByte(&buffer, '\0'); s = bc_read_line(&buf, ">>> "); // 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. for (; !BC_STATUS_IS_ERROR(s) && buf.len > 1 && BC_NO_SIG && s != BC_STATUS_SIGNAL; s = bc_read_line(&buf, ">>> ")) { char c2, *str = buf.v; size_t i, len = buf.len - 1; done = (s == BC_STATUS_EOF); for (i = 0; i < len; ++i) { bool notend = len > i + 1; uchar c = (uchar) str[i]; if (!comment && (i - 1 > len || str[i - 1] != '\\')) { if (BC_IS_BC) string ^= (c == '"'); else if (c == ']') string -= 1; else if (c == '[') string += 1; } if (BC_IS_BC && !string && notend) { c2 = str[i + 1]; if (c == '/' && !comment && c2 == '*') { comment = true; i += 1; } else if (c == '*' && comment && c2 == '/') { comment = false; i += 1; } } } bc_vec_concat(&buffer, buf.v); if (string || comment) continue; if (len >= 2 && str[len - 2] == '\\' && str[len - 1] == '\n') continue; #if BC_ENABLE_HISTORY if (vm->history.stdin_has_data) continue; #endif // BC_ENABLE_HISTORY s = bc_vm_process(buffer.v, true); if (BC_ERR(s)) goto err; bc_vec_empty(&buffer); if (done) break; } if (BC_ERR(s && s != BC_STATUS_EOF)) goto err; else if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL; else if (!BC_STATUS_IS_ERROR(s)) { if (BC_ERR(comment)) s = bc_parse_err(&vm->prs, BC_ERROR_PARSE_COMMENT); else if (BC_ERR(string)) s = bc_parse_err(&vm->prs, BC_ERROR_PARSE_STRING); #if BC_ENABLED else if (BC_ERR(BC_PARSE_NO_EXEC(&vm->prs))) s = bc_parse_err(&vm->prs, BC_ERROR_PARSE_BLOCK); #endif // BC_ENABLED } err: bc_vec_free(&buf); bc_vec_free(&buffer); return s; } #if BC_ENABLED static BcStatus bc_vm_load(const char *name, const char *text) { BcStatus s; bc_lex_file(&vm->prs.l, name); s = bc_parse_text(&vm->prs, text); while (BC_NO_ERR(!s) && vm->prs.l.t != BC_LEX_EOF) s = vm->parse(&vm->prs); return s; } #endif // BC_ENABLED static void bc_vm_defaultMsgs(void) { size_t i; vm->func_header = bc_err_func_header; for (i = 0; i < BC_ERR_IDX_NELEMS + BC_ENABLED; ++i) vm->err_ids[i] = bc_errs[i]; for (i = 0; i < BC_ERROR_NELEMS; ++i) vm->err_msgs[i] = bc_err_msgs[i]; } static void bc_vm_gettext(void) { #if BC_ENABLE_NLS uchar id = 0; int set = 1, msg = 1; size_t i; if (vm->locale == NULL) { vm->catalog = BC_VM_INVALID_CATALOG; bc_vm_defaultMsgs(); return; } vm->catalog = catopen(BC_MAINEXEC, NL_CAT_LOCALE); if (vm->catalog == BC_VM_INVALID_CATALOG) { bc_vm_defaultMsgs(); return; } vm->func_header = catgets(vm->catalog, set, msg, bc_err_func_header); for (set += 1; msg <= BC_ERR_IDX_NELEMS + BC_ENABLED; ++msg) vm->err_ids[msg - 1] = catgets(vm->catalog, set, msg, bc_errs[msg - 1]); i = 0; id = bc_err_ids[i]; for (set = id + 3, msg = 1; i < BC_ERROR_NELEMS; ++i, ++msg) { if (id != bc_err_ids[i]) { msg = 1; id = bc_err_ids[i]; set = id + 3; } vm->err_msgs[i] = catgets(vm->catalog, set, msg, bc_err_msgs[i]); } #else // BC_ENABLE_NLS bc_vm_defaultMsgs(); #endif // BC_ENABLE_NLS } static BcStatus bc_vm_exec(const char* env_exp_exit) { BcStatus s = BC_STATUS_SUCCESS; size_t i; bool has_file = false; #if BC_ENABLED if (BC_IS_BC && (vm->flags & BC_FLAG_L)) { s = bc_vm_load(bc_lib_name, bc_lib); if (BC_ERR(s)) return s; #if BC_ENABLE_EXTRA_MATH if (!BC_IS_POSIX) { s = bc_vm_load(bc_lib2_name, bc_lib2); if (BC_ERR(s)) return s; } #endif // BC_ENABLE_EXTRA_MATH } #endif // BC_ENABLED if (vm->exprs.len) { bc_lex_file(&vm->prs.l, bc_program_exprs_name); s = bc_vm_process(vm->exprs.v, false); if (BC_ERR(s) || getenv(env_exp_exit) != NULL) return s; } for (i = 0; BC_NO_ERR(!s) && i < vm->files.len; ++i) { char *path = *((char**) bc_vec_item(&vm->files, i)); if (!strcmp(path, "")) continue; has_file = true; s = bc_vm_file(path); } if (BC_ERR(s)) return s; if (BC_IS_BC || !has_file) s = bc_vm_stdin(); return s; } BcStatus bc_vm_boot(int argc, char *argv[], const char *env_len, const char* const env_args, const char* env_exp_exit) { BcStatus s; int ttyin, ttyout, ttyerr; #if BC_ENABLE_SIGNALS #ifndef _WIN32 struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_handler = bc_vm_sig; sa.sa_flags = 0; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); #else // _WIN32 SetConsoleCtrlHandler(bc_vm_sig, TRUE); #endif // _WIN32 #endif // BC_ENABLE_SIGNALS vm->file = NULL; bc_vm_gettext(); vm->line_len = (uint16_t) bc_vm_envLen(env_len); bc_vec_init(&vm->files, sizeof(char*), NULL); bc_vec_init(&vm->exprs, sizeof(uchar), NULL); bc_program_init(&vm->prog); bc_parse_init(&vm->prs, &vm->prog, BC_PROG_MAIN); #if BC_ENABLE_HISTORY bc_history_init(&vm->history); #endif // BC_ENABLE_HISTORY #if BC_ENABLED if (BC_IS_BC) vm->flags |= BC_FLAG_S * (getenv("POSIXLY_CORRECT") != NULL); #endif // BC_ENABLED s = bc_vm_envArgs(env_args); if (BC_ERR(s)) goto exit; s = bc_args(argc, argv); if (BC_ERR(s)) goto exit; ttyin = isatty(STDIN_FILENO); ttyout = isatty(STDOUT_FILENO); ttyerr = isatty(STDERR_FILENO); vm->flags |= ttyin ? BC_FLAG_TTYIN : 0; vm->flags |= ttyin && ttyout ? BC_FLAG_I : 0; vm->tty = (ttyin != 0 && ttyerr != 0); if (BC_IS_POSIX) vm->flags &= ~(BC_FLAG_G); vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_POSIX_IBASE; vm->maxes[BC_PROG_GLOBALS_OBASE] = BC_MAX_OBASE; vm->maxes[BC_PROG_GLOBALS_SCALE] = BC_MAX_SCALE; if (BC_IS_BC && !BC_IS_POSIX) vm->maxes[BC_PROG_GLOBALS_IBASE] = BC_NUM_MAX_IBASE; if (BC_IS_BC && BC_I && !(vm->flags & BC_FLAG_Q)) bc_vm_info(NULL); s = bc_vm_exec(env_exp_exit); exit: bc_vm_shutdown(); return !BC_STATUS_IS_ERROR(s) ? BC_STATUS_SUCCESS : s; } #if BC_ENABLED static BcStatus bc_lex_identifier(BcLex *l) { BcStatus s = BC_STATUS_SUCCESS; size_t i; const char *buf = l->buf + l->i - 1; for (i = 0; i < bc_lex_kws_len; ++i) { const BcLexKeyword *kw = bc_lex_kws + i; size_t n = BC_LEX_KW_LEN(kw); if (!strncmp(buf, kw->name, n) && !isalnum(buf[n]) && buf[n] != '_') { l->t = BC_LEX_KW_AUTO + (BcLexType) i; if (!BC_LEX_KW_POSIX(kw)) { s = bc_lex_verr(l, BC_ERROR_POSIX_KW, kw->name); if (BC_ERR(s)) return s; } // We minus 1 because the index has already been incremented. l->i += n - 1; return BC_STATUS_SUCCESS; } } bc_lex_name(l); if (BC_ERR(l->str.len - 1 > 1)) s = bc_lex_verr(l, BC_ERROR_POSIX_NAME_LEN, l->str.v); return s; } static BcStatus bc_lex_string(BcLex *l) { size_t len, nlines = 0, i = l->i; const char *buf = l->buf; char c; l->t = BC_LEX_STR; for (; (c = buf[i]) && c != '"'; ++i) nlines += c == '\n'; if (BC_ERR(c == '\0')) { l->i = i; return bc_lex_err(l, BC_ERROR_PARSE_STRING); } len = i - l->i; bc_vec_string(&l->str, len, l->buf + l->i); l->i = i + 1; l->line += nlines; return BC_STATUS_SUCCESS; } static void bc_lex_assign(BcLex *l, BcLexType with, BcLexType without) { if (l->buf[l->i] == '=') { l->i += 1; l->t = with; } else l->t = without; } BcStatus bc_lex_token(BcLex *l) { BcStatus s = BC_STATUS_SUCCESS; char c = l->buf[l->i++], c2; // This is the workhorse of the lexer. switch (c) { case '\0': case '\n': case '\t': case '\v': case '\f': case '\r': case ' ': { bc_lex_commonTokens(l, c); break; } case '!': { bc_lex_assign(l, BC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT); if (l->t == BC_LEX_OP_BOOL_NOT) { s = bc_lex_verr(l, BC_ERROR_POSIX_BOOL, "!"); if (BC_ERR(s)) return s; } break; } case '"': { s = bc_lex_string(l); break; } case '#': { s = bc_lex_err(l, BC_ERROR_POSIX_COMMENT); if (BC_ERR(s)) return s; bc_lex_lineComment(l); break; } case '%': { bc_lex_assign(l, BC_LEX_OP_ASSIGN_MODULUS, BC_LEX_OP_MODULUS); break; } case '&': { c2 = l->buf[l->i]; if (BC_NO_ERR(c2 == '&')) { s = bc_lex_verr(l, BC_ERROR_POSIX_BOOL, "&&"); if (BC_ERR(s)) return s; l->i += 1; l->t = BC_LEX_OP_BOOL_AND; } else s = bc_lex_invalidChar(l, c); break; } #if BC_ENABLE_EXTRA_MATH case '$': { l->t = BC_LEX_OP_TRUNC; break; } case '@': { bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLACES, BC_LEX_OP_PLACES); break; } #endif // BC_ENABLE_EXTRA_MATH case '(': case ')': { l->t = (BcLexType) (c - '(' + BC_LEX_LPAREN); break; } case '*': { bc_lex_assign(l, BC_LEX_OP_ASSIGN_MULTIPLY, BC_LEX_OP_MULTIPLY); break; } case '+': { c2 = l->buf[l->i]; if (c2 == '+') { l->i += 1; l->t = BC_LEX_OP_INC; } else bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLUS, BC_LEX_OP_PLUS); break; } case ',': { l->t = BC_LEX_COMMA; break; } case '-': { c2 = l->buf[l->i]; if (c2 == '-') { l->i += 1; l->t = BC_LEX_OP_DEC; } else bc_lex_assign(l, BC_LEX_OP_ASSIGN_MINUS, BC_LEX_OP_MINUS); break; } case '.': { c2 = l->buf[l->i]; if (BC_LEX_NUM_CHAR(c2, true, false)) s = bc_lex_number(l, c); else { l->t = BC_LEX_KW_LAST; s = bc_lex_err(l, BC_ERROR_POSIX_DOT); } break; } case '/': { c2 = l->buf[l->i]; if (c2 =='*') s = bc_lex_comment(l); else bc_lex_assign(l, BC_LEX_OP_ASSIGN_DIVIDE, BC_LEX_OP_DIVIDE); break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': // Apparently, GNU bc (and maybe others) allows any uppercase letter as // a number. When single digits, they act like the ones above. When // multi-digit, any letter above the input base is automatically set to // the biggest allowable digit in the input base. case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': { s = bc_lex_number(l, c); break; } case ';': { l->t = BC_LEX_SCOLON; break; } case '<': { #if BC_ENABLE_EXTRA_MATH c2 = l->buf[l->i]; if (c2 == '<') { l->i += 1; bc_lex_assign(l, BC_LEX_OP_ASSIGN_LSHIFT, BC_LEX_OP_LSHIFT); break; } #endif // BC_ENABLE_EXTRA_MATH bc_lex_assign(l, BC_LEX_OP_REL_LE, BC_LEX_OP_REL_LT); break; } case '=': { bc_lex_assign(l, BC_LEX_OP_REL_EQ, BC_LEX_OP_ASSIGN); break; } case '>': { #if BC_ENABLE_EXTRA_MATH c2 = l->buf[l->i]; if (c2 == '>') { l->i += 1; bc_lex_assign(l, BC_LEX_OP_ASSIGN_RSHIFT, BC_LEX_OP_RSHIFT); break; } #endif // BC_ENABLE_EXTRA_MATH bc_lex_assign(l, BC_LEX_OP_REL_GE, BC_LEX_OP_REL_GT); break; } case '[': case ']': { l->t = (BcLexType) (c - '[' + BC_LEX_LBRACKET); break; } case '\\': { if (BC_NO_ERR(l->buf[l->i] == '\n')) { l->i += 1; l->t = BC_LEX_WHITESPACE; } else s = bc_lex_invalidChar(l, c); break; } case '^': { bc_lex_assign(l, BC_LEX_OP_ASSIGN_POWER, BC_LEX_OP_POWER); break; } case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': { s = bc_lex_identifier(l); break; } case '{': case '}': { l->t = (BcLexType) (c - '{' + BC_LEX_LBRACE); break; } case '|': { c2 = l->buf[l->i]; if (BC_NO_ERR(c2 == '|')) { s = bc_lex_verr(l, BC_ERROR_POSIX_BOOL, "||"); if (BC_ERR(s)) return s; l->i += 1; l->t = BC_LEX_OP_BOOL_OR; } else s = bc_lex_invalidChar(l, c); break; } default: { s = bc_lex_invalidChar(l, c); break; } } return s; } #endif // BC_ENABLED #if BC_ENABLED static BcStatus bc_parse_else(BcParse *p); static BcStatus bc_parse_stmt(BcParse *p); static BcStatus bc_parse_expr_err(BcParse *p, uint8_t flags, BcParseNext next); static bool bc_parse_inst_isLeaf(BcInst t) { return (t >= BC_INST_NUM && t <= BC_INST_MAXSCALE) || #if BC_ENABLE_EXTRA_MATH t == BC_INST_TRUNC || #endif // BC_ENABLE_EXTRA_MATH t == BC_INST_INC_POST || t == BC_INST_DEC_POST; } static bool bc_parse_isDelimiter(const BcParse *p) { BcLexType t = p->l.t; bool good = false; if (BC_PARSE_DELIMITER(t)) return true; if (t == BC_LEX_KW_ELSE) { size_t i; uint16_t *fptr = NULL, flags = BC_PARSE_FLAG_ELSE; for (i = 0; i < p->flags.len && BC_PARSE_BLOCK_STMT(flags); ++i) { fptr = bc_vec_item_rev(&p->flags, i); flags = *fptr; if ((flags & BC_PARSE_FLAG_BRACE) && p->l.last != BC_LEX_RBRACE) return false; } good = ((flags & BC_PARSE_FLAG_IF) != 0); } else if (t == BC_LEX_RBRACE) { size_t i; for (i = 0; !good && i < p->flags.len; ++i) { uint16_t *fptr = bc_vec_item_rev(&p->flags, i); good = (((*fptr) & BC_PARSE_FLAG_BRACE) != 0); } } return good; } static void bc_parse_setLabel(BcParse *p) { BcFunc *func = p->func; BcInstPtr *ip = bc_vec_top(&p->exits); size_t *label; assert(func == bc_vec_item(&p->prog->fns, p->fidx)); label = bc_vec_item(&func->labels, ip->idx); *label = func->code.len; bc_vec_pop(&p->exits); } static void bc_parse_createLabel(BcParse *p, size_t idx) { bc_vec_push(&p->func->labels, &idx); } static void bc_parse_createCondLabel(BcParse *p, size_t idx) { bc_parse_createLabel(p, p->func->code.len); bc_vec_push(&p->conds, &idx); } static void bc_parse_createExitLabel(BcParse *p, size_t idx, bool loop) { BcInstPtr ip; assert(p->func == bc_vec_item(&p->prog->fns, p->fidx)); ip.func = loop; ip.idx = idx; ip.len = 0; bc_vec_push(&p->exits, &ip); bc_parse_createLabel(p, SIZE_MAX); } static void bc_parse_operator(BcParse *p, BcLexType type, size_t start, size_t *nexprs) { BcLexType t; uchar l, r = BC_PARSE_OP_PREC(type); uchar left = BC_PARSE_OP_LEFT(type); while (p->ops.len > start) { t = BC_PARSE_TOP_OP(p); if (t == BC_LEX_LPAREN) break; l = BC_PARSE_OP_PREC(t); if (l >= r && (l != r || !left)) break; bc_parse_push(p, BC_PARSE_TOKEN_INST(t)); bc_vec_pop(&p->ops); *nexprs -= !BC_PARSE_OP_PREFIX(t); } bc_vec_push(&p->ops, &type); } static BcStatus bc_parse_rightParen(BcParse *p, size_t *nexs) { BcLexType top; while ((top = BC_PARSE_TOP_OP(p)) != BC_LEX_LPAREN) { bc_parse_push(p, BC_PARSE_TOKEN_INST(top)); bc_vec_pop(&p->ops); *nexs -= !BC_PARSE_OP_PREFIX(top); } bc_vec_pop(&p->ops); return bc_lex_next(&p->l); } static BcStatus bc_parse_params(BcParse *p, uint8_t flags) { BcStatus s; bool comma = false; size_t nparams; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; for (nparams = 0; p->l.t != BC_LEX_RPAREN; ++nparams) { flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); flags |= (BC_PARSE_ARRAY | BC_PARSE_NEEDVAL); s = bc_parse_expr_status(p, flags, bc_parse_next_param); if (BC_ERR(s)) return s; comma = p->l.t == BC_LEX_COMMA; if (comma) { s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; } } if (BC_ERR(comma)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); bc_parse_push(p, BC_INST_CALL); bc_parse_pushIndex(p, nparams); return BC_STATUS_SUCCESS; } static BcStatus bc_parse_call(BcParse *p, char *name, uint8_t flags) { BcStatus s; BcId id; size_t idx; id.name = name; s = bc_parse_params(p, flags); if (BC_ERR(s)) goto err; // We just assert this because bc_parse_params() should // ensure that the next token is what it should be. assert(p->l.t == BC_LEX_RPAREN); // We cannot use bc_program_insertFunc() here // because it will overwrite an existing function. idx = bc_map_index(&p->prog->fn_map, &id); if (idx == BC_VEC_INVALID_IDX) { idx = bc_program_insertFunc(p->prog, name); assert(idx != BC_VEC_INVALID_IDX); // Make sure that this pointer was not invalidated. p->func = bc_vec_item(&p->prog->fns, p->fidx); } else { free(name); idx = ((BcId*) bc_vec_item(&p->prog->fn_map, idx))->idx; } bc_parse_pushIndex(p, idx); return bc_lex_next(&p->l); err: free(name); return s; } static BcStatus bc_parse_name(BcParse *p, BcInst *type, bool *can_assign, uint8_t flags) { BcStatus s; char *name; name = bc_vm_strdup(p->l.str.v); s = bc_lex_next(&p->l); if (BC_ERR(s)) goto err; if (p->l.t == BC_LEX_LBRACKET) { s = bc_lex_next(&p->l); if (BC_ERR(s)) goto err; if (p->l.t == BC_LEX_RBRACKET) { if (BC_ERR(!(flags & BC_PARSE_ARRAY))) { s = bc_parse_err(p, BC_ERROR_PARSE_EXPR); goto err; } *type = BC_INST_ARRAY; *can_assign = false; } else { flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); flags |= BC_PARSE_NEEDVAL; s = bc_parse_expr_status(p, flags, bc_parse_next_elem); if (BC_ERR(s)) goto err; if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) { s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN); goto err; } *type = BC_INST_ARRAY_ELEM; *can_assign = true; } s = bc_lex_next(&p->l); if (BC_ERR(s)) goto err; bc_parse_push(p, *type); bc_parse_pushName(p, name, false); } else if (p->l.t == BC_LEX_LPAREN) { if (BC_ERR(flags & BC_PARSE_NOCALL)) { s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN); goto err; } *type = BC_INST_CALL; *can_assign = false; // Return early because bc_parse_call() frees the name. return bc_parse_call(p, name, flags); } else { *type = BC_INST_VAR; *can_assign = true; bc_parse_push(p, BC_INST_VAR); bc_parse_pushName(p, name, true); } err: free(name); return s; } static BcStatus bc_parse_noArgBuiltin(BcParse *p, BcInst inst) { BcStatus s; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_LPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if ((p->l.t != BC_LEX_RPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); bc_parse_push(p, inst); return bc_lex_next(&p->l); } static BcStatus bc_parse_builtin(BcParse *p, BcLexType type, uint8_t flags, BcInst *prev) { BcStatus s; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_LPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); flags |= BC_PARSE_NEEDVAL; if (type == BC_LEX_KW_LENGTH) flags |= BC_PARSE_ARRAY; s = bc_parse_expr_status(p, flags, bc_parse_next_rel); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_RPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); *prev = type - BC_LEX_KW_LENGTH + BC_INST_LENGTH; bc_parse_push(p, *prev); return bc_lex_next(&p->l); } static BcStatus bc_parse_scale(BcParse *p, BcInst *type, bool *can_assign, uint8_t flags) { BcStatus s; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (p->l.t != BC_LEX_LPAREN) { *type = BC_INST_SCALE; *can_assign = true; bc_parse_push(p, BC_INST_SCALE); return BC_STATUS_SUCCESS; } *type = BC_INST_SCALE_FUNC; *can_assign = false; flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); flags |= BC_PARSE_NEEDVAL; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; s = bc_parse_expr_status(p, flags, bc_parse_next_rel); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_RPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); bc_parse_push(p, BC_INST_SCALE_FUNC); return bc_lex_next(&p->l); } static BcStatus bc_parse_incdec(BcParse *p, BcInst *prev, bool *can_assign, size_t *nexs, uint8_t flags) { BcStatus s; BcLexType type; uchar inst; BcInst etype = *prev; BcLexType last = p->l.last; assert(prev != NULL && can_assign != NULL); if (BC_ERR(last == BC_LEX_OP_INC || last == BC_LEX_OP_DEC || last == BC_LEX_RPAREN)) { return s = bc_parse_err(p, BC_ERROR_PARSE_ASSIGN); } if (BC_PARSE_INST_VAR(etype)) { if (!*can_assign) return bc_parse_err(p, BC_ERROR_PARSE_ASSIGN); *prev = inst = BC_INST_INC_POST + (p->l.t != BC_LEX_OP_INC); bc_parse_push(p, inst); s = bc_lex_next(&p->l); *can_assign = false; } else { *prev = inst = BC_INST_INC_PRE + (p->l.t != BC_LEX_OP_INC); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; type = p->l.t; // Because we parse the next part of the expression // right here, we need to increment this. *nexs = *nexs + 1; if (type == BC_LEX_NAME) s = bc_parse_name(p, prev, can_assign, flags | BC_PARSE_NOCALL); else if (type >= BC_LEX_KW_LAST && type <= BC_LEX_KW_OBASE) { bc_parse_push(p, type - BC_LEX_KW_LAST + BC_INST_LAST); s = bc_lex_next(&p->l); *can_assign = false; } else if (BC_NO_ERR(type == BC_LEX_KW_SCALE)) { s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t == BC_LEX_LPAREN)) s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN); else bc_parse_push(p, BC_INST_SCALE); *can_assign = false; } else s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN); if (BC_NO_ERR(!s)) bc_parse_push(p, inst); } return s; } static BcStatus bc_parse_minus(BcParse *p, BcInst *prev, size_t ops_bgn, bool rparen, bool binlast, size_t *nexprs) { BcStatus s; BcLexType type; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; type = BC_PARSE_LEAF(*prev, binlast, rparen) ? BC_LEX_OP_MINUS : BC_LEX_NEG; *prev = BC_PARSE_TOKEN_INST(type); // We can just push onto the op stack because this is the largest // precedence operator that gets pushed. Inc/dec does not. if (type != BC_LEX_OP_MINUS) bc_vec_push(&p->ops, &type); else bc_parse_operator(p, type, ops_bgn, nexprs); return s; } static BcStatus bc_parse_str(BcParse *p, char inst) { bc_parse_string(p); bc_parse_push(p, inst); return bc_lex_next(&p->l); } static BcStatus bc_parse_print(BcParse *p) { BcStatus s; BcLexType t; bool comma = false; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; t = p->l.t; if (bc_parse_isDelimiter(p)) return bc_parse_err(p, BC_ERROR_PARSE_PRINT); do { if (t == BC_LEX_STR) s = bc_parse_str(p, BC_INST_PRINT_POP); else { s = bc_parse_expr_status(p, BC_PARSE_NEEDVAL, bc_parse_next_print); if (BC_NO_ERR(!s)) bc_parse_push(p, BC_INST_PRINT_POP); } if (BC_ERR(s)) return s; comma = (p->l.t == BC_LEX_COMMA); if (comma) s = bc_lex_next(&p->l); else { if (!bc_parse_isDelimiter(p)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); else break; } t = p->l.t; } while (BC_NO_ERR(!s)); if (BC_ERR(s)) return s; if (BC_ERR(comma)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); return s; } static BcStatus bc_parse_return(BcParse *p) { BcStatus s; BcLexType t; bool paren; uchar inst = BC_INST_RET0; if (BC_ERR(!BC_PARSE_FUNC(p))) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); if (p->func->voidfn) inst = BC_INST_RET_VOID; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; t = p->l.t; paren = t == BC_LEX_LPAREN; if (bc_parse_isDelimiter(p)) bc_parse_push(p, inst); else { s = bc_parse_expr_err(p, BC_PARSE_NEEDVAL, bc_parse_next_expr); if (BC_ERR(s && s != BC_STATUS_EMPTY_EXPR)) return s; else if (s == BC_STATUS_EMPTY_EXPR) { bc_parse_push(p, inst); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; } if (!paren || p->l.last != BC_LEX_RPAREN) { s = bc_parse_err(p, BC_ERROR_POSIX_RET); if (BC_ERR(s)) return s; } else if (BC_ERR(p->func->voidfn)) return bc_parse_verr(p, BC_ERROR_PARSE_RET_VOID, p->func->name); bc_parse_push(p, BC_INST_RET); } return s; } static BcStatus bc_parse_endBody(BcParse *p, bool brace) { BcStatus s = BC_STATUS_SUCCESS; bool has_brace, new_else = false; if (BC_ERR(p->flags.len <= 1)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); if (brace) { assert(p->l.t == BC_LEX_RBRACE); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(!bc_parse_isDelimiter(p))) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); } has_brace = (BC_PARSE_BRACE(p) != 0); do { size_t len = p->flags.len; bool loop; if (has_brace && !brace) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); loop = (BC_PARSE_LOOP_INNER(p) != 0); if (loop || BC_PARSE_ELSE(p)) { if (loop) { size_t *label = bc_vec_top(&p->conds); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, *label); bc_vec_pop(&p->conds); } bc_parse_setLabel(p); bc_vec_pop(&p->flags); } else if (BC_PARSE_FUNC_INNER(p)) { BcInst inst = (p->func->voidfn ? BC_INST_RET_VOID : BC_INST_RET0); bc_parse_push(p, inst); bc_parse_updateFunc(p, BC_PROG_MAIN); bc_vec_pop(&p->flags); } else if (BC_PARSE_BRACE(p) && !BC_PARSE_IF(p)) bc_vec_pop(&p->flags); // This needs to be last to parse nested if's properly. if (BC_PARSE_IF(p) && (len == p->flags.len || !BC_PARSE_BRACE(p))) { while (p->l.t == BC_LEX_NLINE) { s = bc_lex_next(&p->l); if (s) return s; } bc_vec_pop(&p->flags); if (!BC_S) { *(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_IF_END; new_else = (p->l.t == BC_LEX_KW_ELSE); if (new_else) s = bc_parse_else(p); else if (!has_brace && (!BC_PARSE_IF_END(p) || brace)) bc_parse_noElse(p); } else bc_parse_noElse(p); } if (brace && has_brace) brace = false; } while (p->flags.len > 1 && !new_else && (!BC_PARSE_IF_END(p) || brace) && !(has_brace = (BC_PARSE_BRACE(p) != 0))); if (BC_NO_ERR(!s) && BC_ERR(p->flags.len == 1 && brace)) s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN); else if (brace && BC_PARSE_BRACE(p)) { uint16_t flags = BC_PARSE_TOP_FLAG(p); if (!(flags & (BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_LOOP_INNER)) && !(flags & (BC_PARSE_FLAG_IF | BC_PARSE_FLAG_ELSE)) && !(flags & (BC_PARSE_FLAG_IF_END))) { bc_vec_pop(&p->flags); } } return s; } static void bc_parse_startBody(BcParse *p, uint16_t flags) { assert(flags); flags |= (BC_PARSE_TOP_FLAG(p) & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_LOOP)); flags |= BC_PARSE_FLAG_BODY; bc_vec_push(&p->flags, &flags); } void bc_parse_noElse(BcParse *p) { uint16_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p); *flag_ptr = (*flag_ptr & ~(BC_PARSE_FLAG_IF_END)); bc_parse_setLabel(p); } static BcStatus bc_parse_if(BcParse *p) { BcStatus s; size_t idx; uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_LPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; s = bc_parse_expr_status(p, flags, bc_parse_next_rel); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_RPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; bc_parse_push(p, BC_INST_JUMP_ZERO); idx = p->func->labels.len; bc_parse_pushIndex(p, idx); bc_parse_createExitLabel(p, idx, false); bc_parse_startBody(p, BC_PARSE_FLAG_IF); return BC_STATUS_SUCCESS; } static BcStatus bc_parse_else(BcParse *p) { size_t idx = p->func->labels.len; if (BC_ERR(!BC_PARSE_IF_END(p))) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, idx); bc_parse_noElse(p); bc_parse_createExitLabel(p, idx, false); bc_parse_startBody(p, BC_PARSE_FLAG_ELSE); return bc_lex_next(&p->l); } static BcStatus bc_parse_while(BcParse *p) { BcStatus s; size_t idx; uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_LPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; bc_parse_createCondLabel(p, p->func->labels.len); idx = p->func->labels.len; bc_parse_createExitLabel(p, idx, true); s = bc_parse_expr_status(p, flags, bc_parse_next_rel); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_RPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; bc_parse_push(p, BC_INST_JUMP_ZERO); bc_parse_pushIndex(p, idx); bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER); return s; } static BcStatus bc_parse_for(BcParse *p) { BcStatus s; size_t cond_idx, exit_idx, body_idx, update_idx; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_LPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (p->l.t != BC_LEX_SCOLON) s = bc_parse_expr_status(p, 0, bc_parse_next_for); else s = bc_parse_err(p, BC_ERROR_POSIX_FOR); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_SCOLON)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; cond_idx = p->func->labels.len; update_idx = cond_idx + 1; body_idx = update_idx + 1; exit_idx = body_idx + 1; bc_parse_createLabel(p, p->func->code.len); if (p->l.t != BC_LEX_SCOLON) { uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL); s = bc_parse_expr_status(p, flags, bc_parse_next_for); } else { // Set this for the next call to bc_parse_number. // This is safe to set because the current token // is a semicolon, which has no string requirement. bc_vec_string(&p->l.str, strlen(bc_parse_const1), bc_parse_const1); bc_parse_number(p); s = bc_parse_err(p, BC_ERROR_POSIX_FOR); } if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_SCOLON)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; bc_parse_push(p, BC_INST_JUMP_ZERO); bc_parse_pushIndex(p, exit_idx); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, body_idx); bc_parse_createCondLabel(p, update_idx); if (p->l.t != BC_LEX_RPAREN) s = bc_parse_expr_status(p, 0, bc_parse_next_rel); else s = bc_parse_err(p, BC_ERROR_POSIX_FOR); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_RPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, cond_idx); bc_parse_createLabel(p, p->func->code.len); bc_parse_createExitLabel(p, exit_idx, true); s = bc_lex_next(&p->l); if (BC_NO_ERR(!s)) bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER); return s; } static BcStatus bc_parse_loopExit(BcParse *p, BcLexType type) { size_t i; BcInstPtr *ip; if (BC_ERR(!BC_PARSE_LOOP(p))) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); if (type == BC_LEX_KW_BREAK) { if (BC_ERR(!p->exits.len)) return bc_parse_err(p, BC_ERROR_PARSE_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--); assert(ip != NULL && (i < p->exits.len || ip->func)); i = ip->idx; } else i = *((size_t*) bc_vec_top(&p->conds)); bc_parse_push(p, BC_INST_JUMP); bc_parse_pushIndex(p, i); return bc_lex_next(&p->l); } static BcStatus bc_parse_func(BcParse *p) { BcStatus s; bool comma = false, voidfn; uint16_t flags; size_t idx; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_NAME)) return bc_parse_err(p, BC_ERROR_PARSE_FUNC); voidfn = (!BC_IS_POSIX && p->l.t == BC_LEX_NAME && !strcmp(p->l.str.v, "void")); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; voidfn = (voidfn && p->l.t == BC_LEX_NAME); if (voidfn) { s = bc_parse_err(p, BC_ERROR_POSIX_VOID); if (BC_ERR(s)) return s; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; } if (BC_ERR(p->l.t != BC_LEX_LPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_FUNC); assert(p->prog->fns.len == p->prog->fn_map.len); idx = bc_program_insertFunc(p->prog, bc_vm_strdup(p->l.str.v)); assert(idx); bc_parse_updateFunc(p, idx); p->func->voidfn = voidfn; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; while (p->l.t != BC_LEX_RPAREN) { BcType t = BC_TYPE_VAR; if (p->l.t == BC_LEX_OP_MULTIPLY) { t = BC_TYPE_REF; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; s = bc_parse_err(p, BC_ERROR_POSIX_REF); if (BC_ERR(s)) return s; } if (BC_ERR(p->l.t != BC_LEX_NAME)) return bc_parse_err(p, BC_ERROR_PARSE_FUNC); p->func->nparams += 1; bc_vec_string(&p->buf, p->l.str.len, p->l.str.v); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (p->l.t == BC_LEX_LBRACKET) { if (t == BC_TYPE_VAR) t = BC_TYPE_ARRAY; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) return bc_parse_err(p, BC_ERROR_PARSE_FUNC); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; } else if (BC_ERR(t == BC_TYPE_REF)) return bc_parse_verr(p, BC_ERROR_PARSE_REF_VAR, p->buf.v); comma = (p->l.t == BC_LEX_COMMA); if (comma) { s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; } s = bc_func_insert(p->func, p->prog, p->buf.v, t, p->l.line); if (BC_ERR(s)) return s; } if (BC_ERR(comma)) return bc_parse_err(p, BC_ERROR_PARSE_FUNC); flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER; bc_parse_startBody(p, flags); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (p->l.t != BC_LEX_LBRACE) s = bc_parse_err(p, BC_ERROR_POSIX_BRACE); return s; } static BcStatus bc_parse_auto(BcParse *p) { BcStatus s; bool comma, one; if (BC_ERR(!p->auto_part)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; p->auto_part = comma = false; one = p->l.t == BC_LEX_NAME; while (p->l.t == BC_LEX_NAME) { BcType t; bc_vec_string(&p->buf, p->l.str.len - 1, p->l.str.v); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (p->l.t == BC_LEX_LBRACKET) { t = BC_TYPE_ARRAY; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) return bc_parse_err(p, BC_ERROR_PARSE_FUNC); s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; } else t = BC_TYPE_VAR; comma = (p->l.t == BC_LEX_COMMA); if (comma) { s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; } s = bc_func_insert(p->func, p->prog, p->buf.v, t, p->l.line); if (BC_ERR(s)) return s; } if (BC_ERR(comma)) return bc_parse_err(p, BC_ERROR_PARSE_FUNC); if (BC_ERR(!one)) return bc_parse_err(p, BC_ERROR_PARSE_NO_AUTO); if (BC_ERR(!bc_parse_isDelimiter(p))) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); return s; } static BcStatus bc_parse_body(BcParse *p, bool brace) { BcStatus s = BC_STATUS_SUCCESS; uint16_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p); assert(flag_ptr != NULL); assert(p->flags.len >= 2); *flag_ptr &= ~(BC_PARSE_FLAG_BODY); if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) { if (BC_ERR(!brace)) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); p->auto_part = (p->l.t != BC_LEX_KW_AUTO); if (!p->auto_part) { // Make sure this is true to not get a parse error. p->auto_part = true; s = bc_parse_auto(p); if (BC_ERR(s)) return s; } if (p->l.t == BC_LEX_NLINE) s = bc_lex_next(&p->l); } else { size_t len = p->flags.len; assert(*flag_ptr); s = bc_parse_stmt(p); if (BC_NO_ERR(!s) && !brace && !BC_PARSE_BODY(p) && len <= p->flags.len) s = bc_parse_endBody(p, false); } return s; } static BcStatus bc_parse_stmt(BcParse *p) { BcStatus s = BC_STATUS_SUCCESS; size_t len; uint16_t flags; BcLexType type = p->l.t; if (type == BC_LEX_NLINE) return bc_lex_next(&p->l); if (type == BC_LEX_KW_AUTO) return bc_parse_auto(p); p->auto_part = false; if (type != BC_LEX_KW_ELSE) { if (BC_PARSE_IF_END(p)) { bc_parse_noElse(p); if (p->flags.len > 1 && !BC_PARSE_BRACE(p)) s = bc_parse_endBody(p, false); return s; } else if (type == BC_LEX_LBRACE) { if (!BC_PARSE_BODY(p)) { bc_parse_startBody(p, BC_PARSE_FLAG_BRACE); s = bc_lex_next(&p->l); } else { *(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_BRACE; s = bc_lex_next(&p->l); if (BC_NO_ERR(!s)) s = bc_parse_body(p, true); } return s; } else if (BC_PARSE_BODY(p) && !BC_PARSE_BRACE(p)) return bc_parse_body(p, false); } len = p->flags.len; flags = BC_PARSE_TOP_FLAG(p); switch (type) { case BC_LEX_OP_INC: case BC_LEX_OP_DEC: case BC_LEX_OP_MINUS: case BC_LEX_OP_BOOL_NOT: case BC_LEX_LPAREN: case BC_LEX_NAME: case BC_LEX_NUMBER: case BC_LEX_KW_IBASE: case BC_LEX_KW_LAST: case BC_LEX_KW_LENGTH: case BC_LEX_KW_OBASE: case BC_LEX_KW_SCALE: case BC_LEX_KW_SQRT: case BC_LEX_KW_ABS: case BC_LEX_KW_READ: case BC_LEX_KW_MAXIBASE: case BC_LEX_KW_MAXOBASE: case BC_LEX_KW_MAXSCALE: { s = bc_parse_expr_status(p, BC_PARSE_PRINT, bc_parse_next_expr); break; } case BC_LEX_KW_ELSE: { s = bc_parse_else(p); break; } case BC_LEX_SCOLON: { // Do nothing. break; } case BC_LEX_RBRACE: { s = bc_parse_endBody(p, true); break; } case BC_LEX_STR: { s = bc_parse_str(p, BC_INST_PRINT_STR); break; } case BC_LEX_KW_BREAK: case BC_LEX_KW_CONTINUE: { s = bc_parse_loopExit(p, p->l.t); break; } case BC_LEX_KW_FOR: { s = bc_parse_for(p); break; } case BC_LEX_KW_HALT: { bc_parse_push(p, BC_INST_HALT); s = bc_lex_next(&p->l); break; } case BC_LEX_KW_IF: { s = bc_parse_if(p); break; } case BC_LEX_KW_LIMITS: { bc_vm_printf("BC_LONG_BIT = %lu\n", (ulong) BC_LONG_BIT); bc_vm_printf("BC_BASE_DIGS = %lu\n", (ulong) BC_BASE_DIGS); bc_vm_printf("BC_BASE_POW = %lu\n", (ulong) BC_BASE_POW); bc_vm_printf("BC_OVERFLOW_MAX = %lu\n", (ulong) BC_NUM_BIGDIG_MAX); bc_vm_printf("\n"); bc_vm_printf("BC_BASE_MAX = %lu\n", BC_MAX_OBASE); bc_vm_printf("BC_DIM_MAX = %lu\n", BC_MAX_DIM); bc_vm_printf("BC_SCALE_MAX = %lu\n", BC_MAX_SCALE); bc_vm_printf("BC_STRING_MAX = %lu\n", BC_MAX_STRING); bc_vm_printf("BC_NAME_MAX = %lu\n", BC_MAX_NAME); bc_vm_printf("BC_NUM_MAX = %lu\n", BC_MAX_NUM); bc_vm_printf("MAX Exponent = %lu\n", BC_MAX_EXP); bc_vm_printf("Number of vars = %lu\n", BC_MAX_VARS); s = bc_lex_next(&p->l); break; } case BC_LEX_KW_PRINT: { s = bc_parse_print(p); break; } case BC_LEX_KW_QUIT: { // Quit is a compile-time command. We don't exit directly, // so the vm can clean up. Limits do the same thing. s = BC_STATUS_QUIT; break; } case BC_LEX_KW_RETURN: { s = bc_parse_return(p); break; } case BC_LEX_KW_WHILE: { s = bc_parse_while(p); break; } default: { s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN); break; } } if (BC_NO_ERR(!s) && len == p->flags.len && flags == BC_PARSE_TOP_FLAG(p)) { if (BC_ERR(!bc_parse_isDelimiter(p))) s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN); } // Make sure semicolons are eaten. while (BC_NO_ERR(!s) && p->l.t == BC_LEX_SCOLON) s = bc_lex_next(&p->l); return s; } BcStatus bc_parse_parse(BcParse *p) { BcStatus s; assert(p); if (BC_ERR(p->l.t == BC_LEX_EOF)) s = bc_parse_err(p, BC_ERROR_PARSE_EOF); else if (p->l.t == BC_LEX_KW_DEFINE) { if (BC_ERR(BC_PARSE_NO_EXEC(p))) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); s = bc_parse_func(p); } else s = bc_parse_stmt(p); if (BC_ERR((s && s != BC_STATUS_QUIT)) || BC_SIG) s = bc_parse_reset(p, s); return s; } static BcStatus bc_parse_expr_err(BcParse *p, uint8_t flags, BcParseNext next) { BcStatus s = BC_STATUS_SUCCESS; BcInst prev = BC_INST_PRINT; uchar inst = BC_INST_INVALID; BcLexType top, t = p->l.t; size_t nexprs = 0, ops_bgn = p->ops.len; uint32_t i, nparens, nrelops; bool pfirst, rprn, done, get_token, assign, bin_last, incdec, can_assign; assert(!(flags & BC_PARSE_PRINT) || !(flags & BC_PARSE_NEEDVAL)); pfirst = (p->l.t == BC_LEX_LPAREN); nparens = nrelops = 0; rprn = done = get_token = assign = incdec = can_assign = false; bin_last = true; // We want to eat newlines if newlines are not a valid ending token. // This is for spacing in things like for loop headers. if (!(flags & BC_PARSE_NOREAD)) { while (BC_NO_ERR(!s) && (t = p->l.t) == BC_LEX_NLINE) s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; } for (; BC_NO_SIG && BC_NO_ERR(!s) && !done && BC_PARSE_EXPR(t); t = p->l.t) { switch (t) { case BC_LEX_OP_INC: case BC_LEX_OP_DEC: { if (BC_ERR(incdec)) return bc_parse_err(p, BC_ERROR_PARSE_ASSIGN); s = bc_parse_incdec(p, &prev, &can_assign, &nexprs, flags); rprn = get_token = bin_last = false; incdec = true; break; } #if BC_ENABLE_EXTRA_MATH case BC_LEX_OP_TRUNC: { if (BC_ERR(!BC_PARSE_LEAF(prev, bin_last, rprn))) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); // I can just add the instruction because // negative will already be taken care of. bc_parse_push(p, BC_INST_TRUNC); rprn = can_assign = false; get_token = true; break; } #endif // BC_ENABLE_EXTRA_MATH case BC_LEX_OP_MINUS: { s = bc_parse_minus(p, &prev, ops_bgn, rprn, bin_last, &nexprs); rprn = get_token = can_assign = false; bin_last = (prev == BC_INST_MINUS); if (bin_last) incdec = false; break; } case BC_LEX_OP_ASSIGN_POWER: case BC_LEX_OP_ASSIGN_MULTIPLY: case BC_LEX_OP_ASSIGN_DIVIDE: case BC_LEX_OP_ASSIGN_MODULUS: case BC_LEX_OP_ASSIGN_PLUS: case BC_LEX_OP_ASSIGN_MINUS: #if BC_ENABLE_EXTRA_MATH case BC_LEX_OP_ASSIGN_PLACES: case BC_LEX_OP_ASSIGN_LSHIFT: case BC_LEX_OP_ASSIGN_RSHIFT: #endif // BC_ENABLE_EXTRA_MATH case BC_LEX_OP_ASSIGN: { if (!BC_PARSE_INST_VAR(prev)) { s = bc_parse_err(p, BC_ERROR_PARSE_ASSIGN); break; } } // Fallthrough. case BC_LEX_OP_POWER: case BC_LEX_OP_MULTIPLY: case BC_LEX_OP_DIVIDE: case BC_LEX_OP_MODULUS: case BC_LEX_OP_PLUS: #if BC_ENABLE_EXTRA_MATH case BC_LEX_OP_PLACES: case BC_LEX_OP_LSHIFT: case BC_LEX_OP_RSHIFT: #endif // BC_ENABLE_EXTRA_MATH case BC_LEX_OP_REL_EQ: case BC_LEX_OP_REL_LE: case BC_LEX_OP_REL_GE: case BC_LEX_OP_REL_NE: case BC_LEX_OP_REL_LT: case BC_LEX_OP_REL_GT: case BC_LEX_OP_BOOL_NOT: case BC_LEX_OP_BOOL_OR: case BC_LEX_OP_BOOL_AND: { if (BC_PARSE_OP_PREFIX(t)) { if (BC_ERR(!bin_last && !BC_PARSE_OP_PREFIX(p->l.last))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); } else if (BC_ERR(BC_PARSE_PREV_PREFIX(prev) || bin_last)) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); nrelops += (t >= BC_LEX_OP_REL_EQ && t <= BC_LEX_OP_REL_GT); prev = BC_PARSE_TOKEN_INST(t); bc_parse_operator(p, t, ops_bgn, &nexprs); rprn = incdec = can_assign = false; get_token = true; bin_last = !BC_PARSE_OP_PREFIX(t); break; } case BC_LEX_LPAREN: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); nparens += 1; rprn = incdec = can_assign = false; get_token = true; bc_vec_push(&p->ops, &t); break; } case BC_LEX_RPAREN: { // This needs to be a status. The error // is handled in bc_parse_expr_status(). if (BC_ERR(p->l.last == BC_LEX_LPAREN)) return BC_STATUS_EMPTY_EXPR; if (BC_ERR(bin_last || BC_PARSE_PREV_PREFIX(prev))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); if (!nparens) { s = BC_STATUS_SUCCESS; done = true; get_token = false; break; } nparens -= 1; rprn = true; get_token = bin_last = incdec = false; s = bc_parse_rightParen(p, &nexprs); break; } case BC_LEX_NAME: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); get_token = bin_last = false; s = bc_parse_name(p, &prev, &can_assign, flags & ~BC_PARSE_NOCALL); rprn = (prev == BC_INST_CALL); nexprs += 1; break; } case BC_LEX_NUMBER: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); bc_parse_number(p); nexprs += 1; prev = BC_INST_NUM; get_token = true; rprn = bin_last = can_assign = false; break; } case BC_LEX_KW_IBASE: case BC_LEX_KW_LAST: case BC_LEX_KW_OBASE: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); prev = t - BC_LEX_KW_LAST + BC_INST_LAST; bc_parse_push(p, prev); get_token = can_assign = true; rprn = bin_last = false; nexprs += 1; break; } case BC_LEX_KW_LENGTH: case BC_LEX_KW_SQRT: case BC_LEX_KW_ABS: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); s = bc_parse_builtin(p, t, flags, &prev); rprn = get_token = bin_last = incdec = can_assign = false; nexprs += 1; break; } case BC_LEX_KW_READ: case BC_LEX_KW_MAXIBASE: case BC_LEX_KW_MAXOBASE: case BC_LEX_KW_MAXSCALE: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); else if (t == BC_LEX_KW_READ && BC_ERR(flags & BC_PARSE_NOREAD)) return bc_parse_err(p, BC_ERROR_EXEC_REC_READ); else { prev = t - BC_LEX_KW_READ + BC_INST_READ; s = bc_parse_noArgBuiltin(p, prev); } rprn = get_token = bin_last = incdec = can_assign = false; nexprs += 1; break; } case BC_LEX_KW_SCALE: { if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); s = bc_parse_scale(p, &prev, &can_assign, flags); rprn = get_token = bin_last = false; nexprs += 1; break; } default: { #ifndef NDEBUG s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN); break; #endif // NDEBUG } } if (BC_NO_ERR(!s) && get_token) s = bc_lex_next(&p->l); } if (BC_ERR(s)) return s; if (BC_SIG) return BC_STATUS_SIGNAL; while (p->ops.len > ops_bgn) { top = BC_PARSE_TOP_OP(p); assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN; if (BC_ERR(top == BC_LEX_LPAREN || top == BC_LEX_RPAREN)) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); bc_parse_push(p, BC_PARSE_TOKEN_INST(top)); nexprs -= !BC_PARSE_OP_PREFIX(top); bc_vec_pop(&p->ops); } if (BC_ERR(nexprs != 1)) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); for (i = 0; i < next.len && t != next.tokens[i]; ++i); if (BC_ERR(i == next.len && !bc_parse_isDelimiter(p))) return bc_parse_err(p, BC_ERROR_PARSE_EXPR); if (!(flags & BC_PARSE_REL) && nrelops) { s = bc_parse_err(p, BC_ERROR_POSIX_REL_POS); if (BC_ERR(s)) return s; } else if ((flags & BC_PARSE_REL) && nrelops > 1) { s = bc_parse_err(p, BC_ERROR_POSIX_MULTIREL); if (BC_ERR(s)) return s; } if (!(flags & BC_PARSE_NEEDVAL) && !pfirst) { if (assign) { inst = *((uchar*) bc_vec_top(&p->func->code)); inst += (BC_INST_ASSIGN_POWER_NO_VAL - BC_INST_ASSIGN_POWER); } else if (incdec && !(flags & BC_PARSE_PRINT)) { inst = *((uchar*) bc_vec_top(&p->func->code)); inst = BC_INST_INC_NO_VAL + (inst & 0x01); } if (inst >= BC_INST_INC_NO_VAL && inst <= BC_INST_ASSIGN_NO_VAL) { bc_vec_pop(&p->func->code); bc_parse_push(p, inst); } } if ((flags & BC_PARSE_PRINT)) { if (pfirst || !assign) bc_parse_push(p, BC_INST_PRINT); } else if (!(flags & BC_PARSE_NEEDVAL) && (inst < BC_INST_INC_NO_VAL || inst > BC_INST_ASSIGN_NO_VAL)) { bc_parse_push(p, BC_INST_POP); } // We want to eat newlines if newlines are not a valid ending token. // This is for spacing in things like for loop headers. for (incdec = true, i = 0; i < next.len && incdec; ++i) incdec = (next.tokens[i] != BC_LEX_NLINE); if (incdec) { while (BC_NO_ERR(!s) && p->l.t == BC_LEX_NLINE) s = bc_lex_next(&p->l); } return s; } BcStatus bc_parse_expr_status(BcParse *p, uint8_t flags, BcParseNext next) { BcStatus s = bc_parse_expr_err(p, flags, next); if (BC_ERR(s == BC_STATUS_EMPTY_EXPR)) s = bc_parse_err(p, BC_ERROR_PARSE_EMPTY_EXPR); return s; } BcStatus bc_parse_expr(BcParse *p, uint8_t flags) { assert(p); return bc_parse_expr_status(p, flags, bc_parse_next_read); } #endif // BC_ENABLED #if BC_ENABLED int bc_main(int argc, char **argv) { BcStatus s; vm->read_ret = BC_INST_RET; vm->help = bc_help; #if BC_ENABLE_SIGNALS vm->sigmsg = bc_sig_msg; vm->siglen = (uchar) strlen(vm->sigmsg); #endif // BC_ENABLE_SIGNALS vm->next = bc_lex_token; vm->parse = bc_parse_parse; vm->expr = bc_parse_expr; s = bc_vm_boot(argc, argv, "BC_LINE_LENGTH", "BC_ENV_ARGS", "BC_EXPR_EXIT"); return (int) s; } #endif // BC_ENABLED #if DC_ENABLED bool dc_lex_negCommand(BcLex *l) { char c = l->buf[l->i]; return !BC_LEX_NUM_CHAR(c, false, false); } static BcStatus dc_lex_register(BcLex *l) { if (DC_X && isspace(l->buf[l->i - 1])) { char c; bc_lex_whitespace(l); c = l->buf[l->i]; if (!isalnum(c) && c != '_') return bc_lex_verr(l, BC_ERROR_PARSE_CHAR, c); l->i += 1; bc_lex_name(l); } else { bc_vec_npop(&l->str, l->str.len); bc_vec_pushByte(&l->str, (uchar) l->buf[l->i - 1]); bc_vec_pushByte(&l->str, '\0'); l->t = BC_LEX_NAME; } return BC_STATUS_SUCCESS; } static BcStatus dc_lex_string(BcLex *l) { size_t depth = 1, nls = 0, i = l->i; char c; l->t = BC_LEX_STR; bc_vec_npop(&l->str, l->str.len); for (; (c = l->buf[i]) && depth; ++i) { if (c == '\\') { c = l->buf[++i]; if (!c) break; } else { depth += (c == '['); depth -= (c == ']'); } nls += (c == '\n'); if (depth) bc_vec_push(&l->str, &c); } if (BC_ERR(c == '\0' && depth)) { l->i = i; return bc_lex_err(l, BC_ERROR_PARSE_STRING); } bc_vec_pushByte(&l->str, '\0'); l->i = i; l->line += nls; return BC_STATUS_SUCCESS; } BcStatus dc_lex_token(BcLex *l) { BcStatus s = BC_STATUS_SUCCESS; char c = l->buf[l->i++], c2; size_t i; for (i = 0; i < dc_lex_regs_len; ++i) { if (l->last == dc_lex_regs[i]) return dc_lex_register(l); } if (c >= '$' && c <= '~' && (l->t = dc_lex_tokens[(c - '$')]) != BC_LEX_INVALID) { return s; } // This is the workhorse of the lexer. switch (c) { case '\0': case '\n': case '\t': case '\v': case '\f': case '\r': case ' ': { bc_lex_commonTokens(l, c); break; } case '!': { c2 = l->buf[l->i]; if (c2 == '=') l->t = BC_LEX_OP_REL_NE; else if (c2 == '<') l->t = BC_LEX_OP_REL_LE; else if (c2 == '>') l->t = BC_LEX_OP_REL_GE; else return bc_lex_invalidChar(l, c); l->i += 1; break; } case '#': { bc_lex_lineComment(l); break; } case '.': { c2 = l->buf[l->i]; if (BC_NO_ERR(BC_LEX_NUM_CHAR(c2, true, false))) s = bc_lex_number(l, c); else s = bc_lex_invalidChar(l, c); break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': { s = bc_lex_number(l, c); break; } case '[': { s = dc_lex_string(l); break; } default: { s = bc_lex_invalidChar(l, c); break; } } return s; } #endif // DC_ENABLED #if DC_ENABLED static BcStatus dc_parse_register(BcParse *p, bool var) { BcStatus s; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (p->l.t != BC_LEX_NAME) return bc_parse_err(p, BC_ERROR_PARSE_TOKEN); bc_parse_pushName(p, p->l.str.v, var); return s; } static BcStatus dc_parse_string(BcParse *p) { BcFunc f; bc_program_addFunc(p->prog, &f, bc_func_main); bc_parse_string(p); return bc_lex_next(&p->l); } static BcStatus dc_parse_mem(BcParse *p, uchar inst, bool name, bool store) { BcStatus s; bc_parse_push(p, inst); if (name) { s = dc_parse_register(p, inst != BC_INST_ARRAY_ELEM); if (BC_ERR(s)) return s; } if (store) { bc_parse_push(p, BC_INST_SWAP); bc_parse_push(p, BC_INST_ASSIGN_NO_VAL); } return bc_lex_next(&p->l); } static BcStatus dc_parse_cond(BcParse *p, uchar inst) { BcStatus s; bc_parse_push(p, inst); bc_parse_push(p, BC_INST_EXEC_COND); s = dc_parse_register(p, true); if (BC_ERR(s)) return s; s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; if (p->l.t == BC_LEX_KW_ELSE) { s = dc_parse_register(p, true); if (BC_ERR(s)) return s; s = bc_lex_next(&p->l); } else bc_parse_pushIndex(p, SIZE_MAX); return s; } static BcStatus dc_parse_token(BcParse *p, BcLexType t, uint8_t flags) { BcStatus s = BC_STATUS_SUCCESS; uchar inst; bool assign, get_token = false; switch (t) { case BC_LEX_OP_REL_EQ: case BC_LEX_OP_REL_LE: case BC_LEX_OP_REL_GE: case BC_LEX_OP_REL_NE: case BC_LEX_OP_REL_LT: case BC_LEX_OP_REL_GT: { inst = (uchar) (t - BC_LEX_OP_REL_EQ + BC_INST_REL_EQ); s = dc_parse_cond(p, inst); break; } case BC_LEX_SCOLON: case BC_LEX_COLON: { s = dc_parse_mem(p, BC_INST_ARRAY_ELEM, true, t == BC_LEX_COLON); break; } case BC_LEX_STR: { s = dc_parse_string(p); break; } case BC_LEX_NEG: { if (dc_lex_negCommand(&p->l)) { bc_parse_push(p, BC_INST_NEG); get_token = true; break; } s = bc_lex_next(&p->l); if (BC_ERR(s)) return s; } // Fallthrough. case BC_LEX_NUMBER: { bc_parse_number(p); if (t == BC_LEX_NEG) bc_parse_push(p, BC_INST_NEG); get_token = true; break; } case BC_LEX_KW_READ: { if (BC_ERR(flags & BC_PARSE_NOREAD)) s = bc_parse_err(p, BC_ERROR_EXEC_REC_READ); else bc_parse_push(p, BC_INST_READ); get_token = true; break; } case BC_LEX_OP_ASSIGN: case BC_LEX_STORE_PUSH: { assign = t == BC_LEX_OP_ASSIGN; inst = assign ? BC_INST_VAR : BC_INST_PUSH_TO_VAR; s = dc_parse_mem(p, inst, true, assign); break; } case BC_LEX_LOAD: case BC_LEX_LOAD_POP: { inst = t == BC_LEX_LOAD_POP ? BC_INST_PUSH_VAR : BC_INST_LOAD; s = dc_parse_mem(p, inst, true, false); break; } case BC_LEX_STORE_IBASE: case BC_LEX_STORE_OBASE: case BC_LEX_STORE_SCALE: { inst = (uchar) (t - BC_LEX_STORE_IBASE + BC_INST_IBASE); s = dc_parse_mem(p, inst, false, true); break; } default: { s = bc_parse_err(p, BC_ERROR_PARSE_TOKEN); get_token = true; break; } } if (BC_NO_ERR(!s) && get_token) s = bc_lex_next(&p->l); return s; } BcStatus dc_parse_expr(BcParse *p, uint8_t flags) { BcStatus s = BC_STATUS_SUCCESS; BcInst inst; BcLexType t; bool have_expr = false, need_expr = (flags & BC_PARSE_NOREAD) != 0; while (BC_NO_SIG && BC_NO_ERR(!s) && (t = p->l.t) != BC_LEX_EOF) { if (t == BC_LEX_NLINE) { s = bc_lex_next(&p->l); continue; } inst = dc_parse_insts[t]; if (inst != BC_INST_INVALID) { bc_parse_push(p, inst); s = bc_lex_next(&p->l); } else s = dc_parse_token(p, t, flags); have_expr = true; } if (BC_NO_ERR(!s)) { if (BC_SIG) s = BC_STATUS_SIGNAL; else if (BC_ERR(need_expr && !have_expr)) s = bc_vm_err(BC_ERROR_EXEC_READ_EXPR); else if (p->l.t == BC_LEX_EOF && (flags & BC_PARSE_NOCALL)) bc_parse_push(p, BC_INST_POP_EXEC); } return s; } BcStatus dc_parse_parse(BcParse *p) { BcStatus s; assert(p != NULL); if (BC_ERR(p->l.t == BC_LEX_EOF)) s = bc_parse_err(p, BC_ERROR_PARSE_EOF); else s = dc_parse_expr(p, 0); if (BC_ERR(s) || BC_SIG) s = bc_parse_reset(p, s); return s; } #endif // DC_ENABLED #if DC_ENABLED int dc_main(int argc, char **argv) { BcStatus s; vm->read_ret = BC_INST_POP_EXEC; vm->help = dc_help; #if BC_ENABLE_SIGNALS vm->sigmsg = dc_sig_msg; vm->siglen = (uchar) strlen(vm->sigmsg); #endif // BC_ENABLE_SIGNALS vm->next = dc_lex_token; vm->parse = dc_parse_parse; vm->expr = dc_parse_expr; s = bc_vm_boot(argc, argv, "DC_LINE_LENGTH", "DC_ENV_ARGS", "DC_EXPR_EXIT"); return (int) s; } #endif // DC_ENABLED BcVm *vm; int main(int argc, char *argv[]) { int s; char *name; size_t len = strlen(BC_EXECPREFIX); vm = calloc(1, sizeof(BcVm)); if (BC_ERR(vm == NULL)) return (int) bc_vm_err(BC_ERROR_FATAL_ALLOC_ERR); vm->locale = setlocale(LC_ALL, ""); name = strrchr(argv[0], '/'); vm->name = (name == NULL) ? argv[0] : name + 1; if (strlen(vm->name) > len) vm->name += len; #if !DC_ENABLED s = bc_main(argc, argv); #elif !BC_ENABLED s = dc_main(argc, argv); #else if (BC_IS_BC) s = bc_main(argc, argv); else s = dc_main(argc, argv); #endif return s; }