Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- From 61c3eaeaf5fa3d525b1911f7dc37829a049de354 Mon Sep 17 00:00:00 2001
- From: Gavin Howard <yzena.tech@gmail.com>
- Date: Mon, 29 Oct 2018 09:46:57 -0600
- Subject: [PATCH] Add bc and a complete dc
- ---
- miscutils/bc.c | 7581 ++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 7581 insertions(+)
- create mode 100644 miscutils/bc.c
- diff --git a/miscutils/bc.c b/miscutils/bc.c
- new file mode 100644
- index 000000000..a1a8814ea
- --- /dev/null
- +++ b/miscutils/bc.c
- @@ -0,0 +1,7581 @@
- +/* vi: set sw=4 ts=4: */
- +/*
- + * Licensed under GPLv2 or later, see file LICENSE in this source tree.
- + * Copyright (c) 2018 Gavin D. Howard and contributors.
- + * Automatically generated from https://github.com/gavinhoward/bc
- + */
- +//config:config BC
- +//config: bool "bc (46.77 kb; 55.93 kb when combined with dc)"
- +//config: default n
- +//config: help
- +//config: bc is a command-line, arbitrary-precision calculator with a Turing-complete
- +//config: language. See the GNU bc manual (https://www.gnu.org/software/bc/manual/bc.html)
- +//config: and bc spec (http://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html)
- +//config: for details.
- +//config:
- +//config: This bc has four differences to the GNU bc:
- +//config:
- +//config: 1) The period (.) can also be used as a shortcut for "last", as in the BSD bc.
- +//config: 2) Arrays are copied before being passed as arguments to functions. This
- +//config: behavior is required by the bc spec.
- +//config: 3) Arrays can be passed to the builtin "length" function to get the number of
- +//config: elements currently in the array. The following example prints "1":
- +//config:
- +//config: a[0] = 0
- +//config: length(a[])
- +//config:
- +//config: 4) The precedence of the boolean "not" operator (!) is equal to that of the
- +//config: unary minus (-), or negation, operator. This still allows POSIX-compliant
- +//config: scripts to work while somewhat preserving expected behavior (versus C) and
- +//config: making parsing easier.
- +//config:
- +//config: Options:
- +//config:
- +//config: -e expr --expression=expr
- +//config: run "expr" and quit. If multiple expressions or files
- +//config: (see below) are given, they are all run.
- +//config: -f file --file=file run the bc code in "file" and exit. See above as well.
- +//config: -h --help print this usage message and exit
- +//config: -i --interactive force interactive mode
- +//config: -l --mathlib use predefined math routines:
- +//config:
- +//config: s(expr) = sine of expr in radians
- +//config: c(expr) = cosine of expr in radians
- +//config: a(expr) = arctangent of expr, returning radians
- +//config: l(expr) = natural log of expr
- +//config: e(expr) = raises e to the power of expr
- +//config: j(n, x) = Bessel function of integer order n of x
- +//config:
- +//config: -q --quiet don't print version and copyright
- +//config: -s --standard error if any non-POSIX extensions are used
- +//config: -w --warn warn if any non-POSIX extensions are used
- +//config: -v --version print version information and copyright and exit
- +//config:
- +//config:config DC
- +//config: bool "dc (35.90 kb; 55.93 kb when combined with bc)"
- +//config: default n
- +//config: help
- +//config: dc is a reverse-polish notation command-line calculator which supports unlimited
- +//config: precision arithmetic. See the FreeBSD man page
- +//config: (https://www.unix.com/man-page/FreeBSD/1/dc/) and GNU dc manual
- +//config: (https://www.gnu.org/software/bc/manual/dc-1.05/html_mono/dc.html) for details.
- +//config:
- +//config: This dc has a few differences from the two above:
- +//config:
- +//config: 1) When printing a byte stream (command "P"), this bc follows what the FreeBSD
- +//config: dc does.
- +//config: 2) This dc implements the GNU extensions for divmod ("~") and modular
- +//config: exponentiation ("|").
- +//config: 3) This dc implements all FreeBSD extensions, except for "J" and "M".
- +//config: 4) Like the FreeBSD dc, this dc supports extended registers. However, it is
- +//config: implemented differently. When it encounters whitespace where a register
- +//config: should be, it skips the whitespace. If the character following is not
- +//config: a lowercase letter, an error is issued. Otherwise, the register name is
- +//config: parsed by the following regex:
- +//config:
- +//config: [a-z][a-z0-9_]*
- +//config:
- +//config: This generally means that register names will be surrounded by parentheses.
- +//config:
- +//config: Examples:
- +//config:
- +//config: l idx s temp L index S temp2 < do_thing
- +//config:
- +//config: Also note that, like the FreeBSD dc, extended registers are not allowed
- +//config: unless the "-x" option is given.
- +//config:
- +//config: Options:
- +//config:
- +//config: -e expr --expression=expr run "expr" and quit. If multiple expressions or
- +//config: files (see below) are given, they are all run.
- +//config: -f file --file=file run the bc code in "file" and exit. See above.
- +//config: -h --help print this usage message and exit.
- +//config: -V --version print version and copyright and exit.
- +//config: -x --extended-register enable extended register mode.
- +
- +//applet:IF_BC(APPLET(bc, BB_DIR_USR_BIN, BB_SUID_DROP))
- +//applet:IF_DC(APPLET(dc, BB_DIR_USR_BIN, BB_SUID_DROP))
- +
- +//kbuild:lib-$(CONFIG_BC) += bc.o
- +//kbuild:lib-$(CONFIG_DC) += bc.o
- +
- +//usage:#define bc_trivial_usage
- +//usage: "EXPRESSION...\n"
- +//usage: "function_definition\n"
- +//usage:
- +//usage:#define bc_full_usage "\n\n"
- +//usage: "See www.gnu.org/software/bc/manual/bc.html\n"
- +//usage:
- +//usage:#define bc_example_usage
- +//usage: "3 + 4.129\n"
- +//usage: "1903 - 2893\n"
- +//usage: "-129 * 213.28935\n"
- +//usage: "12 / -1932\n"
- +//usage: "12 % 12\n"
- +//usage: "34 ^ 189\n"
- +//usage: "scale = 13\n"
- +//usage: "ibase = 2\n"
- +//usage: "obase = A\n"
- +//usage:
- +//usage:#define dc_trivial_usage
- +//usage: "EXPRESSION..."
- +//usage:
- +//usage:#define dc_full_usage "\n\n"
- +//usage: "Tiny RPN calculator. Operations:\n"
- +//usage: "+, add, -, sub, *, mul, /, div, %, mod, ^, exp, ~, divmod, |, modular exponentiation,\n"
- +//usage: "p - print top of the stack (without popping),\n"
- +//usage: "f - print entire stack,\n"
- +//usage: "k - pop the value and set the precision.\n"
- +//usage: "i - pop the value and set input radix.\n"
- +//usage: "o - pop the value and set output radix.\n"
- +//usage: "Examples: 'dc 2 2 add p' -> 4, 'dc 8 8 mul 2 2 + / p' -> 16"
- +//usage:
- +//usage:#define dc_example_usage
- +//usage: "$ dc 2 2 + p\n"
- +//usage: "4\n"
- +//usage: "$ dc 8 8 \\* 2 2 + / p\n"
- +//usage: "16\n"
- +//usage: "$ dc 0 1 and p\n"
- +//usage: "0\n"
- +//usage: "$ dc 0 1 or p\n"
- +//usage: "1\n"
- +//usage: "$ echo 72 9 div 8 mul p | dc\n"
- +//usage: "64\n"
- +
- +#include <ctype.h>
- +#include <errno.h>
- +#include <stdbool.h>
- +#include <stddef.h>
- +#include <stdint.h>
- +#include <stdio.h>
- +#include <stdlib.h>
- +#include <string.h>
- +
- +#include <fcntl.h>
- +#include <limits.h>
- +#include <signal.h>
- +#include <unistd.h>
- +#include <sys/stat.h>
- +#include <sys/types.h>
- +
- +#include <getopt.h>
- +
- +typedef enum BcStatus {
- +
- + BC_STATUS_SUCCESS,
- +
- + BC_STATUS_ALLOC_ERR,
- + BC_STATUS_IO_ERR,
- + BC_STATUS_BIN_FILE,
- + BC_STATUS_PATH_IS_DIR,
- +
- + BC_STATUS_LEX_BAD_CHAR,
- + BC_STATUS_LEX_NO_STRING_END,
- + BC_STATUS_LEX_NO_COMMENT_END,
- + BC_STATUS_LEX_EOF,
- +#ifdef CONFIG_DC
- + BC_STATUS_LEX_EXTENDED_REG,
- +#endif // CONFIG_DC
- +
- + BC_STATUS_PARSE_BAD_TOKEN,
- + BC_STATUS_PARSE_BAD_EXP,
- + BC_STATUS_PARSE_EMPTY_EXP,
- + BC_STATUS_PARSE_BAD_PRINT,
- + BC_STATUS_PARSE_BAD_FUNC,
- + BC_STATUS_PARSE_BAD_ASSIGN,
- + BC_STATUS_PARSE_NO_AUTO,
- + BC_STATUS_PARSE_DUPLICATE_LOCAL,
- + BC_STATUS_PARSE_NO_BLOCK_END,
- +
- + BC_STATUS_MATH_NEGATIVE,
- + BC_STATUS_MATH_NON_INTEGER,
- + BC_STATUS_MATH_OVERFLOW,
- + BC_STATUS_MATH_DIVIDE_BY_ZERO,
- + BC_STATUS_MATH_BAD_STRING,
- +
- + BC_STATUS_EXEC_FILE_ERR,
- + BC_STATUS_EXEC_MISMATCHED_PARAMS,
- + BC_STATUS_EXEC_UNDEFINED_FUNC,
- + BC_STATUS_EXEC_FILE_NOT_EXECUTABLE,
- + BC_STATUS_EXEC_SIGACTION_FAIL,
- + BC_STATUS_EXEC_NUM_LEN,
- + BC_STATUS_EXEC_NAME_LEN,
- + BC_STATUS_EXEC_STRING_LEN,
- + BC_STATUS_EXEC_ARRAY_LEN,
- + BC_STATUS_EXEC_BAD_IBASE,
- + BC_STATUS_EXEC_BAD_SCALE,
- + BC_STATUS_EXEC_BAD_READ_EXPR,
- + BC_STATUS_EXEC_REC_READ,
- + BC_STATUS_EXEC_BAD_TYPE,
- + BC_STATUS_EXEC_BAD_OBASE,
- + BC_STATUS_EXEC_SIGNAL,
- + BC_STATUS_EXEC_STACK,
- +
- + BC_STATUS_VEC_OUT_OF_BOUNDS,
- + BC_STATUS_VEC_ITEM_EXISTS,
- +
- +#ifdef CONFIG_BC
- + BC_STATUS_POSIX_NAME_LEN,
- + BC_STATUS_POSIX_COMMENT,
- + BC_STATUS_POSIX_BAD_KW,
- + BC_STATUS_POSIX_DOT,
- + BC_STATUS_POSIX_RET_PARENS,
- + BC_STATUS_POSIX_BOOL_OPS,
- + BC_STATUS_POSIX_REL_POS,
- + BC_STATUS_POSIX_MULTIPLE_REL,
- + BC_STATUS_POSIX_FOR_INIT,
- + BC_STATUS_POSIX_FOR_COND,
- + BC_STATUS_POSIX_FOR_END,
- + BC_STATUS_POSIX_BRACE,
- +#endif // CONFIG_BC
- +
- + BC_STATUS_QUIT,
- + BC_STATUS_LIMITS,
- +
- + BC_STATUS_INVALID_OPTION,
- +
- +} BcStatus;
- +
- +#define BC_ERR_IDX_VM (0)
- +#define BC_ERR_IDX_LEX (1)
- +#define BC_ERR_IDX_PARSE (2)
- +#define BC_ERR_IDX_MATH (3)
- +#define BC_ERR_IDX_EXEC (4)
- +#define BC_ERR_IDX_VEC (5)
- +#ifdef CONFIG_BC
- +#define BC_ERR_IDX_POSIX (6)
- +#endif // CONFIG_BC
- +
- +#define BC_VEC_INVALID_IDX ((size_t) -1)
- +#define BC_VEC_START_CAP (1<<5)
- +
- +typedef void (*BcVecFree)(void*);
- +typedef int (*BcVecCmp)(const void*, const void*);
- +
- +typedef struct BcVec {
- +
- + char *v;
- + size_t len;
- + size_t cap;
- + size_t size;
- +
- + BcVecFree dtor;
- +
- +} BcVec;
- +
- +BcStatus bc_vec_init(BcVec *v, size_t esize, BcVecFree dtor);
- +BcStatus bc_vec_expand(BcVec *v, size_t req);
- +
- +void bc_vec_npop(BcVec *v, size_t n);
- +
- +BcStatus bc_vec_push(BcVec *v, const void *data);
- +BcStatus bc_vec_pushByte(BcVec *v, uint8_t data);
- +BcStatus bc_vec_string(BcVec *v, size_t len, const char *str);
- +BcStatus bc_vec_concat(BcVec *v, const char *str);
- +
- +void* bc_vec_item(const BcVec *v, size_t idx);
- +void* bc_vec_item_rev(const BcVec *v, size_t idx);
- +
- +void bc_vec_free(void *vec);
- +
- +#define bc_vec_pop(v) (bc_vec_npop((v), 1))
- +#define bc_vec_top(v) (bc_vec_item_rev((v), 0))
- +
- +BcStatus bc_map_insert(BcVec* v, const void *data, size_t *i);
- +size_t bc_map_index(const BcVec *v, const void *ptr);
- +
- +#define bc_map_init(v) (bc_vec_init((v), sizeof(BcId), bc_id_free))
- +#define bc_map_item(v, idx) (bc_vec_item((v), (idx)))
- +
- +BcStatus bc_args(int argc, char *argv[], unsigned int* flags,
- + BcVec* exprs, BcVec* files);
- +
- +BcStatus bc_read_line(BcVec* vec, const char *prompt);
- +BcStatus bc_read_file(const char *path, char **buf);
- +
- +#define BC_IO_BIN_CHAR(c) ((((c) < ' ' && !isspace((c))) || (c) > '~'))
- +
- +typedef signed char BcDig;
- +
- +typedef struct BcNum {
- +
- + BcDig *restrict num;
- + size_t rdx;
- + size_t len;
- + size_t cap;
- + bool neg;
- +
- +} BcNum;
- +
- +#define BC_NUM_MIN_BASE ((unsigned long) 2)
- +#define BC_NUM_MAX_IBASE ((unsigned long) 16)
- +#define BC_NUM_DEF_SIZE (16)
- +#define BC_NUM_PRINT_WIDTH (69)
- +
- +#ifndef BC_NUM_KARATSUBA_LEN
- +#define BC_NUM_KARATSUBA_LEN (32)
- +#elif BC_NUM_KARATSUBA_LEN < 2
- +#error BC_NUM_KARATSUBA_LEN must be at least 2
- +#endif // BC_NUM_KARATSUBA_LEN
- +
- +#define BC_NUM_NEG(n, neg) ((((ssize_t) (n)) ^ -((ssize_t) (neg))) + (neg))
- +#define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1)
- +#define BC_NUM_INT(n) ((n)->len - (n)->rdx)
- +#define BC_NUM_AREQ(a, b) \
- + (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1)
- +#define BC_NUM_MREQ(a, b, scale) \
- + (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1)
- +
- +typedef BcStatus (*BcNumBinaryOp)(BcNum*, BcNum*, BcNum*, size_t);
- +typedef BcStatus (*BcNumDigitOp)(size_t, size_t, bool, size_t*, size_t);
- +
- +BcStatus bc_num_init(BcNum *n, size_t request);
- +BcStatus bc_num_expand(BcNum *n, size_t req);
- +BcStatus bc_num_copy(BcNum *d, BcNum *s);
- +void bc_num_free(void *num);
- +
- +BcStatus bc_num_ulong(BcNum *n, unsigned long *result);
- +BcStatus bc_num_ulong2num(BcNum *n, unsigned long val);
- +
- +void bc_num_truncate(BcNum *n, size_t places);
- +ssize_t bc_num_cmp(BcNum *a, BcNum *b);
- +
- +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);
- +BcStatus bc_num_sqrt(BcNum *a, BcNum *b, size_t scale);
- +BcStatus bc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, size_t scale);
- +
- +#ifdef CONFIG_DC
- +BcStatus bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d);
- +#endif // CONFIG_DC
- +
- +void bc_num_zero(BcNum *n);
- +void bc_num_one(BcNum *n);
- +void bc_num_ten(BcNum *n);
- +
- +BcStatus bc_num_parse(BcNum *n, const char *val, BcNum *base, size_t base_t);
- +BcStatus bc_num_print(BcNum *n, BcNum *base, size_t base_t, bool newline,
- + size_t *nchars, size_t line_len);
- +BcStatus bc_num_stream(BcNum *n, BcNum *base, size_t *nchars, size_t len);
- +
- +typedef enum BcInst {
- +
- + BC_INST_INC_PRE,
- + BC_INST_DEC_PRE,
- + BC_INST_INC_POST,
- + BC_INST_DEC_POST,
- +
- + BC_INST_NEG,
- +
- + BC_INST_POWER,
- + BC_INST_MULTIPLY,
- + BC_INST_DIVIDE,
- + BC_INST_MODULUS,
- + BC_INST_PLUS,
- + BC_INST_MINUS,
- +
- + 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_NOT,
- + BC_INST_BOOL_OR,
- + BC_INST_BOOL_AND,
- +
- + BC_INST_ASSIGN_POWER,
- + BC_INST_ASSIGN_MULTIPLY,
- + BC_INST_ASSIGN_DIVIDE,
- + BC_INST_ASSIGN_MODULUS,
- + BC_INST_ASSIGN_PLUS,
- + BC_INST_ASSIGN_MINUS,
- + BC_INST_ASSIGN,
- +
- + BC_INST_NUM,
- + BC_INST_VAR,
- + BC_INST_ARRAY_ELEM,
- + BC_INST_ARRAY,
- +
- + BC_INST_CALL,
- +
- + BC_INST_SCALE_FUNC,
- + BC_INST_IBASE,
- + BC_INST_SCALE,
- + BC_INST_LAST,
- + BC_INST_LENGTH,
- + BC_INST_READ,
- + BC_INST_OBASE,
- + BC_INST_SQRT,
- +
- + BC_INST_PRINT,
- + BC_INST_PRINT_POP,
- + BC_INST_STR,
- + BC_INST_PRINT_STR,
- +
- + BC_INST_JUMP,
- + BC_INST_JUMP_ZERO,
- +
- + BC_INST_POP,
- +
- + BC_INST_RET,
- + BC_INST_RET0,
- + BC_INST_POP_EXEC,
- +
- + BC_INST_HALT,
- +
- +#ifdef CONFIG_DC
- + 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,
- +
- + BC_INST_INVALID = -1,
- +#endif // CONFIG_DC
- +
- +} BcInst;
- +
- +typedef struct BcId {
- +
- + char *name;
- + size_t idx;
- +
- +} BcId;
- +
- +typedef struct BcFunc {
- +
- + BcVec code;
- + BcVec labels;
- + size_t nparams;
- + BcVec autos;
- +
- +} BcFunc;
- +
- +typedef enum BcResultType {
- +
- + BC_RESULT_TEMP,
- +
- + BC_RESULT_VAR,
- + BC_RESULT_ARRAY_ELEM,
- + BC_RESULT_ARRAY,
- +
- + BC_RESULT_STR,
- +
- + BC_RESULT_IBASE,
- + BC_RESULT_SCALE,
- + BC_RESULT_LAST,
- +
- + // These are between to calculate ibase, obase, and last from instructions.
- + BC_RESULT_CONSTANT,
- + BC_RESULT_ONE,
- +
- + BC_RESULT_OBASE,
- +
- +} BcResultType;
- +
- +typedef union BcResultData {
- +
- + BcNum n;
- + BcVec v;
- + BcId id;
- +
- +} BcResultData;
- +
- +typedef struct BcResult {
- +
- + BcResultType t;
- + BcResultData d;
- +
- +} BcResult;
- +
- +typedef struct BcInstPtr {
- +
- + size_t func;
- + size_t idx;
- + size_t len;
- +
- +} BcInstPtr;
- +
- +BcStatus bc_func_init(BcFunc *f);
- +BcStatus bc_func_insert(BcFunc *f, char *name, bool var);
- +void bc_func_free(void *func);
- +
- +BcStatus bc_array_init(BcVec *a, bool nums);
- +BcStatus bc_array_copy(BcVec *d, const BcVec *s);
- +
- +BcStatus bc_array_expand(BcVec *a, size_t len);
- +
- +void bc_string_free(void *string);
- +void bc_id_free(void *id);
- +BcStatus bc_result_copy(BcResult *d, BcResult *src);
- +void bc_result_free(void *result);
- +
- +int bc_id_cmp(const void *e1, const void *e2);
- +
- +// BC_LEX_NEG is not used in lexing; it is only for parsing.
- +typedef enum BcLexType {
- +
- + BC_LEX_EOF,
- + BC_LEX_INVALID,
- +
- + BC_LEX_OP_INC,
- + BC_LEX_OP_DEC,
- +
- + BC_LEX_NEG,
- +
- + BC_LEX_OP_POWER,
- + BC_LEX_OP_MULTIPLY,
- + BC_LEX_OP_DIVIDE,
- + BC_LEX_OP_MODULUS,
- + BC_LEX_OP_PLUS,
- + BC_LEX_OP_MINUS,
- +
- + 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_NOT,
- + BC_LEX_OP_BOOL_OR,
- + BC_LEX_OP_BOOL_AND,
- +
- + 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,
- + 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,
- +
- + BC_LEX_KEY_AUTO,
- + BC_LEX_KEY_BREAK,
- + BC_LEX_KEY_CONTINUE,
- + BC_LEX_KEY_DEFINE,
- + BC_LEX_KEY_ELSE,
- + BC_LEX_KEY_FOR,
- + BC_LEX_KEY_HALT,
- + BC_LEX_KEY_IBASE,
- + BC_LEX_KEY_IF,
- + BC_LEX_KEY_LAST,
- + BC_LEX_KEY_LENGTH,
- + BC_LEX_KEY_LIMITS,
- + BC_LEX_KEY_OBASE,
- + BC_LEX_KEY_PRINT,
- + BC_LEX_KEY_QUIT,
- + BC_LEX_KEY_READ,
- + BC_LEX_KEY_RETURN,
- + BC_LEX_KEY_SCALE,
- + BC_LEX_KEY_SQRT,
- + BC_LEX_KEY_WHILE,
- +
- +#ifdef CONFIG_DC
- + BC_LEX_EQ_NO_REG,
- + BC_LEX_OP_MODEXP,
- + BC_LEX_OP_DIVMOD,
- +
- + BC_LEX_COLON,
- + BC_LEX_ELSE,
- + 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_SCALE,
- + BC_LEX_LOAD,
- + BC_LEX_LOAD_POP,
- + BC_LEX_STORE_PUSH,
- + BC_LEX_STORE_OBASE,
- + BC_LEX_PRINT_POP,
- + BC_LEX_NQUIT,
- + BC_LEX_SCALE_FACTOR,
- +#endif // CONFIG_DC
- +
- +} BcLexType;
- +
- +struct BcLex;
- +typedef BcStatus (*BcLexNext)(struct BcLex*);
- +
- +typedef struct BcLex {
- +
- + const char *buffer;
- + size_t idx;
- + size_t line;
- + const char *f;
- + size_t len;
- + bool newline;
- +
- + struct {
- + BcLexType t;
- + BcLexType last;
- + BcVec v;
- + } t;
- +
- + BcLexNext next;
- +
- +} BcLex;
- +
- +BcStatus bc_lex_init(BcLex *l, BcLexNext next);
- +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);
- +void bc_lex_whitespace(BcLex *l);
- +BcStatus bc_lex_number(BcLex *l, char start);
- +BcStatus bc_lex_name(BcLex *l);
- +
- +#define BC_PARSE_STREND ((char) UCHAR_MAX)
- +
- +#define bc_parse_push(p, i) (bc_vec_pushByte(&(p)->func->code, (uint8_t) (i)))
- +#define bc_parse_updateFunc(p, f) \
- + ((p)->func = bc_vec_item(&(p)->prog->fns, ((p)->fidx = (f))))
- +
- +#define BC_PARSE_REL (1<<0)
- +#define BC_PARSE_PRINT (1<<1)
- +#define BC_PARSE_NOCALL (1<<2)
- +#define BC_PARSE_NOREAD (1<<3)
- +#define BC_PARSE_ARRAY (1<<4)
- +
- +#define BC_PARSE_TOP_FLAG_PTR(parse) ((uint8_t*) bc_vec_top(&(parse)->flags))
- +#define BC_PARSE_TOP_FLAG(parse) (*(BC_PARSE_TOP_FLAG_PTR(parse)))
- +
- +#define BC_PARSE_FLAG_FUNC_INNER (1<<0)
- +#define BC_PARSE_FUNC_INNER(parse) \
- + (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_FUNC_INNER)
- +
- +#define BC_PARSE_FLAG_FUNC (1<<1)
- +#define BC_PARSE_FUNC(parse) \
- + (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_FUNC)
- +
- +#define BC_PARSE_FLAG_BODY (1<<2)
- +#define BC_PARSE_BODY(parse) \
- + (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_BODY)
- +
- +#define BC_PARSE_FLAG_LOOP (1<<3)
- +#define BC_PARSE_LOOP(parse) \
- + (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_LOOP)
- +
- +#define BC_PARSE_FLAG_LOOP_INNER (1<<4)
- +#define BC_PARSE_LOOP_INNER(parse) \
- + (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_LOOP_INNER)
- +
- +#define BC_PARSE_FLAG_IF (1<<5)
- +#define BC_PARSE_IF(parse) \
- + (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_IF)
- +
- +#define BC_PARSE_FLAG_ELSE (1<<6)
- +#define BC_PARSE_ELSE(parse) \
- + (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_ELSE)
- +
- +#define BC_PARSE_FLAG_IF_END (1<<7)
- +#define BC_PARSE_IF_END(parse) \
- + (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_IF_END)
- +
- +#define BC_PARSE_CAN_EXEC(parse) \
- + (!(BC_PARSE_TOP_FLAG(parse) & (BC_PARSE_FLAG_FUNC_INNER | \
- + BC_PARSE_FLAG_FUNC | \
- + BC_PARSE_FLAG_BODY | \
- + BC_PARSE_FLAG_LOOP | \
- + BC_PARSE_FLAG_LOOP_INNER | \
- + BC_PARSE_FLAG_IF | \
- + BC_PARSE_FLAG_ELSE | \
- + BC_PARSE_FLAG_IF_END)))
- +
- +typedef struct BcOp {
- +
- + uint8_t prec;
- + bool left;
- +
- +} BcOp;
- +
- +typedef struct BcParseNext {
- +
- + uint32_t len;
- + BcLexType tokens[3];
- +
- +} BcParseNext;
- +
- +#define BC_PARSE_NEXT_TOKENS(...) .tokens = { __VA_ARGS__ }
- +#define BC_PARSE_NEXT(a, ...) { .len = (a), BC_PARSE_NEXT_TOKENS(__VA_ARGS__) }
- +
- +struct BcParse;
- +
- +struct BcProgram;
- +
- +typedef BcStatus (*BcParseInit)(struct BcParse*, struct BcProgram*, size_t);
- +typedef BcStatus (*BcParseParse)(struct BcParse*);
- +typedef BcStatus (*BcParseExpr)(struct BcParse*, uint8_t);
- +
- +typedef struct BcParse {
- +
- + BcParseParse parse;
- +
- + BcLex l;
- +
- + BcVec flags;
- +
- + BcVec exits;
- + BcVec conds;
- +
- + BcVec ops;
- +
- + struct BcProgram *prog;
- + BcFunc *func;
- + size_t fidx;
- +
- + size_t nbraces;
- + bool auto_part;
- +
- +} BcParse;
- +
- +BcStatus bc_parse_create(BcParse *p, struct BcProgram *prog, size_t func,
- + BcParseParse parse, BcLexNext next);
- +void bc_parse_free(BcParse *p);
- +BcStatus bc_parse_reset(BcParse *p, BcStatus s);
- +
- +BcStatus bc_parse_addFunc(BcParse *p, char *name, size_t *idx);
- +BcStatus bc_parse_pushName(BcParse* p, char *name);
- +BcStatus bc_parse_pushIndex(BcParse* p, size_t idx);
- +BcStatus bc_parse_number(BcParse *p, BcInst *prev, size_t* nexs);
- +BcStatus bc_parse_text(BcParse *p, const char *text);
- +
- +#ifdef CONFIG_BC
- +
- +#endif // CONFIG_BC
- +
- +#ifdef CONFIG_BC
- +
- +BcStatus bc_main(int argc, char *argv[]);
- +
- +typedef struct BcLexKeyword {
- +
- + const char name[9];
- + const char len;
- + const bool posix;
- +
- +} BcLexKeyword;
- +
- +#define BC_LEX_KW_ENTRY(a, b, c) { .name = a, .len = (b), .posix = (c) }
- +
- +BcStatus bc_lex_token(BcLex *l);
- +
- +#define BC_PARSE_LEAF(p, rparen) \
- + (((p) >= BC_INST_NUM && (p) <= BC_INST_SQRT) || (rparen) || \
- + (p) == BC_INST_INC_POST || (p) == BC_INST_DEC_POST)
- +
- +// We can calculate the conversion between tokens and exprs by subtracting the
- +// position of the first operator in the lex enum and adding the position of the
- +// first in the expr enum. Note: This only works for binary operators.
- +#define BC_PARSE_TOKEN_INST(t) ((char) ((t) - BC_LEX_NEG + BC_INST_NEG))
- +
- +BcStatus bc_parse_init(BcParse *p, struct BcProgram *prog, size_t func);
- +BcStatus bc_parse_expression(BcParse *p, uint8_t flags);
- +
- +BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next);
- +
- +#endif // CONFIG_BC
- +
- +#ifdef CONFIG_DC
- +
- +#define DC_PARSE_BUF_LEN ((int) (sizeof(uint32_t) * CHAR_BIT))
- +
- +BcStatus dc_main(int argc, char *argv[]);
- +
- +BcStatus dc_lex_token(BcLex *l);
- +
- +BcStatus dc_parse_init(BcParse *p, struct BcProgram *prog, size_t func);
- +BcStatus dc_parse_expr(BcParse *p, uint8_t flags);
- +
- +#endif // CONFIG_DC
- +
- +typedef struct BcProgram {
- +
- + size_t len;
- +
- + size_t scale;
- +
- + BcNum ib;
- + size_t ib_t;
- + BcNum ob;
- + size_t ob_t;
- +
- + BcNum hexb;
- +
- +#ifdef CONFIG_DC
- + BcNum strmb;
- +#endif // CONFIG_DC
- +
- + BcVec results;
- + BcVec stack;
- +
- + BcVec fns;
- + BcVec fn_map;
- +
- + BcVec vars;
- + BcVec var_map;
- +
- + BcVec arrs;
- + BcVec arr_map;
- +
- + BcVec strs;
- + BcVec consts;
- +
- + const char *file;
- +
- + BcNum last;
- + BcNum zero;
- + BcNum one;
- +
- + size_t nchars;
- +
- + BcParseInit parse_init;
- + BcParseExpr parse_expr;
- +
- +} BcProgram;
- +
- +#define BC_PROG_STACK(s, n) ((s)->len >= ((size_t) n))
- +
- +#define BC_PROG_MAIN (0)
- +#define BC_PROG_READ (1)
- +
- +#ifdef CONFIG_DC
- +#define BC_PROG_REQ_FUNCS (2)
- +#endif // CONFIG_DC
- +
- +#define BC_PROG_STR(n) (!(n)->num && !(n)->cap)
- +#define BC_PROG_NUM(r, n) \
- + ((r)->t != BC_RESULT_ARRAY && (r)->t != BC_RESULT_STR && !BC_PROG_STR(n))
- +
- +typedef unsigned long (*BcProgramBuiltIn)(BcNum*);
- +
- +BcStatus bc_program_init(BcProgram *p, size_t line_len,
- + BcParseInit init, BcParseExpr expr);
- +void bc_program_free(BcProgram *program);
- +
- +BcStatus bc_program_addFunc(BcProgram *p, char *name, size_t *idx);
- +BcStatus bc_program_reset(BcProgram *p, BcStatus s);
- +BcStatus bc_program_exec(BcProgram *p);
- +
- +#if !defined(CONFIG_BC) && !defined(CONFIG_DC)
- +#error Must define CONFIG_BC, CONFIG_DC, or both
- +#endif
- +
- +#define VERSION_STR(V) #V
- +#define VERSION_STR2(V) VERSION_STR(V)
- +#define BC_VERSION VERSION_STR2(VERSION)
- +
- +#define BC_FLAG_X (1<<0)
- +#define BC_FLAG_W (1<<1)
- +#define BC_FLAG_S (1<<2)
- +#define BC_FLAG_Q (1<<3)
- +#define BC_FLAG_L (1<<4)
- +#define BC_FLAG_I (1<<5)
- +
- +#define BC_MAX(a, b) ((a) > (b) ? (a) : (b))
- +#define BC_MIN(a, b) ((a) < (b) ? (a) : (b))
- +
- +#define BC_MAX_OBASE ((unsigned long) 999)
- +#define BC_MAX_DIM ((unsigned long) INT_MAX)
- +#define BC_MAX_SCALE ((unsigned long) UINT_MAX)
- +#define BC_MAX_STRING ((unsigned long) UINT_MAX - 1)
- +#define BC_MAX_NAME BC_MAX_STRING
- +#define BC_MAX_NUM BC_MAX_STRING
- +#define BC_MAX_EXP ((unsigned long) LONG_MAX)
- +#define BC_MAX_VARS ((unsigned long) SIZE_MAX - 1)
- +
- +typedef struct BcVmExe {
- +
- + BcParseInit init;
- + BcParseExpr exp;
- +
- + char sbgn;
- + char send;
- +
- +} BcVmExe;
- +
- +typedef struct BcVm {
- +
- + BcParse prs;
- + BcProgram prog;
- +
- + unsigned int flags;
- +
- + BcVec files;
- + BcVec exprs;
- +
- + char *env_args;
- +
- + BcVmExe exe;
- +
- +} BcVm;
- +
- +typedef struct BcGlobals {
- +
- + unsigned long sig;
- + unsigned long sigc;
- + unsigned long signe;
- + long sig_other;
- +
- + long tty;
- + long ttyin;
- +#ifdef CONFIG_BC
- + long posix;
- + long warn;
- +#endif // CONFIG_BC
- +#ifdef CONFIG_DC
- + long exreg;
- +#endif // CONFIG_DC
- +
- + const char *name;
- + const char *sig_msg;
- + // Busybox exclude start.
- + const char *help;
- + // Busybox exclude end.
- + bool bc;
- +
- +} BcGlobals;
- +
- +#ifdef CONFIG_BC
- +BcStatus bc_vm_posixError(BcStatus s, const char *file,
- + size_t line, const char *msg);
- +#endif // CONFIG_BC
- +
- +BcStatus bc_vm_info(const char* const help);
- +BcStatus bc_vm_run(int argc, char *argv[], BcVmExe exe, const char *env_len);
- +
- +#ifdef CONFIG_BC
- +
- +#endif // CONFIG_BC
- +
- +#ifdef CONFIG_DC
- +
- +#endif // CONFIG_DC
- +
- +BcGlobals bcg;
- +
- +#ifdef CONFIG_BC
- +
- +const char bc_name[] = "bc";
- +
- +const char bc_sig_msg[] = "\ninterrupt (type \"quit\" to exit)\n";
- +#endif // CONFIG_BC
- +
- +#ifdef CONFIG_DC
- +const char dc_name[] = "dc";
- +const char dc_sig_msg[] = "\ninterrupt (type \"q\" to exit)\n";
- +#endif // CONFIG_DC
- +
- +const char bc_copyright[] =
- + "Copyright (c) 2018 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_args_env_name = "BC_ENV_ARGS";
- +
- +const char bc_err_fmt[] = "\n%s error: %s\n";
- +const char bc_warn_fmt[] = "\n%s warning: %s\n";
- +const char bc_err_line[] = ":%zu\n\n";
- +
- +const char *bc_errs[] = {
- + "VM",
- + "Lex",
- + "Parse",
- + "Math",
- + "Runtime",
- + "Vector",
- +#ifdef CONFIG_BC
- + "POSIX",
- +#endif // CONFIG_BC
- +};
- +
- +const uint8_t bc_err_ids[] = {
- + BC_ERR_IDX_VM, BC_ERR_IDX_VM, BC_ERR_IDX_VM, BC_ERR_IDX_VM, BC_ERR_IDX_VM,
- + BC_ERR_IDX_LEX, BC_ERR_IDX_LEX, BC_ERR_IDX_LEX, BC_ERR_IDX_LEX,
- +#ifdef CONFIG_DC
- + BC_ERR_IDX_LEX,
- +#endif // CONFIG_DC
- + 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_MATH, BC_ERR_IDX_MATH, BC_ERR_IDX_MATH, BC_ERR_IDX_MATH,
- + BC_ERR_IDX_MATH,
- +#ifdef CONFIG_DC
- + BC_ERR_IDX_MATH,
- +#endif // CONFIG_DC
- + 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_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_VEC, BC_ERR_IDX_VEC,
- +#ifdef CONFIG_BC
- + BC_ERR_IDX_POSIX, BC_ERR_IDX_POSIX, BC_ERR_IDX_POSIX, BC_ERR_IDX_POSIX,
- + BC_ERR_IDX_POSIX, BC_ERR_IDX_POSIX, BC_ERR_IDX_POSIX, BC_ERR_IDX_POSIX,
- + BC_ERR_IDX_POSIX, BC_ERR_IDX_POSIX, BC_ERR_IDX_POSIX, BC_ERR_IDX_POSIX,
- +#endif // CONFIG_BC
- + BC_ERR_IDX_VM, BC_ERR_IDX_VM, BC_ERR_IDX_VM,
- +};
- +
- +const char *bc_err_msgs[] = {
- +
- + NULL,
- + "memory allocation error",
- + "I/O error",
- + "file is not text:",
- + "path is a directory:",
- +
- + "bad character",
- + "string end could not be found",
- + "comment end could not be found",
- + "end of file",
- +#ifdef CONFIG_DC
- + "extended register",
- +#endif // CONFIG_DC
- +
- + "bad token",
- + "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 var has the same name as another",
- + "block end could not be found",
- +
- + "negative number",
- + "non integer number",
- + "overflow",
- + "divide by zero",
- + "bad number string",
- +
- + "could not open file:",
- + "mismatched parameters",
- + "undefined function",
- + "file is not executable:",
- + "could not install signal handler",
- + "number too long: must be [1, BC_NUM_MAX]",
- + "name too long: must be [1, BC_NAME_MAX]",
- + "string too long: must be [1, BC_STRING_MAX]",
- + "array too long; must be [1, BC_DIM_MAX]",
- + "bad ibase; must be [2, 16]",
- + "bad scale; must be [0, BC_SCALE_MAX]",
- + "bad read() expression",
- + "read() call inside of a read() call",
- + "variable is wrong type",
- + "bad obase; must be [2, BC_BASE_MAX]",
- + "signal caught and not handled",
- + "stack has too few elements",
- +
- + "index is out of bounds",
- + "item already exists",
- +
- +#ifdef CONFIG_BC
- + "POSIX only allows one character names; the following is bad:",
- + "POSIX does not allow '#' script comments",
- + "POSIX does not allow the following keyword:",
- + "POSIX does not allow a period ('.') as a shortcut for the last result",
- + "POSIX requires parentheses around return expressions",
- + "POSIX does not allow boolean operators; the following is bad:",
- + "POSIX does not allow comparison operators outside if or loops",
- + "POSIX requires exactly one comparison operator per condition",
- + "POSIX does not allow an empty init expression in a for loop",
- + "POSIX does not allow an empty condition expression in a for loop",
- + "POSIX does not allow an empty update expression in a for loop",
- + "POSIX requires the left brace be on the same line as the function header",
- +#endif // CONFIG_BC
- +
- +};
- +
- +const char bc_func_main[] = "(main)";
- +const char bc_func_read[] = "(read)";
- +
- +#ifdef CONFIG_BC
- +const BcLexKeyword bc_lex_kws[20] = {
- + 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("else", 4, false),
- + BC_LEX_KW_ENTRY("for", 3, true),
- + BC_LEX_KW_ENTRY("halt", 4, false),
- + BC_LEX_KW_ENTRY("ibase", 5, true),
- + BC_LEX_KW_ENTRY("if", 2, true),
- + BC_LEX_KW_ENTRY("last", 4, false),
- + BC_LEX_KW_ENTRY("length", 6, true),
- + BC_LEX_KW_ENTRY("limits", 6, false),
- + BC_LEX_KW_ENTRY("obase", 5, true),
- + BC_LEX_KW_ENTRY("print", 5, false),
- + BC_LEX_KW_ENTRY("quit", 4, true),
- + BC_LEX_KW_ENTRY("read", 4, false),
- + BC_LEX_KW_ENTRY("return", 6, true),
- + BC_LEX_KW_ENTRY("scale", 5, true),
- + BC_LEX_KW_ENTRY("sqrt", 4, true),
- + BC_LEX_KW_ENTRY("while", 5, true),
- +};
- +
- +// This is an array that corresponds to token types. An entry is
- +// true if the token is valid in an expression, false otherwise.
- +const bool bc_parse_exprs[] = {
- + false, false, true, true, true, true, true, true, true, true, true, true,
- + true, true, true, true, true, true, true, true, true, true, true, true,
- + true, true, true, false, false, true, true, false, false, false, false,
- + false, false, false, true, true, false, false, false, false, false, false,
- + false, true, false, true, true, true, true, false, false, true, false, true,
- + true, false,
- +};
- +
- +// This is an array of data for operators that correspond to token types.
- +const BcOp bc_parse_ops[] = {
- + { 0, false }, { 0, false },
- + { 1, false },
- + { 2, false },
- + { 3, true }, { 3, true }, { 3, true },
- + { 4, true }, { 4, true },
- + { 6, true }, { 6, true }, { 6, true }, { 6, true }, { 6, true }, { 6, true },
- + { 1, false },
- + { 7, true }, { 7, true },
- + { 5, false }, { 5, false }, { 5, false }, { 5, false }, { 5, false },
- + { 5, false }, { 5, false },
- +};
- +
- +// These identify what tokens can come after expressions in certain cases.
- +const BcParseNext bc_parse_next_expr =
- + BC_PARSE_NEXT(3, BC_LEX_NLINE, BC_LEX_SCOLON, BC_LEX_RBRACE);
- +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(3, BC_LEX_COMMA, BC_LEX_NLINE, BC_LEX_SCOLON);
- +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(1, BC_LEX_NLINE);
- +#endif // CONFIG_BC
- +
- +#ifdef CONFIG_DC
- +const BcLexType dc_lex_regs[] = {
- + BC_LEX_OP_REL_EQ, BC_LEX_OP_REL_LE, BC_LEX_OP_REL_GE, BC_LEX_OP_REL_NE,
- + BC_LEX_OP_REL_LT, BC_LEX_OP_REL_GT, BC_LEX_SCOLON, BC_LEX_COLON,
- + BC_LEX_ELSE, BC_LEX_LOAD, BC_LEX_LOAD_POP, BC_LEX_OP_ASSIGN,
- + BC_LEX_STORE_PUSH,
- +};
- +
- +const size_t dc_lex_regs_len = sizeof(dc_lex_regs) / sizeof(BcLexType);
- +
- +const BcLexType dc_lex_tokens[] = {
- + BC_LEX_OP_MODULUS, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_LPAREN,
- + BC_LEX_INVALID, BC_LEX_OP_MULTIPLY, BC_LEX_OP_PLUS, BC_LEX_INVALID,
- + BC_LEX_OP_MINUS, BC_LEX_INVALID, BC_LEX_OP_DIVIDE,
- + BC_LEX_INVALID, 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_KEY_READ, BC_LEX_INVALID,
- + BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID,
- + BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_EQ_NO_REG, BC_LEX_INVALID,
- + BC_LEX_KEY_IBASE, BC_LEX_INVALID, BC_LEX_KEY_SCALE, BC_LEX_LOAD_POP,
- + BC_LEX_INVALID, BC_LEX_OP_BOOL_NOT, BC_LEX_KEY_OBASE, BC_LEX_PRINT_STREAM,
- + BC_LEX_NQUIT, BC_LEX_POP, BC_LEX_STORE_PUSH, BC_LEX_INVALID, BC_LEX_INVALID,
- + BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_SCALE_FACTOR, BC_LEX_INVALID,
- + BC_LEX_KEY_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_INVALID, BC_LEX_CLEAR_STACK, BC_LEX_DUPLICATE,
- + BC_LEX_ELSE, BC_LEX_PRINT_STACK, BC_LEX_INVALID, BC_LEX_INVALID,
- + 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_KEY_PRINT,
- + BC_LEX_KEY_QUIT, BC_LEX_SWAP, BC_LEX_OP_ASSIGN, BC_LEX_INVALID,
- + BC_LEX_INVALID, BC_LEX_KEY_SQRT, BC_LEX_INVALID, BC_LEX_EXECUTE,
- + BC_LEX_INVALID, BC_LEX_STACK_LEVEL,
- + BC_LEX_LBRACE, BC_LEX_OP_MODEXP, BC_LEX_INVALID, BC_LEX_OP_DIVMOD,
- + BC_LEX_INVALID
- +};
- +
- +const BcInst dc_parse_insts[] = {
- + BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE,
- + BC_INST_INVALID, BC_INST_POWER, BC_INST_MULTIPLY, BC_INST_DIVIDE,
- + BC_INST_MODULUS, BC_INST_PLUS, BC_INST_MINUS,
- + BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID,
- + BC_INST_INVALID, BC_INST_INVALID,
- + BC_INST_BOOL_NOT, 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, BC_INST_REL_GT, BC_INST_INVALID,
- + BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE,
- + 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, BC_INST_INVALID, BC_INST_IBASE,
- + BC_INST_INVALID, BC_INST_INVALID, BC_INST_LENGTH, BC_INST_INVALID,
- + BC_INST_OBASE, BC_INST_PRINT, BC_INST_QUIT, BC_INST_INVALID,
- + BC_INST_INVALID, BC_INST_SCALE, BC_INST_SQRT, BC_INST_INVALID,
- + BC_INST_REL_EQ, BC_INST_MODEXP, BC_INST_DIVMOD, BC_INST_INVALID,
- + 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, BC_INST_NQUIT, BC_INST_SCALE_FUNC,
- +};
- +#endif // CONFIG_DC
- +
- +const char bc_num_hex_digits[] = "0123456789ABCDEF";
- +
- +const BcNumBinaryOp bc_program_ops[] = {
- + bc_num_pow, bc_num_mul, bc_num_div, bc_num_mod, bc_num_add, bc_num_sub,
- +};
- +
- +const char bc_program_exprs_name[] = "<exprs>";
- +
- +const char bc_program_stdin_name[] = "<stdin>";
- +const char bc_program_ready_msg[] = "ready for more input\n";
- +#ifdef CONFIG_BC
- +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,9,97,117,116,111,32,98,44,115,44,110,44,114,44,100,44,105,44,112,44,102,
- + 44,118,10,9,98,61,105,98,97,115,101,10,9,105,98,97,115,101,61,65,10,9,105,102,
- + 40,120,60,48,41,123,10,9,9,110,61,49,10,9,9,120,61,45,120,10,9,125,10,9,115,
- + 61,115,99,97,108,101,10,9,114,61,54,43,115,43,48,46,52,52,42,120,10,9,115,99,
- + 97,108,101,61,115,99,97,108,101,40,120,41,43,49,10,9,119,104,105,108,101,40,
- + 120,62,49,41,123,10,9,9,100,43,61,49,10,9,9,120,47,61,50,10,9,9,115,99,97,108,
- + 101,43,61,49,10,9,125,10,9,115,99,97,108,101,61,114,10,9,114,61,120,43,49,10,
- + 9,112,61,120,10,9,102,61,118,61,49,10,9,102,111,114,40,105,61,50,59,118,33,
- + 61,48,59,43,43,105,41,123,10,9,9,112,42,61,120,10,9,9,102,42,61,105,10,9,9,
- + 118,61,112,47,102,10,9,9,114,43,61,118,10,9,125,10,9,119,104,105,108,101,40,
- + 40,100,45,45,41,33,61,48,41,114,42,61,114,10,9,115,99,97,108,101,61,115,10,
- + 9,105,98,97,115,101,61,98,10,9,105,102,40,110,33,61,48,41,114,101,116,117,114,
- + 110,40,49,47,114,41,10,9,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,9,97,117,116,111,32,98,44,115,
- + 44,114,44,112,44,97,44,113,44,105,44,118,10,9,98,61,105,98,97,115,101,10,9,
- + 105,98,97,115,101,61,65,10,9,105,102,40,120,60,61,48,41,123,10,9,9,114,61,40,
- + 49,45,49,48,94,115,99,97,108,101,41,47,49,10,9,9,105,98,97,115,101,61,98,10,
- + 9,9,114,101,116,117,114,110,40,114,41,10,9,125,10,9,115,61,115,99,97,108,101,
- + 10,9,115,99,97,108,101,43,61,54,10,9,112,61,50,10,9,119,104,105,108,101,40,
- + 120,62,61,50,41,123,10,9,9,112,42,61,50,10,9,9,120,61,115,113,114,116,40,120,
- + 41,10,9,125,10,9,119,104,105,108,101,40,120,60,61,48,46,53,41,123,10,9,9,112,
- + 42,61,50,10,9,9,120,61,115,113,114,116,40,120,41,10,9,125,10,9,114,61,97,61,
- + 40,120,45,49,41,47,40,120,43,49,41,10,9,113,61,97,42,97,10,9,118,61,49,10,9,
- + 102,111,114,40,105,61,51,59,118,33,61,48,59,105,43,61,50,41,123,10,9,9,97,42,
- + 61,113,10,9,9,118,61,97,47,105,10,9,9,114,43,61,118,10,9,125,10,9,114,42,61,
- + 112,10,9,115,99,97,108,101,61,115,10,9,105,98,97,115,101,61,98,10,9,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,9,97,117,116,111,32,98,44,115,44,114,44,110,44,97,44,113,44,105,
- + 10,9,98,61,105,98,97,115,101,10,9,105,98,97,115,101,61,65,10,9,115,61,115,99,
- + 97,108,101,10,9,115,99,97,108,101,61,49,46,49,42,115,43,50,10,9,97,61,97,40,
- + 49,41,10,9,105,102,40,120,60,48,41,123,10,9,9,110,61,49,10,9,9,120,61,45,120,
- + 10,9,125,10,9,115,99,97,108,101,61,48,10,9,113,61,40,120,47,97,43,50,41,47,
- + 52,10,9,120,61,120,45,52,42,113,42,97,10,9,105,102,40,113,37,50,33,61,48,41,
- + 120,61,45,120,10,9,115,99,97,108,101,61,115,43,50,10,9,114,61,97,61,120,10,
- + 9,113,61,45,120,42,120,10,9,102,111,114,40,105,61,51,59,97,33,61,48,59,105,
- + 43,61,50,41,123,10,9,9,97,42,61,113,47,40,105,42,40,105,45,49,41,41,10,9,9,
- + 114,43,61,97,10,9,125,10,9,115,99,97,108,101,61,115,10,9,105,98,97,115,101,
- + 61,98,10,9,105,102,40,110,33,61,48,41,114,101,116,117,114,110,40,45,114,47,
- + 49,41,10,9,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,9,97,117,116,111,32,98,44,115,10,9,98,61,105,
- + 98,97,115,101,10,9,105,98,97,115,101,61,65,10,9,115,61,115,99,97,108,101,10,
- + 9,115,99,97,108,101,42,61,49,46,50,10,9,120,61,115,40,50,42,97,40,49,41,43,
- + 120,41,10,9,115,99,97,108,101,61,115,10,9,105,98,97,115,101,61,98,10,9,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,9,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,9,98,61,105,98,97,115,101,10,9,105,98,97,115,101,
- + 61,65,10,9,110,61,49,10,9,105,102,40,120,60,48,41,123,10,9,9,110,61,45,49,10,
- + 9,9,120,61,45,120,10,9,125,10,9,105,102,40,120,61,61,49,41,123,10,9,9,105,102,
- + 40,115,99,97,108,101,60,54,53,41,123,10,9,9,9,114,101,116,117,114,110,40,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,41,10,9,9,125,10,9,125,10,
- + 9,105,102,40,120,61,61,46,50,41,123,10,9,9,105,102,40,115,99,97,108,101,60,
- + 54,53,41,123,10,9,9,9,114,101,116,117,114,110,40,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,41,10,9,9,125,10,9,125,10,9,115,61,115,99,97,108,101,
- + 10,9,105,102,40,120,62,46,50,41,123,10,9,9,115,99,97,108,101,43,61,53,10,9,
- + 9,97,61,97,40,46,50,41,10,9,125,10,9,115,99,97,108,101,61,115,43,51,10,9,119,
- + 104,105,108,101,40,120,62,46,50,41,123,10,9,9,109,43,61,49,10,9,9,120,61,40,
- + 120,45,46,50,41,47,40,49,43,46,50,42,120,41,10,9,125,10,9,114,61,117,61,120,
- + 10,9,102,61,45,120,42,120,10,9,116,61,49,10,9,102,111,114,40,105,61,51,59,116,
- + 33,61,48,59,105,43,61,50,41,123,10,9,9,117,42,61,102,10,9,9,116,61,117,47,105,
- + 10,9,9,114,43,61,116,10,9,125,10,9,115,99,97,108,101,61,115,10,9,105,98,97,
- + 115,101,61,98,10,9,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,9,97,117,
- + 116,111,32,98,44,115,44,111,44,97,44,105,44,118,44,102,10,9,98,61,105,98,97,
- + 115,101,10,9,105,98,97,115,101,61,65,10,9,115,61,115,99,97,108,101,10,9,115,
- + 99,97,108,101,61,48,10,9,110,47,61,49,10,9,105,102,40,110,60,48,41,123,10,9,
- + 9,110,61,45,110,10,9,9,105,102,40,110,37,50,61,61,49,41,111,61,49,10,9,125,
- + 10,9,97,61,49,10,9,102,111,114,40,105,61,50,59,105,60,61,110,59,43,43,105,41,
- + 97,42,61,105,10,9,115,99,97,108,101,61,49,46,53,42,115,10,9,97,61,40,120,94,
- + 110,41,47,50,94,110,47,97,10,9,114,61,118,61,49,10,9,102,61,45,120,42,120,47,
- + 52,10,9,115,99,97,108,101,61,115,99,97,108,101,43,108,101,110,103,116,104,40,
- + 97,41,45,115,99,97,108,101,40,97,41,10,9,102,111,114,40,105,61,49,59,118,33,
- + 61,48,59,43,43,105,41,123,10,9,9,118,61,118,42,102,47,105,47,40,110,43,105,
- + 41,10,9,9,114,43,61,118,10,9,125,10,9,115,99,97,108,101,61,115,10,9,105,98,
- + 97,115,101,61,98,10,9,105,102,40,111,33,61,48,41,97,61,45,97,10,9,114,101,116,
- + 117,114,110,40,97,42,114,47,49,41,10,125,10,0
- +};
- +#endif // CONFIG_BC
- +
- +static BcStatus bc_vec_grow(BcVec *v, size_t n) {
- +
- + char *ptr;
- + size_t cap = v->cap * 2;
- +
- + while (cap < v->len + n) cap *= 2;
- + if (!(ptr = realloc(v->v, v->size * cap))) return BC_STATUS_ALLOC_ERR;
- +
- + v->v = ptr;
- + v->cap = cap;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_vec_init(BcVec *v, size_t esize, BcVecFree dtor) {
- +
- + v->size = esize;
- + v->cap = BC_VEC_START_CAP;
- + v->len = 0;
- + v->dtor = dtor;
- +
- + if (!(v->v = malloc(esize * BC_VEC_START_CAP))) return BC_STATUS_ALLOC_ERR;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_vec_expand(BcVec *v, size_t req) {
- +
- + char *ptr;
- +
- + if (v->cap >= req) return BC_STATUS_SUCCESS;
- + if (!(ptr = realloc(v->v, v->size * req))) return BC_STATUS_ALLOC_ERR;
- +
- + v->v = ptr;
- + v->cap = req;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +void bc_vec_npop(BcVec *v, size_t n) {
- + if (!v->dtor) v->len -= n;
- + else {
- + size_t len = v->len - n;
- + while (v->len > len) v->dtor(v->v + (v->size * --v->len));
- + }
- +}
- +
- +BcStatus bc_vec_push(BcVec *v, const void *data) {
- +
- + BcStatus s;
- +
- + if (v->len + 1 > v->cap && (s = bc_vec_grow(v, 1))) return s;
- +
- + memmove(v->v + (v->size * v->len), data, v->size);
- + v->len += 1;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_vec_pushByte(BcVec *v, uint8_t data) {
- + return bc_vec_push(v, &data);
- +}
- +
- +static BcStatus bc_vec_pushAt(BcVec *v, const void *data, size_t idx) {
- +
- + BcStatus s;
- + char *ptr;
- +
- + if (idx == v->len) return bc_vec_push(v, data);
- + if (v->len == v->cap && (s = bc_vec_grow(v, 1))) return s;
- +
- + ptr = v->v + v->size * idx;
- +
- + memmove(ptr + v->size, ptr, v->size * (v->len++ - idx));
- + memmove(ptr, data, v->size);
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_vec_string(BcVec *v, size_t len, const char *str) {
- +
- + BcStatus s;
- +
- + bc_vec_npop(v, v->len);
- + if ((s = bc_vec_expand(v, len + 1))) return s;
- + memcpy(v->v, str, len);
- +
- + v->len = len;
- +
- + return bc_vec_pushByte(v, '\0');
- +}
- +
- +BcStatus bc_vec_concat(BcVec *v, const char *str) {
- +
- + BcStatus s;
- + size_t len;
- +
- + if (!v->len && (s = bc_vec_pushByte(v, '\0'))) return s;
- +
- + len = v->len + strlen(str);
- +
- + if (v->cap < len && (s = bc_vec_grow(v, len - v->len))) return s;
- + strcat(v->v, str);
- +
- + v->len = len;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +void* bc_vec_item(const BcVec *v, size_t idx) {
- + return v->v + v->size * idx;
- +}
- +
- +void* bc_vec_item_rev(const BcVec *v, size_t idx) {
- + 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_veco_find(const BcVec *v, const void *ptr) {
- +
- + size_t low = 0, high = v->len;
- +
- + while (low < high) {
- +
- + size_t mid = (low + high) / 2;
- + uint8_t *item = bc_vec_item(v, mid);
- + int result = bc_id_cmp(ptr, item);
- +
- + if (!result) return mid;
- + else if (result < 0) high = mid;
- + else low = mid + 1;
- + }
- +
- + return low;
- +}
- +
- +BcStatus bc_map_insert(BcVec *v, const void *ptr, size_t *i) {
- +
- + BcStatus s;
- +
- + if ((*i = bc_veco_find(v, ptr)) > v->len) s = BC_STATUS_VEC_OUT_OF_BOUNDS;
- + else if (*i == v->len) s = bc_vec_push(v, ptr);
- + else if (!bc_id_cmp(ptr, bc_vec_item(v, *i))) s = BC_STATUS_VEC_ITEM_EXISTS;
- + else s = bc_vec_pushAt(v, ptr, *i);
- +
- + return s;
- +}
- +
- +size_t bc_map_index(const BcVec* v, const void *ptr) {
- + size_t i = bc_veco_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' },
- + { "mathlib", no_argument, NULL, 'l' },
- + { "quiet", no_argument, NULL, 'q' },
- + { "standard", no_argument, NULL, 's' },
- + { "version", no_argument, NULL, 'v' },
- + { "warn", no_argument, NULL, 'w' },
- + { "extended-register", no_argument, NULL, 'x' },
- + { 0, 0, 0, 0 },
- +
- +};
- +
- +static const char* const bc_args_opt = "e:f:hilqsvVwx";
- +
- +static BcStatus bc_args_exprs(BcVec *exprs, const char *str) {
- + BcStatus s;
- + if ((s = bc_vec_concat(exprs, str))) return s;
- + return bc_vec_concat(exprs, "\n");
- +}
- +
- +static BcStatus bc_args_file(BcVec *exprs, const char *file) {
- +
- + BcStatus s;
- + char *buf;
- +
- + if ((s = bc_read_file(file, &buf))) return s;
- + s = bc_args_exprs(exprs, buf);
- +
- + free(buf);
- +
- + return s;
- +}
- +
- +BcStatus bc_args(int argc, char *argv[], unsigned int *flags,
- + BcVec *exprs, BcVec *files)
- +{
- + BcStatus s = BC_STATUS_SUCCESS;
- + int c, i, idx;
- + bool do_exit = false;
- +
- + idx = c = optind = 0;
- +
- + while ((c = getopt_long(argc, argv, bc_args_opt, bc_args_lopt, &idx)) != -1)
- + {
- + switch (c) {
- +
- + case 0:
- + {
- + // This is the case when a long option is found.
- + break;
- + }
- +
- + case 'e':
- + {
- + if ((s = bc_args_exprs(exprs, optarg))) return s;
- + break;
- + }
- +
- + case 'f':
- + {
- + if ((s = bc_args_file(exprs, optarg))) return s;
- + break;
- + }
- +
- + case 'h':
- + {
- + if ((s = bc_vm_info(bcg.help))) return s;
- + do_exit = true;
- + break;
- + }
- +
- +#ifdef CONFIG_BC
- + case 'i':
- + {
- + if (!bcg.bc) return BC_STATUS_INVALID_OPTION;
- + (*flags) |= BC_FLAG_I;
- + break;
- + }
- +
- + case 'l':
- + {
- + if (!bcg.bc) return BC_STATUS_INVALID_OPTION;
- + (*flags) |= BC_FLAG_L;
- + break;
- + }
- +
- + case 'q':
- + {
- + if (!bcg.bc) return BC_STATUS_INVALID_OPTION;
- + (*flags) |= BC_FLAG_Q;
- + break;
- + }
- +
- + case 's':
- + {
- + if (!bcg.bc) return BC_STATUS_INVALID_OPTION;
- + (*flags) |= BC_FLAG_S;
- + break;
- + }
- +
- + case 'w':
- + {
- + if (!bcg.bc) return BC_STATUS_INVALID_OPTION;
- + (*flags) |= BC_FLAG_W;
- + break;
- + }
- +#endif // CONFIG_BC
- +
- + case 'V':
- + case 'v':
- + {
- + if ((s = bc_vm_info(NULL))) return s;
- + do_exit = true;
- + break;
- + }
- +
- +#ifdef CONFIG_DC
- + case 'x':
- + {
- + if (bcg.bc) return BC_STATUS_INVALID_OPTION;
- + (*flags) |= BC_FLAG_X;
- + break;
- + }
- +#endif // CONFIG_DC
- +
- + // Getopt printed an error message, but we should exit.
- + case '?':
- + default:
- + {
- + return BC_STATUS_INVALID_OPTION;
- + }
- + }
- + }
- +
- + if (do_exit) exit((int) s);
- + if (exprs->len > 1 || !bcg.bc) (*flags) |= BC_FLAG_Q;
- + if (argv[optind] && strcmp(argv[optind], "--") == 0) ++optind;
- +
- + for (i = optind; !s && i < argc; ++i) s = bc_vec_push(files, argv + i);
- +
- + return s;
- +}
- +
- +BcStatus bc_read_line(BcVec *vec, const char* prompt) {
- +
- + BcStatus s;
- + int i;
- + signed char c = 0;
- +
- + if (bcg.ttyin && (fputs(prompt, stderr) == EOF || fflush(stderr) == EOF))
- + return BC_STATUS_IO_ERR;
- +
- + bc_vec_npop(vec, vec->len);
- +
- + while (c != '\n') {
- +
- + if ((i = fgetc(stdin)) == EOF) {
- +
- + if (errno != EINTR) return BC_STATUS_IO_ERR;
- +
- + bcg.sigc = bcg.sig;
- + bcg.signe = 0;
- +
- + if (bcg.ttyin && (fputs(bc_program_ready_msg, stderr) == EOF ||
- + fputs(prompt, stderr) == EOF ||
- + fflush(stderr) == EOF))
- + {
- + return BC_STATUS_IO_ERR;
- + }
- +
- + continue;
- + }
- +
- + if (i > UCHAR_MAX) return BC_STATUS_LEX_BAD_CHAR;
- +
- + c = (char) i;
- + if (BC_IO_BIN_CHAR(c)) return BC_STATUS_BIN_FILE;
- + if ((s = bc_vec_push(vec, &c))) return s;
- + }
- +
- + return bc_vec_pushByte(vec, '\0');
- +}
- +
- +BcStatus bc_read_file(const char *path, char **buf) {
- +
- + BcStatus s = BC_STATUS_IO_ERR;
- + FILE *f;
- + size_t size, read;
- + long res;
- + struct stat pstat;
- +
- + if (!(f = fopen(path, "r"))) return BC_STATUS_EXEC_FILE_ERR;
- +
- + if (fstat(fileno(f), &pstat) == -1) goto malloc_err;
- +
- + if (S_ISDIR(pstat.st_mode)) {
- + s = BC_STATUS_PATH_IS_DIR;
- + goto malloc_err;
- + }
- +
- + if (fseek(f, 0, SEEK_END) == -1) goto malloc_err;
- + if ((res = ftell(f)) < 0) goto malloc_err;
- + if (fseek(f, 0, SEEK_SET) == -1) goto malloc_err;
- +
- + if (!(*buf = malloc((size = (size_t) res) + 1))) {
- + s = BC_STATUS_ALLOC_ERR;
- + goto malloc_err;
- + }
- +
- + if ((read = fread(*buf, 1, size, f)) != size) goto read_err;
- +
- + (*buf)[size] = '\0';
- + s = BC_STATUS_BIN_FILE;
- +
- + for (read = 0; read < size; ++read) {
- + if (BC_IO_BIN_CHAR((*buf)[read])) goto read_err;
- + }
- +
- + fclose(f);
- +
- + return BC_STATUS_SUCCESS;
- +
- +read_err:
- + free(*buf);
- +malloc_err:
- + fclose(f);
- + return s;
- +}
- +
- +static void bc_num_setToZero(BcNum *n, size_t scale) {
- + n->neg = (n->len = 0);
- + n->rdx = scale;
- +}
- +
- +void bc_num_zero(BcNum *n) {
- + bc_num_setToZero(n, 0);
- +}
- +
- +void bc_num_one(BcNum *n) {
- + bc_num_setToZero(n, 0);
- + n->len = 1;
- + n->num[0] = 1;
- +}
- +
- +void bc_num_ten(BcNum *n) {
- + bc_num_setToZero(n, 0);
- + n->len = 2;
- + n->num[0] = 0;
- + n->num[1] = 1;
- +}
- +
- +static BcStatus bc_num_subArrays(BcDig *restrict a, BcDig *restrict b,
- + size_t len)
- +{
- + size_t i, j;
- + for (i = 0; !bcg.signe && i < len; ++i) {
- + for (a[i] -= b[i], j = 0; !bcg.signe && a[i + j] < 0;) {
- + a[i + j++] += 10;
- + a[i + j] -= 1;
- + }
- + }
- + return bcg.signe ? BC_STATUS_EXEC_SIGNAL : BC_STATUS_SUCCESS;
- +}
- +
- +static ssize_t bc_num_compare(BcDig *restrict a, BcDig *restrict b, size_t len)
- +{
- + size_t i;
- + int c = 0;
- + for (i = len - 1; !bcg.signe && i < len && !(c = a[i] - b[i]); --i);
- + return BC_NUM_NEG(i + 1, c < 0);
- +}
- +
- +ssize_t bc_num_cmp(BcNum *a, BcNum *b) {
- +
- + size_t i, min, a_int, b_int, diff;
- + BcDig *max_num, *min_num;
- + bool a_max, neg = false;
- + ssize_t cmp;
- +
- + if (a == b) return 0;
- + else if (!a->len) return BC_NUM_NEG(!!b->len, !b->neg);
- + else if (!b->len) return BC_NUM_NEG(1, a->neg);
- + else 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);
- +
- + if ((a_int -= b_int)) return (ssize_t) a_int;
- +
- + if ((a_max = (a->rdx > b->rdx))) {
- + 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;
- + }
- +
- + if ((cmp = bc_num_compare(max_num, min_num, b_int + min)))
- + return BC_NUM_NEG(cmp, (!a_max) != neg);
- +
- + for (max_num -= diff, i = diff - 1; !bcg.signe && i < diff; --i) {
- + if (max_num[i]) return BC_NUM_NEG(1, (!a_max) != neg);
- + }
- +
- + return 0;
- +}
- +
- +void bc_num_truncate(BcNum *n, size_t places) {
- +
- + if (!places) return;
- +
- + n->rdx -= places;
- +
- + if (n->len) {
- + n->len -= places;
- + memmove(n->num, n->num + places, n->len * sizeof(BcDig));
- + }
- +}
- +
- +static BcStatus bc_num_extend(BcNum *n, size_t places) {
- +
- + BcStatus s;
- + size_t len = n->len + places;
- +
- + if (!places) return BC_STATUS_SUCCESS;
- + if (n->cap < len && (s = bc_num_expand(n, len))) return s;
- +
- + memmove(n->num + places, n->num, sizeof(BcDig) * n->len);
- + memset(n->num, 0, sizeof(BcDig) * places);
- +
- + n->len += places;
- + n->rdx += places;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +static void bc_num_clean(BcNum *n) {
- + while (n->len > 0 && !n->num[n->len - 1]) --n->len;
- + if (!n->len) n->neg = false;
- + else if (n->len < n->rdx) n->len = n->rdx;
- +}
- +
- +static BcStatus bc_num_retireMul(BcNum *n, size_t scale, bool neg1, bool neg2) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- +
- + if (n->rdx < scale) s = bc_num_extend(n, scale - n->rdx);
- + else bc_num_truncate(n, n->rdx - scale);
- +
- + bc_num_clean(n);
- + if (n->len) n->neg = !neg1 != !neg2;
- +
- + return s;
- +}
- +
- +static BcStatus bc_num_split(BcNum *restrict n, size_t idx, BcNum *restrict a,
- + BcNum *restrict b)
- +{
- + BcStatus s = BC_STATUS_SUCCESS;
- +
- + if (idx < n->len) {
- +
- + b->len = n->len - idx;
- + a->len = idx;
- + a->rdx = b->rdx = 0;
- +
- + memcpy(b->num, n->num + idx, b->len * sizeof(BcDig));
- + memcpy(a->num, n->num, idx * sizeof(BcDig));
- + }
- + else {
- + bc_num_zero(b);
- + s = bc_num_copy(a, n);
- + }
- +
- + bc_num_clean(a);
- + bc_num_clean(b);
- +
- + return s;
- +}
- +
- +static BcStatus bc_num_shift(BcNum *n, size_t places) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- +
- + if (!places || !n->len) return BC_STATUS_SUCCESS;
- + if (places + n->len > BC_MAX_NUM) return BC_STATUS_EXEC_NUM_LEN;
- +
- + if (n->rdx >= places) n->rdx -= places;
- + else {
- + s = bc_num_extend(n, places - n->rdx);
- + n->rdx = 0;
- + }
- +
- + bc_num_clean(n);
- +
- + return s;
- +}
- +
- +static BcStatus bc_num_inv(BcNum *a, BcNum *b, size_t scale) {
- +
- + BcNum one;
- + BcDig num[2];
- +
- + one.cap = 2;
- + one.num = num;
- + bc_num_one(&one);
- +
- + return bc_num_div(&one, a, b, scale);
- +}
- +
- +static BcStatus bc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) {
- +
- + BcDig *ptr, *ptr_a, *ptr_b, *ptr_c;
- + size_t i, max, min_rdx, min_int, diff, a_int, b_int;
- + int carry, in;
- +
- + // 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.
- +
- + if (!a->len) {
- + BcStatus s = bc_num_copy(c, b);
- + if (sub && c->len) c->neg = !c->neg;
- + return s;
- + }
- + else if (!b->len) return bc_num_copy(c, a);
- +
- + c->neg = a->neg;
- + c->rdx = BC_MAX(a->rdx, b->rdx);
- + min_rdx = BC_MIN(a->rdx, b->rdx);
- + c->len = 0;
- +
- + if (a->rdx > b->rdx) {
- + diff = a->rdx - b->rdx;
- + ptr = a->num;
- + ptr_a = a->num + diff;
- + ptr_b = b->num;
- + }
- + else {
- + diff = b->rdx - a->rdx;
- + ptr = b->num;
- + ptr_a = a->num;
- + ptr_b = b->num + diff;
- + }
- +
- + for (ptr_c = c->num, i = 0; i < diff; ++i, ++c->len) ptr_c[i] = ptr[i];
- +
- + ptr_c += diff;
- +
- + if ((a_int = BC_NUM_INT(a)) > (b_int = BC_NUM_INT(b))) {
- + min_int = b_int;
- + max = a_int;
- + ptr = ptr_a;
- + }
- + else {
- + min_int = a_int;
- + max = b_int;
- + ptr = ptr_b;
- + }
- +
- + for (carry = 0, i = 0; !bcg.signe && i < min_rdx + min_int; ++i, ++c->len) {
- + in = ((int) ptr_a[i]) + ((int) ptr_b[i]) + carry;
- + carry = in / 10;
- + ptr_c[i] = (BcDig) (in % 10);
- + }
- +
- + for (; !bcg.signe && i < max + min_rdx; ++i, ++c->len) {
- + in = ((int) ptr[i]) + carry;
- + carry = in / 10;
- + ptr_c[i] = (BcDig) (in % 10);
- + }
- +
- + if (bcg.signe) return BC_STATUS_EXEC_SIGNAL;
- +
- + if (carry) c->num[c->len++] = (BcDig) carry;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +static BcStatus bc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) {
- +
- + BcStatus s;
- + ssize_t cmp;
- + BcNum *minuend, *subtrahend;
- + size_t start;
- + bool aneg, bneg, neg;
- +
- + // 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.
- +
- + if (!a->len) {
- + s = bc_num_copy(c, b);
- + if (sub && c->len) c->neg = !c->neg;
- + return s;
- + }
- + else if (!b->len) return bc_num_copy(c, a);
- +
- + aneg = a->neg;
- + bneg = b->neg;
- + a->neg = b->neg = false;
- +
- + cmp = bc_num_cmp(a, b);
- +
- + a->neg = aneg;
- + b->neg = bneg;
- +
- + if (!cmp) {
- + bc_num_setToZero(c, BC_MAX(a->rdx, b->rdx));
- + return BC_STATUS_SUCCESS;
- + }
- + else if (cmp > 0) {
- + neg = a->neg;
- + minuend = a;
- + subtrahend = b;
- + }
- + else {
- + neg = b->neg;
- + if (sub) neg = !neg;
- + minuend = b;
- + subtrahend = a;
- + }
- +
- + if ((s = bc_num_copy(c, minuend))) return s;
- + c->neg = neg;
- +
- + if (c->rdx < subtrahend->rdx) {
- + if ((s = bc_num_extend(c, subtrahend->rdx - c->rdx))) return s;
- + start = 0;
- + }
- + else start = c->rdx - subtrahend->rdx;
- +
- + s = bc_num_subArrays(c->num + start, subtrahend->num, subtrahend->len);
- +
- + bc_num_clean(c);
- +
- + return s;
- +}
- +
- +static BcStatus bc_num_k(BcNum *restrict a, BcNum *restrict b,
- + BcNum *restrict c)
- +{
- + BcStatus s;
- + int carry;
- + size_t i, j, len, max = BC_MAX(a->len, b->len), max2 = (max + 1) / 2;
- + BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp;
- + bool aone = BC_NUM_ONE(a);
- +
- + if (!a->len || !b->len) {
- + bc_num_zero(c);
- + return BC_STATUS_SUCCESS;
- + }
- + else if (aone || BC_NUM_ONE(b)) return bc_num_copy(c, aone ? b : a);
- +
- + if (a->len + b->len < BC_NUM_KARATSUBA_LEN ||
- + a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN)
- + {
- + if ((s = bc_num_expand(c, a->len + b->len + 1))) return s;
- +
- + memset(c->num, 0, sizeof(BcDig) * c->cap);
- + c->len = carry = len = 0;
- +
- + for (i = 0; !bcg.signe && i < b->len; ++i) {
- +
- + for (j = 0; !bcg.signe && j < a->len; ++j) {
- + int in = (int) c->num[i + j];
- + in += ((int) a->num[j]) * ((int) b->num[i]) + carry;
- + carry = in / 10;
- + c->num[i + j] = (BcDig) (in % 10);
- + }
- +
- + if (bcg.signe) return BC_STATUS_EXEC_SIGNAL;
- +
- + c->num[i + j] += (BcDig) carry;
- + len = BC_MAX(len, i + j + !!carry);
- + carry = 0;
- + }
- +
- + c->len = len;
- +
- + return bcg.signe ? BC_STATUS_EXEC_SIGNAL : BC_STATUS_SUCCESS;
- + }
- +
- + if ((s = bc_num_init(&l1, max))) return s;
- + if ((s = bc_num_init(&h1, max))) goto high1_err;
- + if ((s = bc_num_init(&l2, max))) goto low2_err;
- + if ((s = bc_num_init(&h2, max))) goto high2_err;
- + if ((s = bc_num_init(&m1, max))) goto mix1_err;
- + if ((s = bc_num_init(&m2, max))) goto mix2_err;
- + if ((s = bc_num_init(&z0, max))) goto z0_err;
- + if ((s = bc_num_init(&z1, max))) goto z1_err;
- + if ((s = bc_num_init(&z2, max))) goto z2_err;
- + if ((s = bc_num_init(&temp, max + max))) goto temp_err;
- +
- + if ((s = bc_num_split(a, max2, &l1, &h1))) goto err;
- + if ((s = bc_num_split(b, max2, &l2, &h2))) goto err;
- +
- + if ((s = bc_num_add(&h1, &l1, &m1, 0))) goto err;
- + if ((s = bc_num_add(&h2, &l2, &m2, 0))) goto err;
- +
- + if ((s = bc_num_k(&h1, &h2, &z0))) goto err;
- + if ((s = bc_num_k(&m1, &m2, &z1))) goto err;
- + if ((s = bc_num_k(&l1, &l2, &z2))) goto err;
- +
- + if ((s = bc_num_sub(&z1, &z0, &temp, 0))) goto err;
- + if ((s = bc_num_sub(&temp, &z2, &z1, 0))) goto err;
- +
- + if ((s = bc_num_shift(&z0, max2 * 2))) goto err;
- + if ((s = bc_num_shift(&z1, max2))) goto err;
- + if ((s = bc_num_add(&z0, &z1, &temp, 0))) goto err;
- + s = bc_num_add(&temp, &z2, c, 0);
- +
- +err:
- + bc_num_free(&temp);
- +temp_err:
- + bc_num_free(&z2);
- +z2_err:
- + bc_num_free(&z1);
- +z1_err:
- + bc_num_free(&z0);
- +z0_err:
- + bc_num_free(&m2);
- +mix2_err:
- + bc_num_free(&m1);
- +mix1_err:
- + bc_num_free(&h2);
- +high2_err:
- + bc_num_free(&l2);
- +low2_err:
- + bc_num_free(&h1);
- +high1_err:
- + bc_num_free(&l1);
- + return s;
- +}
- +
- +static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
- +
- + BcStatus s;
- + BcNum cpa, cpb;
- + size_t maxrdx = BC_MAX(a->rdx, b->rdx);
- +
- + scale = BC_MAX(scale, a->rdx);
- + scale = BC_MAX(scale, b->rdx);
- + scale = BC_MIN(a->rdx + b->rdx, scale);
- + maxrdx = BC_MAX(maxrdx, scale);
- +
- + if ((s = bc_num_init(&cpa, a->len))) return s;
- + if ((s = bc_num_init(&cpb, b->len))) goto b_err;
- +
- + if ((s = bc_num_copy(&cpa, a))) goto err;
- + if ((s = bc_num_copy(&cpb, b))) goto err;
- + cpa.neg = cpb.neg = false;
- + if ((s = bc_num_shift(&cpa, maxrdx))) goto err;
- + if ((s = bc_num_shift(&cpb, maxrdx))) goto err;
- +
- + if ((s = bc_num_k(&cpa, &cpb, c))) goto err;
- +
- + if ((s = bc_num_expand(c, c->len + (maxrdx += scale)))) goto err;
- + if (c->len < maxrdx) {
- + memset(c->num + c->len, 0, (c->cap - c->len) * sizeof(BcDig));
- + c->len += maxrdx;
- + }
- +
- + c->rdx = maxrdx;
- + s = bc_num_retireMul(c, scale, a->neg, b->neg);
- +
- +err:
- + bc_num_free(&cpb);
- +b_err:
- + bc_num_free(&cpa);
- + return s;
- +}
- +
- +static BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
- +
- + BcStatus s;
- + BcDig *n, *bptr, q;
- + size_t len, end, i;
- + BcNum cp;
- +
- + if (!b->len) return BC_STATUS_MATH_DIVIDE_BY_ZERO;
- + else if (!a->len) {
- + bc_num_setToZero(c, scale);
- + return BC_STATUS_SUCCESS;
- + }
- + else if (BC_NUM_ONE(b)) {
- + if ((s = bc_num_copy(c, a))) return s;
- + return bc_num_retireMul(c, scale, a->neg, b->neg);
- + }
- +
- + if ((s = bc_num_init(&cp, BC_NUM_MREQ(a, b, scale)))) return s;
- + if ((s = bc_num_copy(&cp, a))) goto err;
- +
- + if ((len = b->len) > cp.len) {
- + if ((s = bc_num_expand(&cp, len + 2))) goto err;
- + if ((s = bc_num_extend(&cp, len - cp.len))) goto err;
- + }
- +
- + if (b->rdx > cp.rdx && (s = bc_num_extend(&cp, b->rdx - cp.rdx))) goto err;
- + cp.rdx -= b->rdx;
- + if (scale > cp.rdx && (s = bc_num_extend(&cp, scale - cp.rdx))) goto err;
- +
- + if (b->rdx == b->len) {
- +
- + bool zero = true;
- + for (i = 0; zero && i < len; ++i) zero = !b->num[len - i - 1];
- +
- + len -= i - 1;
- + }
- +
- + if (cp.cap == cp.len && (s = bc_num_expand(&cp, cp.len + 1))) goto err;
- +
- + // We want an extra zero in front to make things simpler.
- + cp.num[cp.len++] = 0;
- + end = cp.len - len;
- +
- + if ((s = bc_num_expand(c, cp.len))) goto err;
- +
- + bc_num_zero(c);
- + memset(c->num + end, 0, (c->cap - end) * sizeof(BcDig));
- + c->rdx = cp.rdx;
- + c->len = cp.len;
- + bptr = b->num;
- +
- + for (i = end - 1; !bcg.signe && !s && i < end; --i) {
- +
- + n = cp.num + i;
- +
- + for (q = 0; (!s && n[len]) || bc_num_compare(n, bptr, len) >= 0; ++q)
- + s = bc_num_subArrays(n, bptr, len);
- +
- + c->num[i] = q;
- + }
- +
- + if (!s) s = bc_num_retireMul(c, scale, a->neg, b->neg);
- +
- +err:
- + bc_num_free(&cp);
- + 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 (!b->len) return BC_STATUS_MATH_DIVIDE_BY_ZERO;
- +
- + if (!a->len) {
- + bc_num_setToZero(d, ts);
- + return BC_STATUS_SUCCESS;
- + }
- +
- + if ((s = bc_num_init(&temp, d->cap))) return s;
- +
- + if ((s = bc_num_d(a, b, c, scale))) goto err;
- +
- + if (scale) scale = ts;
- +
- + if ((s = bc_num_m(c, b, &temp, scale))) goto err;
- + if ((s = bc_num_sub(a, &temp, d, scale))) goto err;
- +
- + if (ts > d->rdx && d->len && (s = bc_num_extend(d, ts - d->rdx))) goto err;
- +
- + neg = d->neg;
- + s = bc_num_retireMul(d, ts, a->neg, b->neg);
- + d->neg = neg;
- +
- +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 = BC_MAX(scale + b->rdx, a->rdx), len = BC_NUM_MREQ(a, b, ts);
- +
- + if ((s = bc_num_init(&c1, len))) return s;
- +
- + 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;
- + BcNum copy;
- + unsigned long pow;
- + size_t i, powrdx, resrdx;
- + bool neg, zero;
- +
- + if (b->rdx) return BC_STATUS_MATH_NON_INTEGER;
- +
- + if (!b->len) {
- + bc_num_one(c);
- + return BC_STATUS_SUCCESS;
- + }
- + else if (!a->len) {
- + bc_num_setToZero(c, scale);
- + return BC_STATUS_SUCCESS;
- + }
- + else if (BC_NUM_ONE(b)) {
- +
- + if (!b->neg) s = bc_num_copy(c, a);
- + else s = bc_num_inv(a, c, scale);
- +
- + return s;
- + }
- +
- + neg = b->neg;
- + b->neg = false;
- +
- + if ((s = bc_num_ulong(b, &pow))) return s;
- + if ((s = bc_num_init(©, a->len))) return s;
- + if ((s = bc_num_copy(©, a))) goto err;
- +
- + if (!neg) scale = BC_MIN(a->rdx * pow, BC_MAX(scale, a->rdx));
- +
- + b->neg = neg;
- +
- + for (powrdx = a->rdx; !bcg.signe && !(pow & 1); pow >>= 1) {
- + powrdx <<= 1;
- + if ((s = bc_num_mul(©, ©, ©, powrdx))) goto err;
- + }
- +
- + if ((s = bc_num_copy(c, ©))) goto err;
- + if (bcg.signe) {
- + s = BC_STATUS_EXEC_SIGNAL;
- + goto err;
- + }
- +
- + for (resrdx = powrdx, pow >>= 1; !bcg.signe && pow != 0; pow >>= 1) {
- + if ((s = bc_num_mul(©, ©, ©, (powrdx <<= 1)))) goto err;
- + if (pow & 1) {
- + resrdx += powrdx;
- + if ((s = bc_num_mul(c, ©, c, resrdx))) goto err;
- + }
- + }
- +
- + if (neg && (s = bc_num_inv(c, c, scale))) goto err;
- + if (bcg.signe) {
- + s = BC_STATUS_EXEC_SIGNAL;
- + goto err;
- + }
- +
- + if (c->rdx > scale) bc_num_truncate(c, c->rdx - scale);
- +
- + for (zero = true, i = 0; zero && i < c->len; ++i) zero = !c->num[i];
- + if (zero) bc_num_setToZero(c, scale);
- +
- +err:
- + bc_num_free(©);
- + return s;
- +}
- +
- +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;
- +
- + if ((init = (c == a))) {
- + ptr_a = &num2;
- + memcpy(ptr_a, c, sizeof(BcNum));
- + }
- + 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) s = bc_num_init(c, req);
- + else s = bc_num_expand(c, req);
- +
- + if (!s) s = op(ptr_a, ptr_b, c, scale);
- +
- + if (init) bc_num_free(&num2);
- + return s;
- +}
- +
- +static bool bc_num_strValid(const char *val, size_t base) {
- +
- + BcDig b;
- + bool small, radix = false;
- + size_t i, len = strlen(val);
- +
- + if (!len) return true;
- +
- + small = base <= 10;
- + b = (BcDig) (small ? base + '0' : base - 10 + 'A');
- +
- + for (i = 0; i < len; ++i) {
- +
- + BcDig c = val[i];
- +
- + if (c == '.') {
- +
- + if (radix) return false;
- +
- + radix = true;
- + continue;
- + }
- +
- + if (c < '0' || (small && c >= b) || (c > '9' && (c < 'A' || c >= b)))
- + return false;
- + }
- +
- + return true;
- +}
- +
- +static BcStatus bc_num_parseDecimal(BcNum *n, const char *val) {
- +
- + BcStatus s;
- + size_t len, i;
- + const char *ptr;
- + bool zero = true;
- +
- + for (i = 0; val[i] == '0'; ++i);
- +
- + val += i;
- + len = strlen(val);
- + bc_num_zero(n);
- +
- + if (len) {
- + for (i = 0; zero && i < len; ++i) zero = val[i] == '0' || val[i] == '.';
- + if ((s = bc_num_expand(n, len))) return s;
- + }
- +
- + ptr = strchr(val, '.');
- +
- + // Explicitly test for NULL here to produce either a 0 or 1.
- + n->rdx = (size_t) ((ptr != NULL) * ((val + len) - (ptr + 1)));
- +
- + if (zero) return BC_STATUS_SUCCESS;
- +
- + for (i = len - 1; i < len; ++n->len, i -= 1 + (i && val[i - 1] == '.'))
- + n->num[n->len] = val[i] - '0';
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +static BcStatus bc_num_parseBase(BcNum *n, const char *val, BcNum *base) {
- +
- + BcStatus s;
- + BcNum temp, mult, result;
- + BcDig c = '\0';
- + bool zero = true;
- + unsigned long v;
- + size_t i, digits, len = strlen(val);
- +
- + bc_num_zero(n);
- +
- + for (i = 0; zero && i < len; ++i) zero = (val[i] == '.' || val[i] == '0');
- + if (zero) return BC_STATUS_SUCCESS;
- +
- + if ((s = bc_num_init(&temp, BC_NUM_DEF_SIZE))) return s;
- + if ((s = bc_num_init(&mult, BC_NUM_DEF_SIZE))) goto mult_err;
- +
- + for (i = 0; i < len && (c = val[i]) != '.'; ++i) {
- +
- + v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
- +
- + if ((s = bc_num_m(n, base, &mult, 0))) goto int_err;
- + if ((s = bc_num_ulong2num(&temp, v))) goto int_err;
- + if ((s = bc_num_add(&mult, &temp, n, 0))) goto int_err;
- + }
- +
- + if (i == len && !(c = val[i])) goto int_err;
- + if ((s = bc_num_init(&result, base->len))) goto int_err;
- +
- + bc_num_zero(&result);
- + bc_num_one(&mult);
- +
- + for (i += 1, digits = 0; i < len && (c = val[i]); ++i, ++digits) {
- +
- + v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
- +
- + if ((s = bc_num_mul(&result, base, &result, 0))) goto err;
- + if ((s = bc_num_ulong2num(&temp, v))) goto err;
- + if ((s = bc_num_add(&result, &temp, &result, 0))) goto err;
- + if ((s = bc_num_mul(&mult, base, &mult, 0))) goto err;
- + }
- +
- + if ((s = bc_num_div(&result, &mult, &result, digits))) goto err;
- + if ((s = bc_num_add(n, &result, n, digits))) goto err;
- +
- + if (n->len) {
- + if (n->rdx < digits && n->len) s = bc_num_extend(n, digits - n->rdx);
- + }
- + else bc_num_zero(n);
- +
- +err:
- + bc_num_free(&result);
- +int_err:
- + bc_num_free(&mult);
- +mult_err:
- + bc_num_free(&temp);
- + return s;
- +}
- +
- +static BcStatus bc_num_printNewline(size_t *nchars, size_t line_len) {
- + if (*nchars == line_len - 1) {
- + if (putchar('\\') == EOF) return BC_STATUS_IO_ERR;
- + if (putchar('\n') == EOF) return BC_STATUS_IO_ERR;
- + *nchars = 0;
- + }
- + return BC_STATUS_SUCCESS;
- +}
- +
- +#ifdef CONFIG_DC
- +static BcStatus bc_num_printChar(size_t num, size_t width, bool radix,
- + size_t *nchars, size_t line_len)
- +{
- + (void) radix, (void) line_len;
- + if (putchar((char) num) == EOF) return BC_STATUS_IO_ERR;
- + *nchars = *nchars + width;
- + return BC_STATUS_SUCCESS;
- +}
- +#endif // CONFIG_DC
- +
- +static BcStatus bc_num_printDigits(size_t num, size_t width, bool radix,
- + size_t *nchars, size_t line_len)
- +{
- + BcStatus s;
- + size_t exp, pow, div;
- +
- + if ((s = bc_num_printNewline(nchars, line_len))) return s;
- + if (putchar(radix ? '.' : ' ') == EOF) return BC_STATUS_IO_ERR;
- + ++(*nchars);
- +
- + if ((s = bc_num_printNewline(nchars, line_len))) return s;
- + for (exp = 0, pow = 1; exp < width - 1; ++exp, pow *= 10);
- +
- + for (exp = 0; exp < width; pow /= 10, ++(*nchars), ++exp) {
- + if ((s = bc_num_printNewline(nchars, line_len))) return s;
- + div = num / pow;
- + num -= div * pow;
- + if (putchar(((char) div) + '0') == EOF) return BC_STATUS_IO_ERR;
- + }
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +static BcStatus bc_num_printHex(size_t num, size_t width, bool radix,
- + size_t *nchars, size_t line_len)
- +{
- + BcStatus s;
- +
- + if (radix) {
- + if ((s = bc_num_printNewline(nchars, line_len))) return s;
- + if (putchar('.') == EOF) return BC_STATUS_IO_ERR;
- + *nchars += 1;
- + }
- +
- + if ((s = bc_num_printNewline(nchars, line_len))) return s;
- + if (putchar(bc_num_hex_digits[num]) == EOF) return BC_STATUS_IO_ERR;
- + *nchars = *nchars + width;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +static BcStatus bc_num_printDecimal(BcNum *n, size_t *nchars, size_t len) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + size_t i, rdx = n->rdx - 1;
- +
- + if (n->neg && putchar('-') == EOF) return BC_STATUS_IO_ERR;
- + (*nchars) += n->neg;
- +
- + for (i = n->len - 1; !s && i < n->len; --i)
- + s = bc_num_printHex((size_t) n->num[i], 1, i == rdx, nchars, len);
- +
- + return s;
- +}
- +
- +static BcStatus bc_num_printNum(BcNum *n, BcNum *base, size_t width,
- + size_t *nchars, size_t len, BcNumDigitOp print)
- +{
- + BcStatus s;
- + BcVec stack;
- + BcNum intp, fracp, digit, frac_len;
- + unsigned long dig, *ptr;
- + size_t i;
- + bool radix;
- +
- + if (!n->len) return print(0, width, false, nchars, len);
- +
- + if ((s = bc_vec_init(&stack, sizeof(long), NULL))) return s;
- + if ((s = bc_num_init(&intp, n->len))) goto int_err;
- + if ((s = bc_num_init(&fracp, n->rdx))) goto frac_err;
- + if ((s = bc_num_init(&digit, width))) goto digit_err;
- + if ((s = bc_num_init(&frac_len, BC_NUM_INT(n)))) goto frac_len_err;
- + if ((s = bc_num_copy(&intp, n))) goto err;
- + bc_num_one(&frac_len);
- +
- + bc_num_truncate(&intp, intp.rdx);
- + if ((s = bc_num_sub(n, &intp, &fracp, 0))) goto err;
- +
- + while (intp.len) {
- + if ((s = bc_num_divmod(&intp, base, &intp, &digit, 0))) goto err;
- + if ((s = bc_num_ulong(&digit, &dig))) goto err;
- + if ((s = bc_vec_push(&stack, &dig))) goto err;
- + }
- +
- + for (i = 0; i < stack.len; ++i) {
- + ptr = bc_vec_item_rev(&stack, i);
- + if ((s = print(*ptr, width, false, nchars, len))) goto err;
- + }
- +
- + if (!n->rdx) goto err;
- +
- + for (radix = true; frac_len.len <= n->rdx; radix = false) {
- + if ((s = bc_num_mul(&fracp, base, &fracp, n->rdx))) goto err;
- + if ((s = bc_num_ulong(&fracp, &dig))) goto err;
- + if ((s = bc_num_ulong2num(&intp, dig))) goto err;
- + if ((s = bc_num_sub(&fracp, &intp, &fracp, 0))) goto err;
- + if ((s = print(dig, width, radix, nchars, len))) goto err;
- + if ((s = bc_num_mul(&frac_len, base, &frac_len, 0))) goto err;
- + }
- +
- +err:
- + bc_num_free(&frac_len);
- +frac_len_err:
- + bc_num_free(&digit);
- +digit_err:
- + bc_num_free(&fracp);
- +frac_err:
- + bc_num_free(&intp);
- +int_err:
- + bc_vec_free(&stack);
- + return s;
- +}
- +
- +static BcStatus bc_num_printBase(BcNum *n, BcNum *base, size_t base_t,
- + size_t *nchars, size_t line_len)
- +{
- + BcStatus s;
- + size_t width, i;
- + BcNumDigitOp print;
- + bool neg = n->neg;
- +
- + if (neg && putchar('-') == EOF) return BC_STATUS_IO_ERR;
- + (*nchars) += neg;
- +
- + n->neg = false;
- +
- + if (base_t <= BC_NUM_MAX_IBASE) {
- + width = 1;
- + print = bc_num_printHex;
- + }
- + else {
- + for (i = base_t - 1, width = 0; i != 0; i /= 10, ++width);
- + print = bc_num_printDigits;
- + }
- +
- + s = bc_num_printNum(n, base, width, nchars, line_len, print);
- + n->neg = neg;
- +
- + return s;
- +}
- +
- +#ifdef CONFIG_DC
- +BcStatus bc_num_stream(BcNum *n, BcNum *base, size_t *nchars, size_t len) {
- + return bc_num_printNum(n, base, 1, nchars, len, bc_num_printChar);
- +}
- +#endif // CONFIG_DC
- +
- +BcStatus bc_num_init(BcNum *n, size_t request) {
- +
- + request = request >= BC_NUM_DEF_SIZE ? request : BC_NUM_DEF_SIZE;
- + memset(n, 0, sizeof(BcNum));
- + if (!(n->num = malloc(request))) return BC_STATUS_ALLOC_ERR;
- +
- + n->cap = request;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_num_expand(BcNum *n, size_t req) {
- +
- + BcDig *temp;
- +
- + req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
- +
- + if (req <= n->cap) return BC_STATUS_SUCCESS;
- + if (!(temp = realloc(n->num, req))) return BC_STATUS_ALLOC_ERR;
- +
- + n->num = temp;
- + n->cap = req;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +void bc_num_free(void *num) {
- + BcNum *n = (BcNum*) num;
- + free(n->num);
- +}
- +
- +BcStatus bc_num_copy(BcNum *d, BcNum *s) {
- +
- + BcStatus status;
- +
- + if (d == s) return BC_STATUS_SUCCESS;
- + if ((status = bc_num_expand(d, s->cap))) return status;
- +
- + d->len = s->len;
- + d->neg = s->neg;
- + d->rdx = s->rdx;
- +
- + memcpy(d->num, s->num, sizeof(BcDig) * d->len);
- +
- + return status;
- +}
- +
- +BcStatus bc_num_parse(BcNum *n, const char *val, BcNum *base, size_t base_t) {
- +
- + BcStatus s;
- +
- + if (!bc_num_strValid(val, base_t)) return BC_STATUS_MATH_BAD_STRING;
- +
- + if (base_t == 10) s = bc_num_parseDecimal(n, val);
- + else s = bc_num_parseBase(n, val, base);
- +
- + return s;
- +}
- +
- +BcStatus bc_num_print(BcNum *n, BcNum *base, size_t base_t, bool newline,
- + size_t *nchars, size_t line_len)
- +{
- + BcStatus s;
- +
- + if ((s = bc_num_printNewline(nchars, line_len))) return s;
- +
- + if (!n->len) {
- + if (putchar('0') == EOF) return BC_STATUS_IO_ERR;
- + ++(*nchars);
- + }
- + else if (base_t == 10) s = bc_num_printDecimal(n, nchars, line_len);
- + else s = bc_num_printBase(n, base, base_t, nchars, line_len);
- +
- + if (s) return s;
- +
- + if (newline) {
- + if (putchar('\n') == EOF) return BC_STATUS_IO_ERR;
- + *nchars = 0;
- + }
- +
- + return s;
- +}
- +
- +BcStatus bc_num_ulong(BcNum *n, unsigned long *result) {
- +
- + size_t i;
- + unsigned long pow;
- +
- + if (n->neg) return BC_STATUS_MATH_NEGATIVE;
- +
- + for (*result = 0, pow = 1, i = n->rdx; i < n->len; ++i) {
- +
- + unsigned long prev = *result, powprev = pow;
- +
- + *result += ((unsigned long) n->num[i]) * pow;
- + pow *= 10;
- +
- + if (*result < prev || pow < powprev) return BC_STATUS_MATH_OVERFLOW;
- + }
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_num_ulong2num(BcNum *n, unsigned long val) {
- +
- + BcStatus s;
- + size_t len;
- + BcDig *ptr;
- + unsigned long i;
- +
- + bc_num_zero(n);
- +
- + if (!val) return BC_STATUS_SUCCESS;
- +
- + for (len = 1, i = ULONG_MAX; i != 0; i /= 10, ++len)
- + if ((s = bc_num_expand(n, len))) return s;
- + for (ptr = n->num, i = 0; val; ++i, ++n->len, val /= 10) ptr[i] = val % 10;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
- + BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_a : bc_num_s;
- + (void) scale;
- + return bc_num_binary(a, b, c, false, op, BC_NUM_AREQ(a, b));
- +}
- +
- +BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
- + BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_s : bc_num_a;
- + (void) scale;
- + return bc_num_binary(a, b, c, true, op, BC_NUM_AREQ(a, b));
- +}
- +
- +BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
- + size_t req = BC_NUM_MREQ(a, b, scale);
- + return bc_num_binary(a, b, c, scale, bc_num_m, req);
- +}
- +
- +BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
- + size_t req = BC_NUM_MREQ(a, b, scale);
- + return bc_num_binary(a, b, c, scale, bc_num_d, req);
- +}
- +
- +BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
- + size_t req = BC_NUM_MREQ(a, b, scale);
- + return bc_num_binary(a, b, c, scale, bc_num_rem, req);
- +}
- +
- +BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
- + return bc_num_binary(a, b, c, scale, bc_num_p, a->len * b->len + 1);
- +}
- +
- +BcStatus bc_num_sqrt(BcNum *a, BcNum *restrict b, size_t scale) {
- +
- + BcStatus s;
- + BcNum num1, num2, half, f, fprime, *x0, *x1, *temp;
- + size_t pow, len, digits, digits1, resrdx, req, times = 0;
- + ssize_t cmp = 1, cmp1 = SSIZE_MAX, cmp2 = SSIZE_MAX;
- +
- + req = BC_MAX(scale, a->rdx) + ((BC_NUM_INT(a)+ 1) >> 1) + 1;
- +
- + if ((s = bc_num_expand(b, req))) goto init_err;
- +
- + if (!a->len) {
- + bc_num_setToZero(b, scale);
- + goto init_err;
- + }
- + else if (a->neg) {
- + s = BC_STATUS_MATH_NEGATIVE;
- + goto init_err;
- + }
- + else if (BC_NUM_ONE(a)) {
- + bc_num_one(b);
- + s = bc_num_extend(b, scale);
- + goto init_err;
- + }
- +
- + len = a->len;
- +
- + scale = BC_MAX(scale, a->rdx) + 1;
- +
- + if ((s = bc_num_init(&num1, len))) goto init_err;
- + if ((s = bc_num_init(&num2, num1.len))) goto num2_err;
- + if ((s = bc_num_init(&half, BC_NUM_DEF_SIZE))) goto two_err;
- +
- + bc_num_one(&half);
- + half.num[0] = 5;
- + half.rdx = 1;
- +
- + len += scale;
- +
- + if ((s = bc_num_init(&f, len))) goto f_err;
- + if ((s = bc_num_init(&fprime, len + scale))) goto fprime_err;
- +
- + x0 = &num1;
- + x1 = &num2;
- +
- + bc_num_one(x0);
- +
- + if ((pow = BC_NUM_INT(a))) {
- +
- + if (pow & 1) x0->num[0] = 2;
- + else x0->num[0] = 6;
- +
- + pow -= 2 - (pow & 1);
- +
- + if ((s = bc_num_extend(x0, pow))) goto err;
- +
- + // Make sure to move the radix back.
- + x0->rdx -= pow;
- + }
- +
- + x0->rdx = digits = digits1 = 0;
- + resrdx = scale + 2;
- + len = BC_NUM_INT(x0) + resrdx - 1;
- +
- + while (!bcg.signe && (cmp || digits < len)) {
- +
- + if ((s = bc_num_d(a, x0, &f, resrdx))) goto err;
- + if ((s = bc_num_add(x0, &f, &fprime, resrdx))) goto err;
- + if ((s = bc_num_m(&fprime, &half, x1, resrdx))) goto err;
- +
- + cmp = bc_num_cmp(x1, x0);
- + digits = x1->len - (unsigned long long) llabs(cmp);
- +
- + if (cmp == cmp2 && digits == digits1) times += 1;
- + else times = 0;
- +
- + resrdx += times > 4;
- +
- + cmp2 = cmp1;
- + cmp1 = cmp;
- + digits1 = digits;
- +
- + temp = x0;
- + x0 = x1;
- + x1 = temp;
- + }
- +
- + if (bcg.signe) {
- + s = BC_STATUS_EXEC_SIGNAL;
- + goto err;
- + }
- +
- + if ((s = bc_num_copy(b, x0))) goto err;
- +
- + if (b->rdx > --scale) bc_num_truncate(b, b->rdx - scale);
- +
- +err:
- + bc_num_free(&fprime);
- +fprime_err:
- + bc_num_free(&f);
- +f_err:
- + bc_num_free(&half);
- +two_err:
- + bc_num_free(&num2);
- +num2_err:
- + bc_num_free(&num1);
- +init_err:
- + 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;
- + size_t ts = BC_MAX(scale + b->rdx, a->rdx), len = BC_NUM_MREQ(a, b, ts);
- +
- + if ((init = (c == a))) {
- + memcpy(&num2, c, sizeof(BcNum));
- + ptr_a = &num2;
- + }
- + else ptr_a = a;
- +
- + if (init) s = bc_num_init(c, len);
- + else s = bc_num_expand(d, ptr_a->len);
- +
- + if (s) return s;
- +
- + s = bc_num_r(ptr_a, b, c, d, scale, ts);
- +
- + if (init) bc_num_free(&num2);
- +
- + return s;
- +}
- +
- +#ifdef CONFIG_DC
- +BcStatus bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) {
- +
- + BcStatus s;
- + BcNum base, exp, two, temp;
- +
- + if ((s = bc_num_expand(d, c->len))) return s;
- +
- + if (!c->len) return BC_STATUS_MATH_DIVIDE_BY_ZERO;
- + if (a->rdx || b->rdx || c->rdx) return BC_STATUS_MATH_NON_INTEGER;
- + if (b->neg) return BC_STATUS_MATH_NEGATIVE;
- +
- + if ((s = bc_num_init(&base, c->len))) return s;
- + if ((s = bc_num_init(&exp, b->len))) goto exp_err;
- + if ((s = bc_num_init(&two, BC_NUM_DEF_SIZE))) goto two_err;
- + if ((s = bc_num_init(&temp, b->len))) goto temp_err;
- +
- + bc_num_one(&two);
- + two.num[0] = 2;
- + bc_num_one(d);
- +
- + if ((s = bc_num_rem(a, c, &base, 0))) goto err;
- + if ((s = bc_num_copy(&exp, b))) goto err;
- +
- + while (exp.len) {
- +
- + if ((s = bc_num_divmod(&exp, &two, &exp, &temp, 0))) goto err;
- +
- + if (BC_NUM_ONE(&temp)) {
- + if ((s = bc_num_m(d, &base, &temp, 0))) goto err;
- + if ((s = bc_num_rem(&temp, c, d, 0))) goto err;
- + }
- +
- + if ((s = bc_num_m(&base, &base, &temp, 0))) goto err;
- + if ((s = bc_num_rem(&temp, c, &base, 0))) goto err;
- + }
- +
- +err:
- + bc_num_free(&temp);
- +temp_err:
- + bc_num_free(&two);
- +two_err:
- + bc_num_free(&exp);
- +exp_err:
- + bc_num_free(&base);
- + return s;
- +}
- +#endif // CONFIG_DC
- +
- +int bc_id_cmp(const void *e1, const void *e2) {
- + return strcmp(((const BcId*) e1)->name, ((const BcId*) e2)->name);
- +}
- +
- +void bc_id_free(void *id) {
- + free(((BcId*) id)->name);
- +}
- +
- +BcStatus bc_func_insert(BcFunc *f, char *name, bool var) {
- +
- + BcId a;
- + size_t i;
- +
- + for (i = 0; i < f->autos.len; ++i) {
- + if (!strcmp(name, ((BcId*) bc_vec_item(&f->autos, i))->name))
- + return BC_STATUS_PARSE_DUPLICATE_LOCAL;
- + }
- +
- + a.idx = var;
- + a.name = name;
- +
- + return bc_vec_push(&f->autos, &a);
- +}
- +
- +BcStatus bc_func_init(BcFunc *f) {
- +
- + BcStatus s;
- +
- + if ((s = bc_vec_init(&f->code, sizeof(char), NULL))) return s;
- + if ((s = bc_vec_init(&f->autos, sizeof(BcId), bc_id_free))) goto err;
- + if ((s = bc_vec_init(&f->labels, sizeof(size_t), NULL))) goto label_err;
- +
- + f->nparams = 0;
- +
- + return BC_STATUS_SUCCESS;
- +
- +label_err:
- + bc_vec_free(&f->autos);
- +err:
- + bc_vec_free(&f->code);
- + return s;
- +}
- +
- +void bc_func_free(void *func) {
- + BcFunc *f = (BcFunc*) func;
- + bc_vec_free(&f->code);
- + bc_vec_free(&f->autos);
- + bc_vec_free(&f->labels);
- +}
- +
- +BcStatus bc_array_init(BcVec *a, bool nums) {
- +
- + BcStatus s;
- +
- + if (nums) s = bc_vec_init(a, sizeof(BcNum), bc_num_free);
- + else s = bc_vec_init(a, sizeof(BcVec), bc_vec_free);
- +
- + if (s) return s;
- +
- + if ((s = bc_array_expand(a, 1))) goto err;
- +
- + return s;
- +
- +err:
- + bc_vec_free(a);
- + return s;
- +}
- +
- +BcStatus bc_array_copy(BcVec *d, const BcVec *s) {
- +
- + BcStatus status;
- + size_t i;
- +
- + bc_vec_npop(d, d->len);
- + if ((status = bc_vec_expand(d, s->cap))) return status;
- + d->len = s->len;
- +
- + for (i = 0; !status && i < s->len; ++i) {
- + BcNum *dnum = bc_vec_item(d, i), *snum = bc_vec_item(s, i);
- + if ((status = bc_num_init(dnum, snum->len))) return status;
- + if ((status = bc_num_copy(dnum, snum))) bc_num_free(dnum);
- + }
- +
- + return status;
- +}
- +
- +BcStatus bc_array_expand(BcVec *a, size_t len) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + BcResultData data;
- +
- + if (a->size == sizeof(BcNum) && a->dtor == bc_num_free) {
- + while (!s && len > a->len) {
- + if ((s = bc_num_init(&data.n, BC_NUM_DEF_SIZE))) return s;
- + if ((s = bc_vec_push(a, &data.n))) bc_num_free(&data.n);
- + }
- + }
- + else {
- + while (!s && len > a->len) {
- + if ((s = bc_array_init(&data.v, true))) return s;
- + if ((s = bc_vec_push(a, &data.v))) bc_vec_free(&data.v);
- + }
- + }
- +
- + return s;
- +}
- +
- +void bc_string_free(void *string) {
- + free(*((char**) string));
- +}
- +
- +#ifdef CONFIG_DC
- +BcStatus bc_result_copy(BcResult *d, BcResult *src) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- +
- + switch ((d->t = src->t)) {
- +
- + case BC_RESULT_TEMP:
- + case BC_RESULT_IBASE:
- + case BC_RESULT_SCALE:
- + case BC_RESULT_OBASE:
- + {
- + if ((s = bc_num_init(&d->d.n, src->d.n.len))) return s;
- + s = bc_num_copy(&d->d.n, &src->d.n);
- + break;
- + }
- +
- + case BC_RESULT_VAR:
- + case BC_RESULT_ARRAY:
- + case BC_RESULT_ARRAY_ELEM:
- + {
- + if ((d->d.id.name = strdup(src->d.id.name))) s = BC_STATUS_SUCCESS;
- + else s = BC_STATUS_ALLOC_ERR;
- + break;
- + }
- +
- + case BC_RESULT_CONSTANT:
- + case BC_RESULT_LAST:
- + case BC_RESULT_ONE:
- + case BC_RESULT_STR:
- + {
- + memcpy(&d->d.n, &src->d.n, sizeof(BcNum));
- + break;
- + }
- + }
- +
- + return s;
- +}
- +#endif // CONFIG_DC
- +
- +void bc_result_free(void *result) {
- +
- + BcResult *r = (BcResult*) result;
- +
- + 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:
- + case BC_RESULT_ARRAY:
- + case BC_RESULT_ARRAY_ELEM:
- + {
- + free(r->d.id.name);
- + break;
- + }
- +
- + default:
- + {
- + // Do nothing.
- + break;
- + }
- + }
- +}
- +
- +void bc_lex_lineComment(BcLex *l) {
- + l->t.t = BC_LEX_WHITESPACE;
- + while (l->idx < l->len && l->buffer[l->idx++] != '\n');
- + --l->idx;
- +}
- +
- +void bc_lex_whitespace(BcLex *l) {
- + char c;
- + l->t.t = BC_LEX_WHITESPACE;
- + for (; (c = l->buffer[l->idx]) != '\n' && isspace(c); ++l->idx);
- +}
- +
- +BcStatus bc_lex_number(BcLex *l, char start) {
- +
- + BcStatus s;
- + const char *buf = l->buffer + l->idx;
- + size_t len, hits = 0, bslashes = 0, i = 0, j;
- + char c = buf[i];
- + bool last_pt, pt = start == '.';
- +
- + last_pt = pt;
- + l->t.t = BC_LEX_NUMBER;
- +
- + while (c && (isdigit(c) || (c >= 'A' && c <= 'F') ||
- + (c == '.' && !pt) || (c == '\\' && buf[i + 1] == '\n')))
- + {
- + if (c != '\\') {
- + last_pt = c == '.';
- + pt = pt || last_pt;
- + }
- + else {
- + ++i;
- + bslashes += 1;
- + }
- +
- + c = buf[++i];
- + }
- +
- + len = i + 1 * !last_pt - bslashes * 2;
- + if (len > BC_MAX_NUM) return BC_STATUS_EXEC_NUM_LEN;
- +
- + bc_vec_npop(&l->t.v, l->t.v.len);
- + if ((s = bc_vec_expand(&l->t.v, len + 1))) return s;
- + if ((s = bc_vec_push(&l->t.v, &start))) return s;
- +
- + for (buf -= 1, j = 1; j < len + hits * 2; ++j) {
- +
- + c = buf[j];
- +
- + // If we have hit a backslash, skip it. We don't have
- + // to check for a newline because it's guaranteed.
- + if (hits < bslashes && c == '\\') {
- + ++hits;
- + ++j;
- + continue;
- + }
- +
- + if ((s = bc_vec_push(&l->t.v, &c))) return s;
- + }
- +
- + if ((s = bc_vec_pushByte(&l->t.v, '\0'))) return s;
- + l->idx += i;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_lex_name(BcLex *l) {
- +
- + BcStatus s;
- + size_t i = 0;
- + const char *buf = l->buffer + l->idx - 1;
- + char c = buf[i];
- +
- + l->t.t = BC_LEX_NAME;
- +
- + while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_') c = buf[++i];
- +
- + if (i > BC_MAX_STRING) return BC_STATUS_EXEC_NAME_LEN;
- + if ((s = bc_vec_string(&l->t.v, i, buf))) return s;
- +
- + // Increment the index. We minus 1 because it has already been incremented.
- + l->idx += i - 1;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_lex_init(BcLex *l, BcLexNext next) {
- + l->next = next;
- + return bc_vec_init(&l->t.v, sizeof(uint8_t), NULL);
- +}
- +
- +void bc_lex_free(BcLex *l) {
- + bc_vec_free(&l->t.v);
- +}
- +
- +void bc_lex_file(BcLex *l, const char *file) {
- + l->line = 1;
- + l->newline = false;
- + l->f = file;
- +}
- +
- +BcStatus bc_lex_next(BcLex *l) {
- +
- + BcStatus s;
- +
- + if ((l->t.last = l->t.t) == BC_LEX_EOF) return BC_STATUS_LEX_EOF;
- +
- + l->line += l->newline;
- + l->t.t = BC_LEX_EOF;
- +
- + if ((l->newline = (l->idx == 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.
- + while (!(s = l->next(l)) && l->t.t == BC_LEX_WHITESPACE);
- +
- + return s;
- +}
- +
- +BcStatus bc_lex_text(BcLex *l, const char *text) {
- + l->buffer = text;
- + l->idx = 0;
- + l->len = strlen(text);
- + l->t.t = l->t.last = BC_LEX_INVALID;
- + return bc_lex_next(l);
- +}
- +
- +#ifdef CONFIG_BC
- +static BcStatus bc_lex_identifier(BcLex *l) {
- +
- + BcStatus s;
- + size_t i;
- + const char *buf = l->buffer + l->idx - 1;
- +
- + for (i = 0; i < sizeof(bc_lex_kws) / sizeof(bc_lex_kws[0]); ++i) {
- +
- + unsigned long len = (unsigned long) bc_lex_kws[i].len;
- +
- + if (!strncmp(buf, bc_lex_kws[i].name, len)) {
- +
- + l->t.t = BC_LEX_KEY_AUTO + (BcLexType) i;
- +
- + if (!bc_lex_kws[i].posix &&
- + (s = bc_vm_posixError(BC_STATUS_POSIX_BAD_KW, l->f,
- + l->line, bc_lex_kws[i].name)))
- + {
- + return s;
- + }
- +
- + // We minus 1 because the index has already been incremented.
- + l->idx += len - 1;
- + return BC_STATUS_SUCCESS;
- + }
- + }
- +
- + if ((s = bc_lex_name(l))) return s;
- +
- + if (l->t.v.len - 1 > 1)
- + s = bc_vm_posixError(BC_STATUS_POSIX_NAME_LEN, l->f, l->line, buf);
- +
- + return s;
- +}
- +
- +static BcStatus bc_lex_string(BcLex *l) {
- +
- + BcStatus s;
- + size_t len, nls = 0, i = l->idx;
- + char c;
- +
- + l->t.t = BC_LEX_STR;
- +
- + for (; (c = l->buffer[i]) && c != '"'; ++i) nls += (c == '\n');
- +
- + if (c == '\0') {
- + l->idx = i;
- + return BC_STATUS_LEX_NO_STRING_END;
- + }
- +
- + if ((len = i - l->idx) > BC_MAX_STRING) return BC_STATUS_EXEC_STRING_LEN;
- + if ((s = bc_vec_string(&l->t.v, len, l->buffer + l->idx))) return s;
- +
- + l->idx = i + 1;
- + l->line += nls;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +static void bc_lex_assign(BcLex *l, BcLexType with, BcLexType without) {
- + if (l->buffer[l->idx] == '=') {
- + ++l->idx;
- + l->t.t = with;
- + }
- + else l->t.t = without;
- +}
- +
- +static BcStatus bc_lex_comment(BcLex *l) {
- +
- + size_t i, nls = 0;
- + const char *buf = l->buffer;
- + bool end = false;
- + char c;
- +
- + l->t.t = BC_LEX_WHITESPACE;
- +
- + for (i = ++l->idx; !end; i += !end) {
- +
- + for (; (c = buf[i]) != '*' && c != '\0'; ++i) nls += (c == '\n');
- +
- + if (c == '\0' || buf[i + 1] == '\0') {
- + l->idx = i;
- + return BC_STATUS_LEX_NO_COMMENT_END;
- + }
- +
- + end = buf[i + 1] == '/';
- + }
- +
- + l->idx = i + 2;
- + l->line += nls;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus bc_lex_token(BcLex *l) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + char c, c2;
- +
- + // This is the workhorse of the lexer.
- + switch ((c = l->buffer[l->idx++])) {
- +
- + case '\0':
- + case '\n':
- + {
- + l->newline = true;
- + l->t.t = !c ? BC_LEX_EOF : BC_LEX_NLINE;
- + break;
- + }
- +
- + case '\t':
- + case '\v':
- + case '\f':
- + case '\r':
- + case ' ':
- + {
- + bc_lex_whitespace(l);
- + break;
- + }
- +
- + case '!':
- + {
- + bc_lex_assign(l, BC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT);
- +
- + if (l->t.t == BC_LEX_OP_BOOL_NOT &&
- + (s = bc_vm_posixError(BC_STATUS_POSIX_BOOL_OPS,
- + l->f, l->line, "!")))
- + {
- + return s;
- + }
- +
- + break;
- + }
- +
- + case '"':
- + {
- + s = bc_lex_string(l);
- + break;
- + }
- +
- + case '#':
- + {
- + if ((s = bc_vm_posixError(BC_STATUS_POSIX_COMMENT,
- + l->f, l->line, NULL)))
- + {
- + return s;
- + }
- +
- + bc_lex_lineComment(l);
- +
- + break;
- + }
- +
- + case '%':
- + {
- + bc_lex_assign(l, BC_LEX_OP_ASSIGN_MODULUS, BC_LEX_OP_MODULUS);
- + break;
- + }
- +
- + case '&':
- + {
- + if ((c2 = l->buffer[l->idx]) == '&') {
- +
- + if ((s = bc_vm_posixError(BC_STATUS_POSIX_BOOL_OPS,
- + l->f, l->line, "&&")))
- + {
- + return s;
- + }
- +
- + ++l->idx;
- + l->t.t = BC_LEX_OP_BOOL_AND;
- + }
- + else {
- + l->t.t = BC_LEX_INVALID;
- + s = BC_STATUS_LEX_BAD_CHAR;
- + }
- +
- + break;
- + }
- +
- + case '(':
- + case ')':
- + {
- + l->t.t = (BcLexType) (c - '(' + BC_LEX_LPAREN);
- + break;
- + }
- +
- + case '*':
- + {
- + bc_lex_assign(l, BC_LEX_OP_ASSIGN_MULTIPLY, BC_LEX_OP_MULTIPLY);
- + break;
- + }
- +
- + case '+':
- + {
- + if ((c2 = l->buffer[l->idx]) == '+') {
- + ++l->idx;
- + l->t.t = BC_LEX_OP_INC;
- + }
- + else bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLUS, BC_LEX_OP_PLUS);
- + break;
- + }
- +
- + case ',':
- + {
- + l->t.t = BC_LEX_COMMA;
- + break;
- + }
- +
- + case '-':
- + {
- + if ((c2 = l->buffer[l->idx]) == '-') {
- + ++l->idx;
- + l->t.t = BC_LEX_OP_DEC;
- + }
- + else bc_lex_assign(l, BC_LEX_OP_ASSIGN_MINUS, BC_LEX_OP_MINUS);
- + break;
- + }
- +
- + case '.':
- + {
- + if (isdigit(l->buffer[l->idx])) s = bc_lex_number(l, c);
- + else {
- + l->t.t = BC_LEX_KEY_LAST;
- + s = bc_vm_posixError(BC_STATUS_POSIX_DOT, l->f, l->line, NULL);
- + }
- + break;
- + }
- +
- + case '/':
- + {
- + if ((c2 = l->buffer[l->idx]) =='*') 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':
- + {
- + s = bc_lex_number(l, c);
- + break;
- + }
- +
- + case ';':
- + {
- + l->t.t = BC_LEX_SCOLON;
- + break;
- + }
- +
- + case '<':
- + {
- + 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 '>':
- + {
- + bc_lex_assign(l, BC_LEX_OP_REL_GE, BC_LEX_OP_REL_GT);
- + break;
- + }
- +
- + case '[':
- + case ']':
- + {
- + l->t.t = (BcLexType) (c - '[' + BC_LEX_LBRACKET);
- + break;
- + }
- +
- + case '\\':
- + {
- + if (l->buffer[l->idx] == '\n') {
- + l->t.t = BC_LEX_WHITESPACE;
- + ++l->idx;
- + }
- + else s = BC_STATUS_LEX_BAD_CHAR;
- + 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.t = (BcLexType) (c - '{' + BC_LEX_LBRACE);
- + break;
- + }
- +
- + case '|':
- + {
- + if ((c2 = l->buffer[l->idx]) == '|') {
- +
- + if ((s = bc_vm_posixError(BC_STATUS_POSIX_BOOL_OPS,
- + l->f, l->line, "||")))
- + {
- + return s;
- + }
- +
- + ++l->idx;
- + l->t.t = BC_LEX_OP_BOOL_OR;
- + }
- + else {
- + l->t.t = BC_LEX_INVALID;
- + s = BC_STATUS_LEX_BAD_CHAR;
- + }
- +
- + break;
- + }
- +
- + default:
- + {
- + l->t.t = BC_LEX_INVALID;
- + s = BC_STATUS_LEX_BAD_CHAR;
- + break;
- + }
- + }
- +
- + return s;
- +}
- +#endif // CONFIG_BC
- +
- +#ifdef CONFIG_DC
- +static BcStatus dc_lex_register(BcLex *l) {
- +
- + BcStatus s;
- +
- + if (isspace(l->buffer[l->idx - 1])) {
- + bc_lex_whitespace(l);
- + ++l->idx;
- + if (!bcg.exreg) s = BC_STATUS_LEX_EXTENDED_REG;
- + else s = bc_lex_name(l);
- + }
- + else {
- + bc_vec_npop(&l->t.v, l->t.v.len);
- + if ((s = bc_vec_pushByte(&l->t.v, l->buffer[l->idx - 1]))) return s;
- + s = bc_vec_pushByte(&l->t.v, '\0');
- + l->t.t = BC_LEX_NAME;
- + }
- +
- + return s;
- +}
- +
- +static BcStatus dc_lex_string(BcLex *l) {
- +
- + BcStatus s;
- + size_t depth = 1, nls = 0, i = l->idx;
- + char c;
- +
- + l->t.t = BC_LEX_STR;
- + bc_vec_npop(&l->t.v, l->t.v.len);
- +
- + for (; (c = l->buffer[i]) && depth; ++i) {
- +
- + depth += (c == '[' && (i == l->idx || l->buffer[i - 1] != '\\'));
- + depth -= (c == ']' && (i == l->idx || l->buffer[i - 1] != '\\'));
- + nls += (c == '\n');
- +
- + if (depth && (s = bc_vec_push(&l->t.v, &c))) return s;
- + }
- +
- + if (c == '\0') {
- + l->idx = i;
- + return BC_STATUS_LEX_NO_STRING_END;
- + }
- +
- + if ((s = bc_vec_pushByte(&l->t.v, '\0'))) return s;
- + if (i - l->idx > BC_MAX_STRING) return BC_STATUS_EXEC_STRING_LEN;
- +
- + l->idx = i;
- + l->line += nls;
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +BcStatus dc_lex_token(BcLex *l) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + char c = l->buffer[l->idx++], c2;
- + size_t i;
- +
- + for (i = 0; i < dc_lex_regs_len; ++i) {
- + if (l->t.last == dc_lex_regs[i]) return dc_lex_register(l);
- + }
- +
- + if (c >= '%' && c <= '~' &&
- + (l->t.t = dc_lex_tokens[(c - '%')]) != BC_LEX_INVALID)
- + {
- + return s;
- + }
- +
- + // This is the workhorse of the lexer.
- + switch (c) {
- +
- + case '\0':
- + {
- + l->t.t = BC_LEX_EOF;
- + break;
- + }
- +
- + case '\n':
- + case '\t':
- + case '\v':
- + case '\f':
- + case '\r':
- + case ' ':
- + {
- + l->newline = (c == '\n');
- + bc_lex_whitespace(l);
- + break;
- + }
- +
- + case '!':
- + {
- + c2 = l->buffer[l->idx];
- +
- + if (c2 == '=') l->t.t = BC_LEX_OP_REL_NE;
- + else if (c2 == '<') l->t.t = BC_LEX_OP_REL_LE;
- + else if (c2 == '>') l->t.t = BC_LEX_OP_REL_GE;
- + else return BC_STATUS_LEX_BAD_CHAR;
- +
- + ++l->idx;
- + break;
- + }
- +
- + case '#':
- + {
- + bc_lex_lineComment(l);
- + break;
- + }
- +
- + case '.':
- + {
- + if (isdigit(l->buffer[l->idx])) s = bc_lex_number(l, c);
- + else s = BC_STATUS_LEX_BAD_CHAR;
- + 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:
- + {
- + l->t.t = BC_LEX_INVALID;
- + s = BC_STATUS_LEX_BAD_CHAR;
- + break;
- + }
- + }
- +
- + return s;
- +}
- +#endif // CONFIG_DC
- +
- +BcStatus bc_parse_addFunc(BcParse *p, char *name, size_t *idx) {
- + BcStatus s = bc_program_addFunc(p->prog, name, idx);
- + p->func = bc_vec_item(&p->prog->fns, p->fidx);
- + return s;
- +}
- +
- +BcStatus bc_parse_pushName(BcParse *p, char *name) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + size_t i = 0, len = strlen(name);
- +
- + for (; !s && i < len; ++i) s = bc_parse_push(p, (char) name[i]);
- + if (s || (s = bc_parse_push(p, BC_PARSE_STREND))) return s;
- +
- + free(name);
- +
- + return s;
- +}
- +
- +BcStatus bc_parse_pushIndex(BcParse *p, size_t idx) {
- +
- + BcStatus s;
- + unsigned char amt, i, nums[sizeof(size_t)];
- +
- + for (amt = 0; idx; ++amt) {
- + nums[amt] = (char) idx;
- + idx = (idx & ((unsigned long) ~(UCHAR_MAX))) >> sizeof(char) * CHAR_BIT;
- + }
- +
- + if ((s = bc_parse_push(p, amt))) return s;
- + for (i = 0; !s && i < amt; ++i) s = bc_parse_push(p, nums[i]);
- +
- + return s;
- +}
- +
- +BcStatus bc_parse_number(BcParse *p, BcInst *prev, size_t *nexs) {
- +
- + BcStatus s;
- + char *num;
- + size_t idx = p->prog->consts.len;
- +
- + if (!(num = strdup(p->l.t.v.v))) return BC_STATUS_ALLOC_ERR;
- +
- + if ((s = bc_vec_push(&p->prog->consts, &num))) {
- + free(num);
- + return s;
- + }
- +
- + if ((s = bc_parse_push(p, BC_INST_NUM))) return s;
- + if ((s = bc_parse_pushIndex(p, idx))) return s;
- +
- + ++(*nexs);
- + (*prev) = BC_INST_NUM;
- +
- + return s;
- +}
- +
- +BcStatus bc_parse_text(BcParse *p, const char *text) {
- +
- + BcStatus s;
- +
- + p->func = bc_vec_item(&p->prog->fns, p->fidx);
- +
- + if (!strcmp(text, "") && !BC_PARSE_CAN_EXEC(p)) {
- + p->l.t.t = BC_LEX_INVALID;
- + if ((s = p->parse(p))) return s;
- + if (!BC_PARSE_CAN_EXEC(p)) return BC_STATUS_EXEC_FILE_NOT_EXECUTABLE;
- + }
- +
- + return bc_lex_text(&p->l, text);
- +}
- +
- +BcStatus bc_parse_reset(BcParse *p, BcStatus s) {
- +
- + if (p->fidx != BC_PROG_MAIN) {
- +
- + p->func->nparams = 0;
- + bc_vec_npop(&p->func->code, p->func->code.len);
- + bc_vec_npop(&p->func->autos, p->func->autos.len);
- + bc_vec_npop(&p->func->labels, p->func->labels.len);
- +
- + bc_parse_updateFunc(p, BC_PROG_MAIN);
- + }
- +
- + p->l.idx = p->l.len;
- + p->l.t.t = BC_LEX_EOF;
- + p->auto_part = (p->nbraces = 0);
- +
- + 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);
- +
- + return bc_program_reset(p->prog, s);
- +}
- +
- +void bc_parse_free(BcParse *p) {
- + bc_vec_free(&p->flags);
- + bc_vec_free(&p->exits);
- + bc_vec_free(&p->conds);
- + bc_vec_free(&p->ops);
- + bc_lex_free(&p->l);
- +}
- +
- +BcStatus bc_parse_create(BcParse *p, BcProgram *prog, size_t func,
- + BcParseParse parse, BcLexNext next)
- +{
- + BcStatus s;
- +
- + memset(p, 0, sizeof(BcParse));
- +
- + if ((s = bc_lex_init(&p->l, next))) return s;
- + if ((s = bc_vec_init(&p->flags, sizeof(uint8_t), NULL))) goto err;
- + if ((s = bc_vec_init(&p->exits, sizeof(BcInstPtr), NULL))) goto err;
- + if ((s = bc_vec_init(&p->conds, sizeof(size_t), NULL))) goto err;
- + if ((s = bc_vec_pushByte(&p->flags, 0))) goto err;
- + if ((s = bc_vec_init(&p->ops, sizeof(BcLexType), NULL))) goto err;
- +
- + p->parse = parse;
- + p->prog = prog;
- + p->auto_part = (p->nbraces = 0);
- + bc_parse_updateFunc(p, func);
- +
- + return s;
- +
- +err:
- + bc_parse_free(p);
- + return s;
- +}
- +
- +#ifdef CONFIG_BC
- +static BcStatus bc_parse_else(BcParse *p);
- +static BcStatus bc_parse_stmt(BcParse *p);
- +
- +static BcStatus bc_parse_operator(BcParse *p, BcVec *ops, BcLexType type,
- + size_t start, size_t *nexprs, bool next)
- +{
- + BcStatus s;
- + BcLexType t;
- + uint8_t l, r = bc_parse_ops[type - BC_LEX_OP_INC].prec;
- + bool left = bc_parse_ops[type - BC_LEX_OP_INC].left;
- +
- + while (ops->len > start &&
- + (t = *((BcLexType*) bc_vec_top(ops))) != BC_LEX_LPAREN &&
- + ((l = bc_parse_ops[t - BC_LEX_OP_INC].prec) < r || (l == r && left)))
- + {
- + if ((s = bc_parse_push(p, BC_PARSE_TOKEN_INST(t)))) return s;
- + bc_vec_pop(ops);
- + *nexprs -= t != BC_LEX_OP_BOOL_NOT && t != BC_LEX_NEG;
- + }
- +
- + if ((s = bc_vec_push(ops, &type))) return s;
- + if (next) s = bc_lex_next(&p->l);
- +
- + return s;
- +}
- +
- +static BcStatus bc_parse_rightParen(BcParse *p, BcVec *ops, size_t *nexs) {
- +
- + BcStatus s;
- + BcLexType top;
- +
- + if (!ops->len) return BC_STATUS_PARSE_BAD_EXP;
- +
- + while ((top = *((BcLexType*) bc_vec_top(ops))) != BC_LEX_LPAREN) {
- +
- + if ((s = bc_parse_push(p, BC_PARSE_TOKEN_INST(top)))) return s;
- +
- + bc_vec_pop(ops);
- + *nexs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_NEG;
- +
- + if (!ops->len) return BC_STATUS_PARSE_BAD_EXP;
- + }
- +
- + bc_vec_pop(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;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + for (nparams = 0; p->l.t.t != BC_LEX_RPAREN; ++nparams) {
- +
- + flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY;
- + if ((s = bc_parse_expr(p, flags, bc_parse_next_param))) return s;
- +
- + comma = p->l.t.t == BC_LEX_COMMA;
- + if (comma && (s = bc_lex_next(&p->l))) return s;
- + }
- +
- + if (comma) return BC_STATUS_PARSE_BAD_TOKEN;
- + if ((s = bc_parse_push(p, BC_INST_CALL))) return s;
- +
- + return bc_parse_pushIndex(p, nparams);
- +}
- +
- +static BcStatus bc_parse_call(BcParse *p, char *name, uint8_t flags) {
- +
- + BcStatus s;
- + BcId entry, *entry_ptr;
- + size_t idx;
- +
- + entry.name = name;
- +
- + if ((s = bc_parse_params(p, flags))) goto err;
- +
- + if (p->l.t.t != BC_LEX_RPAREN) {
- + s = BC_STATUS_PARSE_BAD_TOKEN;
- + goto err;
- + }
- +
- + idx = bc_map_index(&p->prog->fn_map, &entry);
- +
- + if (idx == BC_VEC_INVALID_IDX) {
- +
- + if ((s = bc_parse_addFunc(p, name, &idx))) return s;
- +
- + name = NULL;
- + idx = bc_map_index(&p->prog->fn_map, &entry);
- + }
- + else free(name);
- +
- + entry_ptr = bc_map_item(&p->prog->fn_map, idx);
- + if ((s = bc_parse_pushIndex(p, entry_ptr->idx))) return s;
- +
- + return bc_lex_next(&p->l);
- +
- +err:
- + free(name);
- + return s;
- +}
- +
- +static BcStatus bc_parse_name(BcParse *p, BcInst *type, uint8_t flags) {
- +
- + BcStatus s;
- + char *name;
- +
- + if (!(name = strdup(p->l.t.v.v))) return BC_STATUS_ALLOC_ERR;
- + if ((s = bc_lex_next(&p->l))) goto err;
- +
- + if (p->l.t.t == BC_LEX_LBRACKET) {
- +
- + if ((s = bc_lex_next(&p->l))) goto err;
- +
- + if (p->l.t.t == BC_LEX_RBRACKET) {
- +
- + if (!(flags & BC_PARSE_ARRAY)) {
- + s = BC_STATUS_PARSE_BAD_EXP;
- + goto err;
- + }
- +
- + *type = BC_INST_ARRAY;
- + }
- + else {
- +
- + *type = BC_INST_ARRAY_ELEM;
- +
- + flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
- + if ((s = bc_parse_expr(p, flags, bc_parse_next_elem))) goto err;
- + }
- +
- + if ((s = bc_lex_next(&p->l))) goto err;
- + if ((s = bc_parse_push(p, (char) *type))) goto err;
- + s = bc_parse_pushName(p, name);
- + }
- + else if (p->l.t.t == BC_LEX_LPAREN) {
- +
- + if (flags & BC_PARSE_NOCALL) {
- + s = BC_STATUS_PARSE_BAD_TOKEN;
- + goto err;
- + }
- +
- + *type = BC_INST_CALL;
- + s = bc_parse_call(p, name, flags);
- + }
- + else {
- + *type = BC_INST_VAR;
- + if ((s = bc_parse_push(p, BC_INST_VAR))) goto err;
- + s = bc_parse_pushName(p, name);
- + }
- +
- + return s;
- +
- +err:
- + free(name);
- + return s;
- +}
- +
- +static BcStatus bc_parse_read(BcParse *p) {
- +
- + BcStatus s;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_LPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_RPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + if ((s = bc_parse_push(p, BC_INST_READ))) return s;
- +
- + return bc_lex_next(&p->l);
- +}
- +
- +static BcStatus bc_parse_builtin(BcParse *p, BcLexType type,
- + uint8_t flags, BcInst *prev)
- +{
- + BcStatus s;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_LPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if ((s = bc_parse_expr(p, flags, bc_parse_next_rel))) return s;
- +
- + if (p->l.t.t != BC_LEX_RPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + *prev = (type == BC_LEX_KEY_LENGTH) ? BC_INST_LENGTH : BC_INST_SQRT;
- + if ((s = bc_parse_push(p, (char) *prev))) return s;
- +
- + return bc_lex_next(&p->l);
- +}
- +
- +static BcStatus bc_parse_scale(BcParse *p, BcInst *type, uint8_t flags) {
- +
- + BcStatus s;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + if (p->l.t.t != BC_LEX_LPAREN) {
- + *type = BC_INST_SCALE;
- + return bc_parse_push(p, BC_INST_SCALE);
- + }
- +
- + *type = BC_INST_SCALE_FUNC;
- + flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if ((s = bc_parse_expr(p, flags, bc_parse_next_rel))) return s;
- + if (p->l.t.t != BC_LEX_RPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- + if ((s = bc_parse_push(p, BC_INST_SCALE_FUNC))) return s;
- +
- + return bc_lex_next(&p->l);
- +}
- +
- +static BcStatus bc_parse_incdec(BcParse *p, BcInst *prev, bool *paren_expr,
- + size_t *nexprs, uint8_t flags)
- +{
- + BcStatus s;
- + BcLexType type;
- + char inst;
- + BcInst etype = *prev;
- +
- + if (etype == BC_INST_VAR || etype == BC_INST_ARRAY_ELEM ||
- + etype == BC_INST_SCALE || etype == BC_INST_LAST ||
- + etype == BC_INST_IBASE || etype == BC_INST_OBASE)
- + {
- + *prev = inst = BC_INST_INC_POST + (p->l.t.t != BC_LEX_OP_INC);
- + if ((s = bc_parse_push(p, inst))) return s;
- + s = bc_lex_next(&p->l);
- + }
- + else {
- +
- + *prev = inst = BC_INST_INC_PRE + (p->l.t.t != BC_LEX_OP_INC);
- + *paren_expr = true;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + type = p->l.t.t;
- +
- + // Because we parse the next part of the expression
- + // right here, we need to increment this.
- + *nexprs = *nexprs + 1;
- +
- + switch (type) {
- +
- + case BC_LEX_NAME:
- + {
- + s = bc_parse_name(p, prev, flags | BC_PARSE_NOCALL);
- + break;
- + }
- +
- + case BC_LEX_KEY_IBASE:
- + {
- + if ((s = bc_parse_push(p, BC_INST_IBASE))) return s;
- + s = bc_lex_next(&p->l);
- + break;
- + }
- +
- + case BC_LEX_KEY_LAST:
- + {
- + if ((s = bc_parse_push(p, BC_INST_LAST))) return s;
- + s = bc_lex_next(&p->l);
- + break;
- + }
- +
- + case BC_LEX_KEY_OBASE:
- + {
- + if ((s = bc_parse_push(p, BC_INST_OBASE))) return s;
- + s = bc_lex_next(&p->l);
- + break;
- + }
- +
- + case BC_LEX_KEY_SCALE:
- + {
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t == BC_LEX_LPAREN)
- + return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + s = bc_parse_push(p, BC_INST_SCALE);
- +
- + break;
- + }
- +
- + default:
- + {
- + return BC_STATUS_PARSE_BAD_TOKEN;
- + }
- + }
- +
- + if (!s) s = bc_parse_push(p, inst);
- + }
- +
- + return s;
- +}
- +
- +static BcStatus bc_parse_minus(BcParse *p, BcVec *ops, BcInst *prev,
- + size_t start, bool rparen, size_t *nexprs)
- +{
- + BcStatus s;
- + BcLexType type;
- + BcInst etype = *prev;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + type = rparen || etype == BC_INST_INC_POST || etype == BC_INST_DEC_POST ||
- + (etype >= BC_INST_NUM && etype <= BC_INST_SQRT) ?
- + BC_LEX_OP_MINUS : BC_LEX_NEG;
- + *prev = BC_PARSE_TOKEN_INST(type);
- +
- + if (type == BC_LEX_OP_MINUS)
- + s = bc_parse_operator(p, ops, type, start, nexprs, false);
- + else
- + // We can just push onto the op stack because this is the largest
- + // precedence operator that gets pushed. Inc/dec does not.
- + s = bc_vec_push(ops, &type);
- +
- + return s;
- +}
- +
- +static BcStatus bc_parse_string(BcParse *p, char inst) {
- +
- + BcStatus s;
- + char *str;
- +
- + if (!(str = strdup(p->l.t.v.v))) return BC_STATUS_ALLOC_ERR;
- + if ((s = bc_parse_push(p, BC_INST_STR))) goto err;
- + if ((s = bc_parse_pushIndex(p, p->prog->strs.len))) goto err;
- + if ((s = bc_vec_push(&p->prog->strs, &str))) goto err;
- + if ((s = bc_parse_push(p, inst))) return s;
- +
- + return bc_lex_next(&p->l);
- +
- +err:
- + free(str);
- + return s;
- +}
- +
- +static BcStatus bc_parse_print(BcParse *p) {
- +
- + BcStatus s;
- + BcLexType type;
- + bool comma = false;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + type = p->l.t.t;
- +
- + if (type == BC_LEX_SCOLON || type == BC_LEX_NLINE)
- + return BC_STATUS_PARSE_BAD_PRINT;
- +
- + while (!s && type != BC_LEX_SCOLON && type != BC_LEX_NLINE) {
- +
- + if (type == BC_LEX_STR) s = bc_parse_string(p, BC_INST_PRINT_POP);
- + else {
- + if ((s = bc_parse_expr(p, 0, bc_parse_next_print))) return s;
- + s = bc_parse_push(p, BC_INST_PRINT_POP);
- + }
- +
- + if (s) return s;
- + if ((comma = p->l.t.t == BC_LEX_COMMA)) s = bc_lex_next(&p->l);
- + type = p->l.t.t;
- + }
- +
- + if (s) return s;
- + if (comma) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + return bc_lex_next(&p->l);
- +}
- +
- +static BcStatus bc_parse_return(BcParse *p) {
- +
- + BcStatus s;
- + BcLexType t;
- + bool paren;
- +
- + if (!BC_PARSE_FUNC(p)) return BC_STATUS_PARSE_BAD_TOKEN;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + t = p->l.t.t;
- + paren = t == BC_LEX_LPAREN;
- +
- + if (t == BC_LEX_NLINE || t == BC_LEX_SCOLON)
- + s = bc_parse_push(p, BC_INST_RET0);
- + else {
- +
- + s = bc_parse_expr(p, 0, bc_parse_next_expr);
- + if (s && s != BC_STATUS_PARSE_EMPTY_EXP) return s;
- + else if (s == BC_STATUS_PARSE_EMPTY_EXP) {
- + if ((s = bc_parse_push(p, BC_INST_RET0))) return s;
- + if ((s = bc_lex_next(&p->l))) return s;
- + }
- +
- + if ((!paren || p->l.t.last != BC_LEX_RPAREN) &&
- + (s = bc_vm_posixError(BC_STATUS_POSIX_RET_PARENS,
- + p->l.f, p->l.line, NULL)))
- + {
- + return s;
- + }
- +
- + s = bc_parse_push(p, BC_INST_RET);
- + }
- +
- + return s;
- +}
- +
- +static BcStatus bc_parse_endBody(BcParse *p, bool brace) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- +
- + if (p->flags.len <= 1 || (brace && p->nbraces == 0))
- + return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + if (brace) {
- +
- + if (p->l.t.t == BC_LEX_RBRACE) {
- + if (!p->nbraces) return BC_STATUS_PARSE_BAD_TOKEN;
- + --p->nbraces;
- + if ((s = bc_lex_next(&p->l))) return s;
- + }
- + else return BC_STATUS_PARSE_BAD_TOKEN;
- + }
- +
- + if (BC_PARSE_IF(p)) {
- +
- + uint8_t *flag_ptr;
- +
- + while (p->l.t.t == BC_LEX_NLINE) {
- + if ((s = bc_lex_next(&p->l))) return s;
- + }
- +
- + bc_vec_pop(&p->flags);
- +
- + flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
- + *flag_ptr = (*flag_ptr | BC_PARSE_FLAG_IF_END);
- +
- + if (p->l.t.t == BC_LEX_KEY_ELSE) s = bc_parse_else(p);
- + }
- + else if (BC_PARSE_ELSE(p)) {
- +
- + BcInstPtr *ip;
- + size_t *label;
- +
- + bc_vec_pop(&p->flags);
- +
- + ip = bc_vec_top(&p->exits);
- + label = bc_vec_item(&p->func->labels, ip->idx);
- + *label = p->func->code.len;
- +
- + bc_vec_pop(&p->exits);
- + }
- + else if (BC_PARSE_FUNC_INNER(p)) {
- + if ((s = bc_parse_push(p, BC_INST_RET0))) return s;
- + bc_parse_updateFunc(p, BC_PROG_MAIN);
- + bc_vec_pop(&p->flags);
- + }
- + else {
- +
- + BcInstPtr *ip = bc_vec_top(&p->exits);
- + size_t *label = bc_vec_top(&p->conds);
- +
- + if ((s = bc_parse_push(p, BC_INST_JUMP))) return s;
- + if ((s = bc_parse_pushIndex(p, *label))) return s;
- +
- + label = bc_vec_item(&p->func->labels, ip->idx);
- + *label = p->func->code.len;
- +
- + bc_vec_pop(&p->flags);
- + bc_vec_pop(&p->exits);
- + bc_vec_pop(&p->conds);
- + }
- +
- + return s;
- +}
- +
- +static BcStatus bc_parse_startBody(BcParse *p, uint8_t flags) {
- + uint8_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
- + flags |= (*flag_ptr & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_LOOP));
- + flags |= BC_PARSE_FLAG_BODY;
- + return bc_vec_push(&p->flags, &flags);
- +}
- +
- +static void bc_parse_noElse(BcParse *p) {
- +
- + BcInstPtr *ip;
- + size_t *label;
- + uint8_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
- +
- + *flag_ptr = (*flag_ptr & ~(BC_PARSE_FLAG_IF_END));
- +
- + ip = bc_vec_top(&p->exits);
- + label = bc_vec_item(&p->func->labels, ip->idx);
- + *label = p->func->code.len;
- +
- + bc_vec_pop(&p->exits);
- +}
- +
- +static BcStatus bc_parse_if(BcParse *p) {
- +
- + BcStatus s;
- + BcInstPtr ip;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_LPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if ((s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel))) return s;
- + if (p->l.t.t != BC_LEX_RPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if ((s = bc_parse_push(p, BC_INST_JUMP_ZERO))) return s;
- +
- + ip.idx = p->func->labels.len;
- + ip.func = ip.len = 0;
- +
- + if ((s = bc_parse_pushIndex(p, ip.idx))) return s;
- + if ((s = bc_vec_push(&p->exits, &ip))) return s;
- + if ((s = bc_vec_push(&p->func->labels, &ip.idx))) return s;
- +
- + return bc_parse_startBody(p, BC_PARSE_FLAG_IF);
- +}
- +
- +static BcStatus bc_parse_else(BcParse *p) {
- +
- + BcStatus s;
- + BcInstPtr ip;
- +
- + if (!BC_PARSE_IF_END(p)) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + ip.idx = p->func->labels.len;
- + ip.func = ip.len = 0;
- +
- + if ((s = bc_parse_push(p, BC_INST_JUMP))) return s;
- + if ((s = bc_parse_pushIndex(p, ip.idx))) return s;
- +
- + bc_parse_noElse(p);
- +
- + if ((s = bc_vec_push(&p->exits, &ip))) return s;
- + if ((s = bc_vec_push(&p->func->labels, &ip.idx))) return s;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + return bc_parse_startBody(p, BC_PARSE_FLAG_ELSE);
- +}
- +
- +static BcStatus bc_parse_while(BcParse *p) {
- +
- + BcStatus s;
- + BcInstPtr ip;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_LPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + ip.idx = p->func->labels.len;
- +
- + if ((s = bc_vec_push(&p->func->labels, &p->func->code.len))) return s;
- + if ((s = bc_vec_push(&p->conds, &ip.idx))) return s;
- +
- + ip.idx = p->func->labels.len;
- + ip.func = 1;
- + ip.len = 0;
- +
- + if ((s = bc_vec_push(&p->exits, &ip))) return s;
- + if ((s = bc_vec_push(&p->func->labels, &ip.idx))) return s;
- +
- + if ((s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel))) return s;
- + if (p->l.t.t != BC_LEX_RPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if ((s = bc_parse_push(p, BC_INST_JUMP_ZERO))) return s;
- + if ((s = bc_parse_pushIndex(p, ip.idx))) return s;
- +
- + return bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
- +}
- +
- +static BcStatus bc_parse_for(BcParse *p) {
- +
- + BcStatus s;
- + BcInstPtr ip;
- + size_t cond_idx, exit_idx, body_idx, update_idx;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_LPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + if (p->l.t.t != BC_LEX_SCOLON)
- + s = bc_parse_expr(p, 0, bc_parse_next_for);
- + else
- + s = bc_vm_posixError(BC_STATUS_POSIX_FOR_INIT, p->l.f, p->l.line, NULL);
- +
- + if (s) return s;
- + if (p->l.t.t != BC_LEX_SCOLON) return BC_STATUS_PARSE_BAD_TOKEN;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + cond_idx = p->func->labels.len;
- + update_idx = cond_idx + 1;
- + body_idx = update_idx + 1;
- + exit_idx = body_idx + 1;
- +
- + if ((s = bc_vec_push(&p->func->labels, &p->func->code.len))) return s;
- +
- + if (p->l.t.t != BC_LEX_SCOLON)
- + s = bc_parse_expr(p, BC_PARSE_REL, bc_parse_next_for);
- + else
- + s = bc_vm_posixError(BC_STATUS_POSIX_FOR_COND, p->l.f, p->l.line, NULL);
- +
- + if (s) return s;
- + if (p->l.t.t != BC_LEX_SCOLON) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if ((s = bc_parse_push(p, BC_INST_JUMP_ZERO))) return s;
- + if ((s = bc_parse_pushIndex(p, exit_idx))) return s;
- + if ((s = bc_parse_push(p, BC_INST_JUMP))) return s;
- + if ((s = bc_parse_pushIndex(p, body_idx))) return s;
- +
- + ip.idx = p->func->labels.len;
- +
- + if ((s = bc_vec_push(&p->conds, &update_idx))) return s;
- + if ((s = bc_vec_push(&p->func->labels, &p->func->code.len))) return s;
- +
- + if (p->l.t.t != BC_LEX_RPAREN)
- + s = bc_parse_expr(p, 0, bc_parse_next_rel);
- + else
- + s = bc_vm_posixError(BC_STATUS_POSIX_FOR_END, p->l.f, p->l.line, NULL);
- +
- + if (s) return s;
- +
- + if (p->l.t.t != BC_LEX_RPAREN) return BC_STATUS_PARSE_BAD_TOKEN;
- + if ((s = bc_parse_push(p, BC_INST_JUMP))) return s;
- + if ((s = bc_parse_pushIndex(p, cond_idx))) return s;
- + if ((s = bc_vec_push(&p->func->labels, &p->func->code.len))) return s;
- +
- + ip.idx = exit_idx;
- + ip.func = 1;
- + ip.len = 0;
- +
- + if ((s = bc_vec_push(&p->exits, &ip))) return s;
- + if ((s = bc_vec_push(&p->func->labels, &ip.idx))) return s;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + return bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
- +}
- +
- +static BcStatus bc_parse_loopExit(BcParse *p, BcLexType type) {
- +
- + BcStatus s;
- + size_t i;
- + BcInstPtr *ip;
- +
- + if (!BC_PARSE_LOOP(p)) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + if (type == BC_LEX_KEY_BREAK) {
- +
- + if (!p->exits.len) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + i = p->exits.len - 1;
- + ip = bc_vec_item(&p->exits, i);
- +
- + while (!ip->func && i < p->exits.len) ip = bc_vec_item(&p->exits, i--);
- + if (i >= p->exits.len && !ip->func) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + i = ip->idx;
- + }
- + else i = *((size_t*) bc_vec_top(&p->conds));
- +
- + if ((s = bc_parse_push(p, BC_INST_JUMP))) return s;
- + if ((s = bc_parse_pushIndex(p, i))) return s;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + if (p->l.t.t != BC_LEX_SCOLON && p->l.t.t != BC_LEX_NLINE)
- + return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + return bc_lex_next(&p->l);
- +}
- +
- +static BcStatus bc_parse_func(BcParse *p) {
- +
- + BcStatus s;
- + bool var, comma = false;
- + uint8_t flags;
- + char *name;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_NAME) return BC_STATUS_PARSE_BAD_FUNC;
- +
- + if (!(name = strdup(p->l.t.v.v))) return BC_STATUS_ALLOC_ERR;
- + if ((s = bc_parse_addFunc(p, name, &p->fidx))) return s;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_LPAREN) return BC_STATUS_PARSE_BAD_FUNC;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + while (!s && p->l.t.t != BC_LEX_RPAREN) {
- +
- + if (p->l.t.t != BC_LEX_NAME) return BC_STATUS_PARSE_BAD_FUNC;
- +
- + ++p->func->nparams;
- +
- + if (!(name = strdup(p->l.t.v.v))) return BC_STATUS_ALLOC_ERR;
- + if ((s = bc_lex_next(&p->l))) goto err;
- +
- + var = p->l.t.t != BC_LEX_LBRACKET;
- +
- + if (!var) {
- +
- + if ((s = bc_lex_next(&p->l))) goto err;
- +
- + if (p->l.t.t != BC_LEX_RBRACKET) {
- + s = BC_STATUS_PARSE_BAD_FUNC;
- + goto err;
- + }
- +
- + if ((s = bc_lex_next(&p->l))) goto err;
- + }
- +
- + comma = p->l.t.t == BC_LEX_COMMA;
- + if (comma && (s = bc_lex_next(&p->l))) goto err;
- +
- + if ((s = bc_func_insert(p->func, name, var))) goto err;
- + }
- +
- + if (comma) return BC_STATUS_PARSE_BAD_FUNC;
- +
- + flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_BODY;
- +
- + if ((s = bc_parse_startBody(p, flags))) return s;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + if (p->l.t.t != BC_LEX_LBRACE)
- + s = bc_vm_posixError(BC_STATUS_POSIX_BRACE, p->l.f, p->l.line, NULL);
- +
- + return s;
- +
- +err:
- + free(name);
- + return s;
- +}
- +
- +static BcStatus bc_parse_auto(BcParse *p) {
- +
- + BcStatus s;
- + bool comma, var, one;
- + char *name;
- +
- + if (!p->auto_part) return BC_STATUS_PARSE_BAD_TOKEN;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + p->auto_part = comma = false;
- + one = p->l.t.t == BC_LEX_NAME;
- +
- + while (!s && p->l.t.t == BC_LEX_NAME) {
- +
- + if (!(name = strdup(p->l.t.v.v))) return BC_STATUS_ALLOC_ERR;
- + if ((s = bc_lex_next(&p->l))) goto err;
- +
- + if (!(var = p->l.t.t != BC_LEX_LBRACKET)) {
- +
- + if ((s = bc_lex_next(&p->l))) goto err;
- +
- + if (p->l.t.t != BC_LEX_RBRACKET) {
- + s = BC_STATUS_PARSE_BAD_FUNC;
- + goto err;
- + }
- +
- + if ((s = bc_lex_next(&p->l))) goto err;
- + }
- +
- + comma = p->l.t.t == BC_LEX_COMMA;
- + if (comma && (s = bc_lex_next(&p->l))) goto err;
- +
- + if ((s = bc_func_insert(p->func, name, var))) goto err;
- + }
- +
- + if (comma) return BC_STATUS_PARSE_BAD_FUNC;
- + if (!one) return BC_STATUS_PARSE_NO_AUTO;
- +
- + if (p->l.t.t != BC_LEX_NLINE && p->l.t.t != BC_LEX_SCOLON)
- + return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + return bc_lex_next(&p->l);
- +
- +err:
- + free(name);
- + return s;
- +}
- +
- +static BcStatus bc_parse_body(BcParse *p, bool brace) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + uint8_t *flag_ptr = bc_vec_top(&p->flags);
- +
- + *flag_ptr &= ~(BC_PARSE_FLAG_BODY);
- +
- + if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) {
- +
- + if (!brace) return BC_STATUS_PARSE_BAD_TOKEN;
- + p->auto_part = p->l.t.t != BC_LEX_KEY_AUTO;
- +
- + if (!p->auto_part && (s = bc_parse_auto(p))) return s;
- + if (p->l.t.t == BC_LEX_NLINE) s = bc_lex_next(&p->l);
- + }
- + else {
- + if ((s = bc_parse_stmt(p))) return s;
- + if (!brace) s = bc_parse_endBody(p, false);
- + }
- +
- + return s;
- +}
- +
- +static BcStatus bc_parse_stmt(BcParse *p) {
- +
- + BcStatus s;
- +
- + switch (p->l.t.t) {
- +
- + case BC_LEX_NLINE:
- + {
- + return bc_lex_next(&p->l);
- + }
- +
- + case BC_LEX_KEY_ELSE:
- + {
- + p->auto_part = false;
- + break;
- + }
- +
- + case BC_LEX_LBRACE:
- + {
- + if (!BC_PARSE_BODY(p)) return BC_STATUS_PARSE_BAD_TOKEN;
- +
- + ++p->nbraces;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + return bc_parse_body(p, true);
- + }
- +
- + case BC_LEX_KEY_AUTO:
- + {
- + return bc_parse_auto(p);
- + }
- +
- + default:
- + {
- + p->auto_part = false;
- +
- + if (BC_PARSE_IF_END(p)) {
- + bc_parse_noElse(p);
- + return BC_STATUS_SUCCESS;
- + }
- + else if (BC_PARSE_BODY(p)) return bc_parse_body(p, false);
- +
- + break;
- + }
- + }
- +
- + switch (p->l.t.t) {
- +
- + 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_KEY_IBASE:
- + case BC_LEX_KEY_LAST:
- + case BC_LEX_KEY_LENGTH:
- + case BC_LEX_KEY_OBASE:
- + case BC_LEX_KEY_READ:
- + case BC_LEX_KEY_SCALE:
- + case BC_LEX_KEY_SQRT:
- + {
- + s = bc_parse_expr(p, BC_PARSE_PRINT, bc_parse_next_expr);
- + break;
- + }
- +
- + case BC_LEX_KEY_ELSE:
- + {
- + s = bc_parse_else(p);
- + break;
- + }
- +
- + case BC_LEX_SCOLON:
- + {
- + s = BC_STATUS_SUCCESS;
- + while (!s && p->l.t.t == BC_LEX_SCOLON) s = bc_lex_next(&p->l);
- + break;
- + }
- +
- + case BC_LEX_RBRACE:
- + {
- + s = bc_parse_endBody(p, true);
- + break;
- + }
- +
- + case BC_LEX_STR:
- + {
- + s = bc_parse_string(p, BC_INST_PRINT_STR);
- + break;
- + }
- +
- + case BC_LEX_KEY_BREAK:
- + case BC_LEX_KEY_CONTINUE:
- + {
- + s = bc_parse_loopExit(p, p->l.t.t);
- + break;
- + }
- +
- + case BC_LEX_KEY_FOR:
- + {
- + s = bc_parse_for(p);
- + break;
- + }
- +
- + case BC_LEX_KEY_HALT:
- + {
- + if ((s = bc_parse_push(p, BC_INST_HALT))) return s;
- + s = bc_lex_next(&p->l);
- + break;
- + }
- +
- + case BC_LEX_KEY_IF:
- + {
- + s = bc_parse_if(p);
- + break;
- + }
- +
- + case BC_LEX_KEY_LIMITS:
- + {
- + if ((s = bc_lex_next(&p->l))) return s;
- + s = BC_STATUS_LIMITS;
- + break;
- + }
- +
- + case BC_LEX_KEY_PRINT:
- + {
- + s = bc_parse_print(p);
- + break;
- + }
- +
- + case BC_LEX_KEY_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_KEY_RETURN:
- + {
- + if ((s = bc_parse_return(p))) return s;
- + break;
- + }
- +
- + case BC_LEX_KEY_WHILE:
- + {
- + s = bc_parse_while(p);
- + break;
- + }
- +
- + default:
- + {
- + s = BC_STATUS_PARSE_BAD_TOKEN;
- + break;
- + }
- + }
- +
- + return s;
- +}
- +
- +static BcStatus bc_parse_parse(BcParse *p) {
- +
- + BcStatus s;
- +
- + if (p->l.t.t == BC_LEX_EOF)
- + s = p->flags.len > 0 ? BC_STATUS_PARSE_NO_BLOCK_END : BC_STATUS_LEX_EOF;
- + else if (p->l.t.t == BC_LEX_KEY_DEFINE) {
- + if (!BC_PARSE_CAN_EXEC(p)) return BC_STATUS_PARSE_BAD_TOKEN;
- + s = bc_parse_func(p);
- + }
- + else s = bc_parse_stmt(p);
- +
- + if (s || bcg.signe) s = bc_parse_reset(p, s);
- +
- + return s;
- +}
- +
- +BcStatus bc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + BcInst prev = BC_INST_PRINT;
- + BcLexType top, t = p->l.t.t;
- + size_t nexprs = 0, ops_start = p->ops.len;
- + uint32_t i, nparens, nrelops;
- + bool paren_first, paren_expr, rprn, done, get_token, assign, bin_last;
- +
- + paren_first = p->l.t.t == BC_LEX_LPAREN;
- + nparens = nrelops = 0;
- + paren_expr = rprn = done = get_token = assign = false;
- + bin_last = true;
- +
- + for (; !bcg.signe && !s && !done && bc_parse_exprs[t]; t = p->l.t.t)
- + {
- + switch (t) {
- +
- + case BC_LEX_OP_INC:
- + case BC_LEX_OP_DEC:
- + {
- + s = bc_parse_incdec(p, &prev, &paren_expr, &nexprs, flags);
- + rprn = get_token = bin_last = false;
- + break;
- + }
- +
- + case BC_LEX_OP_MINUS:
- + {
- + s = bc_parse_minus(p, &p->ops, &prev, ops_start, rprn, &nexprs);
- + rprn = get_token = false;
- + bin_last = prev == BC_INST_MINUS;
- + 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:
- + case BC_LEX_OP_ASSIGN:
- + {
- + if (prev != BC_INST_VAR && prev != BC_INST_ARRAY_ELEM &&
- + prev != BC_INST_SCALE && prev != BC_INST_IBASE &&
- + prev != BC_INST_OBASE && prev != BC_INST_LAST)
- + {
- + s = BC_STATUS_PARSE_BAD_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:
- + 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 (((t == BC_LEX_OP_BOOL_NOT) != bin_last) ||
- + (t != BC_LEX_OP_BOOL_NOT && prev == BC_INST_BOOL_NOT))
- + {
- + return BC_STATUS_PARSE_BAD_EXP;
- + }
- +
- + nrelops += t >= BC_LEX_OP_REL_EQ && t <= BC_LEX_OP_REL_GT;
- + prev = BC_PARSE_TOKEN_INST(t);
- + s = bc_parse_operator(p, &p->ops, t, ops_start, &nexprs, true);
- + rprn = get_token = false;
- + bin_last = t != BC_LEX_OP_BOOL_NOT;
- +
- + break;
- + }
- +
- + case BC_LEX_LPAREN:
- + {
- + if (BC_PARSE_LEAF(prev, rprn)) return BC_STATUS_PARSE_BAD_EXP;
- +
- + ++nparens;
- + paren_expr = rprn = bin_last = false;
- + get_token = true;
- + s = bc_vec_push(&p->ops, &t);
- +
- + break;
- + }
- +
- + case BC_LEX_RPAREN:
- + {
- + if (bin_last || prev == BC_INST_BOOL_NOT)
- + return BC_STATUS_PARSE_BAD_EXP;
- +
- + if (nparens == 0) {
- + s = BC_STATUS_SUCCESS;
- + done = true;
- + get_token = false;
- + break;
- + }
- + else if (!paren_expr) return BC_STATUS_PARSE_EMPTY_EXP;
- +
- + --nparens;
- + paren_expr = rprn = true;
- + get_token = bin_last = false;
- +
- + s = bc_parse_rightParen(p, &p->ops, &nexprs);
- +
- + break;
- + }
- +
- + case BC_LEX_NAME:
- + {
- + if (BC_PARSE_LEAF(prev, rprn)) return BC_STATUS_PARSE_BAD_EXP;
- +
- + paren_expr = true;
- + rprn = get_token = bin_last = false;
- + s = bc_parse_name(p, &prev, flags & ~BC_PARSE_NOCALL);
- + ++nexprs;
- +
- + break;
- + }
- +
- + case BC_LEX_NUMBER:
- + {
- + if (BC_PARSE_LEAF(prev, rprn)) return BC_STATUS_PARSE_BAD_EXP;
- +
- + s = bc_parse_number(p, &prev, &nexprs);
- + paren_expr = get_token = true;
- + rprn = bin_last = false;
- +
- + break;
- + }
- +
- + case BC_LEX_KEY_IBASE:
- + case BC_LEX_KEY_LAST:
- + case BC_LEX_KEY_OBASE:
- + {
- + if (BC_PARSE_LEAF(prev, rprn)) return BC_STATUS_PARSE_BAD_EXP;
- +
- + prev = (char) (t - BC_LEX_KEY_IBASE + BC_INST_IBASE);
- + s = bc_parse_push(p, (char) prev);
- +
- + paren_expr = get_token = true;
- + rprn = bin_last = false;
- + ++nexprs;
- +
- + break;
- + }
- +
- + case BC_LEX_KEY_LENGTH:
- + case BC_LEX_KEY_SQRT:
- + {
- + if (BC_PARSE_LEAF(prev, rprn)) return BC_STATUS_PARSE_BAD_EXP;
- +
- + s = bc_parse_builtin(p, t, flags, &prev);
- + paren_expr = true;
- + rprn = get_token = bin_last = false;
- + ++nexprs;
- +
- + break;
- + }
- +
- + case BC_LEX_KEY_READ:
- + {
- + if (BC_PARSE_LEAF(prev, rprn)) return BC_STATUS_PARSE_BAD_EXP;
- + else if (flags & BC_PARSE_NOREAD) s = BC_STATUS_EXEC_REC_READ;
- + else s = bc_parse_read(p);
- +
- + paren_expr = true;
- + rprn = get_token = bin_last = false;
- + ++nexprs;
- + prev = BC_INST_READ;
- +
- + break;
- + }
- +
- + case BC_LEX_KEY_SCALE:
- + {
- + if (BC_PARSE_LEAF(prev, rprn)) return BC_STATUS_PARSE_BAD_EXP;
- +
- + s = bc_parse_scale(p, &prev, flags);
- + paren_expr = true;
- + rprn = get_token = bin_last = false;
- + ++nexprs;
- + prev = BC_INST_SCALE;
- +
- + break;
- + }
- +
- + default:
- + {
- + s = BC_STATUS_PARSE_BAD_TOKEN;
- + break;
- + }
- + }
- +
- + if (!s && get_token) s = bc_lex_next(&p->l);
- + }
- +
- + if (s) return s;
- + if (bcg.signe) return BC_STATUS_EXEC_SIGNAL;
- +
- + while (!s && p->ops.len > ops_start) {
- +
- + top = *((BcLexType*) bc_vec_top(&p->ops));
- + assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN;
- +
- + if (top == BC_LEX_LPAREN || top == BC_LEX_RPAREN)
- + return BC_STATUS_PARSE_BAD_EXP;
- +
- + if ((s = bc_parse_push(p, BC_PARSE_TOKEN_INST(top)))) return s;
- +
- + nexprs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_NEG;
- + bc_vec_pop(&p->ops);
- + }
- +
- + s = BC_STATUS_PARSE_BAD_EXP;
- + if (prev == BC_INST_BOOL_NOT || nexprs != 1) return s;
- +
- + for (i = 0; s && i < next.len; ++i) s *= t != next.tokens[i];
- + if (s) return s;
- +
- + if (!(flags & BC_PARSE_REL) && nrelops &&
- + (s = bc_vm_posixError(BC_STATUS_POSIX_REL_POS,
- + p->l.f, p->l.line, NULL)))
- + {
- + return s;
- + }
- + else if ((flags & BC_PARSE_REL) && nrelops > 1 &&
- + (s = bc_vm_posixError(BC_STATUS_POSIX_MULTIPLE_REL,
- + p->l.f, p->l.line, NULL)))
- + {
- + return s;
- + }
- +
- + if (flags & BC_PARSE_PRINT) {
- +
- + if (paren_first || !assign) {
- + if ((s = bc_parse_push(p, BC_INST_PRINT))) return s;
- + }
- +
- + s = bc_parse_push(p, BC_INST_POP);
- + }
- +
- + return s;
- +}
- +
- +BcStatus bc_parse_init(BcParse *p, BcProgram *prog, size_t func) {
- + return bc_parse_create(p, prog, func, bc_parse_parse, bc_lex_token);
- +}
- +
- +BcStatus bc_parse_expression(BcParse *p, uint8_t flags) {
- + return bc_parse_expr(p, flags, bc_parse_next_read);
- +}
- +#endif // CONFIG_BC
- +
- +#ifdef CONFIG_DC
- +static BcStatus dc_parse_register(BcParse *p) {
- +
- + BcStatus s;
- + char *name;
- +
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_NAME) return BC_STATUS_PARSE_BAD_TOKEN;
- + if (!(name = strdup(p->l.t.v.v))) return BC_STATUS_ALLOC_ERR;
- + if ((s = bc_parse_pushName(p, name))) free(name);
- +
- + return s;
- +}
- +
- +static BcStatus dc_parse_string(BcParse *p) {
- +
- + BcStatus s = BC_STATUS_ALLOC_ERR;
- + char *str, *name, b[DC_PARSE_BUF_LEN + 1];
- + size_t idx, len = p->prog->strs.len;
- +
- + if (sprintf(b, "%0*zu", DC_PARSE_BUF_LEN, len) < 0) return BC_STATUS_IO_ERR;
- + if (!(name = strdup(b))) return s;
- +
- + if (!(str = strdup(p->l.t.v.v))) goto str_err;
- + if ((s = bc_parse_push(p, BC_INST_STR))) goto err;
- + if ((s = bc_parse_pushIndex(p, len))) goto err;
- + if ((s = bc_vec_push(&p->prog->strs, &str))) goto err;
- + if ((s = bc_parse_addFunc(p, name, &idx))) return s;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + return s;
- +
- +err:
- + free(str);
- +str_err:
- + free(name);
- + return s;
- +}
- +
- +static BcStatus dc_parse_mem(BcParse *p, uint8_t inst, bool name, bool store) {
- +
- + BcStatus s;
- +
- + if ((s = bc_parse_push(p, inst))) return s;
- + if (name && (s = dc_parse_register(p))) return s;
- +
- + if (store) {
- + if ((s = bc_parse_push(p, BC_INST_SWAP))) return s;
- + if ((s = bc_parse_push(p, BC_INST_ASSIGN))) return s;
- + if ((s = bc_parse_push(p, BC_INST_POP))) return s;
- + }
- +
- + return bc_lex_next(&p->l);
- +}
- +
- +static BcStatus dc_parse_cond(BcParse *p, uint8_t inst) {
- +
- + BcStatus s;
- +
- + if ((s = bc_parse_push(p, inst))) return s;
- + if ((s = bc_parse_push(p, BC_INST_EXEC_COND))) return s;
- + if ((s = dc_parse_register(p))) return s;
- + if ((s = bc_lex_next(&p->l))) return s;
- +
- + if (p->l.t.t == BC_LEX_ELSE) {
- + if ((s = dc_parse_register(p))) return s;
- + s = bc_lex_next(&p->l);
- + }
- + else s = bc_parse_push(p, BC_PARSE_STREND);
- +
- + return s;
- +}
- +
- +static BcStatus dc_parse_token(BcParse *p, BcLexType t, uint8_t flags) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + BcInst prev;
- + uint8_t 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:
- + {
- + s = dc_parse_cond(p, t - BC_LEX_OP_REL_EQ + BC_INST_REL_EQ);
- + 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:
- + case BC_LEX_NUMBER:
- + {
- + if (t == BC_LEX_NEG) {
- + if ((s = bc_lex_next(&p->l))) return s;
- + if (p->l.t.t != BC_LEX_NUMBER) return BC_STATUS_PARSE_BAD_TOKEN;
- + }
- +
- + s = bc_parse_number(p, &prev, &p->nbraces);
- +
- + if (t == BC_LEX_NEG && !s) s = bc_parse_push(p, BC_INST_NEG);
- + get_token = true;
- +
- + break;
- + }
- +
- + case BC_LEX_KEY_READ:
- + {
- + if (flags & BC_PARSE_NOREAD) s = BC_STATUS_EXEC_REC_READ;
- + else s = 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_SCALE:
- + case BC_LEX_STORE_OBASE:
- + {
- + inst = t - BC_LEX_STORE_IBASE + BC_INST_IBASE;
- + s = dc_parse_mem(p, inst, false, true);
- + break;
- + }
- +
- + default:
- + {
- + s = BC_STATUS_PARSE_BAD_TOKEN;
- + get_token = true;
- + break;
- + }
- + }
- +
- + if (!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;
- +
- + if (flags & BC_PARSE_NOCALL) p->nbraces = p->prog->results.len;
- +
- + while (!s && (t = p->l.t.t) != BC_LEX_EOF) {
- + if ((inst = dc_parse_insts[t]) != BC_INST_INVALID) {
- + if ((s = bc_parse_push(p, inst))) return s;
- + if ((s = bc_lex_next(&p->l))) return s;
- + }
- + else if ((s = dc_parse_token(p, t, flags))) return s;
- + }
- +
- + if (!s && p->l.t.t == BC_LEX_EOF && (flags & BC_PARSE_NOCALL))
- + s = bc_parse_push(p, BC_INST_POP_EXEC);
- +
- + return s;
- +}
- +
- +static BcStatus dc_parse_parse(BcParse *p) {
- +
- + BcStatus s;
- +
- + if (p->l.t.t == BC_LEX_EOF) s = BC_STATUS_LEX_EOF;
- + else s = dc_parse_expr(p, 0);
- +
- + if (s || bcg.signe) s = bc_parse_reset(p, s);
- +
- + return s;
- +}
- +
- +BcStatus dc_parse_init(BcParse *p, BcProgram *prog, size_t func) {
- + return bc_parse_create(p, prog, func, dc_parse_parse, dc_lex_token);
- +}
- +#endif // CONFIG_DC
- +
- +static BcStatus bc_program_search(BcProgram *p, char *id, BcVec **ret, bool var)
- +{
- + BcStatus s;
- + 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;
- +
- + if ((new = (s = bc_map_insert(map, &e, &i)) != BC_STATUS_VEC_ITEM_EXISTS)) {
- + if (s || (s = bc_array_init(&data.v, var))) return s;
- + if ((s = bc_vec_push(v, &data.v))) {
- + bc_vec_free(&data.v);
- + return s;
- + }
- + }
- +
- + ptr = bc_map_item(map, i);
- + if (new && !(ptr->name = strdup(e.name))) return BC_STATUS_ALLOC_ERR;
- + *ret = bc_vec_item(v, ptr->idx);
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +static BcStatus bc_program_num(BcProgram *p, BcResult *r, BcNum **num, bool hex)
- +{
- + BcStatus s = BC_STATUS_SUCCESS;
- +
- + switch (r->t) {
- +
- + case BC_RESULT_STR:
- + case BC_RESULT_TEMP:
- + case BC_RESULT_IBASE:
- + case BC_RESULT_SCALE:
- + case BC_RESULT_OBASE:
- + {
- + *num = &r->d.n;
- + break;
- + }
- +
- + case BC_RESULT_CONSTANT:
- + {
- + char **str = bc_vec_item(&p->consts, r->d.id.idx);
- + size_t base_t, len = strlen(*str);
- + BcNum *base;
- +
- + if ((s = bc_num_init(&r->d.n, len))) return s;
- +
- + hex = hex && len == 1;
- + base = hex ? &p->hexb : &p->ib;
- + base_t = hex ? BC_NUM_MAX_IBASE : p->ib_t;
- +
- + if ((s = bc_num_parse(&r->d.n, *str, base, base_t))) {
- + bc_num_free(&r->d.n);
- + return s;
- + }
- +
- + *num = &r->d.n;
- + r->t = BC_RESULT_TEMP;
- +
- + break;
- + }
- +
- + case BC_RESULT_VAR:
- + case BC_RESULT_ARRAY:
- + case BC_RESULT_ARRAY_ELEM:
- + {
- + BcVec *v;
- +
- + s = bc_program_search(p, r->d.id.name, &v, r->t == BC_RESULT_VAR);
- + if (s) return s;
- +
- + if (r->t == BC_RESULT_ARRAY_ELEM) {
- +
- + if ((v = bc_vec_top(v))->len <= r->d.id.idx) {
- + if ((s = bc_array_expand(v, r->d.id.idx + 1))) return s;
- + }
- +
- + *num = bc_vec_item(v, r->d.id.idx);
- + }
- + else *num = bc_vec_top(v);
- +
- + break;
- + }
- +
- + case BC_RESULT_LAST:
- + {
- + *num = &p->last;
- + break;
- + }
- +
- + case BC_RESULT_ONE:
- + {
- + *num = &p->one;
- + break;
- + }
- + }
- +
- + return s;
- +}
- +
- +static BcStatus bc_program_binOpPrep(BcProgram *p, BcResult **l, BcNum **ln,
- + BcResult **r, BcNum **rn, bool assign)
- +{
- + BcStatus s;
- + bool hex;
- + BcResultType lt, rt;
- +
- + if (!BC_PROG_STACK(&p->results, 2)) return BC_STATUS_EXEC_STACK;
- +
- + *r = bc_vec_item_rev(&p->results, 0);
- + *l = bc_vec_item_rev(&p->results, 1);
- +
- + lt = (*l)->t;
- + rt = (*r)->t;
- + hex = assign && (lt == BC_RESULT_IBASE || lt == BC_RESULT_OBASE);
- +
- + if ((s = bc_program_num(p, *l, ln, false))) return s;
- + if ((s = bc_program_num(p, *r, rn, hex))) return s;
- +
- + // 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 == rt && (lt == BC_RESULT_VAR || lt == BC_RESULT_ARRAY_ELEM))
- + s = bc_program_num(p, *l, ln, false);
- +
- + if (!BC_PROG_NUM((*l), (*ln)) && (!assign || (*l)->t != BC_RESULT_VAR))
- + return BC_STATUS_EXEC_BAD_TYPE;
- + if (!assign && !BC_PROG_NUM((*r), (*ln))) return BC_STATUS_EXEC_BAD_TYPE;
- +
- +#ifdef CONFIG_DC
- +#else // CONFIG_DC
- +#endif // CONFIG_DC
- +
- + return s;
- +}
- +
- +static BcStatus bc_program_binOpRetire(BcProgram *p, BcResult *r) {
- + r->t = BC_RESULT_TEMP;
- + bc_vec_pop(&p->results);
- + bc_vec_pop(&p->results);
- + return bc_vec_push(&p->results, r);
- +}
- +
- +static BcStatus bc_program_prep(BcProgram *p, BcResult **r, BcNum **n) {
- +
- + BcStatus s;
- +
- + if (!BC_PROG_STACK(&p->results, 1)) return BC_STATUS_EXEC_STACK;
- +
- + *r = bc_vec_top(&p->results);
- +
- + if ((s = bc_program_num(p, *r, n, false))) return s;
- +
- +#ifdef CONFIG_DC
- +#endif // CONFIG_DC
- +
- + if (!BC_PROG_NUM((*r), (*n))) return BC_STATUS_EXEC_BAD_TYPE;
- +
- + return s;
- +}
- +
- +static BcStatus bc_program_retire(BcProgram *p, BcResult *r, BcResultType t) {
- + r->t = t;
- + bc_vec_pop(&p->results);
- + return bc_vec_push(&p->results, r);
- +}
- +
- +static BcStatus bc_program_op(BcProgram *p, uint8_t inst) {
- +
- + BcStatus s;
- + BcResult *opd1, *opd2, res;
- + BcNum *n1, *n2 = NULL;
- + BcNumBinaryOp op;
- +
- + if ((s = bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, false))) return s;
- + if ((s = bc_num_init(&res.d.n, BC_NUM_DEF_SIZE))) return s;
- +
- + op = bc_program_ops[inst - BC_INST_POWER];
- + if ((s = op(n1, n2, &res.d.n, p->scale))) goto err;
- + if ((s = bc_program_binOpRetire(p, &res))) goto err;
- +
- + 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;
- + BcFunc *f = bc_vec_item(&p->fns, BC_PROG_READ);
- +
- + for (i = 0; i < p->stack.len; ++i) {
- + if (((BcInstPtr*) bc_vec_item(&p->stack, i))->func == BC_PROG_READ)
- + return BC_STATUS_EXEC_REC_READ;
- + }
- +
- + bc_vec_npop(&f->code, f->code.len);
- +
- + if ((s = bc_vec_init(&buf, sizeof(char), NULL))) return BC_STATUS_ALLOC_ERR;
- + if ((s = bc_read_line(&buf, "read> "))) goto io_err;
- +
- + if ((s = p->parse_init(&parse, p, BC_PROG_READ))) goto io_err;
- + bc_lex_file(&parse.l, bc_program_stdin_name);
- + if ((s = bc_parse_text(&parse, buf.v))) goto exec_err;
- +
- + if ((s = p->parse_expr(&parse, BC_PARSE_NOREAD))) goto exec_err;
- +
- + if (parse.l.t.t != BC_LEX_NLINE && parse.l.t.t != BC_LEX_EOF) {
- + s = BC_STATUS_EXEC_BAD_READ_EXPR;
- + goto exec_err;
- + }
- +
- + 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);
- +
- + if ((s = bc_vec_pushByte(&f->code, BC_INST_POP_EXEC))) goto exec_err;
- + if ((s = bc_vec_push(&p->stack, &ip))) goto exec_err;
- +
- +exec_err:
- + bc_parse_free(&parse);
- +io_err:
- + bc_vec_free(&buf);
- + return s;
- +}
- +
- +static size_t bc_program_index(char *code, size_t *bgn) {
- +
- + uint8_t amt = code[(*bgn)++], i = 0;
- + size_t res = 0;
- +
- + for (; i < amt; ++i, ++(*bgn))
- + res |= (((size_t) ((int) code[*bgn]) & UCHAR_MAX) << (i * CHAR_BIT));
- +
- + return res;
- +}
- +
- +static char* bc_program_name(char *code, size_t *bgn) {
- +
- + size_t i;
- + char byte, *s, *str = code + *bgn, *ptr = strchr(str, BC_PARSE_STREND);
- +
- + if (!(s = malloc(ptr - str + 1))) return NULL;
- +
- + for (i = 0; (byte = (char) code[(*bgn)++]) && byte != BC_PARSE_STREND; ++i)
- + s[i] = byte;
- +
- + s[i] = '\0';
- +
- + return s;
- +}
- +
- +static BcStatus bc_program_printString(const char *str, size_t *nchars) {
- +
- + size_t i, len = strlen(str);
- +
- +#ifdef CONFIG_DC
- + if (!len) return putchar('\0') < 0 ? BC_STATUS_IO_ERR : BC_STATUS_SUCCESS;
- +#endif // CONFIG_DC
- +
- + for (i = 0; i < len; ++i, ++(*nchars)) {
- +
- + int err, c;
- +
- + if ((c = str[i]) != '\\' || i == len - 1) err = putchar(c);
- + else {
- +
- + switch ((c = str[++i])) {
- +
- + case 'a':
- + {
- + err = putchar('\a');
- + break;
- + }
- +
- + case 'b':
- + {
- + err = putchar('\b');
- + break;
- + }
- +
- + case '\\':
- + case 'e':
- + {
- + err = putchar('\\');
- + break;
- + }
- +
- + case 'f':
- + {
- + err = putchar('\f');
- + break;
- + }
- +
- + case 'n':
- + {
- + err = putchar('\n');
- + *nchars = SIZE_MAX;
- + break;
- + }
- +
- + case 'r':
- + {
- + err = putchar('\r');
- + break;
- + }
- +
- + case 'q':
- + {
- + err = putchar('"');
- + break;
- + }
- +
- + case 't':
- + {
- + err = putchar('\t');
- + break;
- + }
- +
- + default:
- + {
- + // Just print the backslash and following character.
- + if (putchar('\\') == EOF) return BC_STATUS_IO_ERR;
- + ++(*nchars);
- + err = putchar(c);
- + break;
- + }
- + }
- + }
- +
- + if (err == EOF) return BC_STATUS_IO_ERR;
- + }
- +
- + return BC_STATUS_SUCCESS;
- +}
- +
- +static BcStatus bc_program_print(BcProgram *p, uint8_t inst, size_t idx) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + BcResult *r;
- + size_t len, i;
- + char *str;
- + BcNum *num = NULL;
- + bool pop = inst != BC_INST_PRINT;
- +
- + if (!BC_PROG_STACK(&p->results, idx + 1)) return BC_STATUS_EXEC_STACK;
- +
- + r = bc_vec_item_rev(&p->results, idx);
- + if ((s = bc_program_num(p, r, &num, false))) return s;
- +
- + if (BC_PROG_NUM(r, num)) {
- + s = bc_num_print(num, &p->ob, p->ob_t, !pop, &p->nchars, p->len);
- + if (!s) s = bc_num_copy(&p->last, num);
- + }
- + else {
- +
- + idx = (r->t == BC_RESULT_STR) ? r->d.id.idx : num->rdx;
- + str = *((char**) bc_vec_item(&p->strs, idx));
- +
- + if (inst == BC_INST_PRINT_STR) {
- + for (i = 0, len = strlen(str); i < len; ++i) {
- + char c = str[i];
- + if (putchar(c) == EOF) return BC_STATUS_IO_ERR;
- + if (c == '\n') p->nchars = SIZE_MAX;
- + ++p->nchars;
- + }
- + }
- + else {
- + if ((s = bc_program_printString(str, &p->nchars))) return s;
- + if (inst == BC_INST_PRINT && putchar('\n') == EOF)
- + s = BC_STATUS_IO_ERR;
- + }
- + }
- +
- + if (!s && pop) bc_vec_pop(&p->results);
- +
- + return s;
- +}
- +
- +static BcStatus bc_program_negate(BcProgram *p) {
- +
- + BcStatus s;
- + BcResult res, *ptr;
- + BcNum *num = NULL;
- +
- + if ((s = bc_program_prep(p, &ptr, &num))) return s;
- + if ((s = bc_num_init(&res.d.n, num->len))) return s;
- + if ((s = bc_num_copy(&res.d.n, num))) goto err;
- +
- + if (res.d.n.len) res.d.n.neg = !res.d.n.neg;
- +
- + if ((s = bc_program_retire(p, &res, BC_RESULT_TEMP))) goto err;
- +
- + return s;
- +
- +err:
- + bc_num_free(&res.d.n);
- + return s;
- +}
- +
- +static BcStatus bc_program_logical(BcProgram *p, uint8_t inst) {
- +
- + BcStatus s;
- + BcResult *opd1, *opd2, res;
- + BcNum *n1, *n2;
- + bool cond = 0;
- + ssize_t cmp;
- +
- + if ((s = bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, false))) return s;
- + if ((s = bc_num_init(&res.d.n, BC_NUM_DEF_SIZE))) return s;
- +
- + if (inst == BC_INST_BOOL_AND)
- + cond = bc_num_cmp(n1, &p->zero) && bc_num_cmp(n2, &p->zero);
- + else if (inst == BC_INST_BOOL_OR)
- + cond = bc_num_cmp(n1, &p->zero) || bc_num_cmp(n2, &p->zero);
- + else {
- +
- + cmp = bc_num_cmp(n1, n2);
- +
- + 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;
- + }
- + }
- + }
- +
- + (cond ? bc_num_one : bc_num_zero)(&res.d.n);
- +
- + if ((s = bc_program_binOpRetire(p, &res))) goto err;
- +
- + return s;
- +
- +err:
- + bc_num_free(&res.d.n);
- + return s;
- +}
- +
- +#ifdef CONFIG_DC
- +static BcStatus bc_program_assignStr(BcProgram *p, BcResult *r, BcVec *v,
- + bool push)
- +{
- + BcStatus s;
- + BcNum n2;
- + BcResult res;
- +
- + memset(&n2, 0, sizeof(BcNum));
- + n2.rdx = res.d.id.idx = r->d.id.idx;
- + res.t = BC_RESULT_STR;
- +
- + if (!push) {
- + if (!BC_PROG_STACK(&p->results, 2)) return BC_STATUS_EXEC_STACK;
- + bc_vec_pop(v);
- + bc_vec_pop(&p->results);
- + }
- +
- + bc_vec_pop(&p->results);
- +
- + if ((s = bc_vec_push(&p->results, &res))) return s;
- +
- + return bc_vec_push(v, &n2);
- +}
- +#endif // CONFIG_DC
- +
- +static BcStatus bc_program_copyToVar(BcProgram *p, char *name, bool var) {
- +
- + BcStatus s;
- + BcResult *ptr, r;
- + BcVec *v;
- + BcNum *n;
- +
- + if (!BC_PROG_STACK(&p->results, 1)) return BC_STATUS_EXEC_STACK;
- +
- + ptr = bc_vec_top(&p->results);
- + if ((ptr->t == BC_RESULT_ARRAY) != !var) return BC_STATUS_EXEC_BAD_TYPE;
- +
- + if ((s = bc_program_search(p, name, &v, var))) return s;
- +
- +#ifdef CONFIG_DC
- + if (ptr->t == BC_RESULT_STR && !var) return BC_STATUS_EXEC_BAD_TYPE;
- + if (ptr->t == BC_RESULT_STR) return bc_program_assignStr(p, ptr, v, true);
- +#endif // CONFIG_DC
- +
- + if ((s = bc_program_num(p, ptr, &n, false))) return s;
- +
- + // Do this once more to make sure that pointers were not invalidated.
- + if ((s = bc_program_search(p, name, &v, var))) return s;
- +
- + if (var) {
- + if ((s = bc_num_init(&r.d.n, BC_NUM_DEF_SIZE))) return s;
- + s = bc_num_copy(&r.d.n, n);
- + }
- + else {
- + if ((s = bc_array_init(&r.d.v, true))) return s;
- + s = bc_array_copy(&r.d.v, (BcVec*) n);
- + }
- +
- + if (s || (s = bc_vec_push(v, &r.d))) goto err;
- +
- + bc_vec_pop(&p->results);
- +
- + return s;
- +
- +err:
- + if (var) bc_num_free(&r.d.n);
- + else bc_vec_free(&r.d.v);
- + return s;
- +}
- +
- +static BcStatus bc_program_assign(BcProgram *p, uint8_t inst) {
- +
- + BcStatus s;
- + BcResult *left, *right, res;
- + BcNum *l = NULL, *r = NULL;
- + unsigned long val, max;
- + bool assign = inst == BC_INST_ASSIGN, ib, sc;
- +
- + if ((s = bc_program_binOpPrep(p, &left, &l, &right, &r, assign))) return s;
- +
- + ib = left->t == BC_RESULT_IBASE;
- + sc = left->t == BC_RESULT_SCALE;
- +
- +#ifdef CONFIG_DC
- +
- + if (right->t == BC_RESULT_STR) {
- +
- + BcVec *v;
- +
- + if (left->t != BC_RESULT_VAR) return BC_STATUS_EXEC_BAD_TYPE;
- + if ((s = bc_program_search(p, left->d.id.name, &v, true))) return s;
- +
- + return bc_program_assignStr(p, right, v, false);
- + }
- +#endif // CONFIG_DC
- +
- + if (left->t == BC_RESULT_CONSTANT || left->t == BC_RESULT_TEMP)
- + return BC_STATUS_PARSE_BAD_ASSIGN;
- +
- +#ifdef CONFIG_BC
- + if (inst == BC_INST_ASSIGN_DIVIDE && !bc_num_cmp(r, &p->zero))
- + return BC_STATUS_MATH_DIVIDE_BY_ZERO;
- +
- + if (assign) s = bc_num_copy(l, r);
- + else s = bc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, p->scale);
- +
- + if (s) return s;
- +#else // CONFIG_BC
- + if ((s = bc_num_copy(l, r))) return s;
- +#endif // CONFIG_BC
- +
- + if (ib || sc || left->t == BC_RESULT_OBASE) {
- +
- + size_t *ptr;
- +
- + if ((s = bc_num_ulong(l, &val))) return s;
- + s = left->t - BC_RESULT_IBASE + BC_STATUS_EXEC_BAD_IBASE;
- +
- + if (sc) {
- + max = BC_MAX_SCALE;
- + ptr = &p->scale;
- + }
- + else {
- + if (val < BC_NUM_MIN_BASE) return s;
- + max = ib ? BC_NUM_MAX_IBASE : BC_MAX_OBASE;
- + ptr = ib ? &p->ib_t : &p->ob_t;
- + }
- +
- + if (val > max) return s;
- + else if (!sc && (s = bc_num_copy(ib ? &p->ib : &p->ob, l))) return s;
- +
- + *ptr = (size_t) val;
- + }
- +
- + if ((s = bc_num_init(&res.d.n, l->len))) return s;
- + if ((s = bc_num_copy(&res.d.n, l))) goto err;
- + if ((s = bc_program_binOpRetire(p, &res))) goto err;
- +
- + return s;
- +
- +err:
- + bc_num_free(&res.d.n);
- + return s;
- +}
- +
- +static BcStatus bc_program_pushVar(BcProgram *p, char *code, size_t *bgn,
- + bool pop, bool copy)
- +{
- + BcStatus s;
- + BcResult r;
- + char *name;
- +#ifdef CONFIG_DC // Exclude
- + BcNum *num;
- + BcVec *v;
- +#else // CONFIG_DC
- + (void) pop, (void) copy;
- +#endif // CONFIG_DC Exclude
- +
- + if (!(name = bc_program_name(code, bgn))) return BC_STATUS_ALLOC_ERR;
- + r.t = BC_RESULT_VAR;
- + r.d.id.name = name;
- +
- +#ifdef CONFIG_DC
- + if ((s = bc_program_search(p, name, &v, true))) goto err;
- + num = bc_vec_top(v);
- +
- + if (pop || copy) {
- +
- + if (!BC_PROG_STACK(v, 2 - copy)) {
- + s = BC_STATUS_EXEC_STACK;
- + goto err;
- + }
- +
- + free(name);
- + name = NULL;
- +
- + if ((pop = !BC_PROG_STR(num))) {
- +
- + r.t = BC_RESULT_TEMP;
- +
- + if ((s = bc_num_init(&r.d.n, BC_NUM_DEF_SIZE))) goto err;
- + if ((s = bc_num_copy(&r.d.n, num))) goto copy_err;
- + }
- + else {
- + r.t = BC_RESULT_STR;
- + r.d.id.idx = num->rdx;
- + }
- +
- + if (!copy) bc_vec_pop(v);
- + }
- +#endif // CONFIG_DC
- +
- + s = bc_vec_push(&p->results, &r);
- +
- +#ifdef CONFIG_DC
- +copy_err:
- + if (s && pop) bc_num_free(&r.d.n);
- +err:
- +#endif // CONFIG_DC
- + if (s) free(name);
- + return s;
- +}
- +
- +static BcStatus bc_program_pushArray(BcProgram *p, char *code,
- + size_t *bgn, uint8_t inst)
- +{
- + BcStatus s;
- + BcResult r;
- + BcNum *num;
- +
- + if (!(r.d.id.name = bc_program_name(code, bgn))) return BC_STATUS_ALLOC_ERR;
- +
- + if (inst == BC_INST_ARRAY) {
- + r.t = BC_RESULT_ARRAY;
- + s = bc_vec_push(&p->results, &r);
- + }
- + else {
- +
- + BcResult *operand;
- + unsigned long temp;
- +
- + if ((s = bc_program_prep(p, &operand, &num))) goto err;
- + if ((s = bc_num_ulong(num, &temp))) goto err;
- +
- + if (temp > BC_MAX_DIM) {
- + s = BC_STATUS_EXEC_ARRAY_LEN;
- + goto err;
- + }
- +
- + r.d.id.idx = (size_t) temp;
- + s = bc_program_retire(p, &r, BC_RESULT_ARRAY_ELEM);
- + }
- +
- +err:
- + if (s) free(r.d.id.name);
- + return s;
- +}
- +
- +#ifdef CONFIG_BC
- +static BcStatus bc_program_incdec(BcProgram *p, uint8_t inst) {
- +
- + BcStatus s;
- + BcResult *ptr, res, copy;
- + BcNum *num = NULL;
- + uint8_t inst2 = inst;
- +
- + if ((s = bc_program_prep(p, &ptr, &num))) return s;
- +
- + if (inst == BC_INST_INC_POST || inst == BC_INST_DEC_POST) {
- + copy.t = BC_RESULT_TEMP;
- + if ((s = bc_num_init(©.d.n, num->len))) return s;
- + if ((s = bc_num_copy(©.d.n, num))) goto err;
- + }
- +
- + res.t = BC_RESULT_ONE;
- + inst = inst == BC_INST_INC_PRE || inst == BC_INST_INC_POST ?
- + BC_INST_ASSIGN_PLUS : BC_INST_ASSIGN_MINUS;
- +
- + if ((s = bc_vec_push(&p->results, &res))) goto err;
- + if ((s = bc_program_assign(p, inst))) goto err;
- +
- + if (inst2 == BC_INST_INC_POST || inst2 == BC_INST_DEC_POST) {
- + bc_vec_pop(&p->results);
- + if ((s = bc_vec_push(&p->results, ©))) goto err;
- + }
- +
- + return s;
- +
- +err:
- + if (inst2 == BC_INST_INC_POST || inst2 == BC_INST_DEC_POST)
- + bc_num_free(©.d.n);
- + return s;
- +}
- +
- +static BcStatus bc_program_call(BcProgram *p, char *code, size_t *idx) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + BcInstPtr ip;
- + size_t i, nparams = bc_program_index(code, idx);
- + BcFunc *func;
- + BcVec *v;
- + BcId *a;
- + BcResultData param;
- + BcResult *arg;
- +
- + ip.idx = 0;
- + ip.func = bc_program_index(code, idx);
- + func = bc_vec_item(&p->fns, ip.func);
- +
- + if (!func->code.len) return BC_STATUS_EXEC_UNDEFINED_FUNC;
- + if (nparams != func->nparams) return BC_STATUS_EXEC_MISMATCHED_PARAMS;
- + ip.len = p->results.len - nparams;
- +
- + for (i = 0; i < nparams; ++i) {
- +
- + a = bc_vec_item(&func->autos, nparams - 1 - i);
- + arg = bc_vec_top(&p->results);
- +
- + if ((!a->idx) != (arg->t == BC_RESULT_ARRAY) || arg->t == BC_RESULT_STR)
- + return BC_STATUS_EXEC_BAD_TYPE;
- +
- + if ((s = bc_program_copyToVar(p, a->name, a->idx))) return s;
- + }
- +
- + for (; i < func->autos.len; ++i) {
- +
- + a = bc_vec_item(&func->autos, i);
- + if ((s = bc_program_search(p, a->name, &v, a->idx))) return s;
- +
- + if (a->idx) {
- + if ((s = bc_num_init(¶m.n, BC_NUM_DEF_SIZE))) return s;
- + if ((s = bc_vec_push(v, ¶m.n))) goto err;
- + }
- + else {
- + if ((s = bc_array_init(¶m.v, true))) return s;
- + if ((s = bc_vec_push(v, ¶m.v))) goto err;
- + }
- + }
- +
- + return bc_vec_push(&p->stack, &ip);
- +
- +err:
- + if (a->idx) bc_num_free(¶m.n);
- + else bc_vec_free(¶m.v);
- + return s;
- +}
- +
- +static BcStatus bc_program_return(BcProgram *p, uint8_t inst) {
- +
- + BcStatus s;
- + BcResult res;
- + BcFunc *f;
- + size_t i;
- + BcInstPtr *ip = bc_vec_top(&p->stack);
- +
- + if (!BC_PROG_STACK(&p->results, ip->len + inst == BC_INST_RET))
- + return BC_STATUS_EXEC_STACK;
- +
- + f = bc_vec_item(&p->fns, ip->func);
- + res.t = BC_RESULT_TEMP;
- +
- + if (inst == BC_INST_RET) {
- +
- + BcNum *num;
- + BcResult *operand = bc_vec_top(&p->results);
- +
- + if ((s = bc_program_num(p, operand, &num, false))) return s;
- + if ((s = bc_num_init(&res.d.n, num->len))) return s;
- + if ((s = bc_num_copy(&res.d.n, num))) goto err;
- + }
- + else {
- + if ((s = bc_num_init(&res.d.n, BC_NUM_DEF_SIZE))) return s;
- + bc_num_zero(&res.d.n);
- + }
- +
- + // We need to pop arguments as well, so this takes that into account.
- + for (i = 0; i < f->autos.len; ++i) {
- +
- + BcVec *v;
- + BcId *a = bc_vec_item(&f->autos, i);
- +
- + if ((s = bc_program_search(p, a->name, &v, a->idx))) goto err;
- +
- + bc_vec_pop(v);
- + }
- +
- + bc_vec_npop(&p->results, p->results.len - ip->len);
- + if ((s = bc_vec_push(&p->results, &res))) goto err;
- + bc_vec_pop(&p->stack);
- +
- + return s;
- +
- +err:
- + bc_num_free(&res.d.n);
- + return s;
- +}
- +#endif // CONFIG_BC
- +
- +static unsigned long bc_program_scale(BcNum *n) {
- + return (unsigned long) n->rdx;
- +}
- +
- +static unsigned long bc_program_len(BcNum *n) {
- +
- + unsigned long len = n->len;
- +
- + if (n->rdx == n->len) {
- + size_t i;
- + for (i = n->len - 1; i < n->len && !n->num[i]; --len, --i);
- + }
- +
- + return len;
- +}
- +
- +static BcStatus bc_program_builtin(BcProgram *p, uint8_t inst) {
- +
- + BcStatus s;
- + BcResult *opnd;
- + BcNum *num = NULL;
- + BcResult res;
- + bool len = inst == BC_INST_LENGTH;
- +
- + if (!BC_PROG_STACK(&p->results, 1)) return BC_STATUS_EXEC_STACK;
- + opnd = bc_vec_top(&p->results);
- + if ((s = bc_program_num(p, opnd, &num, false))) return s;
- +
- +#ifdef CONFIG_DC
- + if (!BC_PROG_NUM(opnd, num) && !len) return BC_STATUS_EXEC_BAD_TYPE;
- +#endif // CONFIG_DC
- +
- + if ((s = bc_num_init(&res.d.n, BC_NUM_DEF_SIZE))) return s;
- +
- + if (inst == BC_INST_SQRT) s = bc_num_sqrt(num, &res.d.n, p->scale);
- +#ifdef CONFIG_BC
- + else if (len && opnd->t == BC_RESULT_ARRAY) {
- + BcVec *vec = (BcVec*) num;
- + s = bc_num_ulong2num(&res.d.n, (unsigned long) vec->len);
- + }
- +#endif // CONFIG_BC
- +#ifdef CONFIG_DC
- + else if (len && !BC_PROG_NUM(opnd, num)) {
- +
- + char **str;
- + size_t idx = opnd->t == BC_RESULT_STR ? opnd->d.id.idx : num->rdx;
- +
- + str = bc_vec_item(&p->strs, idx);
- + s = bc_num_ulong2num(&res.d.n, strlen(*str));
- + }
- +#endif // CONFIG_DC
- + else {
- + BcProgramBuiltIn f = len ? bc_program_len : bc_program_scale;
- + s = bc_num_ulong2num(&res.d.n, f(num));
- + }
- +
- + if (s || (s = bc_program_retire(p, &res, BC_RESULT_TEMP))) goto err;
- +
- + return s;
- +
- +err:
- + bc_num_free(&res.d.n);
- + return s;
- +}
- +
- +#ifdef CONFIG_DC
- +static BcStatus bc_program_divmod(BcProgram *p) {
- +
- + BcStatus s;
- + BcResult *opd1, *opd2, res, res2;
- + BcNum *n1, *n2 = NULL;
- +
- + if ((s = bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, false))) return s;
- + if ((s = bc_num_init(&res.d.n, BC_NUM_DEF_SIZE))) return s;
- + if ((s = bc_num_init(&res2.d.n, n2->len))) goto res2_err;
- +
- + if ((s = bc_num_divmod(n1, n2, &res2.d.n, &res.d.n, p->scale))) goto err;
- +
- + if ((s = bc_program_binOpRetire(p, &res2))) goto err;
- + res.t = BC_RESULT_TEMP;
- + if ((s = bc_vec_push(&p->results, &res))) goto res2_err;
- +
- + return s;
- +
- +err:
- + bc_num_free(&res2.d.n);
- +res2_err:
- + bc_num_free(&res.d.n);
- + return s;
- +}
- +
- +static BcStatus bc_program_modexp(BcProgram *p) {
- +
- + BcStatus s;
- + BcResult *r1, *r2, *r3, res;
- + BcNum *n1, *n2, *n3;
- +
- + if (!BC_PROG_STACK(&p->results, 3)) return BC_STATUS_EXEC_STACK;
- + if ((s = bc_program_binOpPrep(p, &r2, &n2, &r3, &n3, false))) return s;
- +
- + r1 = bc_vec_item_rev(&p->results, 2);
- + if ((s = bc_program_num(p, r1, &n1, false))) return s;
- + if (!BC_PROG_NUM(r1, n1)) return BC_STATUS_EXEC_BAD_TYPE;
- +
- + // Make sure that the values have their pointers updated, if necessary.
- + if (r1->t == BC_RESULT_VAR || r1->t == BC_RESULT_ARRAY_ELEM) {
- + if (r1->t == r2->t && (s = bc_program_num(p, r2, &n2, false))) return s;
- + if (r1->t == r3->t && (s = bc_program_num(p, r3, &n3, false))) return s;
- + }
- +
- + if ((s = bc_num_init(&res.d.n, n3->len))) return s;
- + if ((s = bc_num_modexp(n1, n2, n3, &res.d.n))) goto err;
- +
- + bc_vec_pop(&p->results);
- +
- + if ((s = bc_program_binOpRetire(p, &res))) goto err;
- +
- + return s;
- +
- +err:
- + bc_num_free(&res.d.n);
- + return s;
- +}
- +
- +static BcStatus bc_program_stackLen(BcProgram *p) {
- +
- + BcStatus s;
- + BcResult res;
- + size_t len = p->results.len;
- +
- + res.t = BC_RESULT_TEMP;
- +
- + if ((s = bc_num_init(&res.d.n, BC_NUM_DEF_SIZE))) return s;
- + if ((s = bc_num_ulong2num(&res.d.n, len))) goto err;
- + if ((s = bc_vec_push(&p->results, &res))) goto err;
- +
- + return s;
- +
- +err:
- + bc_num_free(&res.d.n);
- + return s;
- +}
- +
- +static BcStatus bc_program_asciify(BcProgram *p) {
- +
- + BcStatus s;
- + BcResult *r, res;
- + BcNum *num = NULL, n;
- + char *str, *str2, c;
- + size_t len = p->strs.len, idx;
- + unsigned long val;
- +
- + if (!BC_PROG_STACK(&p->results, 1)) return BC_STATUS_EXEC_STACK;
- +
- + r = bc_vec_top(&p->results);
- + if ((s = bc_program_num(p, r, &num, false))) return s;
- +
- + if (BC_PROG_NUM(r, num)) {
- +
- + if ((s = bc_num_init(&n, BC_NUM_DEF_SIZE))) return s;
- + if ((s = bc_num_copy(&n, num))) goto num_err;
- + bc_num_truncate(&n, n.rdx);
- +
- + if ((s = bc_num_mod(&n, &p->strmb, &n, 0))) goto num_err;
- + if ((s = bc_num_ulong(&n, &val))) goto num_err;
- +
- + c = (char) val;
- +
- + bc_num_free(&n);
- + }
- + else {
- + idx = (r->t == BC_RESULT_STR) ? r->d.id.idx : num->rdx;
- + str2 = *((char**) bc_vec_item(&p->strs, idx));
- + c = str2[0];
- + }
- +
- + if (!(str = malloc(2))) return BC_STATUS_ALLOC_ERR;
- +
- + str[0] = c;
- + str[1] = '\0';
- +
- + if (!(str2 = strdup(str))) {
- + s = BC_STATUS_ALLOC_ERR;
- + goto err;
- + }
- +
- + if ((s = bc_program_addFunc(p, str2, &idx))) goto err;
- +
- + if (idx != len + BC_PROG_REQ_FUNCS) {
- +
- + for (idx = 0; idx < p->strs.len; ++idx) {
- + if (!strcmp(*((char**) bc_vec_item(&p->strs, idx)), str)) {
- + len = idx;
- + break;
- + }
- + }
- +
- + free(str);
- + }
- + else if ((s = bc_vec_push(&p->strs, &str))) goto err;
- +
- + res.t = BC_RESULT_STR;
- + res.d.id.idx = len;
- +
- + bc_vec_pop(&p->results);
- +
- + return bc_vec_push(&p->results, &res);
- +
- +num_err:
- + bc_num_free(&n);
- + return s;
- +err:
- + free(str);
- + return s;
- +}
- +
- +static BcStatus bc_program_printStream(BcProgram *p) {
- +
- + BcStatus s;
- + BcResult *r;
- + BcNum *n = NULL;
- + size_t idx;
- + char *str;
- +
- + if (!BC_PROG_STACK(&p->results, 1)) return BC_STATUS_EXEC_STACK;
- +
- + r = bc_vec_top(&p->results);
- + if ((s = bc_program_num(p, r, &n, false))) return s;
- +
- + if (BC_PROG_NUM(r, n)) s = bc_num_stream(n, &p->strmb, &p->nchars, p->len);
- + else {
- + idx = (r->t == BC_RESULT_STR) ? r->d.id.idx : n->rdx;
- + str = *((char**) bc_vec_item(&p->strs, idx));
- + if (printf("%s", str) < 0) s = BC_STATUS_IO_ERR;
- + }
- +
- + return s;
- +}
- +
- +static BcStatus bc_program_nquit(BcProgram *p) {
- +
- + BcStatus s;
- + BcResult *opnd;
- + BcNum *num = NULL;
- + unsigned long val;
- +
- + if ((s = bc_program_prep(p, &opnd, &num))) return s;
- + if ((s = bc_num_ulong(num, &val))) return s;
- +
- + bc_vec_pop(&p->results);
- +
- + if (p->stack.len < val) return BC_STATUS_EXEC_STACK;
- + 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, char *code, size_t *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;
- +
- + if (!BC_PROG_STACK(&p->results, 1)) return BC_STATUS_EXEC_STACK;
- +
- + r = bc_vec_top(&p->results);
- +
- + if (cond) {
- +
- + BcVec *v;
- + char *name, *then_name = bc_program_name(code, bgn), *else_name = NULL;
- +
- + if (code[*bgn] == BC_PARSE_STREND) (*bgn) += 1;
- + else else_name = bc_program_name(code, bgn);
- +
- + if ((exec = r->d.n.len)) name = then_name;
- + else if ((exec = (else_name != NULL))) name = else_name;
- +
- + if (exec) {
- + s = bc_program_search(p, name, &v, true);
- + n = bc_vec_top(v);
- + }
- +
- + free(then_name);
- + free(else_name);
- +
- + if (s || !exec) goto exit;
- + if (!BC_PROG_STR(n)) {
- + s = BC_STATUS_EXEC_BAD_TYPE;
- + goto exit;
- + }
- +
- + sidx = n->rdx;
- + }
- + else {
- +
- + if (r->t == BC_RESULT_STR) sidx = r->d.id.idx;
- + else if (r->t == BC_RESULT_VAR) {
- + if ((s = bc_program_num(p, r, &n, false))) goto exit;
- + if (!BC_PROG_STR(n)) goto exit;
- + sidx = n->rdx;
- + }
- + else goto exit;
- + }
- +
- + fidx = sidx + BC_PROG_REQ_FUNCS;
- +
- + str = bc_vec_item(&p->strs, sidx);
- + f = bc_vec_item(&p->fns, fidx);
- +
- + if (!f->code.len) {
- +
- + if ((s = p->parse_init(&prs, p, fidx))) goto exit;
- + if ((s = bc_parse_text(&prs, *str))) goto err;
- + if ((s = p->parse_expr(&prs, BC_PARSE_NOCALL))) goto err;
- +
- + if (prs.l.t.t != BC_LEX_EOF) {
- + s = BC_STATUS_PARSE_BAD_EXP;
- + goto err;
- + }
- +
- + bc_parse_free(&prs);
- + }
- +
- + ip.idx = 0;
- + ip.len = p->results.len;
- + ip.func = fidx;
- +
- + bc_vec_pop(&p->results);
- +
- + return bc_vec_push(&p->stack, &ip);
- +
- +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);
- + return s;
- +}
- +#endif // CONFIG_DC
- +
- +static BcStatus bc_program_pushGlobal(BcProgram *p, uint8_t inst) {
- +
- + BcStatus s;
- + BcResult res;
- + unsigned long val;
- +
- + res.t = inst - BC_INST_IBASE + BC_RESULT_IBASE;
- + if (inst == BC_INST_IBASE) val = (unsigned long) p->ib_t;
- + else if (inst == BC_INST_SCALE) val = (unsigned long) p->scale;
- + else val = (unsigned long) p->ob_t;
- +
- + if ((s = bc_num_init(&res.d.n, BC_NUM_DEF_SIZE))) return s;
- + if ((s = bc_num_ulong2num(&res.d.n, val))) goto err;
- + if ((s = bc_vec_push(&p->results, &res))) goto err;
- +
- + return s;
- +
- +err:
- + bc_num_free(&res.d.n);
- + return s;
- +}
- +
- +void bc_program_free(BcProgram *p) {
- + bc_num_free(&p->ib);
- + bc_num_free(&p->ob);
- + bc_num_free(&p->hexb);
- +#ifdef CONFIG_DC
- + bc_num_free(&p->strmb);
- +#endif // CONFIG_DC
- + bc_vec_free(&p->fns);
- + bc_vec_free(&p->fn_map);
- + 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->strs);
- + bc_vec_free(&p->consts);
- + bc_vec_free(&p->results);
- + bc_vec_free(&p->stack);
- + bc_num_free(&p->last);
- + bc_num_free(&p->zero);
- + bc_num_free(&p->one);
- +}
- +
- +BcStatus bc_program_init(BcProgram *p, size_t line_len,
- + BcParseInit init, BcParseExpr expr)
- +{
- + BcStatus s;
- + size_t idx;
- + char *main_name, *read_name;
- + BcInstPtr ip;
- +
- + memset(p, 0, sizeof(BcProgram));
- + memset(&ip, 0, sizeof(BcInstPtr));
- +
- + p->nchars = p->scale = 0;
- + p->len = line_len;
- + p->parse_init = init;
- + p->parse_expr = expr;
- +
- + if ((s = bc_num_init(&p->ib, BC_NUM_DEF_SIZE))) return s;
- + bc_num_ten(&p->ib);
- + p->ib_t = 10;
- +
- + if ((s = bc_num_init(&p->ob, BC_NUM_DEF_SIZE))) goto err;
- + bc_num_ten(&p->ob);
- + p->ob_t = 10;
- +
- + if ((s = bc_num_init(&p->hexb, BC_NUM_DEF_SIZE))) goto err;
- + bc_num_ten(&p->hexb);
- + p->hexb.num[0] = 6;
- +
- +#ifdef CONFIG_DC
- + if ((s = bc_num_init(&p->strmb, BC_NUM_DEF_SIZE))) goto err;
- + if ((s = bc_num_ulong2num(&p->strmb, UCHAR_MAX + 1))) goto err;
- +#endif // CONFIG_DC
- +
- + if ((s = bc_num_init(&p->last, BC_NUM_DEF_SIZE))) goto err;
- + bc_num_zero(&p->last);
- +
- + if ((s = bc_num_init(&p->zero, BC_NUM_DEF_SIZE))) goto err;
- + bc_num_zero(&p->zero);
- +
- + if ((s = bc_num_init(&p->one, BC_NUM_DEF_SIZE))) goto err;
- + bc_num_one(&p->one);
- +
- + if ((s = bc_vec_init(&p->fns, sizeof(BcFunc), bc_func_free))) goto err;
- + if ((s = bc_map_init(&p->fn_map))) goto err;
- +
- + if (!(main_name = strdup(bc_func_main))) goto alloc_err;
- + if ((s = bc_program_addFunc(p, main_name, &idx))) goto err;
- +
- + if (!(read_name = strdup(bc_func_read))) goto alloc_err;
- + if ((s = bc_program_addFunc(p, read_name, &idx))) goto err;
- +
- + if ((s = bc_vec_init(&p->vars, sizeof(BcVec), bc_vec_free))) goto err;
- + if ((s = bc_map_init(&p->var_map))) goto err;
- +
- + if ((s = bc_vec_init(&p->arrs, sizeof(BcVec), bc_vec_free))) goto err;
- + if ((s = bc_map_init(&p->arr_map))) goto err;
- +
- + if ((s = bc_vec_init(&p->strs, sizeof(char*), bc_string_free))) goto err;
- + if ((s = bc_vec_init(&p->consts, sizeof(char*), bc_string_free))) goto err;
- + if ((s = bc_vec_init(&p->results, sizeof(BcResult), bc_result_free)))
- + goto err;
- + if ((s = bc_vec_init(&p->stack, sizeof(BcInstPtr), NULL))) goto err;
- + if ((s = bc_vec_push(&p->stack, &ip))) goto err;
- +
- + return s;
- +
- +alloc_err:
- + s = BC_STATUS_ALLOC_ERR;
- +err:
- + bc_program_free(p);
- + return s;
- +}
- +
- +BcStatus bc_program_addFunc(BcProgram *p, char *name, size_t *idx) {
- +
- + BcStatus s;
- + BcId entry, *entry_ptr;
- + BcFunc f;
- +
- + entry.name = name;
- + entry.idx = p->fns.len;
- +
- + if ((s = bc_map_insert(&p->fn_map, &entry, idx))) {
- + free(name);
- + if (s != BC_STATUS_VEC_ITEM_EXISTS) return s;
- + }
- +
- + entry_ptr = bc_map_item(&p->fn_map, *idx);
- + *idx = entry_ptr->idx;
- +
- + if (s == BC_STATUS_VEC_ITEM_EXISTS) {
- +
- + BcFunc *func = bc_vec_item(&p->fns, entry_ptr->idx);
- + s = BC_STATUS_SUCCESS;
- +
- + // We need to reset these, so the function can be repopulated.
- + func->nparams = 0;
- + bc_vec_npop(&func->autos, func->autos.len);
- + bc_vec_npop(&func->code, func->code.len);
- + bc_vec_npop(&func->labels, func->labels.len);
- + }
- + else {
- + if ((s = bc_func_init(&f))) return s;
- + if ((s = bc_vec_push(&p->fns, &f))) bc_func_free(&f);
- + }
- +
- + return s;
- +}
- +
- +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 (!s && bcg.signe && !bcg.tty) return BC_STATUS_QUIT;
- +
- + bcg.sigc += bcg.signe;
- + bcg.signe = bcg.sig != bcg.sigc;
- +
- + if (!s || s == BC_STATUS_EXEC_SIGNAL) {
- + if (bcg.ttyin) {
- + if (fputs(bc_program_ready_msg, stderr) < 0 || fflush(stderr) < 0)
- + s = BC_STATUS_IO_ERR;
- + else s = BC_STATUS_SUCCESS;
- + }
- + else s = BC_STATUS_QUIT;
- + }
- +
- + return s;
- +}
- +
- +BcStatus bc_program_exec(BcProgram *p) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + size_t idx;
- + BcResult r;
- + BcResult *ptr;
- + BcNum *num;
- + BcInstPtr *ip = bc_vec_top(&p->stack);
- + BcFunc *func = bc_vec_item(&p->fns, ip->func);
- + char *code = func->code.v;
- + bool cond = false;
- +
- + while (!s && !bcg.sig_other && ip->idx < func->code.len) {
- +
- + uint8_t inst = code[(ip->idx)++];
- +
- + switch (inst) {
- +
- +#ifdef CONFIG_BC
- + case BC_INST_JUMP_ZERO:
- + {
- + if ((s = bc_program_prep(p, &ptr, &num))) return s;
- + cond = !bc_num_cmp(num, &p->zero);
- + bc_vec_pop(&p->results);
- + }
- + // Fallthrough.
- + case BC_INST_JUMP:
- + {
- + size_t *addr;
- + idx = bc_program_index(code, &ip->idx);
- + addr = bc_vec_item(&func->labels, idx);
- + if (inst == BC_INST_JUMP || cond) ip->idx = *addr;
- + break;
- + }
- +
- + case BC_INST_CALL:
- + {
- + s = bc_program_call(p, code, &ip->idx);
- + break;
- + }
- +
- + case BC_INST_INC_PRE:
- + case BC_INST_DEC_PRE:
- + case BC_INST_INC_POST:
- + case BC_INST_DEC_POST:
- + {
- + s = bc_program_incdec(p, inst);
- + break;
- + }
- +
- + case BC_INST_HALT:
- + {
- + s = BC_STATUS_QUIT;
- + break;
- + }
- +
- + case BC_INST_RET:
- + case BC_INST_RET0:
- + {
- + s = bc_program_return(p, inst);
- + break;
- + }
- +
- + case BC_INST_BOOL_OR:
- + case BC_INST_BOOL_AND:
- +#endif // CONFIG_BC
- + 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);
- + break;
- + }
- +
- + case BC_INST_VAR:
- + {
- + s = bc_program_pushVar(p, code, &ip->idx, false, false);
- + break;
- + }
- +
- + case BC_INST_ARRAY_ELEM:
- + case BC_INST_ARRAY:
- + {
- + s = bc_program_pushArray(p, code, &ip->idx, inst);
- + break;
- + }
- +
- + case BC_INST_LAST:
- + {
- + r.t = BC_RESULT_LAST;
- + s = bc_vec_push(&p->results, &r);
- + break;
- + }
- +
- + case BC_INST_IBASE:
- + case BC_INST_SCALE:
- + case BC_INST_OBASE:
- + {
- + s = bc_program_pushGlobal(p, inst);
- + break;
- + }
- +
- + case BC_INST_SCALE_FUNC:
- + case BC_INST_LENGTH:
- + case BC_INST_SQRT:
- + {
- + s = bc_program_builtin(p, inst);
- + break;
- + }
- +
- + case BC_INST_NUM:
- + {
- + r.t = BC_RESULT_CONSTANT;
- + r.d.id.idx = bc_program_index(code, &ip->idx);
- + s = bc_vec_push(&p->results, &r);
- + break;
- + }
- +
- + case BC_INST_POP:
- + {
- + if (!BC_PROG_STACK(&p->results, 1)) s = BC_STATUS_EXEC_STACK;
- + else bc_vec_pop(&p->results);
- + break;
- + }
- +
- + case BC_INST_POP_EXEC:
- + {
- + bc_vec_pop(&p->stack);
- + 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.id.idx = bc_program_index(code, &ip->idx);
- + s = 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:
- + {
- + s = bc_program_op(p, inst);
- + break;
- + }
- +
- + case BC_INST_BOOL_NOT:
- + {
- + if ((s = bc_program_prep(p, &ptr, &num))) return s;
- + if ((s = bc_num_init(&r.d.n, BC_NUM_DEF_SIZE))) return s;
- +
- + (!bc_num_cmp(num, &p->zero) ? bc_num_one : bc_num_zero)(&r.d.n);
- +
- + s = bc_program_retire(p, &r, BC_RESULT_TEMP);
- + if (s) bc_num_free(&r.d.n);
- +
- + break;
- + }
- +
- + case BC_INST_NEG:
- + {
- + s = bc_program_negate(p);
- + break;
- + }
- +
- +#ifdef CONFIG_BC
- + 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:
- +#endif // CONFIG_BC
- + case BC_INST_ASSIGN:
- + {
- + s = bc_program_assign(p, inst);
- + break;
- + }
- +
- +#ifdef CONFIG_DC
- + 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);
- + break;
- + }
- +
- + case BC_INST_PRINT_STACK:
- + {
- + for (idx = 0; !s && idx < p->results.len; ++idx)
- + s = bc_program_print(p, BC_INST_PRINT, idx);
- + break;
- + }
- +
- + case BC_INST_CLEAR_STACK:
- + {
- + bc_vec_npop(&p->results, p->results.len);
- + break;
- + }
- +
- + case BC_INST_STACK_LEN:
- + {
- + s = bc_program_stackLen(p);
- + break;
- + }
- +
- + case BC_INST_DUPLICATE:
- + {
- + if (!BC_PROG_STACK(&p->results, 1)) return BC_STATUS_EXEC_STACK;
- + ptr = bc_vec_top(&p->results);
- + if ((s = bc_result_copy(&r, ptr))) return s;
- + s = bc_vec_push(&p->results, &r);
- + break;
- + }
- +
- + case BC_INST_SWAP:
- + {
- + BcResult *ptr2;
- +
- + if (!BC_PROG_STACK(&p->results, 2)) return BC_STATUS_EXEC_STACK;
- +
- + 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);
- + 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:
- + {
- + char *name;
- + if (!(name = bc_program_name(code, &ip->idx))) return s;
- + s = bc_program_copyToVar(p, name, true);
- + free(name);
- + break;
- + }
- +
- + case BC_INST_QUIT:
- + {
- + if (p->stack.len <= 2) s = BC_STATUS_QUIT;
- + else bc_vec_npop(&p->stack, 2);
- + break;
- + }
- +
- + case BC_INST_NQUIT:
- + {
- + s = bc_program_nquit(p);
- + break;
- + }
- +#endif // CONFIG_DC
- + }
- +
- + if ((s && s != BC_STATUS_QUIT) || bcg.signe) s = bc_program_reset(p, s);
- +
- + // If the stack has changed, pointers may be invalid.
- + ip = bc_vec_top(&p->stack);
- + func = bc_vec_item(&p->fns, ip->func);
- + code = func->code.v;
- + }
- +
- + return s;
- +}
- +
- +static void bc_vm_sig(int sig) {
- + if (sig == SIGINT) {
- + size_t len = strlen(bcg.sig_msg);
- + if (write(2, bcg.sig_msg, len) == (ssize_t) len)
- + bcg.sig += (bcg.signe = bcg.sig == bcg.sigc);
- + }
- + else bcg.sig_other = 1;
- +}
- +
- +BcStatus bc_vm_info(const char* const help) {
- + if (printf("%s %s\n", bcg.name, BC_VERSION) < 0) return BC_STATUS_IO_ERR;
- + if (puts(bc_copyright) == EOF) return BC_STATUS_IO_ERR;
- + if (help && printf(help, bcg.name) < 0) return BC_STATUS_IO_ERR;
- + return BC_STATUS_SUCCESS;
- +}
- +
- +static BcStatus bc_vm_error(BcStatus s, const char *file, size_t line) {
- +
- + if (!s || s > BC_STATUS_VEC_ITEM_EXISTS) return s;
- +
- + if (fprintf(stderr, bc_err_fmt, bc_errs[bc_err_ids[s]], bc_err_msgs[s]) < 0)
- + return BC_STATUS_IO_ERR;
- + if (fprintf(stderr, " %s", file) < 0) return BC_STATUS_IO_ERR;
- + if (fprintf(stderr, bc_err_line + 4 * !line, line) < 0)
- + return BC_STATUS_IO_ERR;
- +
- + return s * (!bcg.ttyin || !!strcmp(file, bc_program_stdin_name));
- +}
- +
- +#ifdef CONFIG_BC
- +BcStatus bc_vm_posixError(BcStatus s, const char *file,
- + size_t line, const char *msg)
- +{
- + int p = (int) bcg.posix, w = (int) bcg.warn;
- + const char* const fmt = p ? bc_err_fmt : bc_warn_fmt;
- +
- + if (!(p || w) || s < BC_STATUS_POSIX_NAME_LEN) return BC_STATUS_SUCCESS;
- +
- + if (fprintf(stderr, fmt, bc_errs[bc_err_ids[s]], bc_err_msgs[s]) < 0)
- + return BC_STATUS_IO_ERR;
- + if (msg && fprintf(stderr, " %s\n", msg) < 0) return BC_STATUS_IO_ERR;
- + if (fprintf(stderr, " %s", file) < 0) return BC_STATUS_IO_ERR;
- + if (fprintf(stderr, bc_err_line + 4 * !line, line) < 0)
- + return BC_STATUS_IO_ERR;
- +
- + return s * (!bcg.ttyin && !!p);
- +}
- +
- +static BcStatus bc_vm_envArgs(BcVm *vm) {
- +
- + BcStatus s;
- + BcVec args;
- + char *env_args = NULL, *buf;
- +
- + if (!(env_args = getenv(bc_args_env_name))) return BC_STATUS_SUCCESS;
- + if (!(buf = (vm->env_args = strdup(env_args)))) return BC_STATUS_ALLOC_ERR;
- +
- + if ((s = bc_vec_init(&args, sizeof(char*), NULL))) goto err;
- + if ((s = bc_vec_push(&args, &bc_args_env_name))) goto err;
- +
- + while (*buf) {
- + if (!isspace(*buf)) {
- + if ((s = bc_vec_push(&args, &buf))) goto err;
- + while (*buf && !isspace(*buf)) ++buf;
- + if (*buf) (*(buf++)) = '\0';
- + }
- + else ++buf;
- + }
- +
- + s = bc_args((int) args.len, (char**) args.v,
- + &vm->flags, &vm->exprs, &vm->files);
- +
- +err:
- + bc_vec_free(&args);
- + return s;
- +}
- +#endif // CONFIG_BC
- +
- +static size_t bc_vm_envLen(const char *var) {
- +
- + char *lenv;
- + size_t i, len = BC_NUM_PRINT_WIDTH;
- + int num;
- +
- + if ((lenv = getenv(var))) {
- + len = strlen(lenv);
- + for (num = 1, i = 0; num && i < len; ++i) num = isdigit(lenv[i]);
- + if (!num || (len = (size_t) atoi(lenv) - 1) < 2 || len >= INT32_MAX)
- + len = BC_NUM_PRINT_WIDTH;
- + }
- +
- + return len;
- +}
- +
- +static BcStatus bc_vm_process(BcVm *vm, const char *text) {
- +
- + BcStatus s = bc_parse_text(&vm->prs, text);
- +
- + if ((s = bc_vm_error(s, vm->prs.l.f, vm->prs.l.line))) return s;
- +
- + while (vm->prs.l.t.t != BC_LEX_EOF) {
- +
- + if ((s = vm->prs.parse(&vm->prs)) == BC_STATUS_LIMITS) {
- +
- + s = BC_STATUS_IO_ERR;
- +
- + if (putchar('\n') == EOF) return s;
- + if (printf("BC_BASE_MAX = %lu\n", BC_MAX_OBASE) < 0) return s;
- + if (printf("BC_DIM_MAX = %lu\n", BC_MAX_DIM) < 0) return s;
- + if (printf("BC_SCALE_MAX = %lu\n", BC_MAX_SCALE) < 0) return s;
- + if (printf("BC_STRING_MAX = %lu\n", BC_MAX_STRING) < 0) return s;
- + if (printf("BC_NAME_MAX = %lu\n", BC_MAX_NAME) < 0) return s;
- + if (printf("BC_NUM_MAX = %lu\n", BC_MAX_NUM) < 0) return s;
- + if (printf("Max Exponent = %lu\n", BC_MAX_EXP) < 0) return s;
- + if (printf("Number of Vars = %lu\n", BC_MAX_VARS) < 0) return s;
- + if (putchar('\n') == EOF) return s;
- +
- + s = BC_STATUS_SUCCESS;
- + }
- + else if (s == BC_STATUS_QUIT || bcg.sig_other ||
- + (s = bc_vm_error(s, vm->prs.l.f, vm->prs.l.line)))
- + {
- + return s;
- + }
- + }
- +
- + if (BC_PARSE_CAN_EXEC(&vm->prs)) {
- + s = bc_program_exec(&vm->prog);
- + if (!s && bcg.tty && fflush(stdout) == EOF) s = BC_STATUS_IO_ERR;
- + if (s && s != BC_STATUS_QUIT)
- + s = bc_vm_error(bc_program_reset(&vm->prog, s), vm->prs.l.f, 0);
- + }
- +
- + return s;
- +}
- +
- +static BcStatus bc_vm_file(BcVm *vm, const char *file) {
- +
- + BcStatus s;
- + char *data;
- + BcFunc *main_func;
- + BcInstPtr *ip;
- +
- + vm->prog.file = file;
- + if ((s = bc_read_file(file, &data))) return bc_vm_error(s, file, 0);
- +
- + bc_lex_file(&vm->prs.l, file);
- + if ((s = bc_vm_process(vm, data))) goto err;
- +
- + main_func = bc_vec_item(&vm->prog.fns, BC_PROG_MAIN);
- + ip = bc_vec_item(&vm->prog.stack, 0);
- +
- + if (main_func->code.len < ip->idx)
- + s = bc_vm_error(BC_STATUS_EXEC_FILE_NOT_EXECUTABLE, file, 0);
- +
- +err:
- + free(data);
- + return s;
- +}
- +
- +static BcStatus bc_vm_stdin(BcVm *vm) {
- +
- + BcStatus s;
- + BcVec buf, buffer;
- + char c;
- + size_t len, i, str = 0;
- + bool comment = false, notend;
- +
- + vm->prog.file = bc_program_stdin_name;
- + bc_lex_file(&vm->prs.l, bc_program_stdin_name);
- +
- + if ((s = bc_vec_init(&buffer, sizeof(char), NULL))) return s;
- + if ((s = bc_vec_init(&buf, sizeof(char), NULL))) goto buf_err;
- + if ((s = bc_vec_pushByte(&buffer, '\0'))) goto err;
- +
- + // 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.
- + while (!s && !(s = bc_read_line(&buf, ">>> "))) {
- +
- + char *string = buf.v;
- +
- + if ((len = buf.len - 1) == 1) {
- + if (str && buf.v[0] == vm->exe.send) str -= 1;
- + else if (buf.v[0] == vm->exe.sbgn) str += 1;
- + }
- + else if (len > 1 || comment) {
- +
- + for (i = 0; i < len; ++i) {
- +
- + notend = len > i + 1;
- + c = string[i];
- +
- + if (i - 1 > len || string[i - 1] != '\\') {
- + if (vm->exe.sbgn == vm->exe.send) str ^= c == vm->exe.sbgn;
- + else if (c == vm->exe.send) str -= 1;
- + else if (c == vm->exe.sbgn) str += 1;
- + }
- +
- + if (c == '/' && notend && !comment && string[i + 1] == '*') {
- + comment = true;
- + break;
- + }
- + else if (c == '*' && notend && comment && string[i + 1] == '/')
- + comment = false;
- + }
- +
- + if (str || comment || string[len - 2] == '\\') {
- + if ((s = bc_vec_concat(&buffer, buf.v))) goto err;
- + continue;
- + }
- + }
- +
- + if ((s = bc_vec_concat(&buffer, buf.v))) goto err;
- + if ((s = bc_vm_process(vm, buffer.v))) goto err;
- +
- + bc_vec_npop(&buffer, buffer.len);
- + }
- +
- + if (s == BC_STATUS_BIN_FILE) s = bc_vm_error(s, vm->prs.l.f, 0);
- +
- + // I/O error will always happen when stdin is
- + // closed. It's not a problem in that case.
- + s = s == BC_STATUS_IO_ERR || s == BC_STATUS_QUIT ? BC_STATUS_SUCCESS : s;
- +
- + if (str) s = bc_vm_error(BC_STATUS_LEX_NO_STRING_END,
- + vm->prs.l.f, vm->prs.l.line);
- + else if (comment) s = bc_vm_error(BC_STATUS_LEX_NO_COMMENT_END,
- + vm->prs.l.f, vm->prs.l.line);
- +
- +err:
- + bc_vec_free(&buf);
- +buf_err:
- + bc_vec_free(&buffer);
- + return s;
- +}
- +
- +static BcStatus bc_vm_exec(BcVm *vm) {
- +
- + BcStatus s = BC_STATUS_SUCCESS;
- + size_t i;
- +
- +#ifdef CONFIG_BC
- + if (vm->flags & BC_FLAG_L) {
- +
- + bc_lex_file(&vm->prs.l, bc_lib_name);
- + if ((s = bc_parse_text(&vm->prs, bc_lib))) return s;
- +
- + while (!s && vm->prs.l.t.t != BC_LEX_EOF) s = vm->prs.parse(&vm->prs);
- +
- + if (s || (s = bc_program_exec(&vm->prog))) return s;
- + }
- +#endif // CONFIG_BC
- +
- + if (vm->exprs.len) {
- + bc_lex_file(&vm->prs.l, bc_program_exprs_name);
- + if ((s = bc_vm_process(vm, vm->exprs.v))) return s;
- + }
- +
- + for (i = 0; !bcg.sig_other && !s && i < vm->files.len; ++i)
- + s = bc_vm_file(vm, *((char**) bc_vec_item(&vm->files, i)));
- + if ((s && s != BC_STATUS_QUIT) || bcg.sig_other) return s;
- +
- + if ((bcg.bc || !vm->files.len) && !vm->exprs.len) s = bc_vm_stdin(vm);
- + if (!s && !bcg.sig_other && !BC_PARSE_CAN_EXEC(&vm->prs))
- + s = bc_vm_process(vm, "");
- +
- + return s == BC_STATUS_QUIT ? BC_STATUS_SUCCESS : s;
- +}
- +
- +static void bc_vm_free(BcVm *vm) {
- + bc_vec_free(&vm->files);
- + bc_vec_free(&vm->exprs);
- + bc_program_free(&vm->prog);
- + bc_parse_free(&vm->prs);
- + free(vm->env_args);
- +}
- +
- +static BcStatus bc_vm_init(BcVm *vm, BcVmExe exe, const char *env_len) {
- +
- + BcStatus s;
- + size_t len = bc_vm_envLen(env_len);
- + struct sigaction sa;
- +
- + sigemptyset(&sa.sa_mask);
- + sa.sa_handler = bc_vm_sig;
- + sa.sa_flags = 0;
- +
- + if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGPIPE, &sa, NULL) < 0 ||
- + sigaction(SIGHUP, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0)
- + {
- + return BC_STATUS_EXEC_SIGACTION_FAIL;
- + }
- +
- + memset(vm, 0, sizeof(BcVm));
- +
- + vm->exe = exe;
- + vm->flags = 0;
- + vm->env_args = NULL;
- +
- + if ((s = bc_vec_init(&vm->files, sizeof(char*), NULL))) return s;
- + if ((s = bc_vec_init(&vm->exprs, sizeof(char), NULL))) goto err;
- +
- +#ifdef CONFIG_BC
- + vm->flags |= BC_FLAG_S * bcg.bc * (getenv("POSIXLY_CORRECT") != NULL);
- + if (bcg.bc && (s = bc_vm_envArgs(vm))) goto err;
- +#endif // CONFIG_BC
- +
- + if ((s = bc_program_init(&vm->prog, len, exe.init, exe.exp))) goto err;
- + if ((s = exe.init(&vm->prs, &vm->prog, BC_PROG_MAIN))) goto err;
- +
- + return s;
- +
- +err:
- + bc_vm_free(vm);
- + return s;
- +}
- +
- +BcStatus bc_vm_run(int argc, char *argv[], BcVmExe exe, const char *env_len) {
- +
- + BcStatus st;
- + BcVm vm;
- +
- + if ((st = bc_vm_init(&vm, exe, env_len))) return st;
- + if ((st = bc_args(argc, argv, &vm.flags, &vm.exprs, &vm.files))) goto err;
- +
- + bcg.tty = (bcg.ttyin = isatty(0)) || (vm.flags & BC_FLAG_I) || isatty(1);
- +
- +#ifdef CONFIG_BC
- + bcg.posix = vm.flags & BC_FLAG_S;
- + bcg.warn = vm.flags & BC_FLAG_W;
- +#endif // CONFIG_BC
- +#ifdef CONFIG_DC
- + bcg.exreg = vm.flags & BC_FLAG_X;
- +#endif // CONFIG_DC
- +
- + if (bcg.ttyin && !(vm.flags & BC_FLAG_Q)) st = bc_vm_info(NULL);
- + if (!st) st = bc_vm_exec(&vm);
- +
- +err:
- + bc_vm_free(&vm);
- + return st;
- +}
- +
- +#ifdef CONFIG_BC
- +BcStatus bc_main(int argc, char *argv[]) {
- +
- + BcVmExe exec;
- +
- + bcg.bc = true;
- + bcg.name = bc_name;
- + bcg.sig_msg = bc_sig_msg;
- +
- + exec.init = bc_parse_init;
- + exec.exp = bc_parse_expression;
- + exec.sbgn = exec.send = '"';
- +
- + return bc_vm_run(argc, argv, exec, "BC_LINE_LENGTH");
- +}
- +#endif // CONFIG_BC
- +
- +#ifdef CONFIG_DC
- +BcStatus dc_main(int argc, char *argv[]) {
- +
- + BcVmExe exec;
- +
- + bcg.bc = false;
- + bcg.name = dc_name;
- + bcg.sig_msg = dc_sig_msg;
- +
- + exec.init = dc_parse_init;
- + exec.exp = dc_parse_expr;
- + exec.sbgn = '[';
- + exec.send = ']';
- +
- + return bc_vm_run(argc, argv, exec, "DC_LINE_LENGTH");
- +}
- +#endif // CONFIG_DC
- --
- 2.17.1
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement