bc: fix comparison bug, closes 12336
[oweals/busybox.git] / miscutils / bc.c
index 7be2d0b9bb38b0975195167222848930ad368321..c7246ea1a1610195e78de07d9e09b4e38535fcdd 100644 (file)
@@ -1,11 +1,22 @@
 /* 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.
+ * Adapted from https://github.com/gavinhoward/bc
+ * Original code copyright (c) 2018 Gavin D. Howard and contributors.
  */
+//TODO:
+// maybe implement a^b for non-integer b?
+
+#define DEBUG_LEXER   0
+#define DEBUG_COMPILE 0
+#define DEBUG_EXEC    0
+// This can be left enabled for production as well:
+#define SANITY_CHECKS 1
+
 //config:config BC
-//config:      bool "bc (45 kb; 49 kb when combined with dc)"
+//config:      bool "bc (45 kb)"
 //config:      default y
+//config:      select FEATURE_DC_BIG
 //config:      help
 //config:      bc is a command-line, arbitrary-precision calculator with a
 //config:      Turing-complete language. See the GNU bc manual
 //config:           easier.
 //config:        5) "read()" accepts expressions, not only numeric literals.
 //config:
-//config:      Options:
-//config:        -i  --interactive  force interactive mode
-//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:        -l  --mathlib      use predefined math routines:
-//config:              s(expr) sine in radians
-//config:              c(expr) cosine in radians
-//config:              a(expr) arctangent, returning radians
-//config:              l(expr) natural log
-//config:              e(expr) raises e to the power of expr
-//config:              j(n, x) Bessel function of integer order n of x
-//config:
 //config:config DC
-//config:      bool "dc (38 kb; 49 kb when combined with bc)"
+//config:      bool "dc (36 kb)"
 //config:      default y
 //config:      help
 //config:      dc is a reverse-polish notation command-line calculator which
 //config:           whitespace where a register should be, it skips the whitespace.
 //config:           If the character following is not a lowercase letter, an error
 //config:           is issued. Otherwise, the register name is parsed by the
-//config:           following regex:
-//config:              [a-z][a-z0-9_]*
+//config:           following regex: [a-z][a-z0-9_]*
 //config:           This generally means that register names will be surrounded by
 //config:           whitespace. Examples:
 //config:              l idx s temp L index S temp2 < do_thing
 //config:           Also note that, like the FreeBSD dc, extended registers are not
 //config:           allowed unless the "-x" option is given.
 //config:
-//config:config FEATURE_DC_SMALL
-//config:      bool "Minimal dc implementation (4.2 kb), not using bc code base"
-//config:      depends on DC && !BC
-//config:      default n
+//config:if BC || DC  # for menuconfig indenting
+//config:
+//config:config FEATURE_DC_BIG
+//config:      bool "Use bc code base for dc (larger, more features)"
+//config:      default y
 //config:
 //config:config FEATURE_DC_LIBM
 //config:      bool "Enable power and exp functions (requires libm)"
 //config:      default y
-//config:      depends on FEATURE_DC_SMALL
+//config:      depends on DC && !BC && !FEATURE_DC_BIG
 //config:      help
 //config:      Enable power and exp functions.
 //config:      NOTE: This will require libm to be present for linking.
 //config:
-//config:config FEATURE_BC_SIGNALS
+//config:config FEATURE_BC_INTERACTIVE
 //config:      bool "Interactive mode (+4kb)"
 //config:      default y
-//config:      depends on (BC || DC) && !FEATURE_DC_SMALL
+//config:      depends on BC || (DC && FEATURE_DC_BIG)
 //config:      help
 //config:      Enable interactive mode: when started on a tty,
 //config:      ^C interrupts execution and returns to command line,
@@ -98,9 +96,9 @@
 //config:config FEATURE_BC_LONG_OPTIONS
 //config:      bool "Enable bc/dc long options"
 //config:      default y
-//config:      depends on (BC || DC) && !FEATURE_DC_SMALL
-//config:      help
-//config:      Enable long options for bc and dc.
+//config:      depends on BC || (DC && FEATURE_DC_BIG)
+//config:
+//config:endif
 
 //applet:IF_BC(APPLET(bc, BB_DIR_USR_BIN, BB_SUID_DROP))
 //applet:IF_DC(APPLET(dc, BB_DIR_USR_BIN, BB_SUID_DROP))
 //usage:       "obase = A\n"
 //usage:
 //usage:#define dc_trivial_usage
-//usage:       IF_NOT_FEATURE_DC_SMALL("[-x] ")"[-eSCRIPT]... [-fFILE]... [FILE]..."
+//usage:       IF_FEATURE_DC_BIG("[-x] ")"[-eSCRIPT]... [-fFILE]... [FILE]..."
 //usage:
 //usage:#define dc_full_usage "\n"
 //usage:     "\nTiny RPN calculator. Operations:"
-//usage:     "\n+, -, *, /, %, ~, ^," IF_NOT_FEATURE_DC_SMALL(" |,")
-//usage:     "\np - print top of the stack (without popping)"
+//usage:     "\n+, -, *, /, %, ~, ^," IF_FEATURE_DC_BIG(" |,")
+//usage:     "\np - print top of the stack without popping"
 //usage:     "\nf - print entire stack"
 //usage:     "\nk - pop the value and set the precision"
 //usage:     "\ni - pop the value and set input radix"
 #include "libbb.h"
 #include "common_bufsiz.h"
 
-#if ENABLE_FEATURE_DC_SMALL
+#if !ENABLE_BC && !ENABLE_FEATURE_DC_BIG
 # include "dc.c"
 #else
 
-#define DEBUG_LEXER   0
-#define DEBUG_COMPILE 0
-#define DEBUG_EXEC    0
-// This can be left enabled for production as well:
-#define SANITY_CHECKS 1
-
 #if DEBUG_LEXER
 static uint8_t lex_indent;
 #define dbg_lex(...) \
@@ -211,11 +203,10 @@ static uint8_t lex_indent;
 typedef enum BcStatus {
        BC_STATUS_SUCCESS = 0,
        BC_STATUS_FAILURE = 1,
-       BC_STATUS_PARSE_EMPTY_EXP = 2, // bc_parse_expr_empty_ok() uses this
 } BcStatus;
 
-#define BC_VEC_INVALID_IDX ((size_t) -1)
-#define BC_VEC_START_CAP (1 << 5)
+#define BC_VEC_INVALID_IDX  ((size_t) -1)
+#define BC_VEC_START_CAP    (1 << 5)
 
 typedef void (*BcVecFree)(void *) FAST_FUNC;
 
@@ -237,12 +228,12 @@ typedef struct BcNum {
        bool neg;
 } BcNum;
 
-#define BC_NUM_MAX_IBASE        ((unsigned long) 16)
+#define BC_NUM_MAX_IBASE        36
 // larger value might speed up BIGNUM calculations a bit:
-#define BC_NUM_DEF_SIZE         (16)
-#define BC_NUM_PRINT_WIDTH      (69)
+#define BC_NUM_DEF_SIZE         16
+#define BC_NUM_PRINT_WIDTH      69
 
-#define BC_NUM_KARATSUBA_LEN    (32)
+#define BC_NUM_KARATSUBA_LEN    32
 
 typedef enum BcInst {
 #if ENABLE_BC
@@ -251,55 +242,53 @@ typedef enum BcInst {
        BC_INST_INC_POST,
        BC_INST_DEC_POST,
 #endif
-
-       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,
-
+       XC_INST_NEG,            // order
+
+       XC_INST_REL_EQ,         // should
+       XC_INST_REL_LE,         // match
+       XC_INST_REL_GE,         // LEX
+       XC_INST_REL_NE,         // constants
+       XC_INST_REL_LT,         // for
+       XC_INST_REL_GT,         // these
+
+       XC_INST_POWER,          // operations
+       XC_INST_MULTIPLY,       // |
+       XC_INST_DIVIDE,         // |
+       XC_INST_MODULUS,        // |
+       XC_INST_PLUS,           // |
+       XC_INST_MINUS,          // |
+
+       XC_INST_BOOL_NOT,       // |
+       XC_INST_BOOL_OR,        // |
+       XC_INST_BOOL_AND,       // |
 #if ENABLE_BC
-       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_POWER,   // |
+       BC_INST_ASSIGN_MULTIPLY,// |
+       BC_INST_ASSIGN_DIVIDE,  // |
+       BC_INST_ASSIGN_MODULUS, // |
+       BC_INST_ASSIGN_PLUS,    // |
+       BC_INST_ASSIGN_MINUS,   // |
 #endif
-       BC_INST_ASSIGN,
-
-       BC_INST_NUM,
-       BC_INST_VAR,
-       BC_INST_ARRAY_ELEM,
-       BC_INST_ARRAY,
-
-       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,
+       XC_INST_ASSIGN,         // V
+
+       XC_INST_NUM,
+       XC_INST_VAR,
+       XC_INST_ARRAY_ELEM,
+       XC_INST_ARRAY,
+       XC_INST_SCALE_FUNC,
+
+       XC_INST_IBASE,       // order of these constans should match other enums
+       XC_INST_OBASE,       // order of these constans should match other enums
+       XC_INST_SCALE,       // order of these constans should match other enums
+       IF_BC(BC_INST_LAST,) // order of these constans should match other enums
+       XC_INST_LENGTH,
+       XC_INST_READ,
+       XC_INST_SQRT,
+
+       XC_INST_PRINT,
+       XC_INST_PRINT_POP,
+       XC_INST_STR,
+       XC_INST_PRINT_STR,
 
 #if ENABLE_BC
        BC_INST_HALT,
@@ -309,35 +298,35 @@ typedef enum BcInst {
        BC_INST_CALL,
        BC_INST_RET0,
 #endif
-       BC_INST_RET,
+       XC_INST_RET,
 
-       BC_INST_POP,
+       XC_INST_POP,
 #if ENABLE_DC
-       BC_INST_POP_EXEC,
+       DC_INST_POP_EXEC,
 
-       BC_INST_MODEXP,
-       BC_INST_DIVMOD,
+       DC_INST_MODEXP,
+       DC_INST_DIVMOD,
 
-       BC_INST_EXECUTE,
-       BC_INST_EXEC_COND,
+       DC_INST_EXECUTE,
+       DC_INST_EXEC_COND,
 
-       BC_INST_ASCIIFY,
-       BC_INST_PRINT_STREAM,
+       DC_INST_ASCIIFY,
+       DC_INST_PRINT_STREAM,
 
-       BC_INST_PRINT_STACK,
-       BC_INST_CLEAR_STACK,
-       BC_INST_STACK_LEN,
-       BC_INST_DUPLICATE,
-       BC_INST_SWAP,
+       DC_INST_PRINT_STACK,
+       DC_INST_CLEAR_STACK,
+       DC_INST_STACK_LEN,
+       DC_INST_DUPLICATE,
+       DC_INST_SWAP,
 
-       BC_INST_LOAD,
-       BC_INST_PUSH_VAR,
-       BC_INST_PUSH_TO_VAR,
+       DC_INST_LOAD,
+       DC_INST_PUSH_VAR,
+       DC_INST_PUSH_TO_VAR,
 
-       BC_INST_QUIT,
-       BC_INST_NQUIT,
+       DC_INST_QUIT,
+       DC_INST_NQUIT,
 
-       BC_INST_INVALID = -1,
+       DC_INST_INVALID = -1,
 #endif
 } BcInst;
 
@@ -353,24 +342,26 @@ typedef struct BcFunc {
        IF_BC(BcVec strs;)
        IF_BC(BcVec consts;)
        IF_BC(size_t nparams;)
+       IF_BC(bool voidfunc;)
 } BcFunc;
 
 typedef enum BcResultType {
-       BC_RESULT_TEMP,
-
-       BC_RESULT_VAR,
-       BC_RESULT_ARRAY_ELEM,
-       BC_RESULT_ARRAY,
-
-       BC_RESULT_STR,
-
-       //code uses "inst - BC_INST_IBASE + BC_RESULT_IBASE" construct,
-       BC_RESULT_IBASE,  // relative order should match for: BC_INST_IBASE
-       BC_RESULT_SCALE,  // relative order should match for: BC_INST_SCALE
-       BC_RESULT_LAST,   // relative order should match for: BC_INST_LAST
-       BC_RESULT_CONSTANT,
-       BC_RESULT_ONE,
-       BC_RESULT_OBASE,  // relative order should match for: BC_INST_OBASE
+       XC_RESULT_TEMP,
+       IF_BC(BC_RESULT_VOID,) // same as TEMP, but INST_PRINT will ignore it
+
+       XC_RESULT_VAR,
+       XC_RESULT_ARRAY_ELEM,
+       XC_RESULT_ARRAY,
+
+       XC_RESULT_STR,
+
+       //code uses "inst - XC_INST_IBASE + XC_RESULT_IBASE" construct,
+       XC_RESULT_IBASE,       // relative order should match for: XC_INST_IBASE
+       XC_RESULT_OBASE,       // relative order should match for: XC_INST_OBASE
+       XC_RESULT_SCALE,       // relative order should match for: XC_INST_SCALE
+       IF_BC(BC_RESULT_LAST,) // relative order should match for: BC_INST_LAST
+       XC_RESULT_CONSTANT,
+       IF_BC(BC_RESULT_ONE,)
 } BcResultType;
 
 typedef union BcResultData {
@@ -387,62 +378,68 @@ typedef struct BcResult {
 typedef struct BcInstPtr {
        size_t func;
        size_t inst_idx;
-       IF_BC(size_t results_len_before_call;)
 } BcInstPtr;
 
-// BC_LEX_NEG is not used in lexing; it is only for parsing.
+typedef enum BcType {
+       BC_TYPE_VAR,
+       BC_TYPE_ARRAY,
+       BC_TYPE_REF,
+} BcType;
+
 typedef enum BcLexType {
-       BC_LEX_EOF,
-       BC_LEX_INVALID,
+       XC_LEX_EOF,
+       XC_LEX_INVALID,
+
+       XC_LEX_NLINE,
+       XC_LEX_WHITESPACE,
+       XC_LEX_STR,
+       XC_LEX_NAME,
+       XC_LEX_NUMBER,
+
+       XC_LEX_1st_op,
+       XC_LEX_NEG = XC_LEX_1st_op,     // order
+
+       XC_LEX_OP_REL_EQ,               // should
+       XC_LEX_OP_REL_LE,               // match
+       XC_LEX_OP_REL_GE,               // INST
+       XC_LEX_OP_REL_NE,               // constants
+       XC_LEX_OP_REL_LT,               // for
+       XC_LEX_OP_REL_GT,               // these
+
+       XC_LEX_OP_POWER,                // operations
+       XC_LEX_OP_MULTIPLY,             // |
+       XC_LEX_OP_DIVIDE,               // |
+       XC_LEX_OP_MODULUS,              // |
+       XC_LEX_OP_PLUS,                 // |
+       XC_LEX_OP_MINUS,                // |
+       XC_LEX_OP_last = XC_LEX_OP_MINUS,
+#if ENABLE_BC
+       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,               // V
 
        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_LPAREN, // () are 0x28 and 0x29
+       BC_LEX_RPAREN, // must be LPAREN+1: code uses (c - '(' + BC_LEX_LPAREN)
+
+       BC_LEX_LBRACKET, // [] are 0x5B and 0x5D
        BC_LEX_COMMA,
-       BC_LEX_RBRACKET,
+       BC_LEX_RBRACKET, // must be LBRACKET+2: code uses (c - '[' + BC_LEX_LBRACKET)
 
-       BC_LEX_LBRACE, // '{' is 0x7B, '}' is 0x7D,
+       BC_LEX_LBRACE, // {} are 0x7B and 0x7D
        BC_LEX_SCOLON,
-       BC_LEX_RBRACE, // should be LBRACE+2: code uses (c - '{' + BC_LEX_LBRACE)
-
-       BC_LEX_STR,
-       BC_LEX_NAME,
-       BC_LEX_NUMBER,
+       BC_LEX_RBRACE, // must be LBRACE+2: code uses (c - '{' + BC_LEX_LBRACE)
 
        BC_LEX_KEY_1st_keyword,
        BC_LEX_KEY_AUTO = BC_LEX_KEY_1st_keyword,
@@ -452,13 +449,13 @@ typedef enum BcLexType {
        BC_LEX_KEY_ELSE,
        BC_LEX_KEY_FOR,
        BC_LEX_KEY_HALT,
-       // code uses "type - BC_LEX_KEY_IBASE + BC_INST_IBASE" construct,
-       BC_LEX_KEY_IBASE,  // relative order should match for: BC_INST_IBASE
+       // code uses "type - BC_LEX_KEY_IBASE + XC_INST_IBASE" construct,
+       BC_LEX_KEY_IBASE,    // relative order should match for: XC_INST_IBASE
+       BC_LEX_KEY_OBASE,    // relative order should match for: XC_INST_OBASE
        BC_LEX_KEY_IF,
-       BC_LEX_KEY_LAST,   // relative order should match for: BC_INST_LAST
+       BC_LEX_KEY_LAST,     // relative order should match for: BC_INST_LAST
        BC_LEX_KEY_LENGTH,
        BC_LEX_KEY_LIMITS,
-       BC_LEX_KEY_OBASE,  // relative order should match for: BC_INST_OBASE
        BC_LEX_KEY_PRINT,
        BC_LEX_KEY_QUIT,
        BC_LEX_KEY_READ,
@@ -466,35 +463,51 @@ typedef enum BcLexType {
        BC_LEX_KEY_SCALE,
        BC_LEX_KEY_SQRT,
        BC_LEX_KEY_WHILE,
+#endif // ENABLE_BC
 
 #if ENABLE_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,
-
-       // code uses "t - BC_LEX_STORE_IBASE + BC_INST_IBASE" construct,
-       BC_LEX_STORE_IBASE,  // relative order should match for: BC_INST_IBASE
-       BC_LEX_STORE_SCALE,  // relative order should match for: BC_INST_SCALE
-       BC_LEX_LOAD,
-       BC_LEX_LOAD_POP,
-       BC_LEX_STORE_PUSH,
-       BC_LEX_STORE_OBASE,  // relative order should match for: BC_INST_OBASE
-       BC_LEX_PRINT_POP,
-       BC_LEX_NQUIT,
-       BC_LEX_SCALE_FACTOR,
+       DC_LEX_OP_BOOL_NOT = XC_LEX_OP_last + 1,
+       DC_LEX_OP_ASSIGN,
+
+       DC_LEX_LPAREN,
+       DC_LEX_SCOLON,
+       DC_LEX_READ,
+       DC_LEX_IBASE,
+       DC_LEX_SCALE,
+       DC_LEX_OBASE,
+       DC_LEX_LENGTH,
+       DC_LEX_PRINT,
+       DC_LEX_QUIT,
+       DC_LEX_SQRT,
+       DC_LEX_LBRACE,
+
+       DC_LEX_EQ_NO_REG,
+       DC_LEX_OP_MODEXP,
+       DC_LEX_OP_DIVMOD,
+
+       DC_LEX_COLON,
+       DC_LEX_ELSE,
+       DC_LEX_EXECUTE,
+       DC_LEX_PRINT_STACK,
+       DC_LEX_CLEAR_STACK,
+       DC_LEX_STACK_LEVEL,
+       DC_LEX_DUPLICATE,
+       DC_LEX_SWAP,
+       DC_LEX_POP,
+
+       DC_LEX_ASCIIFY,
+       DC_LEX_PRINT_STREAM,
+
+       // code uses "t - DC_LEX_STORE_IBASE + XC_INST_IBASE" construct,
+       DC_LEX_STORE_IBASE,  // relative order should match for: XC_INST_IBASE
+       DC_LEX_STORE_OBASE,  // relative order should match for: XC_INST_OBASE
+       DC_LEX_STORE_SCALE,  // relative order should match for: XC_INST_SCALE
+       DC_LEX_LOAD,
+       DC_LEX_LOAD_POP,
+       DC_LEX_STORE_PUSH,
+       DC_LEX_PRINT_POP,
+       DC_LEX_NQUIT,
+       DC_LEX_SCALE_FACTOR,
 #endif
 } BcLexType;
 // must match order of BC_LEX_KEY_foo etc above
@@ -502,35 +515,35 @@ typedef enum BcLexType {
 struct BcLexKeyword {
        char name8[8];
 };
-#define BC_LEX_KW_ENTRY(a, b) \
+#define LEX_KW_ENTRY(a, b) \
        { .name8 = a /*, .posix = b */ }
 static const struct BcLexKeyword bc_lex_kws[20] = {
-       BC_LEX_KW_ENTRY("auto"    , 1), // 0
-       BC_LEX_KW_ENTRY("break"   , 1), // 1
-       BC_LEX_KW_ENTRY("continue", 0), // 2 note: this one has no terminating NUL
-       BC_LEX_KW_ENTRY("define"  , 1), // 3
-       BC_LEX_KW_ENTRY("else"    , 0), // 4
-       BC_LEX_KW_ENTRY("for"     , 1), // 5
-       BC_LEX_KW_ENTRY("halt"    , 0), // 6
-       BC_LEX_KW_ENTRY("ibase"   , 1), // 7
-       BC_LEX_KW_ENTRY("if"      , 1), // 8
-       BC_LEX_KW_ENTRY("last"    , 0), // 9
-       BC_LEX_KW_ENTRY("length"  , 1), // 10
-       BC_LEX_KW_ENTRY("limits"  , 0), // 11
-       BC_LEX_KW_ENTRY("obase"   , 1), // 12
-       BC_LEX_KW_ENTRY("print"   , 0), // 13
-       BC_LEX_KW_ENTRY("quit"    , 1), // 14
-       BC_LEX_KW_ENTRY("read"    , 0), // 15
-       BC_LEX_KW_ENTRY("return"  , 1), // 16
-       BC_LEX_KW_ENTRY("scale"   , 1), // 17
-       BC_LEX_KW_ENTRY("sqrt"    , 1), // 18
-       BC_LEX_KW_ENTRY("while"   , 1), // 19
+       LEX_KW_ENTRY("auto"    , 1), // 0
+       LEX_KW_ENTRY("break"   , 1), // 1
+       LEX_KW_ENTRY("continue", 0), // 2 note: this one has no terminating NUL
+       LEX_KW_ENTRY("define"  , 1), // 3
+       LEX_KW_ENTRY("else"    , 0), // 4
+       LEX_KW_ENTRY("for"     , 1), // 5
+       LEX_KW_ENTRY("halt"    , 0), // 6
+       LEX_KW_ENTRY("ibase"   , 1), // 7
+       LEX_KW_ENTRY("obase"   , 1), // 8
+       LEX_KW_ENTRY("if"      , 1), // 9
+       LEX_KW_ENTRY("last"    , 0), // 10
+       LEX_KW_ENTRY("length"  , 1), // 11
+       LEX_KW_ENTRY("limits"  , 0), // 12
+       LEX_KW_ENTRY("print"   , 0), // 13
+       LEX_KW_ENTRY("quit"    , 1), // 14
+       LEX_KW_ENTRY("read"    , 0), // 15
+       LEX_KW_ENTRY("return"  , 1), // 16
+       LEX_KW_ENTRY("scale"   , 1), // 17
+       LEX_KW_ENTRY("sqrt"    , 1), // 18
+       LEX_KW_ENTRY("while"   , 1), // 19
 };
-#undef BC_LEX_KW_ENTRY
-#define STRING_if    (bc_lex_kws[8].name8)
+#undef LEX_KW_ENTRY
 #define STRING_else  (bc_lex_kws[4].name8)
-#define STRING_while (bc_lex_kws[19].name8)
 #define STRING_for   (bc_lex_kws[5].name8)
+#define STRING_if    (bc_lex_kws[9].name8)
+#define STRING_while (bc_lex_kws[19].name8)
 enum {
        POSIX_KWORD_MASK = 0
                | (1 << 0)  // 0
@@ -542,10 +555,10 @@ enum {
                | (0 << 6)  // 6
                | (1 << 7)  // 7
                | (1 << 8)  // 8
-               | (0 << 9)  // 9
-               | (1 << 10) // 10
-               | (0 << 11) // 11
-               | (1 << 12) // 12
+               | (1 << 9)  // 9
+               | (0 << 10) // 10
+               | (1 << 11) // 11
+               | (0 << 12) // 12
                | (0 << 13) // 13
                | (1 << 14) // 14
                | (0 << 15) // 15
@@ -554,29 +567,29 @@ enum {
                | (1 << 18) // 18
                | (1 << 19) // 19
 };
-#define bc_lex_kws_POSIX(i) ((1 << (i)) & POSIX_KWORD_MASK)
+#define keyword_is_POSIX(i) ((1 << (i)) & POSIX_KWORD_MASK)
 
 // This is a bit array that corresponds to token types. An entry is
 // true if the token is valid in an expression, false otherwise.
 // Used to figure out when expr parsing should stop *without error message*
 // - 0 element indicates this condition. 1 means "this token is to be eaten
-// as part of the expression", token can them still be determined to be invalid
+// as part of the expression", it can then still be determined to be invalid
 // by later processing.
 enum {
 #define EXBITS(a,b,c,d,e,f,g,h) \
        ((uint64_t)((a << 0)+(b << 1)+(c << 2)+(d << 3)+(e << 4)+(f << 5)+(g << 6)+(h << 7)))
-       BC_PARSE_EXPRS_BITS = 0
-       + (EXBITS(0,0,1,1,1,1,1,1) << (0*8)) //  0: eof    inval ++    --     -     ^     *    /
-       + (EXBITS(1,1,1,1,1,1,1,1) << (1*8)) //  8: %      +     -     ==     <=    >=    !=   <
-       + (EXBITS(1,1,1,1,1,1,1,1) << (2*8)) // 16: >      !     ||    &&     ^=    *=    /=   %=
-       + (EXBITS(1,1,1,0,0,1,1,0) << (3*8)) // 24: +=     -=    =     NL     WS    (     )    [
-       + (EXBITS(0,0,0,0,0,0,1,1) << (4*8)) // 32: ,      ]     {     ;      }     STR   NAME NUM
-       + (EXBITS(0,0,0,0,0,0,0,1) << (5*8)) // 40: auto   break cont  define else  for   halt ibase
-       + (EXBITS(0,1,1,1,1,0,0,1) << (6*8)) // 48: if     last  len   limits obase print quit read - bug, why "limits" is allowed?
-       + (EXBITS(0,1,1,0,0,0,0,0) << (7*8)) // 56: return scale sqrt  while
+       BC_PARSE_EXPRS_BITS = 0              // corresponding BC_LEX_xyz:
+       + (EXBITS(0,0,0,0,0,1,1,1) << (0*8)) //  0: EOF    INVAL  NL     WS     STR    NAME   NUM    -
+       + (EXBITS(1,1,1,1,1,1,1,1) << (1*8)) //  8: ==     <=     >=     !=     <      >      ^      *
+       + (EXBITS(1,1,1,1,1,1,1,1) << (2*8)) // 16: /      %      +      -      !      ||     &&     ^=
+       + (EXBITS(1,1,1,1,1,1,1,1) << (3*8)) // 24: *=     /=     %=     +=     -=     =      ++     --
+       + (EXBITS(1,1,0,0,0,0,0,0) << (4*8)) // 32: (      )      [      ,      ]      {      ;      }
+       + (EXBITS(0,0,0,0,0,0,0,1) << (5*8)) // 40: auto   break  cont   define else   for    halt   ibase
+       + (EXBITS(1,0,1,1,0,0,0,1) << (6*8)) // 48: obase  if     last   length limits print  quit   read
+       + (EXBITS(0,1,1,0,0,0,0,0) << (7*8)) // 56: return scale  sqrt   while
 #undef EXBITS
 };
-static ALWAYS_INLINE long bc_parse_exprs(unsigned i)
+static ALWAYS_INLINE long lex_allowed_in_bc_expr(unsigned i)
 {
 #if ULONG_MAX > 0xffffffff
        // 64-bit version (will not work correctly for 32-bit longs!)
@@ -593,88 +606,125 @@ static ALWAYS_INLINE long bc_parse_exprs(unsigned i)
 }
 
 // This is an array of data for operators that correspond to
-// [BC_LEX_OP_INC...BC_LEX_OP_ASSIGN] token types.
-static const uint8_t bc_parse_ops[] = {
+// [XC_LEX_1st_op...] token types.
+static const uint8_t bc_ops_prec_and_assoc[] ALIGN1 = {
 #define OP(p,l) ((int)(l) * 0x10 + (p))
-       OP(0, false), OP( 0, false ), // inc dec
        OP(1, false), // neg
+       OP(6, true ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true ), // == <= >= != < >
        OP(2, false), // pow
        OP(3, true ), OP( 3, true  ), OP( 3, true  ), // mul div mod
        OP(4, true ), OP( 4, true  ), // + -
-       OP(6, true ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true ), // == <= >= != < >
        OP(1, false), // not
        OP(7, true ), OP( 7, true  ), // or and
        OP(5, false), OP( 5, false ), OP( 5, false ), OP( 5, false ), OP( 5, false ), // ^= *= /= %= +=
        OP(5, false), OP( 5, false ), // -= =
+       OP(0, false), OP( 0, false ), // inc dec
 #undef OP
 };
-#define bc_parse_op_PREC(i) (bc_parse_ops[i] & 0x0f)
-#define bc_parse_op_LEFT(i) (bc_parse_ops[i] & 0x10)
+#define bc_operation_PREC(i) (bc_ops_prec_and_assoc[i] & 0x0f)
+#define bc_operation_LEFT(i) (bc_ops_prec_and_assoc[i] & 0x10)
 #endif // ENABLE_BC
 
 #if ENABLE_DC
-static const //BcInst - should be this type. Using signed narrow type since BC_INST_INVALID is -1
+static const //BcLexType - should be this type
+uint8_t
+dc_char_to_LEX[] ALIGN1 = {
+       // %&'(
+       XC_LEX_OP_MODULUS, XC_LEX_INVALID, XC_LEX_INVALID, DC_LEX_LPAREN,
+       // )*+,
+       XC_LEX_INVALID, XC_LEX_OP_MULTIPLY, XC_LEX_OP_PLUS, XC_LEX_INVALID,
+       // -./
+       XC_LEX_OP_MINUS, XC_LEX_INVALID, XC_LEX_OP_DIVIDE,
+       // 0123456789
+       XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID,
+       XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID,
+       XC_LEX_INVALID, XC_LEX_INVALID,
+       // :;<=>?@
+       DC_LEX_COLON, DC_LEX_SCOLON, XC_LEX_OP_REL_GT, XC_LEX_OP_REL_EQ,
+       XC_LEX_OP_REL_LT, DC_LEX_READ, XC_LEX_INVALID,
+       // ABCDEFGH
+       XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID,
+       XC_LEX_INVALID, XC_LEX_INVALID, DC_LEX_EQ_NO_REG, XC_LEX_INVALID,
+       // IJKLMNOP
+       DC_LEX_IBASE, XC_LEX_INVALID, DC_LEX_SCALE, DC_LEX_LOAD_POP,
+       XC_LEX_INVALID, DC_LEX_OP_BOOL_NOT, DC_LEX_OBASE, DC_LEX_PRINT_STREAM,
+       // QRSTUVWX
+       DC_LEX_NQUIT, DC_LEX_POP, DC_LEX_STORE_PUSH, XC_LEX_INVALID,
+       XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID, DC_LEX_SCALE_FACTOR,
+       // YZ
+       XC_LEX_INVALID, DC_LEX_LENGTH,
+       // [\]
+       XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID,
+       // ^_`
+       XC_LEX_OP_POWER, XC_LEX_NEG, XC_LEX_INVALID,
+       // abcdefgh
+       DC_LEX_ASCIIFY, XC_LEX_INVALID, DC_LEX_CLEAR_STACK, DC_LEX_DUPLICATE,
+       DC_LEX_ELSE, DC_LEX_PRINT_STACK, XC_LEX_INVALID, XC_LEX_INVALID,
+       // ijklmnop
+       DC_LEX_STORE_IBASE, XC_LEX_INVALID, DC_LEX_STORE_SCALE, DC_LEX_LOAD,
+       XC_LEX_INVALID, DC_LEX_PRINT_POP, DC_LEX_STORE_OBASE, DC_LEX_PRINT,
+       // qrstuvwx
+       DC_LEX_QUIT, DC_LEX_SWAP, DC_LEX_OP_ASSIGN, XC_LEX_INVALID,
+       XC_LEX_INVALID, DC_LEX_SQRT, XC_LEX_INVALID, DC_LEX_EXECUTE,
+       // yz
+       XC_LEX_INVALID, DC_LEX_STACK_LEVEL,
+       // {|}~
+       DC_LEX_LBRACE, DC_LEX_OP_MODEXP, XC_LEX_INVALID, DC_LEX_OP_DIVMOD,
+};
+static const //BcInst - should be this type. Using signed narrow type since DC_INST_INVALID is -1
 int8_t
-dc_parse_insts[] = {
-       BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE,
-       BC_INST_INVALID, BC_INST_POWER, BC_INST_MULTIPLY, BC_INST_DIVIDE,
-       BC_INST_MODULUS, BC_INST_PLUS, BC_INST_MINUS,
-       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,
+dc_LEX_to_INST[] ALIGN1 = { //starts at XC_LEX_OP_POWER // corresponding XC/DC_LEX_xyz:
+       XC_INST_POWER,       XC_INST_MULTIPLY,          // XC_LEX_OP_POWER    XC_LEX_OP_MULTIPLY
+       XC_INST_DIVIDE,      XC_INST_MODULUS,           // XC_LEX_OP_DIVIDE   XC_LEX_OP_MODULUS
+       XC_INST_PLUS,        XC_INST_MINUS,             // XC_LEX_OP_PLUS     XC_LEX_OP_MINUS
+       XC_INST_BOOL_NOT,                               // DC_LEX_OP_BOOL_NOT
+       DC_INST_INVALID,                                // DC_LEX_OP_ASSIGN
+       XC_INST_REL_GT,                                 // DC_LEX_LPAREN
+       DC_INST_INVALID,                                // DC_LEX_SCOLON
+       DC_INST_INVALID,                                // DC_LEX_READ
+       XC_INST_IBASE,                                  // DC_LEX_IBASE
+       XC_INST_SCALE,                                  // DC_LEX_SCALE
+       XC_INST_OBASE,                                  // DC_LEX_OBASE
+       XC_INST_LENGTH,                                 // DC_LEX_LENGTH
+       XC_INST_PRINT,                                  // DC_LEX_PRINT
+       DC_INST_QUIT,                                   // DC_LEX_QUIT
+       XC_INST_SQRT,                                   // DC_LEX_SQRT
+       XC_INST_REL_GE,                                 // DC_LEX_LBRACE
+       XC_INST_REL_EQ,                                 // DC_LEX_EQ_NO_REG
+       DC_INST_MODEXP,      DC_INST_DIVMOD,            // DC_LEX_OP_MODEXP   DC_LEX_OP_DIVMOD
+       DC_INST_INVALID,     DC_INST_INVALID,           // DC_LEX_COLON       DC_LEX_ELSE
+       DC_INST_EXECUTE,                                // DC_LEX_EXECUTE
+       DC_INST_PRINT_STACK, DC_INST_CLEAR_STACK,       // DC_LEX_PRINT_STACK DC_LEX_CLEAR_STACK
+       DC_INST_STACK_LEN,   DC_INST_DUPLICATE,         // DC_LEX_STACK_LEVEL DC_LEX_DUPLICATE
+       DC_INST_SWAP,        XC_INST_POP,               // DC_LEX_SWAP        DC_LEX_POP
+       DC_INST_ASCIIFY,     DC_INST_PRINT_STREAM,      // DC_LEX_ASCIIFY     DC_LEX_PRINT_STREAM
+       DC_INST_INVALID,     DC_INST_INVALID,           // DC_LEX_STORE_IBASE DC_LEX_STORE_OBASE
+       DC_INST_INVALID,     DC_INST_INVALID,           // DC_LEX_STORE_SCALE DC_LEX_LOAD
+       DC_INST_INVALID,     DC_INST_INVALID,           // DC_LEX_LOAD_POP    DC_LEX_STORE_PUSH
+       XC_INST_PRINT,       DC_INST_NQUIT,             // DC_LEX_PRINT_POP   DC_LEX_NQUIT
+       XC_INST_SCALE_FUNC,                             // DC_LEX_SCALE_FACTOR
+       // DC_INST_INVALID in this table either means that corresponding LEX
+       // is not possible for dc, or that it does not compile one-to-one
+       // to a single INST.
 };
 #endif // ENABLE_DC
 
-typedef struct BcLex {
-       const char *buf;
-       size_t i;
-       size_t line;
-       size_t len;
-       bool newline;
-       struct {
-               BcLexType t;
-               BcLexType last;
-               BcVec v;
-       } t;
-} BcLex;
-
-#define BC_PARSE_STREND         (0xff)
-
-#if ENABLE_BC
-# define BC_PARSE_REL           (1 << 0)
-# define BC_PARSE_PRINT         (1 << 1)
-# define BC_PARSE_ARRAY         (1 << 2)
-# define BC_PARSE_NOCALL        (1 << 3)
-#endif
-
 typedef struct BcParse {
-       BcLex l;
-
-       IF_BC(BcVec exits;)
-       IF_BC(BcVec conds;)
-       IF_BC(BcVec ops;)
+       smallint lex;      // was BcLexType // first member is most used
+       smallint lex_last; // was BcLexType
+       size_t lex_line;
+       const char *lex_inbuf;
+       const char *lex_next_at; // last lex_next() was called at this string
+       const char *lex_filename;
+       FILE *lex_input_fp;
+       BcVec  lex_strnumbuf;
 
        BcFunc *func;
        size_t fidx;
-
        IF_BC(size_t in_funcdef;)
+       IF_BC(BcVec exits;)
+       IF_BC(BcVec conds;)
+       IF_BC(BcVec ops;)
 } BcParse;
 
 typedef struct BcProgram {
@@ -700,82 +750,26 @@ typedef struct BcProgram {
        IF_DC(BcVec strs;)
        IF_DC(BcVec consts;)
 
-       const char *file;
-
        BcNum zero;
        IF_BC(BcNum one;)
        IF_BC(BcNum last;)
 } BcProgram;
 
-#define BC_PROG_MAIN (0)
-#define BC_PROG_READ (1)
-#if ENABLE_DC
-#define BC_PROG_REQ_FUNCS (2)
-#endif
-
-#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))
-
-#define BC_FLAG_W (1 << 0)
-#define BC_FLAG_V (1 << 1)
-#define BC_FLAG_S (1 << 2)
-#define BC_FLAG_Q (1 << 3)
-#define BC_FLAG_L (1 << 4)
-#define BC_FLAG_I (1 << 5)
-#define DC_FLAG_X (1 << 6)
-
-#define BC_MAX(a, b) ((a) > (b) ? (a) : (b))
-#define BC_MIN(a, b) ((a) < (b) ? (a) : (b))
-
-#define BC_MAX_OBASE    ((unsigned) 999)
-#define BC_MAX_DIM      ((unsigned) INT_MAX)
-#define BC_MAX_SCALE    ((unsigned) UINT_MAX)
-#define BC_MAX_STRING   ((unsigned) UINT_MAX - 1)
-#define BC_MAX_NUM      BC_MAX_STRING
-// Unused apart from "limits" message. Just show a "biggish number" there.
-//#define BC_MAX_NAME     BC_MAX_STRING
-//#define BC_MAX_EXP      ((unsigned long) LONG_MAX)
-//#define BC_MAX_VARS     ((unsigned long) SIZE_MAX - 1)
-#define BC_MAX_NAME_STR "999999999"
-#define BC_MAX_EXP_STR  "999999999"
-#define BC_MAX_VARS_STR "999999999"
-
-#define BC_MAX_OBASE_STR "999"
+struct globals {
+       BcParse prs; // first member is most used
 
-#if INT_MAX == 2147483647
-# define BC_MAX_DIM_STR "2147483647"
-#elif INT_MAX == 9223372036854775807
-# define BC_MAX_DIM_STR "9223372036854775807"
-#else
-# error Strange INT_MAX
-#endif
+       // For error messages. Can be set to current parsed line,
+       // or [TODO] to current executing line (can be before last parsed one)
+       size_t err_line;
 
-#if UINT_MAX == 4294967295
-# define BC_MAX_SCALE_STR  "4294967295"
-# define BC_MAX_STRING_STR "4294967294"
-#elif UINT_MAX == 18446744073709551615
-# define BC_MAX_SCALE_STR  "18446744073709551615"
-# define BC_MAX_STRING_STR "18446744073709551614"
-#else
-# error Strange UINT_MAX
-#endif
-#define BC_MAX_NUM_STR BC_MAX_STRING_STR
+       BcVec input_buffer;
 
-struct globals {
-       IF_FEATURE_BC_SIGNALS(smallint ttyin;)
+       IF_FEATURE_BC_INTERACTIVE(smallint ttyin;)
        IF_FEATURE_CLEAN_UP(smallint exiting;)
 
-       BcParse prs;
        BcProgram prog;
 
-       // For error messages. Can be set to current parsed line,
-       // or [TODO] to current executing line (can be before last parsed one)
-       unsigned err_line;
-
        BcVec files;
-       BcVec input_buffer;
-       FILE *input_fp;
 
        char *env_args;
 
@@ -793,7 +787,7 @@ struct globals {
 #define G_posix (ENABLE_BC && (option_mask32 & BC_FLAG_S))
 #define G_warn  (ENABLE_BC && (option_mask32 & BC_FLAG_W))
 #define G_exreg (ENABLE_DC && (option_mask32 & DC_FLAG_X))
-#if ENABLE_FEATURE_BC_SIGNALS
+#if ENABLE_FEATURE_BC_INTERACTIVE
 # define G_interrupt bb_got_signal
 # define G_ttyin     G.ttyin
 #else
@@ -808,6 +802,59 @@ struct globals {
 #define IS_BC (ENABLE_BC && (!ENABLE_DC || applet_name[0] == 'b'))
 #define IS_DC (ENABLE_DC && (!ENABLE_BC || applet_name[0] != 'b'))
 
+#if ENABLE_BC
+# define BC_PARSE_REL           (1 << 0)
+# define BC_PARSE_PRINT         (1 << 1)
+# define BC_PARSE_ARRAY         (1 << 2)
+# define BC_PARSE_NOCALL        (1 << 3)
+#endif
+
+#define BC_PROG_MAIN      0
+#define BC_PROG_READ      1
+#if ENABLE_DC
+#define BC_PROG_REQ_FUNCS 2
+#endif
+
+#define BC_FLAG_W (1 << 0)
+#define BC_FLAG_V (1 << 1)
+#define BC_FLAG_S (1 << 2)
+#define BC_FLAG_Q (1 << 3)
+#define BC_FLAG_L (1 << 4)
+#define BC_FLAG_I ((1 << 5) * ENABLE_DC)
+#define DC_FLAG_X ((1 << 6) * ENABLE_DC)
+
+#define BC_MAX_OBASE    ((unsigned) 999)
+#define BC_MAX_DIM      ((unsigned) INT_MAX)
+#define BC_MAX_SCALE    ((unsigned) UINT_MAX)
+#define BC_MAX_STRING   ((unsigned) UINT_MAX - 1)
+#define BC_MAX_NUM      BC_MAX_STRING
+// Unused apart from "limits" message. Just show a "biggish number" there.
+//#define BC_MAX_EXP      ((unsigned long) LONG_MAX)
+//#define BC_MAX_VARS     ((unsigned long) SIZE_MAX - 1)
+#define BC_MAX_EXP_STR  "999999999"
+#define BC_MAX_VARS_STR "999999999"
+
+#define BC_MAX_OBASE_STR "999"
+
+#if INT_MAX == 2147483647
+# define BC_MAX_DIM_STR "2147483647"
+#elif INT_MAX == 9223372036854775807
+# define BC_MAX_DIM_STR "9223372036854775807"
+#else
+# error Strange INT_MAX
+#endif
+
+#if UINT_MAX == 4294967295U
+# define BC_MAX_SCALE_STR  "4294967295"
+# define BC_MAX_STRING_STR "4294967294"
+#elif UINT_MAX == 18446744073709551615U
+# define BC_MAX_SCALE_STR  "18446744073709551615"
+# define BC_MAX_STRING_STR "18446744073709551614"
+#else
+# error Strange UINT_MAX
+#endif
+#define BC_MAX_NUM_STR BC_MAX_STRING_STR
+
 // In configurations where errors abort instead of propagating error
 // return code up the call chain, functions returning BC_STATUS
 // actually don't return anything, they always succeed and return "void".
@@ -819,7 +866,7 @@ struct globals {
 // To make code more readable, each such function has a "z"
 // ("always returning zero") prefix, i.e. zbc_foo or zdc_foo.
 //
-#if ENABLE_FEATURE_BC_SIGNALS || ENABLE_FEATURE_CLEAN_UP
+#if ENABLE_FEATURE_BC_INTERACTIVE || ENABLE_FEATURE_CLEAN_UP
 # define ERRORS_ARE_FATAL 0
 # define ERRORFUNC        /*nothing*/
 # define IF_ERROR_RETURN_POSSIBLE(a)  a
@@ -835,120 +882,51 @@ struct globals {
 # define COMMA_SUCCESS    ,BC_STATUS_SUCCESS
 #endif
 
-#define STACK_HAS_MORE_THAN(s, n)          ((s)->len > ((size_t)(n)))
-#define STACK_HAS_EQUAL_OR_MORE_THAN(s, n) ((s)->len >= ((size_t)(n)))
-
-#define BC_NUM_NEG(n, neg)      ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg))
-#define BC_NUM_ONE(n)           ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1)
-#define BC_NUM_INT(n)           ((n)->len - (n)->rdx)
-//#define BC_NUM_AREQ(a, b)       (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1)
-static /*ALWAYS_INLINE*/ size_t BC_NUM_AREQ(BcNum *a, BcNum *b)
-{
-       return BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1;
-}
-//#define BC_NUM_MREQ(a, b, scale) (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1)
-static /*ALWAYS_INLINE*/ size_t BC_NUM_MREQ(BcNum *a, BcNum *b, size_t scale)
-{
-       return BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx) + 1;
-}
-
-typedef void (*BcNumDigitOp)(size_t, size_t, bool) FAST_FUNC;
-
-typedef BC_STATUS (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t) FAST_FUNC;
-
-static BC_STATUS zbc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale,
-                       BcNumBinaryOp op, size_t req);
-static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-
-static FAST_FUNC BC_STATUS zbc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
-       BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_a : zbc_num_s;
-       (void) scale;
-       RETURN_STATUS(zbc_num_binary(a, b, c, false, op, BC_NUM_AREQ(a, b)));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
-       BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_s : zbc_num_a;
-       (void) scale;
-       RETURN_STATUS(zbc_num_binary(a, b, c, true, op, BC_NUM_AREQ(a, b)));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
-       size_t req = BC_NUM_MREQ(a, b, scale);
-       RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_m, req));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
-       size_t req = BC_NUM_MREQ(a, b, scale);
-       RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_d, req));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
-       size_t req = BC_NUM_MREQ(a, b, scale);
-       RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_rem, req));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
-       RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_p, a->len * b->len + 1));
-}
+//
+// Utility routines
+//
 
-static const BcNumBinaryOp zbc_program_ops[] = {
-       zbc_num_pow, zbc_num_mul, zbc_num_div, zbc_num_mod, zbc_num_add, zbc_num_sub,
-};
-#define zbc_num_add(...) (zbc_num_add(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_sub(...) (zbc_num_sub(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_mul(...) (zbc_num_mul(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_div(...) (zbc_num_div(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_mod(...) (zbc_num_mod(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_pow(...) (zbc_num_pow(__VA_ARGS__) COMMA_SUCCESS)
+#define BC_MAX(a, b) ((a) > (b) ? (a) : (b))
+#define BC_MIN(a, b) ((a) < (b) ? (a) : (b))
 
 static void fflush_and_check(void)
 {
        fflush_all();
        if (ferror(stdout) || ferror(stderr))
-               bb_perror_msg_and_die("output error");
+               bb_simple_perror_msg_and_die("output error");
 }
 
 #if ENABLE_FEATURE_CLEAN_UP
 #define QUIT_OR_RETURN_TO_MAIN \
 do { \
-       IF_FEATURE_BC_SIGNALS(G_ttyin = 0;) /* do not loop in main loop anymore */ \
+       IF_FEATURE_BC_INTERACTIVE(G_ttyin = 0;) /* do not loop in main loop anymore */ \
        G_exiting = 1; \
        return BC_STATUS_FAILURE; \
 } while (0)
 #else
-#define QUIT_OR_RETURN_TO_MAIN quit()
-#endif
-
 static void quit(void) NORETURN;
 static void quit(void)
 {
        if (ferror(stdin))
-               bb_perror_msg_and_die("input error");
+               bb_simple_perror_msg_and_die("input error");
        fflush_and_check();
        dbg_exec("quit(): exiting with exitcode SUCCESS");
        exit(0);
 }
+#define QUIT_OR_RETURN_TO_MAIN quit()
+#endif
 
 static void bc_verror_msg(const char *fmt, va_list p)
 {
        const char *sv = sv; // for compiler
-       if (G.prog.file) {
+       if (G.prs.lex_filename) {
                sv = applet_name;
-               applet_name = xasprintf("%s: %s:%u", applet_name, G.prog.file, G.err_line);
+               applet_name = xasprintf("%s: %s:%lu", applet_name,
+                       G.prs.lex_filename, (unsigned long)G.err_line
+               );
        }
        bb_verror_msg(fmt, p, NULL);
-       if (G.prog.file) {
+       if (G.prs.lex_filename) {
                free((char*)applet_name);
                applet_name = sv;
        }
@@ -968,13 +946,13 @@ static NOINLINE ERRORFUNC int bc_error_fmt(const char *fmt, ...)
 }
 
 #if ENABLE_BC
-static NOINLINE int bc_posix_error_fmt(const char *fmt, ...)
+static NOINLINE BC_STATUS zbc_posix_error_fmt(const char *fmt, ...)
 {
        va_list p;
 
        // Are non-POSIX constructs totally ok?
        if (!(option_mask32 & (BC_FLAG_S|BC_FLAG_W)))
-               return BC_STATUS_SUCCESS; // yes
+               RETURN_STATUS(BC_STATUS_SUCCESS); // yes
 
        va_start(p, fmt);
        bc_verror_msg(fmt, p);
@@ -982,11 +960,13 @@ static NOINLINE int bc_posix_error_fmt(const char *fmt, ...)
 
        // Do we treat non-POSIX constructs as errors?
        if (!(option_mask32 & BC_FLAG_S))
-               return BC_STATUS_SUCCESS; // no, it's a warning
+               RETURN_STATUS(BC_STATUS_SUCCESS); // no, it's a warning
+
        if (ENABLE_FEATURE_CLEAN_UP || G_ttyin)
-               return BC_STATUS_FAILURE;
+               RETURN_STATUS(BC_STATUS_FAILURE);
        exit(1);
 }
+#define zbc_posix_error_fmt(...) (zbc_posix_error_fmt(__VA_ARGS__) COMMA_SUCCESS)
 #endif
 
 // We use error functions with "return bc_error(FMT[, PARAMS])" idiom.
@@ -998,19 +978,44 @@ static ERRORFUNC int bc_error(const char *msg)
 {
        IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("%s", msg);
 }
+static ERRORFUNC int bc_error_at(const char *msg)
+{
+       const char *err_at = G.prs.lex_next_at;
+       if (err_at) {
+               IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt(
+                       "%s at '%.*s'",
+                       msg,
+                       (int)(strchrnul(err_at, '\n') - err_at),
+                       err_at
+               );
+       }
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("%s", msg);
+}
 static ERRORFUNC int bc_error_bad_character(char c)
 {
        if (!c)
                IF_ERROR_RETURN_POSSIBLE(return) bc_error("NUL character");
        IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("bad character '%c'", c);
 }
+#if ENABLE_BC
+static ERRORFUNC int bc_error_bad_function_definition(void)
+{
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad function definition");
+}
+#endif
 static ERRORFUNC int bc_error_bad_expression(void)
 {
-       IF_ERROR_RETURN_POSSIBLE(return) bc_error("bad expression");
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad expression");
+}
+static ERRORFUNC int bc_error_bad_assignment(void)
+{
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_at(
+               "bad assignment: left side must be variable or array element"
+       );
 }
 static ERRORFUNC int bc_error_bad_token(void)
 {
-       IF_ERROR_RETURN_POSSIBLE(return) bc_error("bad token");
+       IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad token");
 }
 static ERRORFUNC int bc_error_stack_has_too_few_elements(void)
 {
@@ -1021,22 +1026,26 @@ static ERRORFUNC int bc_error_variable_is_wrong_type(void)
        IF_ERROR_RETURN_POSSIBLE(return) bc_error("variable is wrong type");
 }
 #if ENABLE_BC
-static int bc_POSIX_requires(const char *msg)
+static BC_STATUS zbc_POSIX_requires(const char *msg)
 {
-       return bc_posix_error_fmt("POSIX requires %s", msg);
+       RETURN_STATUS(zbc_posix_error_fmt("POSIX requires %s", msg));
 }
-static int bc_POSIX_does_not_allow(const char *msg)
+#define zbc_POSIX_requires(...) (zbc_POSIX_requires(__VA_ARGS__) COMMA_SUCCESS)
+static BC_STATUS zbc_POSIX_does_not_allow(const char *msg)
 {
-       return bc_posix_error_fmt("%s%s", "POSIX does not allow ", msg);
+       RETURN_STATUS(zbc_posix_error_fmt("%s%s", "POSIX does not allow ", msg));
 }
-static int bc_POSIX_does_not_allow_bool_ops_this_is_bad(const char *msg)
+#define zbc_POSIX_does_not_allow(...) (zbc_POSIX_does_not_allow(__VA_ARGS__) COMMA_SUCCESS)
+static BC_STATUS zbc_POSIX_does_not_allow_bool_ops_this_is_bad(const char *msg)
 {
-       return bc_posix_error_fmt("%s%s %s", "POSIX does not allow ", "boolean operators; the following is bad:", msg);
+       RETURN_STATUS(zbc_posix_error_fmt("%s%s %s", "POSIX does not allow ", "boolean operators; this is bad:", msg));
 }
-static int bc_POSIX_does_not_allow_empty_X_expression_in_for(const char *msg)
+#define zbc_POSIX_does_not_allow_bool_ops_this_is_bad(...) (zbc_POSIX_does_not_allow_bool_ops_this_is_bad(__VA_ARGS__) COMMA_SUCCESS)
+static BC_STATUS zbc_POSIX_does_not_allow_empty_X_expression_in_for(const char *msg)
 {
-       return bc_posix_error_fmt("%san empty %s expression in a for loop", "POSIX does not allow ", msg);
+       RETURN_STATUS(zbc_posix_error_fmt("%san empty %s expression in 'for()'", "POSIX does not allow ", msg));
 }
+#define zbc_POSIX_does_not_allow_empty_X_expression_in_for(...) (zbc_POSIX_does_not_allow_empty_X_expression_in_for(__VA_ARGS__) COMMA_SUCCESS)
 #endif
 
 static void bc_vec_grow(BcVec *v, size_t n)
@@ -1091,17 +1100,27 @@ static void bc_vec_pop_all(BcVec *v)
        bc_vec_npop(v, v->len);
 }
 
-static size_t bc_vec_push(BcVec *v, const void *data)
+static size_t bc_vec_npush(BcVec *v, size_t n, const void *data)
 {
        size_t len = v->len;
-       if (len >= v->cap) bc_vec_grow(v, 1);
-       memmove(v->v + (v->size * len), data, v->size);
-       v->len++;
+       if (len + n > v->cap) bc_vec_grow(v, n);
+       memmove(v->v + (v->size * len), data, v->size * n);
+       v->len = len + n;
        return len;
 }
 
-// G.prog.results often needs "pop old operand, push result" idiom.
-// Can do this without a few extra ops
+static size_t bc_vec_push(BcVec *v, const void *data)
+{
+       return bc_vec_npush(v, 1, data);
+       //size_t len = v->len;
+       //if (len >= v->cap) bc_vec_grow(v, 1);
+       //memmove(v->v + (v->size * len), data, v->size);
+       //v->len = len + 1;
+       //return len;
+}
+
+// G.prog.results often needs "pop old operand, push result" idiom.
+// Can do this without a few extra ops
 static size_t bc_result_pop_and_push(const void *data)
 {
        BcVec *v = &G.prog.results;
@@ -1153,23 +1172,6 @@ static void bc_vec_string(BcVec *v, size_t len, const char *str)
        bc_vec_pushZeroByte(v);
 }
 
-#if ENABLE_FEATURE_BC_SIGNALS && ENABLE_FEATURE_EDITING
-static void bc_vec_concat(BcVec *v, const char *str)
-{
-       size_t len, slen;
-
-       if (v->len == 0) bc_vec_pushZeroByte(v);
-
-       slen = strlen(str);
-       len = v->len + slen;
-
-       if (v->cap < len) bc_vec_grow(v, slen);
-       strcpy(v->v + v->len - 1, str);
-
-       v->len = len;
-}
-#endif
-
 static void *bc_vec_item(const BcVec *v, size_t idx)
 {
        return v->v + v->size * idx;
@@ -1192,23 +1194,23 @@ static FAST_FUNC void bc_vec_free(void *vec)
        free(v->v);
 }
 
-static BcFunc* bc_program_func(size_t idx)
+static BcFunc* xc_program_func(size_t idx)
 {
        return bc_vec_item(&G.prog.fns, idx);
 }
 // BC_PROG_MAIN is zeroth element, so:
-#define bc_program_func_BC_PROG_MAIN() ((BcFunc*)(G.prog.fns.v))
+#define xc_program_func_BC_PROG_MAIN() ((BcFunc*)(G.prog.fns.v))
 
 #if ENABLE_BC
 static BcFunc* bc_program_current_func(void)
 {
        BcInstPtr *ip = bc_vec_top(&G.prog.exestack);
-       BcFunc *func = bc_program_func(ip->func);
+       BcFunc *func = xc_program_func(ip->func);
        return func;
 }
 #endif
 
-static char** bc_program_str(size_t idx)
+static char** xc_program_str(size_t idx)
 {
 #if ENABLE_BC
        if (IS_BC) {
@@ -1219,7 +1221,7 @@ static char** bc_program_str(size_t idx)
        IF_DC(return bc_vec_item(&G.prog.strs, idx);)
 }
 
-static char** bc_program_const(size_t idx)
+static char** xc_program_const(size_t idx)
 {
 #if ENABLE_BC
        if (IS_BC) {
@@ -1273,110 +1275,12 @@ static int bc_map_insert(BcVec *v, const void *ptr, size_t *i)
        return 1; // "was inserted"
 }
 
-#if ENABLE_BC
 static size_t bc_map_find_exact(const BcVec *v, const void *ptr)
 {
        size_t i = bc_map_find_ge(v, ptr);
        if (i >= v->len) return BC_VEC_INVALID_IDX;
        return bc_id_cmp(ptr, bc_vec_item(v, i)) ? BC_VEC_INVALID_IDX : i;
 }
-#endif
-
-static int bad_input_byte(char c)
-{
-       if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'?
-        || c > 0x7e
-       ) {
-               bc_error_fmt("illegal character 0x%02x", c);
-               return 1;
-       }
-       return 0;
-}
-
-// Note: it _appends_ data from fp to vec.
-static void bc_read_line(BcVec *vec, FILE *fp)
-{
- again:
-       fflush_and_check();
-
-#if ENABLE_FEATURE_BC_SIGNALS
-       if (G_interrupt) { // ^C was pressed
- intr:
-               if (fp != stdin) {
-                       // ^C while running a script (bc SCRIPT): die.
-                       // We do not return to interactive prompt:
-                       // user might be running us from a shell,
-                       // and SCRIPT might be intended to terminate
-                       // (e.g. contain a "halt" stmt).
-                       // ^C dropping user into a bc prompt instead of
-                       // the shell would be unexpected.
-                       xfunc_die();
-               }
-               // ^C while interactive input
-               G_interrupt = 0;
-               // GNU bc says "interrupted execution."
-               // GNU dc says "Interrupt!"
-               fputs("\ninterrupted execution\n", stderr);
-       }
-
-# if ENABLE_FEATURE_EDITING
-       if (G_ttyin && fp == stdin) {
-               int n, i;
-#  define line_buf bb_common_bufsiz1
-               n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE);
-               if (n <= 0) { // read errors or EOF, or ^D, or ^C
-                       if (n == 0) // ^C
-                               goto intr;
-                       bc_vec_pushZeroByte(vec); // ^D or EOF (or error)
-                       return;
-               }
-               i = 0;
-               for (;;) {
-                       char c = line_buf[i++];
-                       if (!c) break;
-                       if (bad_input_byte(c)) goto again;
-               }
-               bc_vec_concat(vec, line_buf);
-#  undef line_buf
-       } else
-# endif
-#endif
-       {
-               int c;
-               bool bad_chars = 0;
-               size_t len = vec->len;
-
-               do {
-#if ENABLE_FEATURE_BC_SIGNALS
-                       if (G_interrupt) {
-                               // ^C was pressed: ignore entire line, get another one
-                               vec->len = len;
-                               goto intr;
-                       }
-#endif
-                       do c = fgetc(fp); while (c == '\0');
-                       if (c == EOF) {
-                               if (ferror(fp))
-                                       bb_perror_msg_and_die("input error");
-                               // Note: EOF does not append '\n'
-                               break;
-                       }
-                       bad_chars |= bad_input_byte(c);
-                       bc_vec_pushByte(vec, (char)c);
-               } while (c != '\n');
-
-               if (bad_chars) {
-                       // Bad chars on this line
-                       if (!G.prog.file) { // stdin
-                               // ignore entire line, get another one
-                               vec->len = len;
-                               goto again;
-                       }
-                       bb_perror_msg_and_die("file '%s' is not text", G.prog.file);
-               }
-               bc_vec_pushZeroByte(vec);
-       }
-}
 
 static void bc_num_setToZero(BcNum *n, size_t scale)
 {
@@ -1439,28 +1343,35 @@ static void bc_num_copy(BcNum *d, BcNum *s)
        }
 }
 
-static BC_STATUS zbc_num_ulong(BcNum *n, unsigned long *result_p)
+static BC_STATUS zbc_num_ulong_abs(BcNum *n, unsigned long *result_p)
 {
        size_t i;
-       unsigned long pow, result;
-
-       if (n->neg) RETURN_STATUS(bc_error("negative number"));
-
-       for (result = 0, pow = 1, i = n->rdx; i < n->len; ++i) {
-               unsigned long prev = result, powprev = pow;
-
-               result += ((unsigned long) n->num[i]) * pow;
-               pow *= 10;
-
-               if (result < prev || pow < powprev)
+       unsigned long result;
+
+       result = 0;
+       i = n->len;
+       while (i > n->rdx) {
+               unsigned long prev = result;
+               result = result * 10 + n->num[--i];
+               // Even overflowed N*10 can still satisfy N*10>=N. For example,
+               //    0x1ff00000 * 10 is 0x13f600000,
+               // or 0x3f600000 truncated to 32 bits. Which is larger.
+               // However, (N*10)/8 < N check is always correct.
+               if ((result / 8) < prev)
                        RETURN_STATUS(bc_error("overflow"));
-               prev = result;
-               powprev = pow;
        }
        *result_p = result;
 
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
+#define zbc_num_ulong_abs(...) (zbc_num_ulong_abs(__VA_ARGS__) COMMA_SUCCESS)
+
+static BC_STATUS zbc_num_ulong(BcNum *n, unsigned long *result_p)
+{
+       if (n->neg) RETURN_STATUS(bc_error("negative number"));
+
+       RETURN_STATUS(zbc_num_ulong_abs(n, result_p));
+}
 #define zbc_num_ulong(...) (zbc_num_ulong(__VA_ARGS__) COMMA_SUCCESS)
 
 #if ULONG_MAX == 0xffffffffUL // 10 digits: 4294967295
@@ -1520,6 +1431,20 @@ static ssize_t bc_num_compare(BcDig *restrict a, BcDig *restrict b, size_t len)
        }
 }
 
+#define BC_NUM_NEG(n, neg)      ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg))
+#define BC_NUM_ONE(n)           ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1)
+#define BC_NUM_INT(n)           ((n)->len - (n)->rdx)
+//#define BC_NUM_AREQ(a, b)       (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1)
+static /*ALWAYS_INLINE*/ size_t BC_NUM_AREQ(BcNum *a, BcNum *b)
+{
+       return BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1;
+}
+//#define BC_NUM_MREQ(a, b, scale) (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1)
+static /*ALWAYS_INLINE*/ size_t BC_NUM_MREQ(BcNum *a, BcNum *b, size_t scale)
+{
+       return BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx) + 1;
+}
+
 static ssize_t bc_num_cmp(BcNum *a, BcNum *b)
 {
        size_t i, min, a_int, b_int, diff;
@@ -1540,7 +1465,10 @@ static ssize_t bc_num_cmp(BcNum *a, BcNum *b)
        b_int = BC_NUM_INT(b);
        a_int -= b_int;
 
-       if (a_int != 0) return (ssize_t) a_int;
+       if (a_int != 0) {
+               if (neg) return - (ssize_t) a_int;
+               return (ssize_t) a_int;
+       }
 
        a_max = (a->rdx > b->rdx);
        if (a_max) {
@@ -1656,6 +1584,99 @@ static BC_STATUS zbc_num_shift(BcNum *n, size_t places)
 }
 #define zbc_num_shift(...) (zbc_num_shift(__VA_ARGS__) COMMA_SUCCESS)
 
+typedef BC_STATUS (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t) FAST_FUNC;
+
+static BC_STATUS zbc_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 (c == a) {
+               ptr_a = &num2;
+               memcpy(ptr_a, c, sizeof(BcNum));
+               init = true;
+       } else
+               ptr_a = a;
+
+       if (c == b) {
+               ptr_b = &num2;
+               if (c != a) {
+                       memcpy(ptr_b, c, sizeof(BcNum));
+                       init = true;
+               }
+       } else
+               ptr_b = b;
+
+       if (init)
+               bc_num_init(c, req);
+       else
+               bc_num_expand(c, req);
+
+       s = BC_STATUS_SUCCESS;
+       IF_ERROR_RETURN_POSSIBLE(s =) op(ptr_a, ptr_b, c, scale);
+
+       if (init) bc_num_free(&num2);
+
+       RETURN_STATUS(s);
+}
+#define zbc_num_binary(...) (zbc_num_binary(__VA_ARGS__) COMMA_SUCCESS)
+
+static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+
+static FAST_FUNC BC_STATUS zbc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+       BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_a : zbc_num_s;
+       (void) scale;
+       RETURN_STATUS(zbc_num_binary(a, b, c, false, op, BC_NUM_AREQ(a, b)));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+       BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_s : zbc_num_a;
+       (void) scale;
+       RETURN_STATUS(zbc_num_binary(a, b, c, true, op, BC_NUM_AREQ(a, b)));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+       size_t req = BC_NUM_MREQ(a, b, scale);
+       RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_m, req));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+       size_t req = BC_NUM_MREQ(a, b, scale);
+       RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_d, req));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+       size_t req = BC_NUM_MREQ(a, b, scale);
+       RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_rem, req));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+       RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_p, a->len * b->len + 1));
+}
+
+static const BcNumBinaryOp zxc_program_ops[] = {
+       zbc_num_pow, zbc_num_mul, zbc_num_div, zbc_num_mod, zbc_num_add, zbc_num_sub,
+};
+#define zbc_num_add(...) (zbc_num_add(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_sub(...) (zbc_num_sub(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_mul(...) (zbc_num_mul(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_div(...) (zbc_num_div(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_mod(...) (zbc_num_mod(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_pow(...) (zbc_num_pow(__VA_ARGS__) COMMA_SUCCESS)
+
 static BC_STATUS zbc_num_inv(BcNum *a, BcNum *b, size_t scale)
 {
        BcNum one;
@@ -1844,7 +1865,7 @@ static FAST_FUNC BC_STATUS zbc_num_k(BcNum *restrict a, BcNum *restrict b,
                        c->num[i + j] += (BcDig) carry;
                        len = BC_MAX(len, i + j + !!carry);
 
-#if ENABLE_FEATURE_BC_SIGNALS
+#if ENABLE_FEATURE_BC_INTERACTIVE
                        // a=2^1000000
                        // a*a <- without check below, this will not be interruptible
                        if (G_interrupt) return BC_STATUS_FAILURE;
@@ -2011,7 +2032,7 @@ static FAST_FUNC BC_STATUS zbc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size
                for (q = 0; n[len] != 0 || bc_num_compare(n, b->num, len) >= 0; ++q)
                        bc_num_subArrays(n, b->num, len);
                c->num[i] = q;
-#if ENABLE_FEATURE_BC_SIGNALS
+#if ENABLE_FEATURE_BC_INTERACTIVE
                // a=2^100000
                // scale=40000
                // 1/a <- without check below, this will not be interruptible
@@ -2088,7 +2109,10 @@ static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size
        size_t i, powrdx, resrdx;
        bool neg;
 
-       if (b->rdx) RETURN_STATUS(bc_error("non integer number"));
+       // GNU bc does not allow 2^2.0 - we do
+       for (i = 0; i < b->rdx; i++)
+               if (b->num[i] != 0)
+                       RETURN_STATUS(bc_error("not an integer"));
 
        if (b->len == 0) {
                bc_num_one(c);
@@ -2107,10 +2131,9 @@ static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size
        }
 
        neg = b->neg;
-       b->neg = false;
-
-       s = zbc_num_ulong(b, &pow);
+       s = zbc_num_ulong_abs(b, &pow);
        if (s) RETURN_STATUS(s);
+       // b is not used beyond this point
 
        bc_num_init(&copy, a->len);
        bc_num_copy(&copy, a);
@@ -2122,7 +2145,6 @@ static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size
                        scale = a->rdx * pow;
        }
 
-       b->neg = neg;
 
        for (powrdx = a->rdx; !(pow & 1); pow >>= 1) {
                powrdx <<= 1;
@@ -2174,227 +2196,40 @@ static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size
 }
 #define zbc_num_p(...) (zbc_num_p(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale,
-                              BcNumBinaryOp op, size_t req)
+static BC_STATUS zbc_num_sqrt(BcNum *a, BcNum *restrict b, size_t scale)
 {
        BcStatus s;
-       BcNum num2, *ptr_a, *ptr_b;
-       bool init = false;
+       BcNum num1, num2, half, f, fprime, *x0, *x1, *temp;
+       BcDig half_digs[1];
+       size_t pow, len, digs, digs1, resrdx, req, times = 0;
+       ssize_t cmp = 1, cmp1 = SSIZE_MAX, cmp2 = SSIZE_MAX;
 
-       if (c == a) {
-               ptr_a = &num2;
-               memcpy(ptr_a, c, sizeof(BcNum));
-               init = true;
-       } else
-               ptr_a = a;
+       req = BC_MAX(scale, a->rdx) + ((BC_NUM_INT(a) + 1) >> 1) + 1;
+       bc_num_expand(b, req);
 
-       if (c == b) {
-               ptr_b = &num2;
-               if (c != a) {
-                       memcpy(ptr_b, c, sizeof(BcNum));
-                       init = true;
-               }
-       } else
-               ptr_b = b;
-
-       if (init)
-               bc_num_init(c, req);
-       else
-               bc_num_expand(c, req);
-
-       s = BC_STATUS_SUCCESS;
-       IF_ERROR_RETURN_POSSIBLE(s =) op(ptr_a, ptr_b, c, scale);
-
-       if (init) bc_num_free(&num2);
-
-       RETURN_STATUS(s);
-}
-#define zbc_num_binary(...) (zbc_num_binary(__VA_ARGS__) COMMA_SUCCESS)
-
-static bool bc_num_strValid(const char *val, size_t base)
-{
-       BcDig b;
-       bool radix;
-
-       b = (BcDig)(base <= 10 ? base + '0' : base - 10 + 'A');
-       radix = false;
-       for (;;) {
-               BcDig c = *val++;
-               if (c == '\0')
-                       break;
-               if (c == '.') {
-                       if (radix) return false;
-                       radix = true;
-                       continue;
-               }
-               if (c < '0' || c >= b || (c > '9' && c < 'A'))
-                       return false;
-       }
-       return true;
-}
-
-// Note: n is already "bc_num_zero()"ed,
-// leading zeroes in "val" are removed
-static void bc_num_parseDecimal(BcNum *n, const char *val)
-{
-       size_t len, i;
-       const char *ptr;
-
-       len = strlen(val);
-       if (len == 0)
-               return;
-
-       bc_num_expand(n, len);
-
-       ptr = strchr(val, '.');
-
-       n->rdx = 0;
-       if (ptr != NULL)
-               n->rdx = (size_t)((val + len) - (ptr + 1));
-
-       for (i = 0; val[i]; ++i) {
-               if (val[i] != '0' && val[i] != '.') {
-                       // Not entirely zero value - convert it, and exit
-                       i = len - 1;
-                       for (;;) {
-                               n->num[n->len] = val[i] - '0';
-                               ++n->len;
- skip_dot:
-                               if (i == 0) break;
-                               if (val[--i] == '.') goto skip_dot;
-                       }
-                       break;
-               }
-       }
-       // if for() exits without hitting if(), the value is entirely zero
-}
-
-// Note: n is already "bc_num_zero()"ed,
-// leading zeroes in "val" are removed
-static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
-{
-       BcStatus s;
-       BcNum temp, mult, result;
-       BcNum base;
-       BcDig base_digs[ULONG_NUM_BUFSIZE];
-       BcDig c = '\0';
-       unsigned long v;
-       size_t i, digits;
-
-       for (i = 0; ; ++i) {
-               if (val[i] == '\0')
-                       return;
-               if (val[i] != '.' && val[i] != '0')
-                       break;
-       }
-
-       bc_num_init_DEF_SIZE(&temp);
-       bc_num_init_DEF_SIZE(&mult);
-       base.cap = ARRAY_SIZE(base_digs);
-       base.num = base_digs;
-       bc_num_ulong2num(&base, base_t);
-
-       for (;;) {
-               c = *val++;
-               if (c == '\0') goto int_err;
-               if (c == '.') break;
-
-               v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
-
-               s = zbc_num_mul(n, &base, &mult, 0);
-               if (s) goto int_err;
-               bc_num_ulong2num(&temp, v);
-               s = zbc_num_add(&mult, &temp, n, 0);
-               if (s) goto int_err;
-       }
-
-       bc_num_init(&result, base.len);
-       //bc_num_zero(&result); - already is
-       bc_num_one(&mult);
-
-       digits = 0;
-       for (;;) {
-               c = *val++;
-               if (c == '\0') break;
-               digits++;
-
-               v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
-
-               s = zbc_num_mul(&result, &base, &result, 0);
-               if (s) goto err;
-               bc_num_ulong2num(&temp, v);
-               s = zbc_num_add(&result, &temp, &result, 0);
-               if (s) goto err;
-               s = zbc_num_mul(&mult, &base, &mult, 0);
-               if (s) goto err;
-       }
-
-       s = zbc_num_div(&result, &mult, &result, digits);
-       if (s) goto err;
-       s = zbc_num_add(n, &result, n, digits);
-       if (s) goto err;
-
-       if (n->len != 0) {
-               if (n->rdx < digits)
-                       bc_num_extend(n, digits - n->rdx);
-       } else
-               bc_num_zero(n);
- err:
-       bc_num_free(&result);
- int_err:
-       bc_num_free(&mult);
-       bc_num_free(&temp);
-}
-
-static BC_STATUS zbc_num_parse(BcNum *n, const char *val, unsigned base_t)
-{
-       if (!bc_num_strValid(val, base_t))
-               RETURN_STATUS(bc_error("bad number string"));
-
-       bc_num_zero(n);
-       while (*val == '0') val++;
-
-       if (base_t == 10)
-               bc_num_parseDecimal(n, val);
-       else
-               bc_num_parseBase(n, val, base_t);
-
-       RETURN_STATUS(BC_STATUS_SUCCESS);
-}
-#define zbc_num_parse(...) (zbc_num_parse(__VA_ARGS__) COMMA_SUCCESS)
-
-static BC_STATUS zbc_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, digs, digs1, 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;
-       bc_num_expand(b, req);
-
-       if (a->len == 0) {
-               bc_num_setToZero(b, scale);
-               RETURN_STATUS(BC_STATUS_SUCCESS);
-       }
-       if (a->neg) {
-               RETURN_STATUS(bc_error("negative number"));
-       }
-       if (BC_NUM_ONE(a)) {
-               bc_num_one(b);
-               bc_num_extend(b, scale);
-               RETURN_STATUS(BC_STATUS_SUCCESS);
-       }
+       if (a->len == 0) {
+               bc_num_setToZero(b, scale);
+               RETURN_STATUS(BC_STATUS_SUCCESS);
+       }
+       if (a->neg) {
+               RETURN_STATUS(bc_error("negative number"));
+       }
+       if (BC_NUM_ONE(a)) {
+               bc_num_one(b);
+               bc_num_extend(b, scale);
+               RETURN_STATUS(BC_STATUS_SUCCESS);
+       }
 
        scale = BC_MAX(scale, a->rdx) + 1;
        len = a->len + scale;
 
        bc_num_init(&num1, len);
        bc_num_init(&num2, len);
-       bc_num_init_DEF_SIZE(&half);
 
+       half.cap = ARRAY_SIZE(half_digs);
+       half.num = half_digs;
        bc_num_one(&half);
-       half.num[0] = 5;
+       half_digs[0] = 5;
        half.rdx = 1;
 
        bc_num_init(&f, len);
@@ -2457,7 +2292,6 @@ static BC_STATUS zbc_num_sqrt(BcNum *a, BcNum *restrict b, size_t scale)
  err:
        bc_num_free(&fprime);
        bc_num_free(&f);
-       bc_num_free(&half);
        bc_num_free(&num2);
        bc_num_free(&num1);
        RETURN_STATUS(s);
@@ -2495,22 +2329,25 @@ static BC_STATUS zdc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d)
 {
        BcStatus s;
        BcNum base, exp, two, temp;
+       BcDig two_digs[1];
 
        if (c->len == 0)
                RETURN_STATUS(bc_error("divide by zero"));
        if (a->rdx || b->rdx || c->rdx)
-               RETURN_STATUS(bc_error("non integer number"));
+               RETURN_STATUS(bc_error("not an integer"));
        if (b->neg)
                RETURN_STATUS(bc_error("negative number"));
 
        bc_num_expand(d, c->len);
        bc_num_init(&base, c->len);
        bc_num_init(&exp, b->len);
-       bc_num_init_DEF_SIZE(&two);
        bc_num_init(&temp, b->len);
 
+       two.cap = ARRAY_SIZE(two_digs);
+       two.num = two_digs;
        bc_num_one(&two);
-       two.num[0] = 2;
+       two_digs[0] = 2;
+
        bc_num_one(d);
 
        s = zbc_num_rem(a, c, &base, 0);
@@ -2535,7 +2372,6 @@ static BC_STATUS zdc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d)
        }
  err:
        bc_num_free(&temp);
-       bc_num_free(&two);
        bc_num_free(&exp);
        bc_num_free(&base);
        RETURN_STATUS(s);
@@ -2543,29 +2379,6 @@ static BC_STATUS zdc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d)
 #define zdc_num_modexp(...) (zdc_num_modexp(__VA_ARGS__) COMMA_SUCCESS)
 #endif // ENABLE_DC
 
-#if ENABLE_BC
-static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var)
-{
-       BcId *autoid;
-       BcId a;
-       size_t i;
-
-       autoid = (void*)f->autos.v;
-       for (i = 0; i < f->autos.len; i++, autoid++) {
-               if (strcmp(name, autoid->name) == 0)
-                       RETURN_STATUS(bc_error("function parameter or auto var has the same name as another"));
-       }
-
-       a.idx = var;
-       a.name = name;
-
-       bc_vec_push(&f->autos, &a);
-
-       RETURN_STATUS(BC_STATUS_SUCCESS);
-}
-#define zbc_func_insert(...) (zbc_func_insert(__VA_ARGS__) COMMA_SUCCESS)
-#endif
-
 static FAST_FUNC void bc_string_free(void *string)
 {
        free(*(char**)string);
@@ -2644,153 +2457,483 @@ static void dc_result_copy(BcResult *d, BcResult *src)
        d->t = src->t;
 
        switch (d->t) {
-               case BC_RESULT_TEMP:
-               case BC_RESULT_IBASE:
-               case BC_RESULT_SCALE:
-               case BC_RESULT_OBASE:
+               case XC_RESULT_TEMP:
+               case XC_RESULT_IBASE:
+               case XC_RESULT_SCALE:
+               case XC_RESULT_OBASE:
                        bc_num_init(&d->d.n, src->d.n.len);
                        bc_num_copy(&d->d.n, &src->d.n);
                        break;
-               case BC_RESULT_VAR:
-               case BC_RESULT_ARRAY:
-               case BC_RESULT_ARRAY_ELEM:
+               case XC_RESULT_VAR:
+               case XC_RESULT_ARRAY:
+               case XC_RESULT_ARRAY_ELEM:
                        d->d.id.name = xstrdup(src->d.id.name);
                        break;
-               case BC_RESULT_CONSTANT:
-               case BC_RESULT_LAST:
-               case BC_RESULT_ONE:
-               case BC_RESULT_STR:
+               case XC_RESULT_CONSTANT:
+               case XC_RESULT_STR:
                        memcpy(&d->d.n, &src->d.n, sizeof(BcNum));
                        break;
+               default: // placate compiler
+                       // BC_RESULT_VOID, BC_RESULT_LAST, BC_RESULT_ONE - do not happen
+                       break;
        }
 }
 #endif // ENABLE_DC
 
-static FAST_FUNC void bc_result_free(void *result)
-{
-       BcResult *r = (BcResult *) result;
+static FAST_FUNC void bc_result_free(void *result)
+{
+       BcResult *r = (BcResult *) result;
+
+       switch (r->t) {
+               case XC_RESULT_TEMP:
+               IF_BC(case BC_RESULT_VOID:)
+               case XC_RESULT_IBASE:
+               case XC_RESULT_SCALE:
+               case XC_RESULT_OBASE:
+                       bc_num_free(&r->d.n);
+                       break;
+               case XC_RESULT_VAR:
+               case XC_RESULT_ARRAY:
+               case XC_RESULT_ARRAY_ELEM:
+                       free(r->d.id.name);
+                       break;
+               default:
+                       // Do nothing.
+                       break;
+       }
+}
+
+static int bad_input_byte(char c)
+{
+       if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'?
+        || c > 0x7e
+       ) {
+               bc_error_fmt("illegal character 0x%02x", c);
+               return 1;
+       }
+       return 0;
+}
+
+static void xc_read_line(BcVec *vec, FILE *fp)
+{
+ again:
+       bc_vec_pop_all(vec);
+       fflush_and_check();
+
+#if ENABLE_FEATURE_BC_INTERACTIVE
+       if (G_interrupt) { // ^C was pressed
+ intr:
+               if (fp != stdin) {
+                       // ^C while running a script (bc SCRIPT): die.
+                       // We do not return to interactive prompt:
+                       // user might be running us from a shell,
+                       // and SCRIPT might be intended to terminate
+                       // (e.g. contain a "halt" stmt).
+                       // ^C dropping user into a bc prompt instead of
+                       // the shell would be unexpected.
+                       xfunc_die();
+               }
+               // ^C while interactive input
+               G_interrupt = 0;
+               // GNU bc says "interrupted execution."
+               // GNU dc says "Interrupt!"
+               fputs("\ninterrupted execution\n", stderr);
+       }
+
+# if ENABLE_FEATURE_EDITING
+       if (G_ttyin && fp == stdin) {
+               int n, i;
+#  define line_buf bb_common_bufsiz1
+               n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE);
+               if (n <= 0) { // read errors or EOF, or ^D, or ^C
+                       if (n == 0) // ^C
+                               goto intr;
+                       bc_vec_pushZeroByte(vec); // ^D or EOF (or error)
+                       return;
+               }
+               i = 0;
+               for (;;) {
+                       char c = line_buf[i++];
+                       if (c == '\0') break;
+                       if (bad_input_byte(c)) goto again;
+               }
+               bc_vec_string(vec, n, line_buf);
+#  undef line_buf
+       } else
+# endif
+#endif
+       {
+               int c;
+               bool bad_chars = 0;
+
+               do {
+ get_char:
+#if ENABLE_FEATURE_BC_INTERACTIVE
+                       if (G_interrupt) {
+                               // ^C was pressed: ignore entire line, get another one
+                               goto again;
+                       }
+#endif
+                       c = fgetc(fp);
+                       if (c == '\0')
+                               goto get_char;
+                       if (c == EOF) {
+                               if (ferror(fp))
+                                       bb_simple_perror_msg_and_die("input error");
+                               // Note: EOF does not append '\n'
+                               break;
+                       }
+                       bad_chars |= bad_input_byte(c);
+                       bc_vec_pushByte(vec, (char)c);
+               } while (c != '\n');
+
+               if (bad_chars) {
+                       // Bad chars on this line
+                       if (!G.prs.lex_filename) { // stdin
+                               // ignore entire line, get another one
+                               goto again;
+                       }
+                       bb_perror_msg_and_die("file '%s' is not text", G.prs.lex_filename);
+               }
+               bc_vec_pushZeroByte(vec);
+       }
+}
+
+//
+// Parsing routines
+//
+
+// "Input numbers may contain the characters 0-9 and A-Z.
+// (Note: They must be capitals.  Lower case letters are variable names.)
+// Single digit numbers always have the value of the digit regardless of
+// the value of ibase. (i.e. A = 10.) For multi-digit numbers, bc changes
+// all input digits greater or equal to ibase to the value of ibase-1.
+// This makes the number ZZZ always be the largest 3 digit number of the
+// input base."
+static bool xc_num_strValid(const char *val)
+{
+       bool radix = false;
+       for (;;) {
+               BcDig c = *val++;
+               if (c == '\0')
+                       break;
+               if (c == '.') {
+                       if (radix) return false;
+                       radix = true;
+                       continue;
+               }
+               if ((c < '0' || c > '9') && (c < 'A' || c > 'Z'))
+                       return false;
+       }
+       return true;
+}
+
+// Note: n is already "bc_num_zero()"ed,
+// leading zeroes in "val" are removed
+static void bc_num_parseDecimal(BcNum *n, const char *val)
+{
+       size_t len, i;
+       const char *ptr;
+
+       len = strlen(val);
+       if (len == 0)
+               return;
+
+       bc_num_expand(n, len + 1); // +1 for e.g. "A" converting into 10
+
+       ptr = strchr(val, '.');
+
+       n->rdx = 0;
+       if (ptr != NULL)
+               n->rdx = (size_t)((val + len) - (ptr + 1));
+
+       for (i = 0; val[i]; ++i) {
+               if (val[i] != '0' && val[i] != '.') {
+                       // Not entirely zero value - convert it, and exit
+                       if (len == 1) {
+                               unsigned c = val[0] - '0';
+                               n->len = 1;
+                               if (c > 9) { // A-Z => 10-36
+                                       n->len = 2;
+                                       c -= ('A' - '9' - 1);
+                                       n->num[1] = c/10;
+                                       c = c%10;
+                               }
+                               n->num[0] = c;
+                               break;
+                       }
+                       i = len - 1;
+                       for (;;) {
+                               char c = val[i] - '0';
+                               if (c > 9) // A-Z => 9
+                                       c = 9;
+                               n->num[n->len] = c;
+                               n->len++;
+ skip_dot:
+                               if (i == 0) break;
+                               if (val[--i] == '.') goto skip_dot;
+                       }
+                       break;
+               }
+       }
+       // if for() exits without hitting if(), the value is entirely zero
+}
+
+// Note: n is already "bc_num_zero()"ed,
+// leading zeroes in "val" are removed
+static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
+{
+       BcStatus s;
+       BcNum mult, result;
+       BcNum temp;
+       BcNum base;
+       BcDig temp_digs[ULONG_NUM_BUFSIZE];
+       BcDig base_digs[ULONG_NUM_BUFSIZE];
+       size_t digits;
+
+       bc_num_init_DEF_SIZE(&mult);
+
+       temp.cap = ARRAY_SIZE(temp_digs);
+       temp.num = temp_digs;
+
+       base.cap = ARRAY_SIZE(base_digs);
+       base.num = base_digs;
+       bc_num_ulong2num(&base, base_t);
+       base_t--;
+
+       for (;;) {
+               unsigned v;
+               char c;
+
+               c = *val++;
+               if (c == '\0') goto int_err;
+               if (c == '.') break;
+
+               v = (unsigned)(c <= '9' ? c - '0' : c - 'A' + 10);
+               if (v > base_t) v = base_t;
+
+               s = zbc_num_mul(n, &base, &mult, 0);
+               if (s) goto int_err;
+               bc_num_ulong2num(&temp, v);
+               s = zbc_num_add(&mult, &temp, n, 0);
+               if (s) goto int_err;
+       }
+
+       bc_num_init(&result, base.len);
+       //bc_num_zero(&result); - already is
+       bc_num_one(&mult);
+
+       digits = 0;
+       for (;;) {
+               unsigned v;
+               char c;
+
+               c = *val++;
+               if (c == '\0') break;
+               digits++;
+
+               v = (unsigned)(c <= '9' ? c - '0' : c - 'A' + 10);
+               if (v > base_t) v = base_t;
+
+               s = zbc_num_mul(&result, &base, &result, 0);
+               if (s) goto err;
+               bc_num_ulong2num(&temp, v);
+               s = zbc_num_add(&result, &temp, &result, 0);
+               if (s) goto err;
+               s = zbc_num_mul(&mult, &base, &mult, 0);
+               if (s) goto err;
+       }
+
+       s = zbc_num_div(&result, &mult, &result, digits);
+       if (s) goto err;
+       s = zbc_num_add(n, &result, n, digits);
+       if (s) goto err;
+
+       if (n->len != 0) {
+               if (n->rdx < digits)
+                       bc_num_extend(n, digits - n->rdx);
+       } else
+               bc_num_zero(n);
+ err:
+       bc_num_free(&result);
+ int_err:
+       bc_num_free(&mult);
+}
+
+static BC_STATUS zxc_num_parse(BcNum *n, const char *val, unsigned base_t)
+{
+       size_t i;
+
+       if (!xc_num_strValid(val))
+               RETURN_STATUS(bc_error("bad number string"));
+
+       bc_num_zero(n);
+       while (*val == '0')
+               val++;
+       for (i = 0; ; ++i) {
+               if (val[i] == '\0')
+                       RETURN_STATUS(BC_STATUS_SUCCESS);
+               if (val[i] != '.' && val[i] != '0')
+                       break;
+       }
+
+       if (base_t == 10 || val[1] == '\0')
+               // Decimal, or single-digit number
+               bc_num_parseDecimal(n, val);
+       else
+               bc_num_parseBase(n, val, base_t);
 
-       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;
+       RETURN_STATUS(BC_STATUS_SUCCESS);
+}
+#define zxc_num_parse(...) (zxc_num_parse(__VA_ARGS__) COMMA_SUCCESS)
+
+// p->lex_inbuf points to the current string to be parsed.
+// if p->lex_inbuf points to '\0', it's either EOF or it points after
+// last processed line's terminating '\n' (and more reading needs to be done
+// to get next character).
+//
+// If you are in a situation where that is a possibility, call peek_inbuf().
+// If necessary, it performs more reading and changes p->lex_inbuf,
+// then it returns *p->lex_inbuf (which will be '\0' only if it's EOF).
+// After it, just referencing *p->lex_inbuf is valid, and if it wasn't '\0',
+// it's ok to do p->lex_inbuf++ once without end-of-buffer checking.
+//
+// eat_inbuf() is equvalent to "peek_inbuf(); if (c) p->lex_inbuf++":
+// it returns current char and advances the pointer (if not EOF).
+// After eat_inbuf(), referencing p->lex_inbuf[-1] and *p->lex_inbuf is valid.
+//
+// In many cases, you can use fast *p->lex_inbuf instead of peek_inbuf():
+// unless prev char might have been '\n', *p->lex_inbuf is '\0' ONLY
+// on real EOF, not end-of-buffer.
+//
+// bc cases to test interactively:
+// 1 #comment\  - prints "1<newline>" at once (comment is not continued)
+// 1 #comment/* - prints "1<newline>" at once
+// 1 #comment"  - prints "1<newline>" at once
+// 1\#comment   - error at once (\ is not a line continuation)
+// 1 + /*"*/2   - prints "3<newline>" at once
+// 1 + /*#*/2   - prints "3<newline>" at once
+// "str\"       - prints "str\" at once
+// "str#"       - prints "str#" at once
+// "str/*"      - prints "str/*" at once
+// "str#\       - waits for second line
+// end"         - ...prints "str#\<newline>end"
+static char peek_inbuf(void)
+{
+       if (*G.prs.lex_inbuf == '\0'
+        && G.prs.lex_input_fp
+       ) {
+               xc_read_line(&G.input_buffer, G.prs.lex_input_fp);
+               G.prs.lex_inbuf = G.input_buffer.v;
+               if (G.input_buffer.len <= 1) // on EOF, len is 1 (NUL byte)
+                       G.prs.lex_input_fp = NULL;
        }
+       return *G.prs.lex_inbuf;
+}
+static char eat_inbuf(void)
+{
+       char c = peek_inbuf();
+       if (c) G.prs.lex_inbuf++;
+       return c;
 }
 
-static void bc_lex_lineComment(BcLex *l)
+static void xc_lex_lineComment(void)
 {
+       BcParse *p = &G.prs;
+       char c;
+
        // Try: echo -n '#foo' | bc
-       size_t i;
-       l->t.t = BC_LEX_WHITESPACE;
-       i = l->i;
-       while (i < l->len && l->buf[i] != '\n')
-               i++;
-       l->i = i;
+       p->lex = XC_LEX_WHITESPACE;
+
+       // Not peek_inbuf(): we depend on input being done in whole lines:
+       // '\0' which isn't the EOF can only be seen after '\n'.
+       while ((c = *p->lex_inbuf) != '\n' && c != '\0')
+               p->lex_inbuf++;
 }
 
-static void bc_lex_whitespace(BcLex *l)
+static void xc_lex_whitespace(void)
 {
-       l->t.t = BC_LEX_WHITESPACE;
+       BcParse *p = &G.prs;
+
+       p->lex = XC_LEX_WHITESPACE;
        for (;;) {
-               char c = l->buf[l->i];
-               if (c == '\n') // this is BC_LEX_NLINE, not BC_LEX_WHITESPACE
+               // We depend here on input being done in whole lines:
+               // '\0' which isn't the EOF can only be seen after '\n'.
+               char c = *p->lex_inbuf;
+               if (c == '\n') // this is XC_LEX_NLINE, not XC_LEX_WHITESPACE
                        break;
                if (!isspace(c))
                        break;
-               l->i++;
+               p->lex_inbuf++;
        }
 }
 
-static BC_STATUS zbc_lex_number(BcLex *l, char start)
+static BC_STATUS zxc_lex_number(char last)
 {
-       const char *buf = l->buf + l->i;
-       size_t len, i, ccnt;
+       BcParse *p = &G.prs;
        bool pt;
-
-       pt = (start == '.');
-       l->t.t = BC_LEX_NUMBER;
-       ccnt = i = 0;
+       char last_valid_ch;
+
+       bc_vec_pop_all(&p->lex_strnumbuf);
+       bc_vec_pushByte(&p->lex_strnumbuf, last);
+
+// bc: "Input numbers may contain the characters 0-9 and A-Z.
+// (Note: They must be capitals.  Lower case letters are variable names.)
+// Single digit numbers always have the value of the digit regardless of
+// the value of ibase. (i.e. A = 10.) For multi-digit numbers, bc changes
+// all input digits greater or equal to ibase to the value of ibase-1.
+// This makes the number ZZZ always be the largest 3 digit number of the
+// input base."
+// dc only allows A-F, the rules about single-char and multi-char are the same.
+       last_valid_ch = (IS_BC ? 'Z' : 'F');
+       pt = (last == '.');
+       p->lex = XC_LEX_NUMBER;
        for (;;) {
-               char c = buf[i];
+               // We depend here on input being done in whole lines:
+               // '\0' which isn't the EOF can only be seen after '\n'.
+               char c = *p->lex_inbuf;
+ check_c:
                if (c == '\0')
                        break;
-               if (c == '\\' && buf[i + 1] == '\n') {
-                       i += 2;
-                       //number_of_backslashes++ - see comment below
-                       continue;
+               if (c == '\\' && p->lex_inbuf[1] == '\n') {
+                       p->lex_inbuf += 2;
+                       p->lex_line++;
+                       dbg_lex("++p->lex_line=%zd", p->lex_line);
+                       c = peek_inbuf(); // force next line to be read
+                       goto check_c;
                }
-               if (!isdigit(c) && (c < 'A' || c > 'F')) {
+               if (!isdigit(c) && (c < 'A' || c > last_valid_ch)) {
                        if (c != '.') break;
                        // if '.' was already seen, stop on second one:
                        if (pt) break;
                        pt = true;
                }
-               // buf[i] is one of "0-9A-F."
-               i++;
-               if (c != '.')
-                       ccnt = i;
-       }
-       //ccnt is the number of chars in the number string, excluding possible
-       //trailing "[\<newline>].[\<newline>]" (with any number of \<NL> repetitions).
-       //i is buf[i] index of the first not-yet-parsed char after that.
-       l->i += i;
-
-       // This might overestimate the size, if there are "\<NL>"'s
-       // in the number. Subtracting number_of_backslashes*2 correctly
-       // is not that easy: consider that in the case of "NNN.\<NL>"
-       // loop above will count "\<NL>" before it realizes it is not
-       // in fact *inside* the number:
-       len = ccnt + 1; // +1 byte for NUL termination
-
-       // This check makes sense only if size_t is (much) larger than BC_MAX_NUM.
-       if (SIZE_MAX > (BC_MAX_NUM | 0xff)) {
-               if (len > BC_MAX_NUM)
-                       RETURN_STATUS(bc_error("number too long: must be [1,"BC_MAX_NUM_STR"]"));
-       }
-
-       bc_vec_pop_all(&l->t.v);
-       bc_vec_expand(&l->t.v, 1 + len);
-       bc_vec_push(&l->t.v, &start);
-
-       while (ccnt != 0) {
-               // If we have hit a backslash, skip it. We don't have
-               // to check for a newline because it's guaranteed.
-               if (*buf == '\\') {
-                       buf += 2;
-                       ccnt -= 2;
-                       continue;
-               }
-               bc_vec_push(&l->t.v, buf);
-               buf++;
-               ccnt--;
+               // c is one of "0-9A-Z."
+               last = c;
+               bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf);
+               p->lex_inbuf++;
        }
+       if (last == '.') // remove trailing '.' if any
+               bc_vec_pop(&p->lex_strnumbuf);
+       bc_vec_pushZeroByte(&p->lex_strnumbuf);
 
-       bc_vec_pushZeroByte(&l->t.v);
-
+       G.err_line = G.prs.lex_line;
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
-#define zbc_lex_number(...) (zbc_lex_number(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_lex_number(...) (zxc_lex_number(__VA_ARGS__) COMMA_SUCCESS)
 
-static void bc_lex_name(BcLex *l)
+static void xc_lex_name(void)
 {
+       BcParse *p = &G.prs;
        size_t i;
        const char *buf;
 
-       l->t.t = BC_LEX_NAME;
+       p->lex = XC_LEX_NAME;
 
+       // Since names can't cross lines with \<newline>,
+       // we depend on the fact that whole line is in the buffer
        i = 0;
-       buf = l->buf + l->i - 1;
+       buf = p->lex_inbuf - 1;
        for (;;) {
                char c = buf[i];
                if ((c < 'a' || c > 'z') && !isdigit(c) && c != '_') break;
@@ -2804,185 +2947,83 @@ static void bc_lex_name(BcLex *l)
                        return bc_error("name too long: must be [1,"BC_MAX_STRING_STR"]");
        }
 #endif
-       bc_vec_string(&l->t.v, i, buf);
+       bc_vec_string(&p->lex_strnumbuf, i, buf);
 
        // Increment the index. We minus 1 because it has already been incremented.
-       l->i += i - 1;
+       p->lex_inbuf += i - 1;
 
        //return BC_STATUS_SUCCESS;
 }
 
-static void bc_lex_init(BcLex *l)
-{
-       bc_char_vec_init(&l->t.v);
-}
-
-static void bc_lex_free(BcLex *l)
-{
-       bc_vec_free(&l->t.v);
-}
-
-static void bc_lex_file(BcLex *l)
-{
-       G.err_line = l->line = 1;
-       l->newline = false;
-}
-
-static bool bc_lex_more_input(BcLex *l)
-{
-       size_t str;
-       bool comment;
-
-       bc_vec_pop_all(&G.input_buffer);
-
-       // This loop is complex because the vm tries not to send any lines that end
-       // with a backslash to the parser. The reason for that is because the parser
-       // treats a backslash+newline combo as whitespace, per the bc spec. In that
-       // case, and for strings and comments, the parser will expect more stuff.
-       comment = false;
-       str = 0;
-       for (;;) {
-               size_t prevlen = G.input_buffer.len;
-               char *string;
-
-               bc_read_line(&G.input_buffer, G.input_fp);
-               // No more input means EOF
-               if (G.input_buffer.len <= prevlen + 1) // (we expect +1 for NUL byte)
-                       break;
-
-               string = G.input_buffer.v + prevlen;
-               while (*string) {
-                       char c = *string;
-                       if (string == G.input_buffer.v || string[-1] != '\\') {
-                               if (IS_BC)
-                                       str ^= (c == '"');
-                               else {
-                                       if (c == ']')
-                                               str -= 1;
-                                       else if (c == '[')
-                                               str += 1;
-                               }
-                       }
-                       string++;
-                       if (c == '/' && *string == '*') {
-                               comment = true;
-                               string++;
-                               continue;
-                       }
-                       if (c == '*' && *string == '/') {
-                               comment = false;
-                               string++;
-                       }
-               }
-               if (str != 0 || comment) {
-                       G.input_buffer.len--; // backstep over the trailing NUL byte
-                       continue;
-               }
-
-               // Check for backslash+newline.
-               // we do not check that last char is '\n' -
-               // if it is not, then it's EOF, and looping back
-               // to bc_read_line() will detect it:
-               string -= 2;
-               if (string >= G.input_buffer.v && *string == '\\') {
-                       G.input_buffer.len--;
-                       continue;
-               }
-
-               break;
-       }
-
-       l->buf = G.input_buffer.v;
-       l->i = 0;
-//     bb_error_msg("G.input_buffer.len:%d '%s'", G.input_buffer.len, G.input_buffer.v);
-       l->len = G.input_buffer.len - 1; // do not include NUL
-
-       return l->len != 0;
-}
-
-IF_BC(static BC_STATUS zbc_lex_token(BcLex *l);)
-IF_DC(static BC_STATUS zdc_lex_token(BcLex *l);)
+IF_BC(static BC_STATUS zbc_lex_token(void);)
+IF_DC(static BC_STATUS zdc_lex_token(void);)
 #define zbc_lex_token(...) (zbc_lex_token(__VA_ARGS__) COMMA_SUCCESS)
 #define zdc_lex_token(...) (zdc_lex_token(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_lex_next(BcLex *l)
+static BC_STATUS zxc_lex_next(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
 
-       l->t.last = l->t.t;
-       if (l->t.last == BC_LEX_EOF) RETURN_STATUS(bc_error("end of file"));
-
-       l->line += l->newline;
-       G.err_line = l->line;
-       l->newline = false;
+       G.err_line = p->lex_line;
+       p->lex_last = p->lex;
+//why?
+//     if (p->lex_last == XC_LEX_EOF)
+//             RETURN_STATUS(bc_error("end of file"));
 
        // Loop until failure or we don't have whitespace. This
        // is so the parser doesn't get inundated with whitespace.
-       // Comments are also BC_LEX_WHITESPACE tokens and eaten here.
+       // Comments are also XC_LEX_WHITESPACE tokens and eaten here.
        s = BC_STATUS_SUCCESS;
        do {
-               if (l->i == l->len) {
-                       l->t.t = BC_LEX_EOF;
-                       if (!G.input_fp)
+               if (*p->lex_inbuf == '\0') {
+                       p->lex = XC_LEX_EOF;
+                       if (peek_inbuf() == '\0')
                                RETURN_STATUS(BC_STATUS_SUCCESS);
-                       if (!bc_lex_more_input(l)) {
-                               G.input_fp = NULL;
-                               RETURN_STATUS(BC_STATUS_SUCCESS);
-                       }
-                       // here it's guaranteed that l->i is below l->len
                }
+               p->lex_next_at = p->lex_inbuf;
                dbg_lex("next string to parse:'%.*s'",
-                       (int)(strchrnul(l->buf + l->i, '\n') - (l->buf + l->i)),
-                       l->buf + l->i);
+                       (int)(strchrnul(p->lex_next_at, '\n') - p->lex_next_at),
+                       p->lex_next_at
+               );
                if (IS_BC) {
-                       IF_BC(s = zbc_lex_token(l));
+                       IF_BC(s = zbc_lex_token());
                } else {
-                       IF_DC(s = zdc_lex_token(l));
+                       IF_DC(s = zdc_lex_token());
                }
-       } while (!s && l->t.t == BC_LEX_WHITESPACE);
-       dbg_lex("l->t.t from string:%d", l->t.t);
+       } while (!s && p->lex == XC_LEX_WHITESPACE);
+       dbg_lex("p->lex from string:%d", p->lex);
 
        RETURN_STATUS(s);
 }
-#define zbc_lex_next(...) (zbc_lex_next(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_lex_next(...) (zxc_lex_next(__VA_ARGS__) COMMA_SUCCESS)
 
 #if ENABLE_BC
-static BC_STATUS zbc_lex_skip_if_at_NLINE(BcLex *l)
+static BC_STATUS zbc_lex_skip_if_at_NLINE(void)
 {
-       if (l->t.t == BC_LEX_NLINE)
-               RETURN_STATUS(zbc_lex_next(l));
+       if (G.prs.lex == XC_LEX_NLINE)
+               RETURN_STATUS(zxc_lex_next());
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
 #define zbc_lex_skip_if_at_NLINE(...) (zbc_lex_skip_if_at_NLINE(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_lex_next_and_skip_NLINE(BcLex *l)
+static BC_STATUS zbc_lex_next_and_skip_NLINE(void)
 {
        BcStatus s;
-       s = zbc_lex_next(l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
        // if(cond)<newline>stmt is accepted too (but not 2+ newlines)
-       s = zbc_lex_skip_if_at_NLINE(l);
+       s = zbc_lex_skip_if_at_NLINE();
        RETURN_STATUS(s);
 }
 #define zbc_lex_next_and_skip_NLINE(...) (zbc_lex_next_and_skip_NLINE(__VA_ARGS__) COMMA_SUCCESS)
-#endif
-
-static BC_STATUS zbc_lex_text_init(BcLex *l, const char *text)
-{
-       l->buf = text;
-       l->i = 0;
-       l->len = strlen(text);
-       l->t.t = l->t.last = BC_LEX_INVALID;
-       RETURN_STATUS(zbc_lex_next(l));
-}
-#define zbc_lex_text_init(...) (zbc_lex_text_init(__VA_ARGS__) COMMA_SUCCESS)
 
-#if ENABLE_BC
-static BC_STATUS zbc_lex_identifier(BcLex *l)
+static BC_STATUS zbc_lex_identifier(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        unsigned i;
-       const char *buf = l->buf + l->i - 1;
+       const char *buf = p->lex_inbuf - 1;
 
        for (i = 0; i < ARRAY_SIZE(bc_lex_kws); ++i) {
                const char *keyword8 = bc_lex_kws[i].name8;
@@ -2997,298 +3038,317 @@ static BC_STATUS zbc_lex_identifier(BcLex *l)
                // buf starts with keyword bc_lex_kws[i]
                if (isalnum(buf[j]) || buf[j]=='_')
                        continue; // "ifz" does not match "if" keyword, "if." does
-               l->t.t = BC_LEX_KEY_1st_keyword + i;
-               if (!bc_lex_kws_POSIX(i)) {
-                       s = bc_posix_error_fmt("%sthe '%.8s' keyword", "POSIX does not allow ", bc_lex_kws[i].name8);
-                       IF_ERROR_RETURN_POSSIBLE(if (s) RETURN_STATUS(s));
+               p->lex = BC_LEX_KEY_1st_keyword + i;
+               if (!keyword_is_POSIX(i)) {
+                       s = zbc_posix_error_fmt("%sthe '%.8s' keyword", "POSIX does not allow ", bc_lex_kws[i].name8);
+                       if (s) RETURN_STATUS(s);
                }
 
                // We minus 1 because the index has already been incremented.
-               l->i += j - 1;
+               p->lex_inbuf += j - 1;
                RETURN_STATUS(BC_STATUS_SUCCESS);
        }
 
-       bc_lex_name(l);
+       xc_lex_name();
        s = BC_STATUS_SUCCESS;
 
-       if (l->t.v.len > 2) {
+       if (p->lex_strnumbuf.len > 2) {
                // Prevent this:
                // >>> qwe=1
-               // bc: POSIX only allows one character names; the following is bad: 'qwe=1
+               // bc: POSIX only allows one character names; this is bad: 'qwe=1
                // '
                unsigned len = strchrnul(buf, '\n') - buf;
-               s = bc_posix_error_fmt("POSIX only allows one character names; the following is bad: '%.*s'", len, buf);
+               s = zbc_posix_error_fmt("POSIX only allows one character names; this is bad: '%.*s'", len, buf);
        }
 
        RETURN_STATUS(s);
 }
 #define zbc_lex_identifier(...) (zbc_lex_identifier(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_lex_string(BcLex *l)
+static BC_STATUS zbc_lex_string(void)
 {
-       size_t len, nls, i;
+       BcParse *p = &G.prs;
 
-       l->t.t = BC_LEX_STR;
-
-       nls = 0;
-       i = l->i;
+       p->lex = XC_LEX_STR;
+       bc_vec_pop_all(&p->lex_strnumbuf);
        for (;;) {
-               char c = l->buf[i];
+               char c = peek_inbuf(); // strings can cross lines
                if (c == '\0') {
-                       l->i = i;
-                       RETURN_STATUS(bc_error("string end could not be found"));
+                       RETURN_STATUS(bc_error("unterminated string"));
                }
                if (c == '"')
                        break;
-               nls += (c == '\n');
-               i++;
-       }
-
-       len = i - l->i;
-       // This check makes sense only if size_t is (much) larger than BC_MAX_STRING.
-       if (SIZE_MAX > (BC_MAX_STRING | 0xff)) {
-               if (len > BC_MAX_STRING)
-                       RETURN_STATUS(bc_error("string too long: must be [1,"BC_MAX_STRING_STR"]"));
+               if (c == '\n') {
+                       p->lex_line++;
+                       dbg_lex("++p->lex_line=%zd", p->lex_line);
+               }
+               bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf);
+               p->lex_inbuf++;
        }
-       bc_vec_string(&l->t.v, len, l->buf + l->i);
-
-       l->i = i + 1;
-       l->line += nls;
-       G.err_line = l->line;
+       bc_vec_pushZeroByte(&p->lex_strnumbuf);
+       p->lex_inbuf++;
 
+       G.err_line = p->lex_line;
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
 #define zbc_lex_string(...) (zbc_lex_string(__VA_ARGS__) COMMA_SUCCESS)
 
-static void bc_lex_assign(BcLex *l, unsigned with_and_without)
+static void parse_lex_by_checking_eq_sign(unsigned with_and_without)
 {
-       if (l->buf[l->i] == '=') {
-               ++l->i;
+       BcParse *p = &G.prs;
+       if (*p->lex_inbuf == '=') {
+               // ^^^ not using peek_inbuf() since '==' etc can't be split across lines
+               p->lex_inbuf++;
                with_and_without >>= 8; // store "with" value
        } // else store "without" value
-       l->t.t = (with_and_without & 0xff);
+       p->lex = (with_and_without & 0xff);
 }
-#define bc_lex_assign(l, with, without) \
-       bc_lex_assign(l, ((with)<<8)|(without))
+#define parse_lex_by_checking_eq_sign(with, without) \
+       parse_lex_by_checking_eq_sign(((with)<<8)|(without))
 
-static BC_STATUS zbc_lex_comment(BcLex *l)
+static BC_STATUS zbc_lex_comment(void)
 {
-       size_t i, nls = 0;
-       const char *buf = l->buf;
+       BcParse *p = &G.prs;
 
-       l->t.t = BC_LEX_WHITESPACE;
-       i = l->i; /* here buf[l->i] is the '*' of opening comment delimiter */
+       p->lex = XC_LEX_WHITESPACE;
+       // here lex_inbuf is at '*' of opening comment delimiter
        for (;;) {
-               char c = buf[++i];
+               char c;
+
+               p->lex_inbuf++;
+               c = peek_inbuf();
  check_star:
                if (c == '*') {
-                       c = buf[++i];
+                       p->lex_inbuf++;
+                       c = *p->lex_inbuf; // no need to peek_inbuf()
                        if (c == '/')
                                break;
                        goto check_star;
                }
                if (c == '\0') {
-                       l->i = i;
-                       RETURN_STATUS(bc_error("comment end could not be found"));
+                       RETURN_STATUS(bc_error("unterminated comment"));
+               }
+               if (c == '\n') {
+                       p->lex_line++;
+                       dbg_lex("++p->lex_line=%zd", p->lex_line);
                }
-               nls += (c == '\n');
        }
+       p->lex_inbuf++; // skip trailing '/'
 
-       l->i = i + 1;
-       l->line += nls;
-       G.err_line = l->line;
-
+       G.err_line = p->lex_line;
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
 #define zbc_lex_comment(...) (zbc_lex_comment(__VA_ARGS__) COMMA_SUCCESS)
 
 #undef zbc_lex_token
-static BC_STATUS zbc_lex_token(BcLex *l)
+static BC_STATUS zbc_lex_token(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s = BC_STATUS_SUCCESS;
-       char c = l->buf[l->i++], c2;
+       char c = eat_inbuf();
+       char c2;
 
        // This is the workhorse of the lexer.
        switch (c) {
-//             case '\0': // probably never reached
-//                     l->i--;
-//                     l->t.t = BC_LEX_EOF;
-//                     l->newline = true;
-//                     break;
-               case '\n':
-                       l->t.t = BC_LEX_NLINE;
-                       l->newline = true;
-                       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_POSIX_does_not_allow_bool_ops_this_is_bad("!");
-                               IF_ERROR_RETURN_POSSIBLE(if (s) RETURN_STATUS(s));
-                       }
-                       break;
-               case '"':
-                       s = zbc_lex_string(l);
-                       break;
-               case '#':
-                       s = bc_POSIX_does_not_allow("'#' script comments");
-                       IF_ERROR_RETURN_POSSIBLE(if (s) RETURN_STATUS(s));
-                       bc_lex_lineComment(l);
-                       break;
-               case '%':
-                       bc_lex_assign(l, BC_LEX_OP_ASSIGN_MODULUS, BC_LEX_OP_MODULUS);
-                       break;
-               case '&':
-                       c2 = l->buf[l->i];
-                       if (c2 == '&') {
-                               s = bc_POSIX_does_not_allow_bool_ops_this_is_bad("&&");
-                               IF_ERROR_RETURN_POSSIBLE(if (s) RETURN_STATUS(s));
-                               ++l->i;
-                               l->t.t = BC_LEX_OP_BOOL_AND;
-                       } else {
-                               l->t.t = BC_LEX_INVALID;
-                               s = bc_error_bad_character('&');
-                       }
-                       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 '+':
-                       c2 = l->buf[l->i];
-                       if (c2 == '+') {
-                               ++l->i;
-                               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 '-':
-                       c2 = l->buf[l->i];
-                       if (c2 == '-') {
-                               ++l->i;
-                               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->buf[l->i]))
-                               s = zbc_lex_number(l, c);
-                       else {
-                               l->t.t = BC_LEX_KEY_LAST;
-                               s = bc_POSIX_does_not_allow("a period ('.') as a shortcut for the last result");
-                       }
-                       break;
-               case '/':
-                       c2 = l->buf[l->i];
-                       if (c2 == '*')
-                               s = zbc_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 = zbc_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->buf[l->i] == '\n') {
-                               l->t.t = BC_LEX_WHITESPACE;
-                               ++l->i;
-                       } else
-                               s = bc_error_bad_character(c);
-                       break;
-               case '^':
-                       bc_lex_assign(l, BC_LEX_OP_ASSIGN_POWER, BC_LEX_OP_POWER);
-                       break;
-               case 'a':
-               case 'b':
-               case 'c':
-               case 'd':
-               case 'e':
-               case 'f':
-               case 'g':
-               case 'h':
-               case 'i':
-               case 'j':
-               case 'k':
-               case 'l':
-               case 'm':
-               case 'n':
-               case 'o':
-               case 'p':
-               case 'q':
-               case 'r':
-               case 's':
-               case 't':
-               case 'u':
-               case 'v':
-               case 'w':
-               case 'x':
-               case 'y':
-               case 'z':
-                       s = zbc_lex_identifier(l);
-                       break;
-               case '{':
-               case '}':
-                       l->t.t = (BcLexType)(c - '{' + BC_LEX_LBRACE);
-                       break;
-               case '|':
-                       c2 = l->buf[l->i];
-                       if (c2 == '|') {
-                               s = bc_POSIX_does_not_allow_bool_ops_this_is_bad("||");
-                               IF_ERROR_RETURN_POSSIBLE(if (s) RETURN_STATUS(s));
-                               ++l->i;
-                               l->t.t = BC_LEX_OP_BOOL_OR;
-                       } else {
-                               l->t.t = BC_LEX_INVALID;
-                               s = bc_error_bad_character(c);
-                       }
-                       break;
-               default:
-                       l->t.t = BC_LEX_INVALID;
+//     case '\0': // probably never reached
+//             p->lex_inbuf--;
+//             p->lex = XC_LEX_EOF;
+//             break;
+       case '\n':
+               p->lex_line++;
+               dbg_lex("++p->lex_line=%zd", p->lex_line);
+               p->lex = XC_LEX_NLINE;
+               break;
+       case '\t':
+       case '\v':
+       case '\f':
+       case '\r':
+       case ' ':
+               xc_lex_whitespace();
+               break;
+       case '!':
+               parse_lex_by_checking_eq_sign(XC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT);
+               if (p->lex == BC_LEX_OP_BOOL_NOT) {
+                       s = zbc_POSIX_does_not_allow_bool_ops_this_is_bad("!");
+                       if (s) RETURN_STATUS(s);
+               }
+               break;
+       case '"':
+               s = zbc_lex_string();
+               break;
+       case '#':
+               s = zbc_POSIX_does_not_allow("'#' script comments");
+               if (s) RETURN_STATUS(s);
+               xc_lex_lineComment();
+               break;
+       case '%':
+               parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_MODULUS, XC_LEX_OP_MODULUS);
+               break;
+       case '&':
+               c2 = *p->lex_inbuf;
+               if (c2 == '&') {
+                       s = zbc_POSIX_does_not_allow_bool_ops_this_is_bad("&&");
+                       if (s) RETURN_STATUS(s);
+                       p->lex_inbuf++;
+                       p->lex = BC_LEX_OP_BOOL_AND;
+               } else {
+                       p->lex = XC_LEX_INVALID;
+                       s = bc_error_bad_character('&');
+               }
+               break;
+       case '(':
+       case ')':
+               p->lex = (BcLexType)(c - '(' + BC_LEX_LPAREN);
+               break;
+       case '*':
+               parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_MULTIPLY, XC_LEX_OP_MULTIPLY);
+               break;
+       case '+':
+               c2 = *p->lex_inbuf;
+               if (c2 == '+') {
+                       p->lex_inbuf++;
+                       p->lex = BC_LEX_OP_INC;
+               } else
+                       parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_PLUS, XC_LEX_OP_PLUS);
+               break;
+       case ',':
+               p->lex = BC_LEX_COMMA;
+               break;
+       case '-':
+               c2 = *p->lex_inbuf;
+               if (c2 == '-') {
+                       p->lex_inbuf++;
+                       p->lex = BC_LEX_OP_DEC;
+               } else
+                       parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_MINUS, XC_LEX_OP_MINUS);
+               break;
+       case '.':
+               if (isdigit(*p->lex_inbuf))
+                       s = zxc_lex_number(c);
+               else {
+                       p->lex = BC_LEX_KEY_LAST;
+                       s = zbc_POSIX_does_not_allow("'.' as 'last'");
+               }
+               break;
+       case '/':
+               c2 = *p->lex_inbuf;
+               if (c2 == '*')
+                       s = zbc_lex_comment();
+               else
+                       parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_DIVIDE, XC_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':
+       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 = zxc_lex_number(c);
+               break;
+       case ';':
+               p->lex = BC_LEX_SCOLON;
+               break;
+       case '<':
+               parse_lex_by_checking_eq_sign(XC_LEX_OP_REL_LE, XC_LEX_OP_REL_LT);
+               break;
+       case '=':
+               parse_lex_by_checking_eq_sign(XC_LEX_OP_REL_EQ, BC_LEX_OP_ASSIGN);
+               break;
+       case '>':
+               parse_lex_by_checking_eq_sign(XC_LEX_OP_REL_GE, XC_LEX_OP_REL_GT);
+               break;
+       case '[':
+       case ']':
+               p->lex = (BcLexType)(c - '[' + BC_LEX_LBRACKET);
+               break;
+       case '\\':
+               if (*p->lex_inbuf == '\n') {
+                       p->lex = XC_LEX_WHITESPACE;
+                       p->lex_inbuf++;
+               } else
                        s = bc_error_bad_character(c);
-                       break;
+               break;
+       case '^':
+               parse_lex_by_checking_eq_sign(BC_LEX_OP_ASSIGN_POWER, XC_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 = zbc_lex_identifier();
+               break;
+       case '{':
+       case '}':
+               p->lex = (BcLexType)(c - '{' + BC_LEX_LBRACE);
+               break;
+       case '|':
+               c2 = *p->lex_inbuf;
+               if (c2 == '|') {
+                       s = zbc_POSIX_does_not_allow_bool_ops_this_is_bad("||");
+                       if (s) RETURN_STATUS(s);
+                       p->lex_inbuf++;
+                       p->lex = BC_LEX_OP_BOOL_OR;
+               } else {
+                       p->lex = XC_LEX_INVALID;
+                       s = bc_error_bad_character(c);
+               }
+               break;
+       default:
+               p->lex = XC_LEX_INVALID;
+               s = bc_error_bad_character(c);
+               break;
        }
 
        RETURN_STATUS(s);
@@ -3297,210 +3357,158 @@ static BC_STATUS zbc_lex_token(BcLex *l)
 #endif // ENABLE_BC
 
 #if ENABLE_DC
-static BC_STATUS zdc_lex_register(BcLex *l)
+static BC_STATUS zdc_lex_register(void)
 {
-       if (isspace(l->buf[l->i - 1])) {
-               bc_lex_whitespace(l);
-               ++l->i;
-               if (!G_exreg)
-                       RETURN_STATUS(bc_error("extended register"));
-               bc_lex_name(l);
-       }
-       else {
-               bc_vec_pop_all(&l->t.v);
-               bc_vec_push(&l->t.v, &l->buf[l->i - 1]);
-               bc_vec_pushZeroByte(&l->t.v);
-               l->t.t = BC_LEX_NAME;
+       BcParse *p = &G.prs;
+       if (G_exreg && isspace(*p->lex_inbuf)) {
+               xc_lex_whitespace(); // eats whitespace (but not newline)
+               p->lex_inbuf++; // xc_lex_name() expects this
+               xc_lex_name();
+       } else {
+               bc_vec_pop_all(&p->lex_strnumbuf);
+               bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf++);
+               bc_vec_pushZeroByte(&p->lex_strnumbuf);
+               p->lex = XC_LEX_NAME;
        }
 
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
 #define zdc_lex_register(...) (zdc_lex_register(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zdc_lex_string(BcLex *l)
+static BC_STATUS zdc_lex_string(void)
 {
-       size_t depth, nls, i;
+       BcParse *p = &G.prs;
+       size_t depth;
 
-       l->t.t = BC_LEX_STR;
-       bc_vec_pop_all(&l->t.v);
+       p->lex = XC_LEX_STR;
+       bc_vec_pop_all(&p->lex_strnumbuf);
 
-       nls = 0;
        depth = 1;
-       i = l->i;
        for (;;) {
-               char c = l->buf[i];
+               char c = peek_inbuf();
                if (c == '\0') {
-                       l->i = i;
-                       RETURN_STATUS(bc_error("string end could not be found"));
+                       RETURN_STATUS(bc_error("unterminated string"));
                }
-               nls += (c == '\n');
-               if (i == l->i || l->buf[i - 1] != '\\') {
-                       if (c == '[') depth++;
-                       if (c == ']')
-                               if (--depth == 0)
-                                       break;
+               if (c == '[') depth++;
+               if (c == ']')
+                       if (--depth == 0)
+                               break;
+               if (c == '\n') {
+                       p->lex_line++;
+                       dbg_lex("++p->lex_line=%zd", p->lex_line);
                }
-               bc_vec_push(&l->t.v, &l->buf[i]);
-               i++;
+               bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf);
+               p->lex_inbuf++;
        }
-       i++;
-
-       bc_vec_pushZeroByte(&l->t.v);
-       // This check makes sense only if size_t is (much) larger than BC_MAX_STRING.
-       if (SIZE_MAX > (BC_MAX_STRING | 0xff)) {
-               if (i - l->i > BC_MAX_STRING)
-                       RETURN_STATUS(bc_error("string too long: must be [1,"BC_MAX_STRING_STR"]"));
-       }
-
-       l->i = i;
-       l->line += nls;
-       G.err_line = l->line;
+       bc_vec_pushZeroByte(&p->lex_strnumbuf);
+       p->lex_inbuf++; // skip trailing ']'
 
+       G.err_line = p->lex_line;
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
 #define zdc_lex_string(...) (zdc_lex_string(__VA_ARGS__) COMMA_SUCCESS)
 
 #undef zdc_lex_token
-static BC_STATUS zdc_lex_token(BcLex *l)
+static BC_STATUS zdc_lex_token(void)
 {
        static const //BcLexType - should be this type, but narrower type saves size:
        uint8_t
-       dc_lex_regs[] = {
-               BC_LEX_OP_REL_EQ, BC_LEX_OP_REL_LE, BC_LEX_OP_REL_GE, BC_LEX_OP_REL_NE,
-               BC_LEX_OP_REL_LT, BC_LEX_OP_REL_GT, BC_LEX_SCOLON, BC_LEX_COLON,
-               BC_LEX_ELSE, BC_LEX_LOAD, BC_LEX_LOAD_POP, BC_LEX_OP_ASSIGN,
-               BC_LEX_STORE_PUSH,
-       };
-       static const //BcLexType - should be this type
-       uint8_t
-       dc_lex_tokens[] = {
-               /* %&'( */
-               BC_LEX_OP_MODULUS, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_LPAREN,
-               /* )*+, */
-               BC_LEX_INVALID, BC_LEX_OP_MULTIPLY, BC_LEX_OP_PLUS, BC_LEX_INVALID,
-               /* -./ */
-               BC_LEX_OP_MINUS, BC_LEX_INVALID, BC_LEX_OP_DIVIDE,
-               /* 0123456789 */
-               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,
-               /* ABCDEFGH */
-               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,
-               /* IJKLMNOP */
-               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,
-               /* QRSTUVWXY */
-               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,
-               /* Z[\] */
-               BC_LEX_KEY_LENGTH, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_INVALID,
-               /* ^_` */
-               BC_LEX_OP_POWER, BC_LEX_NEG, BC_LEX_INVALID,
-               /* abcdefgh */
-               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,
-               /* ijklmnop */
-               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,
-               /* qrstuvwx */
-               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,
-               /* yz */
-               BC_LEX_INVALID, BC_LEX_STACK_LEVEL,
-               /* {|}~ */
-               BC_LEX_LBRACE, BC_LEX_OP_MODEXP, BC_LEX_INVALID, BC_LEX_OP_DIVMOD,
+       dc_lex_regs[] ALIGN1 = {
+               XC_LEX_OP_REL_EQ, XC_LEX_OP_REL_LE, XC_LEX_OP_REL_GE, XC_LEX_OP_REL_NE,
+               XC_LEX_OP_REL_LT, XC_LEX_OP_REL_GT, DC_LEX_SCOLON, DC_LEX_COLON,
+               DC_LEX_ELSE, DC_LEX_LOAD, DC_LEX_LOAD_POP, DC_LEX_OP_ASSIGN,
+               DC_LEX_STORE_PUSH,
        };
 
-       BcStatus s = BC_STATUS_SUCCESS;
-       char c = l->buf[l->i++], c2;
+       BcParse *p = &G.prs;
+       BcStatus s;
+       char c, c2;
        size_t i;
 
        for (i = 0; i < ARRAY_SIZE(dc_lex_regs); ++i) {
-               if (l->t.last == dc_lex_regs[i])
-                       RETURN_STATUS(zdc_lex_register(l));
+               if (p->lex_last == dc_lex_regs[i])
+                       RETURN_STATUS(zdc_lex_register());
        }
 
+       s = BC_STATUS_SUCCESS;
+       c = eat_inbuf();
        if (c >= '%' && c <= '~'
-        && (l->t.t = dc_lex_tokens[c - '%']) != BC_LEX_INVALID
+        && (p->lex = dc_char_to_LEX[c - '%']) != XC_LEX_INVALID
        ) {
                RETURN_STATUS(s);
        }
 
        // This is the workhorse of the lexer.
        switch (c) {
-//             case '\0': // probably never reached
-//                     l->t.t = BC_LEX_EOF;
-//                     break;
-               case '\n':
-                       // '\n' is BC_LEX_NLINE, not BC_LEX_WHITESPACE
-                       // (and "case '\n':" is not just empty here)
-                       // only to allow interactive dc have a way to exit
-                       // "parse" stage of "parse,execute" loop
-                       // on <enter>, not on _next_ token (which would mean
-                       // commands are not executed on pressing <enter>).
-                       // IOW: typing "1p<enter>" should print "1" _at once_,
-                       // not after some more input.
-                       l->t.t = BC_LEX_NLINE;
-                       l->newline = true;
-                       break;
-               case '\t':
-               case '\v':
-               case '\f':
-               case '\r':
-               case ' ':
-                       l->newline = (c == '\n');
-                       bc_lex_whitespace(l);
-                       break;
-               case '!':
-                       c2 = l->buf[l->i];
-                       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_STATUS(bc_error_bad_character(c));
-                       ++l->i;
-                       break;
-               case '#':
-                       bc_lex_lineComment(l);
-                       break;
-               case '.':
-                       if (isdigit(l->buf[l->i]))
-                               s = zbc_lex_number(l, c);
-                       else
-                               s = bc_error_bad_character(c);
-                       break;
-               case '0':
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case '5':
-               case '6':
-               case '7':
-               case '8':
-               case '9':
-               case 'A':
-               case 'B':
-               case 'C':
-               case 'D':
-               case 'E':
-               case 'F':
-                       s = zbc_lex_number(l, c);
-                       break;
-               case '[':
-                       s = zdc_lex_string(l);
-                       break;
-               default:
-                       l->t.t = BC_LEX_INVALID;
+//     case '\0': // probably never reached
+//             p->lex = XC_LEX_EOF;
+//             break;
+       case '\n':
+               // '\n' is XC_LEX_NLINE, not XC_LEX_WHITESPACE
+               // (and "case '\n':" is not just empty here)
+               // only to allow interactive dc have a way to exit
+               // "parse" stage of "parse,execute" loop
+               // on <enter>, not on _next_ token (which would mean
+               // commands are not executed on pressing <enter>).
+               // IOW: typing "1p<enter>" should print "1" _at once_,
+               // not after some more input.
+               p->lex_line++;
+               dbg_lex("++p->lex_line=%zd", p->lex_line);
+               p->lex = XC_LEX_NLINE;
+               break;
+       case '\t':
+       case '\v':
+       case '\f':
+       case '\r':
+       case ' ':
+               xc_lex_whitespace();
+               break;
+       case '!':
+               c2 = *p->lex_inbuf;
+               if (c2 == '=')
+                       p->lex = XC_LEX_OP_REL_NE;
+               else if (c2 == '<')
+                       p->lex = XC_LEX_OP_REL_LE;
+               else if (c2 == '>')
+                       p->lex = XC_LEX_OP_REL_GE;
+               else
+                       RETURN_STATUS(bc_error_bad_character(c));
+               p->lex_inbuf++;
+               break;
+       case '#':
+               xc_lex_lineComment();
+               break;
+       case '.':
+               if (isdigit(*p->lex_inbuf))
+                       s = zxc_lex_number(c);
+               else
                        s = bc_error_bad_character(c);
-                       break;
+               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 = zxc_lex_number(c);
+               break;
+       case '[':
+               s = zdc_lex_string();
+               break;
+       default:
+               p->lex = XC_LEX_INVALID;
+               s = bc_error_bad_character(c);
+               break;
        }
 
        RETURN_STATUS(s);
@@ -3508,70 +3516,104 @@ static BC_STATUS zdc_lex_token(BcLex *l)
 #define zdc_lex_token(...) (zdc_lex_token(__VA_ARGS__) COMMA_SUCCESS)
 #endif // ENABLE_DC
 
-static void bc_parse_push(BcParse *p, char i)
+static void xc_parse_push(unsigned i)
 {
-       dbg_compile("%s:%d pushing bytecode %zd:%d", __func__, __LINE__, p->func->code.len, i);
-       bc_vec_pushByte(&p->func->code, i);
+       BcVec *code = &G.prs.func->code;
+       dbg_compile("%s:%d pushing bytecode %zd:%d", __func__, __LINE__, code->len, i);
+       bc_vec_pushByte(code, (uint8_t)i);
 }
 
-static void bc_parse_pushName(BcParse *p, char *name)
+static void xc_parse_pushName(char *name)
 {
-       while (*name)
-               bc_parse_push(p, *name++);
-       bc_parse_push(p, BC_PARSE_STREND);
+#if 1
+       BcVec *code = &G.prs.func->code;
+       size_t pos = code->len;
+       size_t len = strlen(name) + 1;
+
+       bc_vec_expand(code, pos + len);
+       strcpy(code->v + pos, name);
+       code->len = pos + len;
+#else
+       // Smaller code, but way slow:
+       do {
+               xc_parse_push(*name);
+       } while (*name++);
+#endif
 }
 
-static void bc_parse_pushIndex(BcParse *p, size_t idx)
+// Indexes < 0xfc are encoded verbatim, else first byte is
+// 0xfc, 0xfd, 0xfe or 0xff, encoding "1..4 bytes",
+// followed by that many bytes, lsb first.
+// (The above describes 32-bit case).
+#define SMALL_INDEX_LIMIT (0x100 - sizeof(size_t))
+
+static void bc_vec_pushIndex(BcVec *v, size_t idx)
 {
        size_t mask;
        unsigned amt;
 
        dbg_lex("%s:%d pushing index %zd", __func__, __LINE__, idx);
+       if (idx < SMALL_INDEX_LIMIT) {
+               bc_vec_pushByte(v, idx);
+               return;
+       }
+
        mask = ((size_t)0xff) << (sizeof(idx) * 8 - 8);
        amt = sizeof(idx);
-       do {
+       for (;;) {
                if (idx & mask) break;
                mask >>= 8;
                amt--;
-       } while (amt != 0);
+       }
+       // amt is at least 1 here - "one byte of length data follows"
 
-       bc_parse_push(p, amt);
+       bc_vec_pushByte(v, (SMALL_INDEX_LIMIT - 1) + amt);
 
-       while (idx != 0) {
-               bc_parse_push(p, (unsigned char)idx);
+       do {
+               bc_vec_pushByte(v, (unsigned char)idx);
                idx >>= 8;
-       }
+       } while (idx != 0);
+}
+
+static void xc_parse_pushIndex(size_t idx)
+{
+       bc_vec_pushIndex(&G.prs.func->code, idx);
+}
+
+static void xc_parse_pushInst_and_Index(unsigned inst, size_t idx)
+{
+       xc_parse_push(inst);
+       xc_parse_pushIndex(idx);
 }
 
 #if ENABLE_BC
-static void bc_parse_pushJUMP(BcParse *p, size_t idx)
+static void bc_parse_pushJUMP(size_t idx)
 {
-       bc_parse_push(p, BC_INST_JUMP);
-       bc_parse_pushIndex(p, idx);
+       xc_parse_pushInst_and_Index(BC_INST_JUMP, idx);
 }
 
-static void bc_parse_pushJUMP_ZERO(BcParse *p, size_t idx)
+static void bc_parse_pushJUMP_ZERO(size_t idx)
 {
-       bc_parse_push(p, BC_INST_JUMP_ZERO);
-       bc_parse_pushIndex(p, idx);
+       xc_parse_pushInst_and_Index(BC_INST_JUMP_ZERO, idx);
 }
 
-static BC_STATUS zbc_parse_pushSTR(BcParse *p)
+static BC_STATUS zbc_parse_pushSTR(void)
 {
-       char *str = xstrdup(p->l.t.v.v);
+       BcParse *p = &G.prs;
+       char *str = xstrdup(p->lex_strnumbuf.v);
 
-       bc_parse_push(p, BC_INST_STR);
-       bc_parse_pushIndex(p, p->func->strs.len);
+       xc_parse_pushInst_and_Index(XC_INST_STR, p->func->strs.len);
        bc_vec_push(&p->func->strs, &str);
 
-       RETURN_STATUS(zbc_lex_next(&p->l));
+       RETURN_STATUS(zxc_lex_next());
 }
 #define zbc_parse_pushSTR(...) (zbc_parse_pushSTR(__VA_ARGS__) COMMA_SUCCESS)
 #endif
 
-static void bc_parse_pushNUM(BcParse *p)
+static void xc_parse_pushNUM(void)
 {
-       char *num = xstrdup(p->l.t.v.v);
+       BcParse *p = &G.prs;
+       char *num = xstrdup(p->lex_strnumbuf.v);
 #if ENABLE_BC && ENABLE_DC
        size_t idx = bc_vec_push(IS_BC ? &p->func->consts : &G.prog.consts, &num);
 #elif ENABLE_BC
@@ -3579,21 +3621,21 @@ static void bc_parse_pushNUM(BcParse *p)
 #else // DC
        size_t idx = bc_vec_push(&G.prog.consts, &num);
 #endif
-       bc_parse_push(p, BC_INST_NUM);
-       bc_parse_pushIndex(p, idx);
+       xc_parse_pushInst_and_Index(XC_INST_NUM, idx);
 }
 
-static BC_STATUS zbc_parse_text_init(BcParse *p, const char *text)
+static BC_STATUS zxc_parse_text_init(const char *text)
 {
-       p->func = bc_program_func(p->fidx);
-
-       RETURN_STATUS(zbc_lex_text_init(&p->l, text));
+       G.prs.func = xc_program_func(G.prs.fidx);
+       G.prs.lex_inbuf = text;
+       G.prs.lex = G.prs.lex_last = XC_LEX_INVALID;
+       RETURN_STATUS(zxc_lex_next());
 }
-#define zbc_parse_text_init(...) (zbc_parse_text_init(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_parse_text_init(...) (zxc_parse_text_init(__VA_ARGS__) COMMA_SUCCESS)
 
 // Called when parsing or execution detects a failure,
 // resets execution structures.
-static void bc_program_reset(void)
+static void xc_program_reset(void)
 {
        BcFunc *f;
        BcInstPtr *ip;
@@ -3601,55 +3643,57 @@ static void bc_program_reset(void)
        bc_vec_npop(&G.prog.exestack, G.prog.exestack.len - 1);
        bc_vec_pop_all(&G.prog.results);
 
-       f = bc_program_func_BC_PROG_MAIN();
+       f = xc_program_func_BC_PROG_MAIN();
        ip = bc_vec_top(&G.prog.exestack);
        ip->inst_idx = f->code.len;
 }
 
 // Called when parsing code detects a failure,
 // resets parsing structures.
-static void bc_parse_reset(BcParse *p)
+static void xc_parse_reset(void)
 {
+       BcParse *p = &G.prs;
        if (p->fidx != BC_PROG_MAIN) {
                bc_func_free(p->func);
                bc_func_init(p->func);
 
                p->fidx = BC_PROG_MAIN;
-               p->func = bc_program_func_BC_PROG_MAIN();
+               p->func = xc_program_func_BC_PROG_MAIN();
        }
 
-       p->l.i = p->l.len;
-       p->l.t.t = BC_LEX_EOF;
+       p->lex_inbuf += strlen(p->lex_inbuf);
+       p->lex = XC_LEX_EOF;
 
        IF_BC(bc_vec_pop_all(&p->exits);)
        IF_BC(bc_vec_pop_all(&p->conds);)
        IF_BC(bc_vec_pop_all(&p->ops);)
 
-       bc_program_reset();
+       xc_program_reset();
 }
 
-static void bc_parse_free(BcParse *p)
+static void xc_parse_free(void)
 {
-       IF_BC(bc_vec_free(&p->exits);)
-       IF_BC(bc_vec_free(&p->conds);)
-       IF_BC(bc_vec_free(&p->ops);)
-       bc_lex_free(&p->l);
+       IF_BC(bc_vec_free(&G.prs.exits);)
+       IF_BC(bc_vec_free(&G.prs.conds);)
+       IF_BC(bc_vec_free(&G.prs.ops);)
+       bc_vec_free(&G.prs.lex_strnumbuf);
 }
 
-static void bc_parse_create(BcParse *p, size_t fidx)
+static void xc_parse_create(size_t fidx)
 {
+       BcParse *p = &G.prs;
        memset(p, 0, sizeof(BcParse));
 
-       bc_lex_init(&p->l);
+       bc_char_vec_init(&p->lex_strnumbuf);
        IF_BC(bc_vec_init(&p->exits, sizeof(size_t), NULL);)
        IF_BC(bc_vec_init(&p->conds, sizeof(size_t), NULL);)
        IF_BC(bc_vec_init(&p->ops, sizeof(BcLexType), NULL);)
 
        p->fidx = fidx;
-       p->func = bc_program_func(fidx);
+       p->func = xc_program_func(fidx);
 }
 
-static void bc_program_add_fn(void)
+static void xc_program_add_fn(void)
 {
        //size_t idx;
        BcFunc f;
@@ -3680,84 +3724,72 @@ static size_t bc_program_addFunc(char *name)
        if (!inserted) {
                // There is already a function with this name.
                // It'll be redefined now, clear old definition.
-               BcFunc *func = bc_program_func(entry_ptr->idx);
+               BcFunc *func = xc_program_func(entry_ptr->idx);
                bc_func_free(func);
                bc_func_init(func);
        } else {
-               bc_program_add_fn();
+               xc_program_add_fn();
        }
 
        return idx;
 }
 
-#define BC_PARSE_TOP_OP(p) (*((BcLexType *) bc_vec_top(&(p)->ops)))
-#define BC_PARSE_LEAF(p, rparen)                                \
-       (((p) >= BC_INST_NUM && (p) <= BC_INST_SQRT) || (rparen) || \
-        (p) == BC_INST_INC_POST || (p) == BC_INST_DEC_POST)
-
+#define BC_PARSE_TOP_OP(p) (*(BcLexType*)bc_vec_top(&(p)->ops))
 // 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_TOKEN_2_INST(t) ((char) ((t) - BC_LEX_NEG + BC_INST_NEG))
-
-static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags);
-
-static BC_STATUS zbc_parse_expr(BcParse *p, uint8_t flags)
-{
-       BcStatus s;
+#define BC_TOKEN_2_INST(t) ((char) ((t) - XC_LEX_OP_POWER + XC_INST_POWER))
 
-       s = bc_parse_expr_empty_ok(p, flags);
-       if (s == BC_STATUS_PARSE_EMPTY_EXP)
-               RETURN_STATUS(bc_error("empty expression"));
-       RETURN_STATUS(s);
-}
+static BC_STATUS zbc_parse_expr(uint8_t flags);
 #define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed);
+static BC_STATUS zbc_parse_stmt_possibly_auto(bool auto_allowed);
 #define zbc_parse_stmt_possibly_auto(...) (zbc_parse_stmt_possibly_auto(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_stmt(BcParse *p)
+static BC_STATUS zbc_parse_stmt(void)
 {
-       RETURN_STATUS(zbc_parse_stmt_possibly_auto(p, false));
+       RETURN_STATUS(zbc_parse_stmt_possibly_auto(false));
 }
 #define zbc_parse_stmt(...) (zbc_parse_stmt(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_stmt_allow_NLINE_before(BcParse *p, const char *after_X)
+static BC_STATUS zbc_parse_stmt_allow_NLINE_before(const char *after_X)
 {
+       BcParse *p = &G.prs;
        // "if(cond)<newline>stmt" is accepted too, but not 2+ newlines.
        // Same for "else", "while()", "for()".
-       BcStatus s = zbc_lex_next_and_skip_NLINE(&p->l);
+       BcStatus s = zbc_lex_next_and_skip_NLINE();
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t == BC_LEX_NLINE)
+       if (p->lex == XC_LEX_NLINE)
                RETURN_STATUS(bc_error_fmt("no statement after '%s'", after_X));
 
-       RETURN_STATUS(zbc_parse_stmt(p));
+       RETURN_STATUS(zbc_parse_stmt());
 }
 #define zbc_parse_stmt_allow_NLINE_before(...) (zbc_parse_stmt_allow_NLINE_before(__VA_ARGS__) COMMA_SUCCESS)
 
-static void bc_parse_operator(BcParse *p, BcLexType type, size_t start,
-                                  size_t *nexprs)
+static void bc_parse_operator(BcLexType type, size_t start, size_t *nexprs)
 {
-       char l, r = bc_parse_op_PREC(type - BC_LEX_OP_INC);
-       bool left = bc_parse_op_LEFT(type - BC_LEX_OP_INC);
+       BcParse *p = &G.prs;
+       char l, r = bc_operation_PREC(type - XC_LEX_1st_op);
+       bool left = bc_operation_LEFT(type - XC_LEX_1st_op);
 
        while (p->ops.len > start) {
                BcLexType t = BC_PARSE_TOP_OP(p);
                if (t == BC_LEX_LPAREN) break;
 
-               l = bc_parse_op_PREC(t - BC_LEX_OP_INC);
+               l = bc_operation_PREC(t - XC_LEX_1st_op);
                if (l >= r && (l != r || !left)) break;
 
-               bc_parse_push(p, BC_TOKEN_2_INST(t));
+               xc_parse_push(BC_TOKEN_2_INST(t));
                bc_vec_pop(&p->ops);
-               *nexprs -= (t != BC_LEX_OP_BOOL_NOT && t != BC_LEX_NEG);
+               *nexprs -= (t != BC_LEX_OP_BOOL_NOT && t != XC_LEX_NEG);
        }
 
        bc_vec_push(&p->ops, &type);
 }
 
-static BC_STATUS zbc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs)
+static BC_STATUS zbc_parse_rightParen(size_t ops_bgn, size_t *nexs)
 {
+       BcParse *p = &G.prs;
        BcLexType top;
 
        if (p->ops.len <= ops_bgn)
@@ -3765,10 +3797,10 @@ static BC_STATUS zbc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs)
        top = BC_PARSE_TOP_OP(p);
 
        while (top != BC_LEX_LPAREN) {
-               bc_parse_push(p, BC_TOKEN_2_INST(top));
+               xc_parse_push(BC_TOKEN_2_INST(top));
 
                bc_vec_pop(&p->ops);
-               *nexs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_NEG;
+               *nexs -= (top != BC_LEX_OP_BOOL_NOT && top != XC_LEX_NEG);
 
                if (p->ops.len <= ops_bgn)
                        RETURN_STATUS(bc_error_bad_expression());
@@ -3777,57 +3809,58 @@ static BC_STATUS zbc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs)
 
        bc_vec_pop(&p->ops);
 
-       RETURN_STATUS(zbc_lex_next(&p->l));
+       RETURN_STATUS(BC_STATUS_SUCCESS);
 }
 #define zbc_parse_rightParen(...) (zbc_parse_rightParen(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_params(BcParse *p, uint8_t flags)
+static BC_STATUS zbc_parse_params(uint8_t flags)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        size_t nparams;
 
-       dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
+       dbg_lex("%s:%d p->lex:%d", __func__, __LINE__, p->lex);
        flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY;
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
        nparams = 0;
-       if (p->l.t.t != BC_LEX_RPAREN) {
+       if (p->lex != BC_LEX_RPAREN) {
                for (;;) {
-                       s = zbc_parse_expr(p, flags);
+                       s = zbc_parse_expr(flags);
                        if (s) RETURN_STATUS(s);
                        nparams++;
-                       if (p->l.t.t != BC_LEX_COMMA) {
-                               if (p->l.t.t == BC_LEX_RPAREN)
+                       if (p->lex != BC_LEX_COMMA) {
+                               if (p->lex == BC_LEX_RPAREN)
                                        break;
                                RETURN_STATUS(bc_error_bad_token());
                        }
-                       s = zbc_lex_next(&p->l);
+                       s = zxc_lex_next();
                        if (s) RETURN_STATUS(s);
                }
        }
 
-       bc_parse_push(p, BC_INST_CALL);
-       bc_parse_pushIndex(p, nparams);
+       xc_parse_pushInst_and_Index(BC_INST_CALL, nparams);
 
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
 #define zbc_parse_params(...) (zbc_parse_params(__VA_ARGS__) COMMA_SUCCESS)
 
 // Note: takes ownership of 'name' (must be malloced)
-static BC_STATUS zbc_parse_call(BcParse *p, char *name, uint8_t flags)
+static BC_STATUS zbc_parse_call(char *name, uint8_t flags)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        BcId entry, *entry_ptr;
        size_t idx;
 
        entry.name = name;
 
-       s = zbc_parse_params(p, flags);
+       s = zbc_parse_params(flags);
        if (s) goto err;
 
-       if (p->l.t.t != BC_LEX_RPAREN) {
+       if (p->lex != BC_LEX_RPAREN) {
                s = bc_error_bad_token();
                goto err;
        }
@@ -3842,56 +3875,57 @@ static BC_STATUS zbc_parse_call(BcParse *p, char *name, uint8_t flags)
                free(name);
 
        entry_ptr = bc_vec_item(&G.prog.fn_map, idx);
-       bc_parse_pushIndex(p, entry_ptr->idx);
+       xc_parse_pushIndex(entry_ptr->idx);
 
-       RETURN_STATUS(zbc_lex_next(&p->l));
+       RETURN_STATUS(zxc_lex_next());
  err:
        free(name);
        RETURN_STATUS(s);
 }
 #define zbc_parse_call(...) (zbc_parse_call(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_name(BcParse *p, BcInst *type, uint8_t flags)
+static BC_STATUS zbc_parse_name(BcInst *type, uint8_t flags)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        char *name;
 
-       name = xstrdup(p->l.t.v.v);
-       s = zbc_lex_next(&p->l);
+       name = xstrdup(p->lex_strnumbuf.v);
+       s = zxc_lex_next();
        if (s) goto err;
 
-       if (p->l.t.t == BC_LEX_LBRACKET) {
-               s = zbc_lex_next(&p->l);
+       if (p->lex == BC_LEX_LBRACKET) {
+               s = zxc_lex_next();
                if (s) goto err;
 
-               if (p->l.t.t == BC_LEX_RBRACKET) {
+               if (p->lex == BC_LEX_RBRACKET) {
                        if (!(flags & BC_PARSE_ARRAY)) {
                                s = bc_error_bad_expression();
                                goto err;
                        }
-                       *type = BC_INST_ARRAY;
+                       *type = XC_INST_ARRAY;
                } else {
-                       *type = BC_INST_ARRAY_ELEM;
+                       *type = XC_INST_ARRAY_ELEM;
                        flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
-                       s = zbc_parse_expr(p, flags);
+                       s = zbc_parse_expr(flags);
                        if (s) goto err;
                }
-               s = zbc_lex_next(&p->l);
+               s = zxc_lex_next();
                if (s) goto err;
-               bc_parse_push(p, *type);
-               bc_parse_pushName(p, name);
+               xc_parse_push(*type);
+               xc_parse_pushName(name);
                free(name);
-       } else if (p->l.t.t == BC_LEX_LPAREN) {
+       } else if (p->lex == BC_LEX_LPAREN) {
                if (flags & BC_PARSE_NOCALL) {
                        s = bc_error_bad_token();
                        goto err;
                }
                *type = BC_INST_CALL;
-               s = zbc_parse_call(p, name, flags);
+               s = zbc_parse_call(name, flags);
        } else {
-               *type = BC_INST_VAR;
-               bc_parse_push(p, BC_INST_VAR);
-               bc_parse_pushName(p, name);
+               *type = XC_INST_VAR;
+               xc_parse_push(XC_INST_VAR);
+               xc_parse_pushName(name);
                free(name);
        }
 
@@ -3902,180 +3936,189 @@ static BC_STATUS zbc_parse_name(BcParse *p, BcInst *type, uint8_t flags)
 }
 #define zbc_parse_name(...) (zbc_parse_name(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_read(BcParse *p)
+static BC_STATUS zbc_parse_read(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
+       if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
+       if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
 
-       bc_parse_push(p, BC_INST_READ);
+       xc_parse_push(XC_INST_READ);
 
-       RETURN_STATUS(zbc_lex_next(&p->l));
+       RETURN_STATUS(s);
 }
 #define zbc_parse_read(...) (zbc_parse_read(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_builtin(BcParse *p, BcLexType type, uint8_t flags,
-                                 BcInst *prev)
+static BC_STATUS zbc_parse_builtin(BcLexType type, uint8_t flags, BcInst *prev)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
+       if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
 
        flags = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | BC_PARSE_ARRAY;
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       s = zbc_parse_expr(p, flags);
+       s = zbc_parse_expr(flags);
        if (s) RETURN_STATUS(s);
 
-       if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
+       if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
 
-       *prev = (type == BC_LEX_KEY_LENGTH) ? BC_INST_LENGTH : BC_INST_SQRT;
-       bc_parse_push(p, *prev);
+       *prev = (type == BC_LEX_KEY_LENGTH) ? XC_INST_LENGTH : XC_INST_SQRT;
+       xc_parse_push(*prev);
 
-       RETURN_STATUS(zbc_lex_next(&p->l));
+       RETURN_STATUS(s);
 }
 #define zbc_parse_builtin(...) (zbc_parse_builtin(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_scale(BcParse *p, BcInst *type, uint8_t flags)
+static BC_STATUS zbc_parse_scale(BcInst *type, uint8_t flags)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       if (p->l.t.t != BC_LEX_LPAREN) {
-               *type = BC_INST_SCALE;
-               bc_parse_push(p, BC_INST_SCALE);
+       if (p->lex != BC_LEX_LPAREN) {
+               *type = XC_INST_SCALE;
+               xc_parse_push(XC_INST_SCALE);
                RETURN_STATUS(BC_STATUS_SUCCESS);
        }
 
-       *type = BC_INST_SCALE_FUNC;
+       *type = XC_INST_SCALE_FUNC;
        flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       s = zbc_parse_expr(p, flags);
+       s = zbc_parse_expr(flags);
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_RPAREN)
+       if (p->lex != BC_LEX_RPAREN)
                RETURN_STATUS(bc_error_bad_token());
-       bc_parse_push(p, BC_INST_SCALE_FUNC);
+       xc_parse_push(XC_INST_SCALE_FUNC);
 
-       RETURN_STATUS(zbc_lex_next(&p->l));
+       RETURN_STATUS(zxc_lex_next());
 }
 #define zbc_parse_scale(...) (zbc_parse_scale(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_incdec(BcParse *p, BcInst *prev, bool *paren_expr,
-                                size_t *nexprs, uint8_t flags)
+static BC_STATUS zbc_parse_incdec(BcInst *prev, size_t *nexs, uint8_t flags)
 {
+       BcParse *p = &G.prs;
        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
+       if (etype == XC_INST_VAR || etype == XC_INST_ARRAY_ELEM
+        || etype == XC_INST_SCALE || etype == BC_INST_LAST
+        || etype == XC_INST_IBASE || etype == XC_INST_OBASE
        ) {
-               *prev = inst = BC_INST_INC_POST + (p->l.t.t != BC_LEX_OP_INC);
-               bc_parse_push(p, inst);
-               s = zbc_lex_next(&p->l);
+               *prev = inst = BC_INST_INC_POST + (p->lex != BC_LEX_OP_INC);
+               xc_parse_push(inst);
+               s = zxc_lex_next();
        } else {
-               *prev = inst = BC_INST_INC_PRE + (p->l.t.t != BC_LEX_OP_INC);
-               *paren_expr = true;
+               *prev = inst = BC_INST_INC_PRE + (p->lex != BC_LEX_OP_INC);
 
-               s = zbc_lex_next(&p->l);
+               s = zxc_lex_next();
                if (s) RETURN_STATUS(s);
-               type = p->l.t.t;
+               type = p->lex;
 
                // Because we parse the next part of the expression
                // right here, we need to increment this.
-               *nexprs = *nexprs + 1;
+               *nexs = *nexs + 1;
 
                switch (type) {
-                       case BC_LEX_NAME:
-                               s = zbc_parse_name(p, prev, flags | BC_PARSE_NOCALL);
-                               break;
-                       case BC_LEX_KEY_IBASE:
-                       case BC_LEX_KEY_LAST:
-                       case BC_LEX_KEY_OBASE:
-                               bc_parse_push(p, type - BC_LEX_KEY_IBASE + BC_INST_IBASE);
-                               s = zbc_lex_next(&p->l);
-                               break;
-                       case BC_LEX_KEY_SCALE:
-                               s = zbc_lex_next(&p->l);
-                               if (s) RETURN_STATUS(s);
-                               if (p->l.t.t == BC_LEX_LPAREN)
-                                       s = bc_error_bad_token();
-                               else
-                                       bc_parse_push(p, BC_INST_SCALE);
-                               break;
-                       default:
+               case XC_LEX_NAME:
+                       s = zbc_parse_name(prev, flags | BC_PARSE_NOCALL);
+                       break;
+               case BC_LEX_KEY_IBASE:
+               case BC_LEX_KEY_LAST:
+               case BC_LEX_KEY_OBASE:
+                       xc_parse_push(type - BC_LEX_KEY_IBASE + XC_INST_IBASE);
+                       s = zxc_lex_next();
+                       break;
+               case BC_LEX_KEY_SCALE:
+                       s = zxc_lex_next();
+                       if (s) RETURN_STATUS(s);
+                       if (p->lex == BC_LEX_LPAREN)
                                s = bc_error_bad_token();
-                               break;
+                       else
+                               xc_parse_push(XC_INST_SCALE);
+                       break;
+               default:
+                       s = bc_error_bad_token();
+                       break;
                }
 
-               if (!s) bc_parse_push(p, inst);
+               if (!s) xc_parse_push(inst);
        }
 
        RETURN_STATUS(s);
 }
 #define zbc_parse_incdec(...) (zbc_parse_incdec(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_minus(BcParse *p, BcInst *prev, size_t ops_bgn,
-                               bool rparen, size_t *nexprs)
+static int bc_parse_inst_isLeaf(BcInst p)
+{
+       return (p >= XC_INST_NUM && p <= XC_INST_SQRT)
+               || p == BC_INST_INC_POST
+               || p == BC_INST_DEC_POST
+               ;
+}
+#define BC_PARSE_LEAF(prev, bin_last, rparen) \
+       (!(bin_last) && ((rparen) || bc_parse_inst_isLeaf(prev)))
+
+static BC_STATUS zbc_parse_minus(BcInst *prev, size_t ops_bgn,
+                               bool rparen, bool bin_last, size_t *nexprs)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        BcLexType type;
-       BcInst etype = *prev;
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(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;
+       type = BC_PARSE_LEAF(*prev, bin_last, rparen) ? XC_LEX_OP_MINUS : XC_LEX_NEG;
        *prev = BC_TOKEN_2_INST(type);
 
        // We can just push onto the op stack because this is the largest
        // precedence operator that gets pushed. Inc/dec does not.
-       if (type != BC_LEX_OP_MINUS)
+       if (type != XC_LEX_OP_MINUS)
                bc_vec_push(&p->ops, &type);
        else
-               bc_parse_operator(p, type, ops_bgn, nexprs);
+               bc_parse_operator(type, ops_bgn, nexprs);
 
        RETURN_STATUS(s);
 }
 #define zbc_parse_minus(...) (zbc_parse_minus(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_print(BcParse *p)
+static BC_STATUS zbc_parse_print(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        BcLexType type;
 
        for (;;) {
-               s = zbc_lex_next(&p->l);
+               s = zxc_lex_next();
                if (s) RETURN_STATUS(s);
-               type = p->l.t.t;
-               if (type == BC_LEX_STR) {
-                       s = zbc_parse_pushSTR(p);
+               type = p->lex;
+               if (type == XC_LEX_STR) {
+                       s = zbc_parse_pushSTR();
                } else {
-                       s = zbc_parse_expr(p, 0);
+                       s = zbc_parse_expr(0);
                }
                if (s) RETURN_STATUS(s);
-               bc_parse_push(p, BC_INST_PRINT_POP);
-               if (p->l.t.t != BC_LEX_COMMA)
+               xc_parse_push(XC_INST_PRINT_POP);
+               if (p->lex != BC_LEX_COMMA)
                        break;
        }
 
@@ -4083,33 +4126,32 @@ static BC_STATUS zbc_parse_print(BcParse *p)
 }
 #define zbc_parse_print(...) (zbc_parse_print(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_return(BcParse *p)
+static BC_STATUS zbc_parse_return(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        BcLexType t;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       t = p->l.t.t;
-       if (t == BC_LEX_NLINE || t == BC_LEX_SCOLON)
-               bc_parse_push(p, BC_INST_RET0);
+       t = p->lex;
+       if (t == XC_LEX_NLINE || t == BC_LEX_SCOLON || t == BC_LEX_RBRACE)
+               xc_parse_push(BC_INST_RET0);
        else {
-               bool paren = (t == BC_LEX_LPAREN);
-               s = bc_parse_expr_empty_ok(p, 0);
-               if (s == BC_STATUS_PARSE_EMPTY_EXP) {
-                       bc_parse_push(p, BC_INST_RET0);
-                       s = zbc_lex_next(&p->l);
-               }
+//TODO: if (p->func->voidfunc) ERROR
+               s = zbc_parse_expr(0);
                if (s) RETURN_STATUS(s);
 
-               if (!paren || p->l.t.last != BC_LEX_RPAREN) {
-                       s = bc_POSIX_requires("parentheses around return expressions");
-                       IF_ERROR_RETURN_POSSIBLE(if (s) RETURN_STATUS(s));
+               if (t != BC_LEX_LPAREN   // "return EXPR", no ()
+                || p->lex_last != BC_LEX_RPAREN  // example: "return (a) + b"
+               ) {
+                       s = zbc_POSIX_requires("parentheses around return expressions");
+                       if (s) RETURN_STATUS(s);
                }
 
-               bc_parse_push(p, BC_INST_RET);
+               xc_parse_push(XC_INST_RET);
        }
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
@@ -4117,73 +4159,76 @@ static BC_STATUS zbc_parse_return(BcParse *p)
 }
 #define zbc_parse_return(...) (zbc_parse_return(__VA_ARGS__) COMMA_SUCCESS)
 
-static void rewrite_label_to_current(BcParse *p, size_t idx)
+static void rewrite_label_to_current(size_t idx)
 {
+       BcParse *p = &G.prs;
        size_t *label = bc_vec_item(&p->func->labels, idx);
        *label = p->func->code.len;
 }
 
-static BC_STATUS zbc_parse_if(BcParse *p)
+static BC_STATUS zbc_parse_if(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        size_t ip_idx;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
+       if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       s = zbc_parse_expr(p, BC_PARSE_REL);
+       s = zbc_parse_expr(BC_PARSE_REL);
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
+       if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
 
        // Encode "if zero, jump to ..."
        // Pushed value (destination of the jump) is uninitialized,
        // will be rewritten to be address of "end of if()" or of "else".
        ip_idx = bc_vec_push(&p->func->labels, &ip_idx);
-       bc_parse_pushJUMP_ZERO(p, ip_idx);
+       bc_parse_pushJUMP_ZERO(ip_idx);
 
-       s = zbc_parse_stmt_allow_NLINE_before(p, STRING_if);
+       s = zbc_parse_stmt_allow_NLINE_before(STRING_if);
        if (s) RETURN_STATUS(s);
 
-       dbg_lex("%s:%d in if after stmt: p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
-       if (p->l.t.t == BC_LEX_KEY_ELSE) {
+       dbg_lex("%s:%d in if after stmt: p->lex:%d", __func__, __LINE__, p->lex);
+       if (p->lex == BC_LEX_KEY_ELSE) {
                size_t ip2_idx;
 
                // Encode "after then_stmt, jump to end of if()"
                ip2_idx = bc_vec_push(&p->func->labels, &ip2_idx);
                dbg_lex("%s:%d after if() then_stmt: BC_INST_JUMP to %zd", __func__, __LINE__, ip2_idx);
-               bc_parse_pushJUMP(p, ip2_idx);
+               bc_parse_pushJUMP(ip2_idx);
 
                dbg_lex("%s:%d rewriting 'if_zero' label to jump to 'else'-> %zd", __func__, __LINE__, p->func->code.len);
-               rewrite_label_to_current(p, ip_idx);
+               rewrite_label_to_current(ip_idx);
 
                ip_idx = ip2_idx;
 
-               s = zbc_parse_stmt_allow_NLINE_before(p, STRING_else);
+               s = zbc_parse_stmt_allow_NLINE_before(STRING_else);
                if (s) RETURN_STATUS(s);
        }
 
        dbg_lex("%s:%d rewriting label to jump after 'if' body-> %zd", __func__, __LINE__, p->func->code.len);
-       rewrite_label_to_current(p, ip_idx);
+       rewrite_label_to_current(ip_idx);
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
        RETURN_STATUS(s);
 }
 #define zbc_parse_if(...) (zbc_parse_if(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_while(BcParse *p)
+static BC_STATUS zbc_parse_while(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        size_t cond_idx;
        size_t ip_idx;
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
-       s = zbc_lex_next(&p->l);
+       if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
        cond_idx = bc_vec_push(&p->func->labels, &p->func->code.len);
@@ -4193,20 +4238,20 @@ static BC_STATUS zbc_parse_while(BcParse *p)
        bc_vec_push(&p->exits, &ip_idx);
        bc_vec_push(&p->func->labels, &ip_idx);
 
-       s = zbc_parse_expr(p, BC_PARSE_REL);
+       s = zbc_parse_expr(BC_PARSE_REL);
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
+       if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
 
-       bc_parse_pushJUMP_ZERO(p, ip_idx);
+       bc_parse_pushJUMP_ZERO(ip_idx);
 
-       s = zbc_parse_stmt_allow_NLINE_before(p, STRING_while);
+       s = zbc_parse_stmt_allow_NLINE_before(STRING_while);
        if (s) RETURN_STATUS(s);
 
        dbg_lex("%s:%d BC_INST_JUMP to %zd", __func__, __LINE__, cond_idx);
-       bc_parse_pushJUMP(p, cond_idx);
+       bc_parse_pushJUMP(cond_idx);
 
        dbg_lex("%s:%d rewriting label-> %zd", __func__, __LINE__, p->func->code.len);
-       rewrite_label_to_current(p, ip_idx);
+       rewrite_label_to_current(ip_idx);
 
        bc_vec_pop(&p->exits);
        bc_vec_pop(&p->conds);
@@ -4215,29 +4260,30 @@ static BC_STATUS zbc_parse_while(BcParse *p)
 }
 #define zbc_parse_while(...) (zbc_parse_while(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_for(BcParse *p)
+static BC_STATUS zbc_parse_for(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        size_t cond_idx, exit_idx, body_idx, update_idx;
 
-       dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
-       s = zbc_lex_next(&p->l);
+       dbg_lex("%s:%d p->lex:%d", __func__, __LINE__, p->lex);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
-       s = zbc_lex_next(&p->l);
+       if (p->lex != BC_LEX_LPAREN) RETURN_STATUS(bc_error_bad_token());
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       if (p->l.t.t != BC_LEX_SCOLON) {
-               s = zbc_parse_expr(p, 0);
-               bc_parse_push(p, BC_INST_POP);
+       if (p->lex != BC_LEX_SCOLON) {
+               s = zbc_parse_expr(0);
+               xc_parse_push(XC_INST_POP);
                if (s) RETURN_STATUS(s);
        } else {
-               s = bc_POSIX_does_not_allow_empty_X_expression_in_for("init");
-               IF_ERROR_RETURN_POSSIBLE(if (s) RETURN_STATUS(s);)
+               s = zbc_POSIX_does_not_allow_empty_X_expression_in_for("init");
+               if (s) RETURN_STATUS(s);
        }
 
-       if (p->l.t.t != BC_LEX_SCOLON) RETURN_STATUS(bc_error_bad_token());
-       s = zbc_lex_next(&p->l);
+       if (p->lex != BC_LEX_SCOLON) RETURN_STATUS(bc_error_bad_token());
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
        cond_idx = bc_vec_push(&p->func->labels, &p->func->code.len);
@@ -4245,53 +4291,53 @@ static BC_STATUS zbc_parse_for(BcParse *p)
        body_idx = update_idx + 1;
        exit_idx = body_idx + 1;
 
-       if (p->l.t.t != BC_LEX_SCOLON)
-               s = zbc_parse_expr(p, BC_PARSE_REL);
+       if (p->lex != BC_LEX_SCOLON)
+               s = zbc_parse_expr(BC_PARSE_REL);
        else {
-               // Set this for the next call to bc_parse_pushNUM().
+               // Set this for the next call to xc_parse_pushNUM().
                // This is safe to set because the current token is a semicolon,
                // which has no string requirement.
-               bc_vec_string(&p->l.t.v, 1, "1");
-               bc_parse_pushNUM(p);
-               s = bc_POSIX_does_not_allow_empty_X_expression_in_for("condition");
+               bc_vec_string(&p->lex_strnumbuf, 1, "1");
+               xc_parse_pushNUM();
+               s = zbc_POSIX_does_not_allow_empty_X_expression_in_for("condition");
        }
        if (s) RETURN_STATUS(s);
 
-       if (p->l.t.t != BC_LEX_SCOLON) RETURN_STATUS(bc_error_bad_token());
+       if (p->lex != BC_LEX_SCOLON) RETURN_STATUS(bc_error_bad_token());
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       bc_parse_pushJUMP_ZERO(p, exit_idx);
-       bc_parse_pushJUMP(p, body_idx);
+       bc_parse_pushJUMP_ZERO(exit_idx);
+       bc_parse_pushJUMP(body_idx);
 
        bc_vec_push(&p->conds, &update_idx);
        bc_vec_push(&p->func->labels, &p->func->code.len);
 
-       if (p->l.t.t != BC_LEX_RPAREN) {
-               s = zbc_parse_expr(p, 0);
+       if (p->lex != BC_LEX_RPAREN) {
+               s = zbc_parse_expr(0);
                if (s) RETURN_STATUS(s);
-               if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
-               bc_parse_push(p, BC_INST_POP);
+               if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
+               xc_parse_push(XC_INST_POP);
        } else {
-               s = bc_POSIX_does_not_allow_empty_X_expression_in_for("update");
-               IF_ERROR_RETURN_POSSIBLE(if (s) RETURN_STATUS(s);)
+               s = zbc_POSIX_does_not_allow_empty_X_expression_in_for("update");
+               if (s) RETURN_STATUS(s);
        }
 
-       bc_parse_pushJUMP(p, cond_idx);
+       bc_parse_pushJUMP(cond_idx);
        bc_vec_push(&p->func->labels, &p->func->code.len);
 
        bc_vec_push(&p->exits, &exit_idx);
        bc_vec_push(&p->func->labels, &exit_idx);
 
-       s = zbc_parse_stmt_allow_NLINE_before(p, STRING_for);
+       s = zbc_parse_stmt_allow_NLINE_before(STRING_for);
        if (s) RETURN_STATUS(s);
 
        dbg_lex("%s:%d BC_INST_JUMP to %zd", __func__, __LINE__, update_idx);
-       bc_parse_pushJUMP(p, update_idx);
+       bc_parse_pushJUMP(update_idx);
 
        dbg_lex("%s:%d rewriting label-> %zd", __func__, __LINE__, p->func->code.len);
-       rewrite_label_to_current(p, exit_idx);
+       rewrite_label_to_current(exit_idx);
 
        bc_vec_pop(&p->exits);
        bc_vec_pop(&p->conds);
@@ -4300,9 +4346,9 @@ static BC_STATUS zbc_parse_for(BcParse *p)
 }
 #define zbc_parse_for(...) (zbc_parse_for(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_break_or_continue(BcParse *p, BcLexType type)
+static BC_STATUS zbc_parse_break_or_continue(BcLexType type)
 {
-       BcStatus s;
+       BcParse *p = &G.prs;
        size_t i;
 
        if (type == BC_LEX_KEY_BREAK) {
@@ -4312,102 +4358,152 @@ static BC_STATUS zbc_parse_break_or_continue(BcParse *p, BcLexType type)
        } else {
                i = *(size_t*)bc_vec_top(&p->conds);
        }
+       bc_parse_pushJUMP(i);
+
+       RETURN_STATUS(zxc_lex_next());
+}
+#define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__) COMMA_SUCCESS)
 
-       bc_parse_pushJUMP(p, i);
+static BC_STATUS zbc_func_insert(BcFunc *f, char *name, BcType type)
+{
+       BcId *autoid;
+       BcId a;
+       size_t i;
 
-       s = zbc_lex_next(&p->l);
-       if (s) RETURN_STATUS(s);
+       autoid = (void*)f->autos.v;
+       for (i = 0; i < f->autos.len; i++, autoid++) {
+               if (strcmp(name, autoid->name) == 0
+                && type == (BcType) autoid->idx
+               ) {
+                       RETURN_STATUS(bc_error("duplicate function parameter or auto name"));
+               }
+       }
 
-       if (p->l.t.t != BC_LEX_SCOLON && p->l.t.t != BC_LEX_NLINE)
-               RETURN_STATUS(bc_error_bad_token());
+       a.idx = type;
+       a.name = name;
 
-       RETURN_STATUS(zbc_lex_next(&p->l));
+       bc_vec_push(&f->autos, &a);
+
+       RETURN_STATUS(BC_STATUS_SUCCESS);
 }
-#define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_func_insert(...) (zbc_func_insert(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_funcdef(BcParse *p)
+static BC_STATUS zbc_parse_funcdef(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
-       bool var, comma = false;
+       bool comma, voidfunc;
        char *name;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
+       if (s) RETURN_STATUS(s);
+       if (p->lex != XC_LEX_NAME)
+               RETURN_STATUS(bc_error_bad_function_definition());
+
+       // To be maximally both POSIX and GNU-compatible,
+       // "void" is not treated as a normal keyword:
+       // you can have variable named "void", and even a function
+       // named "void": "define void() { return 6; }" is ok.
+       // _Only_ "define void f() ..." syntax treats "void"
+       // specially.
+       voidfunc = (strcmp(p->lex_strnumbuf.v, "void") == 0);
+
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_NAME)
-               RETURN_STATUS(bc_error("bad function definition"));
 
-       name = xstrdup(p->l.t.v.v);
-       p->fidx = bc_program_addFunc(name);
-       p->func = bc_program_func(p->fidx);
+       voidfunc = (voidfunc && p->lex == XC_LEX_NAME);
+       if (voidfunc) {
+               s = zxc_lex_next();
+               if (s) RETURN_STATUS(s);
+       }
+
+       if (p->lex != BC_LEX_LPAREN)
+               RETURN_STATUS(bc_error_bad_function_definition());
 
-       s = zbc_lex_next(&p->l);
-       if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_LPAREN)
-               RETURN_STATUS(bc_error("bad function definition"));
-       s = zbc_lex_next(&p->l);
+       p->fidx = bc_program_addFunc(xstrdup(p->lex_strnumbuf.v));
+       p->func = xc_program_func(p->fidx);
+       p->func->voidfunc = voidfunc;
+
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       while (p->l.t.t != BC_LEX_RPAREN) {
-               if (p->l.t.t != BC_LEX_NAME)
-                       RETURN_STATUS(bc_error("bad function definition"));
+       comma = false;
+       while (p->lex != BC_LEX_RPAREN) {
+               BcType t = BC_TYPE_VAR;
+
+               if (p->lex == XC_LEX_OP_MULTIPLY) {
+                       t = BC_TYPE_REF;
+                       s = zxc_lex_next();
+                       if (s) RETURN_STATUS(s);
+                       s = zbc_POSIX_does_not_allow("references");
+                       if (s) RETURN_STATUS(s);
+               }
+
+               if (p->lex != XC_LEX_NAME)
+                       RETURN_STATUS(bc_error_bad_function_definition());
 
                ++p->func->nparams;
 
-               name = xstrdup(p->l.t.v.v);
-               s = zbc_lex_next(&p->l);
+               name = xstrdup(p->lex_strnumbuf.v);
+               s = zxc_lex_next();
                if (s) goto err;
 
-               var = p->l.t.t != BC_LEX_LBRACKET;
-
-               if (!var) {
-                       s = zbc_lex_next(&p->l);
+               if (p->lex == BC_LEX_LBRACKET) {
+                       if (t == BC_TYPE_VAR) t = BC_TYPE_ARRAY;
+                       s = zxc_lex_next();
                        if (s) goto err;
 
-                       if (p->l.t.t != BC_LEX_RBRACKET) {
-                               s = bc_error("bad function definition");
+                       if (p->lex != BC_LEX_RBRACKET) {
+                               s = bc_error_bad_function_definition();
                                goto err;
                        }
 
-                       s = zbc_lex_next(&p->l);
+                       s = zxc_lex_next();
                        if (s) goto err;
                }
+               else if (t == BC_TYPE_REF) {
+                       s = bc_error_at("vars can't be references");
+                       goto err;
+               }
 
-               comma = p->l.t.t == BC_LEX_COMMA;
+               comma = p->lex == BC_LEX_COMMA;
                if (comma) {
-                       s = zbc_lex_next(&p->l);
+                       s = zxc_lex_next();
                        if (s) goto err;
                }
 
-               s = zbc_func_insert(p->func, name, var);
+               s = zbc_func_insert(p->func, name, t);
                if (s) goto err;
        }
 
-       if (comma) RETURN_STATUS(bc_error("bad function definition"));
+       if (comma) RETURN_STATUS(bc_error_bad_function_definition());
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       if (p->l.t.t != BC_LEX_LBRACE)
-               s = bc_POSIX_requires("the left brace be on the same line as the function header");
+       if (p->lex != BC_LEX_LBRACE) {
+               s = zbc_POSIX_requires("the left brace be on the same line as the function header");
+               if (s) RETURN_STATUS(s);
+       }
 
        // Prevent "define z()<newline>" from being interpreted as function with empty stmt as body
-       s = zbc_lex_skip_if_at_NLINE(&p->l);
+       s = zbc_lex_skip_if_at_NLINE();
        if (s) RETURN_STATUS(s);
-       //GNU bc requires a {} block even if function body has single stmt, enforce this?
-       if (p->l.t.t != BC_LEX_LBRACE)
+       // GNU bc requires a {} block even if function body has single stmt, enforce this
+       if (p->lex != BC_LEX_LBRACE)
                RETURN_STATUS(bc_error("function { body } expected"));
 
        p->in_funcdef++; // to determine whether "return" stmt is allowed, and such
-       s = zbc_parse_stmt_possibly_auto(p, true);
+       s = zbc_parse_stmt_possibly_auto(true);
        p->in_funcdef--;
        if (s) RETURN_STATUS(s);
 
-       bc_parse_push(p, BC_INST_RET0);
+       xc_parse_push(BC_INST_RET0);
 
        // Subsequent code generation is into main program
        p->fidx = BC_PROG_MAIN;
-       p->func = bc_program_func_BC_PROG_MAIN();
+       p->func = xc_program_func_BC_PROG_MAIN();
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
        RETURN_STATUS(s);
@@ -4418,56 +4514,57 @@ static BC_STATUS zbc_parse_funcdef(BcParse *p)
 }
 #define zbc_parse_funcdef(...) (zbc_parse_funcdef(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_auto(BcParse *p)
+static BC_STATUS zbc_parse_auto(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
-       bool comma, var, one;
        char *name;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       comma = false;
-       one = p->l.t.t == BC_LEX_NAME;
+       for (;;) {
+               BcType t;
 
-       while (p->l.t.t == BC_LEX_NAME) {
-               name = xstrdup(p->l.t.v.v);
-               s = zbc_lex_next(&p->l);
+               if (p->lex != XC_LEX_NAME)
+                       RETURN_STATUS(bc_error_at("bad 'auto' syntax"));
+
+               name = xstrdup(p->lex_strnumbuf.v);
+               s = zxc_lex_next();
                if (s) goto err;
 
-               var = p->l.t.t != BC_LEX_LBRACKET;
-               if (!var) {
-                       s = zbc_lex_next(&p->l);
+               t = BC_TYPE_VAR;
+               if (p->lex == BC_LEX_LBRACKET) {
+                       t = BC_TYPE_ARRAY;
+                       s = zxc_lex_next();
                        if (s) goto err;
 
-                       if (p->l.t.t != BC_LEX_RBRACKET) {
-                               s = bc_error("bad function definition");
+                       if (p->lex != BC_LEX_RBRACKET) {
+                               s = bc_error_at("bad 'auto' syntax");
                                goto err;
                        }
-
-                       s = zbc_lex_next(&p->l);
-                       if (s) goto err;
-               }
-
-               comma = p->l.t.t == BC_LEX_COMMA;
-               if (comma) {
-                       s = zbc_lex_next(&p->l);
+                       s = zxc_lex_next();
                        if (s) goto err;
                }
 
-               s = zbc_func_insert(p->func, name, var);
+               s = zbc_func_insert(p->func, name, t);
                if (s) goto err;
-       }
-
-       if (comma) RETURN_STATUS(bc_error("bad function definition"));
-       if (!one) RETURN_STATUS(bc_error("no auto variable found"));
 
-       if (p->l.t.t != BC_LEX_NLINE && p->l.t.t != BC_LEX_SCOLON)
-               RETURN_STATUS(bc_error_bad_token());
+               if (p->lex == XC_LEX_NLINE
+                || p->lex == BC_LEX_SCOLON
+               //|| p->lex == BC_LEX_RBRACE // allow "define f() {auto a}"
+               ) {
+                       break;
+               }
+               if (p->lex != BC_LEX_COMMA)
+                       RETURN_STATUS(bc_error_at("bad 'auto' syntax"));
+               s = zxc_lex_next(); // skip comma
+               if (s) RETURN_STATUS(s);
+       }
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
-       RETURN_STATUS(zbc_lex_next(&p->l));
+       RETURN_STATUS(BC_STATUS_SUCCESS);
  err:
        free(name);
        dbg_lex_done("%s:%d done (ERROR)", __func__, __LINE__);
@@ -4476,112 +4573,123 @@ static BC_STATUS zbc_parse_auto(BcParse *p)
 #define zbc_parse_auto(...) (zbc_parse_auto(__VA_ARGS__) COMMA_SUCCESS)
 
 #undef zbc_parse_stmt_possibly_auto
-static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed)
+static BC_STATUS zbc_parse_stmt_possibly_auto(bool auto_allowed)
 {
+       BcParse *p = &G.prs;
        BcStatus s = BC_STATUS_SUCCESS;
 
-       dbg_lex_enter("%s:%d entered, p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
+       dbg_lex_enter("%s:%d entered, p->lex:%d", __func__, __LINE__, p->lex);
 
-       if (p->l.t.t == BC_LEX_NLINE) {
-               dbg_lex_done("%s:%d done (seen BC_LEX_NLINE)", __func__, __LINE__);
-               RETURN_STATUS(zbc_lex_next(&p->l));
+       if (p->lex == XC_LEX_NLINE) {
+               dbg_lex_done("%s:%d done (seen XC_LEX_NLINE)", __func__, __LINE__);
+               RETURN_STATUS(s);
        }
-       if (p->l.t.t == BC_LEX_SCOLON) {
+       if (p->lex == BC_LEX_SCOLON) {
                dbg_lex_done("%s:%d done (seen BC_LEX_SCOLON)", __func__, __LINE__);
-               RETURN_STATUS(zbc_lex_next(&p->l));
+               RETURN_STATUS(s);
        }
 
-       if (p->l.t.t == BC_LEX_LBRACE) {
+       if (p->lex == BC_LEX_LBRACE) {
                dbg_lex("%s:%d BC_LEX_LBRACE: (auto_allowed:%d)", __func__, __LINE__, auto_allowed);
                do {
-                       s = zbc_lex_next(&p->l);
+                       s = zxc_lex_next();
                        if (s) RETURN_STATUS(s);
-               } while (p->l.t.t == BC_LEX_NLINE);
-               if (auto_allowed && p->l.t.t == BC_LEX_KEY_AUTO) {
+               } while (p->lex == XC_LEX_NLINE);
+               if (auto_allowed && p->lex == BC_LEX_KEY_AUTO) {
                        dbg_lex("%s:%d calling zbc_parse_auto()", __func__, __LINE__);
-                       s = zbc_parse_auto(p);
+                       s = zbc_parse_auto();
                        if (s) RETURN_STATUS(s);
                }
-               while (p->l.t.t != BC_LEX_RBRACE) {
+               while (p->lex != BC_LEX_RBRACE) {
                        dbg_lex("%s:%d block parsing loop", __func__, __LINE__);
-                       s = zbc_parse_stmt(p);
+                       s = zbc_parse_stmt();
+                       if (s) RETURN_STATUS(s);
+                       // Check that next token is a correct stmt delimiter -
+                       // disallows "print 1 print 2" and such.
+                       if (p->lex == BC_LEX_RBRACE)
+                               break;
+                       if (p->lex != BC_LEX_SCOLON
+                        && p->lex != XC_LEX_NLINE
+                       ) {
+                               RETURN_STATUS(bc_error_at("bad statement terminator"));
+                       }
+                       s = zxc_lex_next();
                        if (s) RETURN_STATUS(s);
                }
-               s = zbc_lex_next(&p->l);
+               s = zxc_lex_next();
                dbg_lex_done("%s:%d done (seen BC_LEX_RBRACE)", __func__, __LINE__);
                RETURN_STATUS(s);
        }
 
-       dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
-       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 = zbc_parse_expr(p, BC_PARSE_PRINT);
-                       break;
-               case BC_LEX_STR:
-                       s = zbc_parse_pushSTR(p);
-                       bc_parse_push(p, BC_INST_PRINT_STR);
-                       break;
-               case BC_LEX_KEY_BREAK:
-               case BC_LEX_KEY_CONTINUE:
-                       s = zbc_parse_break_or_continue(p, p->l.t.t);
-                       break;
-               case BC_LEX_KEY_FOR:
-                       s = zbc_parse_for(p);
-                       break;
-               case BC_LEX_KEY_HALT:
-                       bc_parse_push(p, BC_INST_HALT);
-                       s = zbc_lex_next(&p->l);
-                       break;
-               case BC_LEX_KEY_IF:
-                       s = zbc_parse_if(p);
-                       break;
-               case BC_LEX_KEY_LIMITS:
-                       // "limits" is a compile-time command,
-                       // the output is produced at _parse time_.
-                       printf(
-                               "BC_BASE_MAX     = "BC_MAX_OBASE_STR "\n"
-                               "BC_DIM_MAX      = "BC_MAX_DIM_STR   "\n"
-                               "BC_SCALE_MAX    = "BC_MAX_SCALE_STR "\n"
-                               "BC_STRING_MAX   = "BC_MAX_STRING_STR"\n"
-                               "BC_NAME_MAX     = "BC_MAX_NAME_STR  "\n"
-                               "BC_NUM_MAX      = "BC_MAX_NUM_STR   "\n"
-                               "MAX Exponent    = "BC_MAX_EXP_STR   "\n"
-                               "Number of vars  = "BC_MAX_VARS_STR  "\n"
-                       );
-                       s = zbc_lex_next(&p->l);
-                       break;
-               case BC_LEX_KEY_PRINT:
-                       s = zbc_parse_print(p);
-                       break;
-               case BC_LEX_KEY_QUIT:
-                       // "quit" is a compile-time command. For example,
-                       // "if (0 == 1) quit" terminates when parsing the statement,
-                       // not when it is executed
-                       QUIT_OR_RETURN_TO_MAIN;
-               case BC_LEX_KEY_RETURN:
-                       if (!p->in_funcdef)
-                               RETURN_STATUS(bc_error("'return' not in a function"));
-                       s = zbc_parse_return(p);
-                       break;
-               case BC_LEX_KEY_WHILE:
-                       s = zbc_parse_while(p);
-                       break;
-               default:
-                       s = bc_error_bad_token();
-                       break;
+       dbg_lex("%s:%d p->lex:%d", __func__, __LINE__, p->lex);
+       switch (p->lex) {
+       case XC_LEX_OP_MINUS:
+       case BC_LEX_OP_INC:
+       case BC_LEX_OP_DEC:
+       case BC_LEX_OP_BOOL_NOT:
+       case BC_LEX_LPAREN:
+       case XC_LEX_NAME:
+       case XC_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 = zbc_parse_expr(BC_PARSE_PRINT);
+               break;
+       case XC_LEX_STR:
+               s = zbc_parse_pushSTR();
+               xc_parse_push(XC_INST_PRINT_STR);
+               break;
+       case BC_LEX_KEY_BREAK:
+       case BC_LEX_KEY_CONTINUE:
+               s = zbc_parse_break_or_continue(p->lex);
+               break;
+       case BC_LEX_KEY_FOR:
+               s = zbc_parse_for();
+               break;
+       case BC_LEX_KEY_HALT:
+               xc_parse_push(BC_INST_HALT);
+               s = zxc_lex_next();
+               break;
+       case BC_LEX_KEY_IF:
+               s = zbc_parse_if();
+               break;
+       case BC_LEX_KEY_LIMITS:
+               // "limits" is a compile-time command,
+               // the output is produced at _parse time_.
+               printf(
+                       "BC_BASE_MAX     = "BC_MAX_OBASE_STR "\n"
+                       "BC_DIM_MAX      = "BC_MAX_DIM_STR   "\n"
+                       "BC_SCALE_MAX    = "BC_MAX_SCALE_STR "\n"
+                       "BC_STRING_MAX   = "BC_MAX_STRING_STR"\n"
+               //      "BC_NUM_MAX      = "BC_MAX_NUM_STR   "\n" - GNU bc does not show this
+                       "MAX Exponent    = "BC_MAX_EXP_STR   "\n"
+                       "Number of vars  = "BC_MAX_VARS_STR  "\n"
+               );
+               s = zxc_lex_next();
+               break;
+       case BC_LEX_KEY_PRINT:
+               s = zbc_parse_print();
+               break;
+       case BC_LEX_KEY_QUIT:
+               // "quit" is a compile-time command. For example,
+               // "if (0 == 1) quit" terminates when parsing the statement,
+               // not when it is executed
+               QUIT_OR_RETURN_TO_MAIN;
+       case BC_LEX_KEY_RETURN:
+               if (!p->in_funcdef)
+                       RETURN_STATUS(bc_error("'return' not in a function"));
+               s = zbc_parse_return();
+               break;
+       case BC_LEX_KEY_WHILE:
+               s = zbc_parse_while();
+               break;
+       default:
+               s = bc_error_bad_token();
+               break;
        }
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
@@ -4589,19 +4697,22 @@ static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed)
 }
 #define zbc_parse_stmt_possibly_auto(...) (zbc_parse_stmt_possibly_auto(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p)
+static BC_STATUS zbc_parse_stmt_or_funcdef(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
-       if (p->l.t.t == BC_LEX_EOF)
-               s = bc_error("end of file");
-       else if (p->l.t.t == BC_LEX_KEY_DEFINE) {
-               dbg_lex("%s:%d p->l.t.t:BC_LEX_KEY_DEFINE", __func__, __LINE__);
-               s = zbc_parse_funcdef(p);
+//why?
+//     if (p->lex == XC_LEX_EOF)
+//             s = bc_error("end of file");
+//     else
+       if (p->lex == BC_LEX_KEY_DEFINE) {
+               dbg_lex("%s:%d p->lex:BC_LEX_KEY_DEFINE", __func__, __LINE__);
+               s = zbc_parse_funcdef();
        } else {
-               dbg_lex("%s:%d p->l.t.t:%d (not BC_LEX_KEY_DEFINE)", __func__, __LINE__, p->l.t.t);
-               s = zbc_parse_stmt(p);
+               dbg_lex("%s:%d p->lex:%d (not BC_LEX_KEY_DEFINE)", __func__, __LINE__, p->lex);
+               s = zbc_parse_stmt();
        }
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
@@ -4609,291 +4720,321 @@ static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p)
 }
 #define zbc_parse_stmt_or_funcdef(...) (zbc_parse_stmt_or_funcdef(__VA_ARGS__) COMMA_SUCCESS)
 
-// This is not a "z" function: can also return BC_STATUS_PARSE_EMPTY_EXP
-static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags)
+#undef zbc_parse_expr
+static BC_STATUS zbc_parse_expr(uint8_t flags)
 {
-       BcStatus s = BC_STATUS_SUCCESS;
-       BcInst prev = BC_INST_PRINT;
-       BcLexType top, t = p->l.t.t;
+       BcParse *p = &G.prs;
+       BcInst prev = XC_INST_PRINT;
        size_t nexprs = 0, ops_bgn = p->ops.len;
        unsigned nparens, nrelops;
-       bool paren_first, paren_expr, rprn, done, get_token, assign, bin_last;
+       bool paren_first, rprn, assign, bin_last, incdec;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
-       paren_first = p->l.t.t == BC_LEX_LPAREN;
+       paren_first = (p->lex == BC_LEX_LPAREN);
        nparens = nrelops = 0;
-       paren_expr = rprn = done = get_token = assign = false;
+       rprn = assign = incdec = false;
        bin_last = true;
 
-       for (; !G_interrupt && !s && !done && bc_parse_exprs(t); t = p->l.t.t) {
+       for (;;) {
+               bool get_token;
+               BcStatus s;
+               BcLexType t = p->lex;
+
+               if (!lex_allowed_in_bc_expr(t))
+                       break;
+
                dbg_lex("%s:%d t:%d", __func__, __LINE__, t);
+               get_token = false;
+               s = BC_STATUS_SUCCESS;
                switch (t) {
-                       case BC_LEX_OP_INC:
-                       case BC_LEX_OP_DEC:
-                               s = zbc_parse_incdec(p, &prev, &paren_expr, &nexprs, flags);
-                               rprn = get_token = bin_last = false;
-                               break;
-                       case BC_LEX_OP_MINUS:
-                               s = zbc_parse_minus(p, &prev, ops_bgn, 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_error("bad assignment:"
-                                               " left side must be variable"
-                                               " or array element"
-                                       ); // note: shared string
-                                       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_error_bad_expression();
-                               }
-                               nrelops += t >= BC_LEX_OP_REL_EQ && t <= BC_LEX_OP_REL_GT;
-                               prev = BC_TOKEN_2_INST(t);
-                               bc_parse_operator(p, t, ops_bgn, &nexprs);
-                               s = zbc_lex_next(&p->l);
-                               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_error_bad_expression();
-                               ++nparens;
-                               paren_expr = rprn = bin_last = false;
-                               get_token = true;
-                               bc_vec_push(&p->ops, &t);
-                               break;
-                       case BC_LEX_RPAREN:
-                               if (bin_last || prev == BC_INST_BOOL_NOT)
-                                       return bc_error_bad_expression();
-                               if (nparens == 0) {
-                                       s = BC_STATUS_SUCCESS;
-                                       done = true;
-                                       get_token = false;
-                                       break;
-                               }
-                               if (!paren_expr) {
-                                       dbg_lex_done("%s:%d done (returning EMPTY_EXP)", __func__, __LINE__);
-                                       return BC_STATUS_PARSE_EMPTY_EXP;
-                               }
-                               --nparens;
-                               paren_expr = rprn = true;
-                               get_token = bin_last = false;
-                               s = zbc_parse_rightParen(p, ops_bgn, &nexprs);
-                               break;
-                       case BC_LEX_NAME:
-                               if (BC_PARSE_LEAF(prev, rprn))
-                                       return bc_error_bad_expression();
-                               paren_expr = true;
-                               rprn = get_token = bin_last = false;
-                               s = zbc_parse_name(p, &prev, flags & ~BC_PARSE_NOCALL);
-                               ++nexprs;
-                               break;
-                       case BC_LEX_NUMBER:
-                               if (BC_PARSE_LEAF(prev, rprn))
-                                       return bc_error_bad_expression();
-                               bc_parse_pushNUM(p);
-                               nexprs++;
-                               prev = BC_INST_NUM;
-                               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_error_bad_expression();
-                               prev = (char) (t - BC_LEX_KEY_IBASE + BC_INST_IBASE);
-                               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_error_bad_expression();
-                               s = zbc_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_error_bad_expression();
-                               else
-                                       s = zbc_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_error_bad_expression();
-                               s = zbc_parse_scale(p, &prev, flags);
-                               paren_expr = true;
-                               rprn = get_token = bin_last = false;
-                               ++nexprs;
-                               prev = BC_INST_SCALE;
-                               break;
-                       default:
-                               s = bc_error_bad_token();
-                               break;
+               case BC_LEX_OP_INC:
+               case BC_LEX_OP_DEC:
+                       dbg_lex("%s:%d LEX_OP_INC/DEC", __func__, __LINE__);
+                       if (incdec) RETURN_STATUS(bc_error_bad_assignment());
+                       s = zbc_parse_incdec(&prev, &nexprs, flags);
+                       incdec = true;
+                       rprn = bin_last = false;
+                       //get_token = false; - already is
+                       break;
+               case XC_LEX_OP_MINUS:
+                       dbg_lex("%s:%d LEX_OP_MINUS", __func__, __LINE__);
+                       s = zbc_parse_minus(&prev, ops_bgn, rprn, bin_last, &nexprs);
+                       rprn = false;
+                       //get_token = false; - already is
+                       bin_last = (prev == XC_INST_MINUS);
+                       if (bin_last) incdec = false;
+                       break;
+               case BC_LEX_OP_ASSIGN_POWER:
+               case BC_LEX_OP_ASSIGN_MULTIPLY:
+               case BC_LEX_OP_ASSIGN_DIVIDE:
+               case BC_LEX_OP_ASSIGN_MODULUS:
+               case BC_LEX_OP_ASSIGN_PLUS:
+               case BC_LEX_OP_ASSIGN_MINUS:
+               case BC_LEX_OP_ASSIGN:
+                       dbg_lex("%s:%d LEX_ASSIGNxyz", __func__, __LINE__);
+                       if (prev != XC_INST_VAR && prev != XC_INST_ARRAY_ELEM
+                        && prev != XC_INST_SCALE && prev != XC_INST_IBASE
+                        && prev != XC_INST_OBASE && prev != BC_INST_LAST
+                       ) {
+                               RETURN_STATUS(bc_error_bad_assignment());
+                       }
+               // Fallthrough.
+               case XC_LEX_OP_POWER:
+               case XC_LEX_OP_MULTIPLY:
+               case XC_LEX_OP_DIVIDE:
+               case XC_LEX_OP_MODULUS:
+               case XC_LEX_OP_PLUS:
+               case XC_LEX_OP_REL_EQ:
+               case XC_LEX_OP_REL_LE:
+               case XC_LEX_OP_REL_GE:
+               case XC_LEX_OP_REL_NE:
+               case XC_LEX_OP_REL_LT:
+               case XC_LEX_OP_REL_GT:
+               case BC_LEX_OP_BOOL_NOT:
+               case BC_LEX_OP_BOOL_OR:
+               case BC_LEX_OP_BOOL_AND:
+                       dbg_lex("%s:%d LEX_OP_xyz", __func__, __LINE__);
+                       if (t == BC_LEX_OP_BOOL_NOT) {
+                               if (!bin_last && p->lex_last != BC_LEX_OP_BOOL_NOT)
+                                       RETURN_STATUS(bc_error_bad_expression());
+                       } else if (prev == XC_INST_BOOL_NOT) {
+                               RETURN_STATUS(bc_error_bad_expression());
+                       }
+
+                       nrelops += (t >= XC_LEX_OP_REL_EQ && t <= XC_LEX_OP_REL_GT);
+                       prev = BC_TOKEN_2_INST(t);
+                       bc_parse_operator(t, ops_bgn, &nexprs);
+                       rprn = incdec = false;
+                       get_token = true;
+                       bin_last = (t != BC_LEX_OP_BOOL_NOT);
+                       break;
+               case BC_LEX_LPAREN:
+                       dbg_lex("%s:%d LEX_LPAREN", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
+                               RETURN_STATUS(bc_error_bad_expression());
+                       bc_vec_push(&p->ops, &t);
+                       nparens++;
+                       get_token = true;
+                       rprn = incdec = false;
+                       break;
+               case BC_LEX_RPAREN:
+                       dbg_lex("%s:%d LEX_RPAREN", __func__, __LINE__);
+//why?
+//                     if (p->lex_last == BC_LEX_LPAREN) {
+//                             RETURN_STATUS(bc_error_at("empty expression"));
+//                     }
+                       if (bin_last || prev == XC_INST_BOOL_NOT)
+                               RETURN_STATUS(bc_error_bad_expression());
+                       if (nparens == 0) {
+                               goto exit_loop;
+                       }
+                       s = zbc_parse_rightParen(ops_bgn, &nexprs);
+                       nparens--;
+                       get_token = true;
+                       rprn = true;
+                       bin_last = incdec = false;
+                       break;
+               case XC_LEX_NAME:
+                       dbg_lex("%s:%d LEX_NAME", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
+                               RETURN_STATUS(bc_error_bad_expression());
+                       s = zbc_parse_name(&prev, flags & ~BC_PARSE_NOCALL);
+                       rprn = (prev == BC_INST_CALL);
+                       bin_last = false;
+                       //get_token = false; - already is
+                       nexprs++;
+                       break;
+               case XC_LEX_NUMBER:
+                       dbg_lex("%s:%d LEX_NUMBER", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
+                               RETURN_STATUS(bc_error_bad_expression());
+                       xc_parse_pushNUM();
+                       prev = XC_INST_NUM;
+                       get_token = true;
+                       rprn = bin_last = false;
+                       nexprs++;
+                       break;
+               case BC_LEX_KEY_IBASE:
+               case BC_LEX_KEY_LAST:
+               case BC_LEX_KEY_OBASE:
+                       dbg_lex("%s:%d LEX_IBASE/LAST/OBASE", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
+                               RETURN_STATUS(bc_error_bad_expression());
+                       prev = (char) (t - BC_LEX_KEY_IBASE + XC_INST_IBASE);
+                       xc_parse_push((char) prev);
+                       get_token = true;
+                       rprn = bin_last = false;
+                       nexprs++;
+                       break;
+               case BC_LEX_KEY_LENGTH:
+               case BC_LEX_KEY_SQRT:
+                       dbg_lex("%s:%d LEX_LEN/SQRT", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
+                               RETURN_STATUS(bc_error_bad_expression());
+                       s = zbc_parse_builtin(t, flags, &prev);
+                       get_token = true;
+                       rprn = bin_last = incdec = false;
+                       nexprs++;
+                       break;
+               case BC_LEX_KEY_READ:
+                       dbg_lex("%s:%d LEX_READ", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
+                               RETURN_STATUS(bc_error_bad_expression());
+                       s = zbc_parse_read();
+                       prev = XC_INST_READ;
+                       get_token = true;
+                       rprn = bin_last = incdec = false;
+                       nexprs++;
+                       break;
+               case BC_LEX_KEY_SCALE:
+                       dbg_lex("%s:%d LEX_SCALE", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, bin_last, rprn))
+                               RETURN_STATUS(bc_error_bad_expression());
+                       s = zbc_parse_scale(&prev, flags);
+                       //get_token = false; - already is
+                       rprn = bin_last = false;
+                       nexprs++;
+                       break;
+               default:
+                       RETURN_STATUS(bc_error_bad_token());
                }
 
-               if (!s && get_token) s = zbc_lex_next(&p->l);
+               if (s || G_interrupt) // error, or ^C: stop parsing
+                       RETURN_STATUS(BC_STATUS_FAILURE);
+               if (get_token) {
+                       s = zxc_lex_next();
+                       if (s) RETURN_STATUS(s);
+               }
        }
-
-       if (s) return s;
-       if (G_interrupt) return BC_STATUS_FAILURE; // ^C: stop parsing
+ exit_loop:
 
        while (p->ops.len > ops_bgn) {
-               top = BC_PARSE_TOP_OP(p);
-               assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN;
+               BcLexType top = BC_PARSE_TOP_OP(p);
+               assign = (top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN);
 
                if (top == BC_LEX_LPAREN || top == BC_LEX_RPAREN)
-                       return bc_error_bad_expression();
+                       RETURN_STATUS(bc_error_bad_expression());
 
-               bc_parse_push(p, BC_TOKEN_2_INST(top));
+               xc_parse_push(BC_TOKEN_2_INST(top));
 
-               nexprs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_NEG;
+               nexprs -= (top != BC_LEX_OP_BOOL_NOT && top != XC_LEX_NEG);
                bc_vec_pop(&p->ops);
        }
 
-       if (prev == BC_INST_BOOL_NOT || nexprs != 1)
-               return bc_error_bad_expression();
+       if (prev == XC_INST_BOOL_NOT || nexprs != 1)
+               RETURN_STATUS(bc_error_bad_expression());
 
        if (!(flags & BC_PARSE_REL) && nrelops) {
-               s = bc_POSIX_does_not_allow("comparison operators outside if or loops");
-               IF_ERROR_RETURN_POSSIBLE(if (s) return s);
+               BcStatus s;
+               s = zbc_POSIX_does_not_allow("comparison operators outside if or loops");
+               if (s) RETURN_STATUS(s);
        } else if ((flags & BC_PARSE_REL) && nrelops > 1) {
-               s = bc_POSIX_requires("exactly one comparison operator per condition");
-               IF_ERROR_RETURN_POSSIBLE(if (s) return s);
+               BcStatus s;
+               s = zbc_POSIX_requires("exactly one comparison operator per condition");
+               if (s) RETURN_STATUS(s);
        }
 
        if (flags & BC_PARSE_PRINT) {
-               if (paren_first || !assign) bc_parse_push(p, BC_INST_PRINT);
-               bc_parse_push(p, BC_INST_POP);
+               if (paren_first || !assign)
+                       xc_parse_push(XC_INST_PRINT);
+               xc_parse_push(XC_INST_POP);
        }
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
-       return s;
+       RETURN_STATUS(BC_STATUS_SUCCESS);
 }
+#define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__) COMMA_SUCCESS)
 
 #endif // ENABLE_BC
 
 #if ENABLE_DC
 
-static BC_STATUS zdc_parse_register(BcParse *p)
+static BC_STATUS zdc_parse_register(void)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       if (p->l.t.t != BC_LEX_NAME) RETURN_STATUS(bc_error_bad_token());
+       if (p->lex != XC_LEX_NAME) RETURN_STATUS(bc_error_bad_token());
 
-       bc_parse_pushName(p, p->l.t.v.v);
+       xc_parse_pushName(p->lex_strnumbuf.v);
 
        RETURN_STATUS(s);
 }
 #define zdc_parse_register(...) (zdc_parse_register(__VA_ARGS__) COMMA_SUCCESS)
 
-static void dc_parse_string(BcParse *p)
+static void dc_parse_string(void)
 {
+       BcParse *p = &G.prs;
        char *str;
        size_t len = G.prog.strs.len;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
 
-       str = xstrdup(p->l.t.v.v);
-       bc_parse_push(p, BC_INST_STR);
-       bc_parse_pushIndex(p, len);
+       str = xstrdup(p->lex_strnumbuf.v);
+       xc_parse_pushInst_and_Index(XC_INST_STR, len);
        bc_vec_push(&G.prog.strs, &str);
 
-       // Explanation needed here
-       bc_program_add_fn();
-       p->func = bc_program_func(p->fidx);
+       // Add an empty function so that if zdc_program_execStr ever needs to
+       // parse the string into code (from the 'x' command) there's somewhere
+       // to store the bytecode.
+       xc_program_add_fn();
+       p->func = xc_program_func(p->fidx);
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
 }
 
-static BC_STATUS zdc_parse_mem(BcParse *p, uint8_t inst, bool name, bool store)
+static BC_STATUS zdc_parse_mem(uint8_t inst, bool name, bool store)
 {
        BcStatus s;
 
-       bc_parse_push(p, inst);
+       xc_parse_push(inst);
        if (name) {
-               s = zdc_parse_register(p);
+               s = zdc_parse_register();
                if (s) RETURN_STATUS(s);
        }
 
        if (store) {
-               bc_parse_push(p, BC_INST_SWAP);
-               bc_parse_push(p, BC_INST_ASSIGN);
-               bc_parse_push(p, BC_INST_POP);
+               xc_parse_push(DC_INST_SWAP);
+               xc_parse_push(XC_INST_ASSIGN);
+               xc_parse_push(XC_INST_POP);
        }
 
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
 #define zdc_parse_mem(...) (zdc_parse_mem(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zdc_parse_cond(BcParse *p, uint8_t inst)
+static BC_STATUS zdc_parse_cond(uint8_t inst)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
 
-       bc_parse_push(p, inst);
-       bc_parse_push(p, BC_INST_EXEC_COND);
+       xc_parse_push(inst);
+       xc_parse_push(DC_INST_EXEC_COND);
 
-       s = zdc_parse_register(p);
+       s = zdc_parse_register();
        if (s) RETURN_STATUS(s);
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
        // Note that 'else' part can not be on the next line:
        // echo -e '[1p]sa [2p]sb 2 1>a eb' | dc - OK, prints "2"
        // echo -e '[1p]sa [2p]sb 2 1>a\neb' | dc - parse error
-       if (p->l.t.t == BC_LEX_ELSE) {
-               s = zdc_parse_register(p);
+       if (p->lex == DC_LEX_ELSE) {
+               s = zdc_parse_register();
                if (s) RETURN_STATUS(s);
-               s = zbc_lex_next(&p->l);
+               s = zxc_lex_next();
        } else {
-               bc_parse_push(p, BC_PARSE_STREND);
+               xc_parse_push('\0');
        }
 
        RETURN_STATUS(s);
 }
 #define zdc_parse_cond(...) (zdc_parse_cond(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zdc_parse_token(BcParse *p, BcLexType t)
+static BC_STATUS zdc_parse_token(BcLexType t)
 {
        BcStatus s;
        uint8_t inst;
@@ -4903,88 +5044,100 @@ static BC_STATUS zdc_parse_token(BcParse *p, BcLexType t)
        s = BC_STATUS_SUCCESS;
        get_token = true;
        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 = zdc_parse_cond(p, t - BC_LEX_OP_REL_EQ + BC_INST_REL_EQ);
-                       get_token = false;
-                       break;
-               case BC_LEX_SCOLON:
-               case BC_LEX_COLON:
-                       s = zdc_parse_mem(p, BC_INST_ARRAY_ELEM, true, t == BC_LEX_COLON);
-                       break;
-               case BC_LEX_STR:
-                       dbg_lex("%s:%d LEX_STR", __func__, __LINE__);
-                       dc_parse_string(p);
-                       break;
-               case BC_LEX_NEG:
-               case BC_LEX_NUMBER:
-                       dbg_lex("%s:%d LEX_NEG/NUMBER", __func__, __LINE__);
-                       if (t == BC_LEX_NEG) {
-                               s = zbc_lex_next(&p->l);
-                               if (s) RETURN_STATUS(s);
-                               if (p->l.t.t != BC_LEX_NUMBER)
-                                       RETURN_STATUS(bc_error_bad_token());
-                       }
-                       bc_parse_pushNUM(p);
-                       if (t == BC_LEX_NEG) bc_parse_push(p, BC_INST_NEG);
-                       break;
-               case BC_LEX_KEY_READ:
-                       bc_parse_push(p, BC_INST_READ);
-                       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 = zdc_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 = zdc_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 = zdc_parse_mem(p, inst, false, true);
-                       break;
-               default:
-                       dbg_lex_done("%s:%d done (bad token)", __func__, __LINE__);
+       case XC_LEX_OP_REL_EQ:
+       case XC_LEX_OP_REL_LE:
+       case XC_LEX_OP_REL_GE:
+       case XC_LEX_OP_REL_NE:
+       case XC_LEX_OP_REL_LT:
+       case XC_LEX_OP_REL_GT:
+               dbg_lex("%s:%d LEX_OP_REL_xyz", __func__, __LINE__);
+               s = zdc_parse_cond(t - XC_LEX_OP_REL_EQ + XC_INST_REL_EQ);
+               get_token = false;
+               break;
+       case DC_LEX_SCOLON:
+       case DC_LEX_COLON:
+               dbg_lex("%s:%d LEX_[S]COLON", __func__, __LINE__);
+               s = zdc_parse_mem(XC_INST_ARRAY_ELEM, true, t == DC_LEX_COLON);
+               break;
+       case XC_LEX_STR:
+               dbg_lex("%s:%d LEX_STR", __func__, __LINE__);
+               dc_parse_string();
+               break;
+       case XC_LEX_NEG:
+               dbg_lex("%s:%d LEX_NEG", __func__, __LINE__);
+               s = zxc_lex_next();
+               if (s) RETURN_STATUS(s);
+               if (G.prs.lex != XC_LEX_NUMBER)
                        RETURN_STATUS(bc_error_bad_token());
+               xc_parse_pushNUM();
+               xc_parse_push(XC_INST_NEG);
+               break;
+       case XC_LEX_NUMBER:
+               dbg_lex("%s:%d LEX_NUMBER", __func__, __LINE__);
+               xc_parse_pushNUM();
+               break;
+       case DC_LEX_READ:
+               dbg_lex("%s:%d LEX_KEY_READ", __func__, __LINE__);
+               xc_parse_push(XC_INST_READ);
+               break;
+       case DC_LEX_OP_ASSIGN:
+       case DC_LEX_STORE_PUSH:
+               dbg_lex("%s:%d LEX_OP_ASSIGN/STORE_PUSH", __func__, __LINE__);
+               assign = (t == DC_LEX_OP_ASSIGN);
+               inst = assign ? XC_INST_VAR : DC_INST_PUSH_TO_VAR;
+               s = zdc_parse_mem(inst, true, assign);
+               break;
+       case DC_LEX_LOAD:
+       case DC_LEX_LOAD_POP:
+               dbg_lex("%s:%d LEX_OP_LOAD[_POP]", __func__, __LINE__);
+               inst = t == DC_LEX_LOAD_POP ? DC_INST_PUSH_VAR : DC_INST_LOAD;
+               s = zdc_parse_mem(inst, true, false);
+               break;
+       case DC_LEX_STORE_IBASE:
+       case DC_LEX_STORE_SCALE:
+       case DC_LEX_STORE_OBASE:
+               dbg_lex("%s:%d LEX_OP_STORE_I/OBASE/SCALE", __func__, __LINE__);
+               inst = t - DC_LEX_STORE_IBASE + XC_INST_IBASE;
+               s = zdc_parse_mem(inst, false, true);
+               break;
+       default:
+               dbg_lex_done("%s:%d done (bad token)", __func__, __LINE__);
+               RETURN_STATUS(bc_error_bad_token());
        }
 
-       if (!s && get_token) s = zbc_lex_next(&p->l);
+       if (!s && get_token) s = zxc_lex_next();
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
        RETURN_STATUS(s);
 }
 #define zdc_parse_token(...) (zdc_parse_token(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zdc_parse_expr(BcParse *p)
+static BC_STATUS zdc_parse_expr(void)
 {
-       BcInst inst;
-       BcStatus s;
+       BcParse *p = &G.prs;
+       int i;
 
-       inst = dc_parse_insts[p->l.t.t];
-       if (inst != BC_INST_INVALID) {
-               bc_parse_push(p, inst);
-               s = zbc_lex_next(&p->l);
-       } else {
-               s = zdc_parse_token(p, p->l.t.t);
+       if (p->lex == XC_LEX_NLINE)
+               RETURN_STATUS(zxc_lex_next());
+
+       i = (int)p->lex - (int)XC_LEX_OP_POWER;
+       if (i >= 0) {
+               BcInst inst = dc_LEX_to_INST[i];
+               if (inst != DC_INST_INVALID) {
+                       xc_parse_push(inst);
+                       RETURN_STATUS(zxc_lex_next());
+               }
        }
-       RETURN_STATUS(s);
+       RETURN_STATUS(zdc_parse_token(p->lex));
 }
 #define zdc_parse_expr(...) (zdc_parse_expr(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zdc_parse_exprs_until_eof(BcParse *p)
+static BC_STATUS zdc_parse_exprs_until_eof(void)
 {
-       dbg_lex_enter("%s:%d entered, p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
-       while (p->l.t.t != BC_LEX_EOF) {
-               BcStatus s = zdc_parse_expr(p);
+       BcParse *p = &G.prs;
+       dbg_lex_enter("%s:%d entered, p->lex:%d", __func__, __LINE__, p->lex);
+       while (p->lex != XC_LEX_EOF) {
+               BcStatus s = zdc_parse_expr();
                if (s) RETURN_STATUS(s);
        }
 
@@ -4995,12 +5148,75 @@ static BC_STATUS zdc_parse_exprs_until_eof(BcParse *p)
 
 #endif // ENABLE_DC
 
-static BcVec* bc_program_search(char *id, bool var)
+//
+// Execution engine
+//
+
+#define BC_PROG_STR(n) (!(n)->num && !(n)->cap)
+#define BC_PROG_NUM(r, n) \
+       ((r)->t != XC_RESULT_ARRAY && (r)->t != XC_RESULT_STR && !BC_PROG_STR(n))
+
+#define STACK_HAS_MORE_THAN(s, n)          ((s)->len > ((size_t)(n)))
+#define STACK_HAS_EQUAL_OR_MORE_THAN(s, n) ((s)->len >= ((size_t)(n)))
+
+static size_t xc_program_index(char *code, size_t *bgn)
+{
+       unsigned char *bytes = (void*)(code + *bgn);
+       unsigned amt;
+       unsigned i;
+       size_t res;
+
+       amt = *bytes++;
+       if (amt < SMALL_INDEX_LIMIT) {
+               *bgn += 1;
+               return amt;
+       }
+       amt -= (SMALL_INDEX_LIMIT - 1); // amt is 1 or more here
+       *bgn += amt + 1;
+
+       res = 0;
+       i = 0;
+       do {
+               res |= (size_t)(*bytes++) << i;
+               i += 8;
+       } while (--amt != 0);
+
+       return res;
+}
+
+static char *xc_program_name(char *code, size_t *bgn)
+{
+       code += *bgn;
+       *bgn += strlen(code) + 1;
+
+       return xstrdup(code);
+}
+
+static BcVec* xc_program_dereference(BcVec *vec)
+{
+       BcVec *v;
+       size_t vidx, nidx, i = 0;
+
+       //assert(vec->size == sizeof(uint8_t));
+
+       vidx = xc_program_index(vec->v, &i);
+       nidx = xc_program_index(vec->v, &i);
+
+       v = bc_vec_item(&G.prog.arrs, vidx);
+       v = bc_vec_item(v, nidx);
+
+       //assert(v->size != sizeof(uint8_t));
+
+       return v;
+}
+
+static BcVec* xc_program_search(char *id, BcType type)
 {
        BcId e, *ptr;
        BcVec *v, *map;
        size_t i;
        int new;
+       bool var = (type == BC_TYPE_VAR);
 
        v = var ? &G.prog.vars : &G.prog.arrs;
        map = var ? &G.prog.var_map : &G.prog.arr_map;
@@ -5020,76 +5236,82 @@ static BcVec* bc_program_search(char *id, bool var)
        return bc_vec_item(v, ptr->idx);
 }
 
-// 'num' need not be initialized on entry
-static BC_STATUS zbc_program_num(BcResult *r, BcNum **num, bool hex)
-{
-       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: {
-                       BcStatus s;
-                       char *str;
-                       unsigned base_t;
-                       size_t len;
-
-                       str = *bc_program_const(r->d.id.idx);
-                       len = strlen(str);
-
-                       bc_num_init(&r->d.n, len);
-
-                       hex = hex && len == 1;
-                       base_t = hex ? 16 : G.prog.ib_t;
-                       s = zbc_num_parse(&r->d.n, str, base_t);
-                       if (s) {
-                               bc_num_free(&r->d.n);
-                               RETURN_STATUS(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;
+// 'num' need not be initialized on entry
+static BC_STATUS zxc_program_num(BcResult *r, BcNum **num)
+{
+       switch (r->t) {
+       case XC_RESULT_STR:
+       case XC_RESULT_TEMP:
+       IF_BC(case BC_RESULT_VOID:)
+       case XC_RESULT_IBASE:
+       case XC_RESULT_SCALE:
+       case XC_RESULT_OBASE:
+               *num = &r->d.n;
+               break;
+       case XC_RESULT_CONSTANT: {
+               BcStatus s;
+               char *str;
+               size_t len;
+
+               str = *xc_program_const(r->d.id.idx);
+               len = strlen(str);
 
-                       v = bc_program_search(r->d.id.name, r->t == BC_RESULT_VAR);
+               bc_num_init(&r->d.n, len);
 
-                       if (r->t == BC_RESULT_ARRAY_ELEM) {
-                               v = bc_vec_top(v);
-                               if (v->len <= r->d.id.idx) bc_array_expand(v, r->d.id.idx + 1);
-                               *num = bc_vec_item(v, r->d.id.idx);
-                       } else
-                               *num = bc_vec_top(v);
-                       break;
+               s = zxc_num_parse(&r->d.n, str, G.prog.ib_t);
+               if (s) {
+                       bc_num_free(&r->d.n);
+                       RETURN_STATUS(s);
+               }
+               *num = &r->d.n;
+               r->t = XC_RESULT_TEMP;
+               break;
+       }
+       case XC_RESULT_VAR:
+       case XC_RESULT_ARRAY:
+       case XC_RESULT_ARRAY_ELEM: {
+               BcType type = (r->t == XC_RESULT_VAR) ? BC_TYPE_VAR : BC_TYPE_ARRAY;
+               BcVec *v = xc_program_search(r->d.id.name, type);
+               void *p = bc_vec_top(v);
+
+               if (r->t == XC_RESULT_ARRAY_ELEM) {
+                       size_t idx = r->d.id.idx;
+
+                       v = p;
+                       if (v->size == sizeof(uint8_t))
+                               v = xc_program_dereference(v);
+                       //assert(v->size == sizeof(BcNum));
+                       if (v->len <= idx)
+                               bc_array_expand(v, idx + 1);
+                       *num = bc_vec_item(v, idx);
+               } else {
+                       *num = p;
                }
+               break;
+       }
 #if ENABLE_BC
-               case BC_RESULT_LAST:
-                       *num = &G.prog.last;
-                       break;
-               case BC_RESULT_ONE:
-                       *num = &G.prog.one;
-                       break;
+       case BC_RESULT_LAST:
+               *num = &G.prog.last;
+               break;
+       case BC_RESULT_ONE:
+               *num = &G.prog.one;
+               break;
+#endif
+#if SANITY_CHECKS
+       default:
+               // Testing the theory that dc does not reach LAST/ONE
+               bb_error_msg_and_die("BUG:%d", r->t);
 #endif
-               default:
-                       // Testing the theory that dc does not reach LAST/ONE
-                       bb_error_msg_and_die("BUG:%d", r->t);
        }
 
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
-#define zbc_program_num(...) (zbc_program_num(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_num(...) (zxc_program_num(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_program_binOpPrep(BcResult **l, BcNum **ln,
+static BC_STATUS zxc_program_binOpPrep(BcResult **l, BcNum **ln,
                                      BcResult **r, BcNum **rn, bool assign)
 {
        BcStatus s;
-       bool hex;
        BcResultType lt, rt;
 
        if (!STACK_HAS_MORE_THAN(&G.prog.results, 1))
@@ -5098,39 +5320,39 @@ static BC_STATUS zbc_program_binOpPrep(BcResult **l, BcNum **ln,
        *r = bc_vec_item_rev(&G.prog.results, 0);
        *l = bc_vec_item_rev(&G.prog.results, 1);
 
-       lt = (*l)->t;
-       rt = (*r)->t;
-       hex = assign && (lt == BC_RESULT_IBASE || lt == BC_RESULT_OBASE);
-
-       s = zbc_program_num(*l, ln, false);
+       s = zxc_program_num(*l, ln);
        if (s) RETURN_STATUS(s);
-       s = zbc_program_num(*r, rn, hex);
+       s = zxc_program_num(*r, rn);
        if (s) RETURN_STATUS(s);
 
+       lt = (*l)->t;
+       rt = (*r)->t;
+
        // 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 = zbc_program_num(*l, ln, false);
+       if (lt == rt && (lt == XC_RESULT_VAR || lt == XC_RESULT_ARRAY_ELEM)) {
+               s = zxc_program_num(*l, ln);
                if (s) RETURN_STATUS(s);
        }
 
-       if (!BC_PROG_NUM((*l), (*ln)) && (!assign || (*l)->t != BC_RESULT_VAR))
+       if (!BC_PROG_NUM((*l), (*ln)) && (!assign || (*l)->t != XC_RESULT_VAR))
                RETURN_STATUS(bc_error_variable_is_wrong_type());
        if (!assign && !BC_PROG_NUM((*r), (*ln)))
                RETURN_STATUS(bc_error_variable_is_wrong_type());
 
        RETURN_STATUS(s);
 }
-#define zbc_program_binOpPrep(...) (zbc_program_binOpPrep(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_binOpPrep(...) (zxc_program_binOpPrep(__VA_ARGS__) COMMA_SUCCESS)
 
-static void bc_program_binOpRetire(BcResult *r)
+static void xc_program_binOpRetire(BcResult *r)
 {
-       r->t = BC_RESULT_TEMP;
+       r->t = XC_RESULT_TEMP;
        bc_vec_pop(&G.prog.results);
        bc_result_pop_and_push(r);
 }
 
-static BC_STATUS zbc_program_prep(BcResult **r, BcNum **n)
+// Note: *r and *n need not be initialized by caller
+static BC_STATUS zxc_program_prep(BcResult **r, BcNum **n)
 {
        BcStatus s;
 
@@ -5138,7 +5360,7 @@ static BC_STATUS zbc_program_prep(BcResult **r, BcNum **n)
                RETURN_STATUS(bc_error_stack_has_too_few_elements());
        *r = bc_vec_top(&G.prog.results);
 
-       s = zbc_program_num(*r, n, false);
+       s = zxc_program_num(*r, n);
        if (s) RETURN_STATUS(s);
 
        if (!BC_PROG_NUM((*r), (*n)))
@@ -5146,127 +5368,84 @@ static BC_STATUS zbc_program_prep(BcResult **r, BcNum **n)
 
        RETURN_STATUS(s);
 }
-#define zbc_program_prep(...) (zbc_program_prep(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_prep(...) (zxc_program_prep(__VA_ARGS__) COMMA_SUCCESS)
 
-static void bc_program_retire(BcResult *r, BcResultType t)
+static void xc_program_retire(BcResult *r, BcResultType t)
 {
        r->t = t;
        bc_result_pop_and_push(r);
 }
 
-static BC_STATUS zbc_program_op(char inst)
+static BC_STATUS zxc_program_op(char inst)
 {
        BcStatus s;
        BcResult *opd1, *opd2, res;
-       BcNum *n1, *n2 = NULL;
+       BcNum *n1, *n2;
 
-       s = zbc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false);
+       s = zxc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false);
        if (s) RETURN_STATUS(s);
        bc_num_init_DEF_SIZE(&res.d.n);
 
        s = BC_STATUS_SUCCESS;
-       IF_ERROR_RETURN_POSSIBLE(s =) zbc_program_ops[inst - BC_INST_POWER](n1, n2, &res.d.n, G.prog.scale);
+       IF_ERROR_RETURN_POSSIBLE(s =) zxc_program_ops[inst - XC_INST_POWER](n1, n2, &res.d.n, G.prog.scale);
        if (s) goto err;
-       bc_program_binOpRetire(&res);
+       xc_program_binOpRetire(&res);
 
        RETURN_STATUS(s);
  err:
        bc_num_free(&res.d.n);
        RETURN_STATUS(s);
 }
-#define zbc_program_op(...) (zbc_program_op(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_op(...) (zxc_program_op(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_program_read(void)
+static BC_STATUS zxc_program_read(void)
 {
-       const char *sv_file;
        BcStatus s;
-       BcParse parse;
+       BcParse sv_parse;
        BcVec buf;
        BcInstPtr ip;
        BcFunc *f;
 
-       f = bc_program_func(BC_PROG_READ);
-       bc_vec_pop_all(&f->code);
-
-       sv_file = G.prog.file;
-       G.prog.file = NULL;
-
        bc_char_vec_init(&buf);
-       bc_read_line(&buf, stdin);
+       xc_read_line(&buf, stdin);
+
+       f = xc_program_func(BC_PROG_READ);
+       bc_vec_pop_all(&f->code);
 
-       bc_parse_create(&parse, BC_PROG_READ);
-       bc_lex_file(&parse.l);
+       sv_parse = G.prs; // struct copy
+       xc_parse_create(BC_PROG_READ);
+       //G.err_line = G.prs.lex_line = 1; - not needed, error line info is not printed for read()
 
-       s = zbc_parse_text_init(&parse, buf.v);
+       s = zxc_parse_text_init(buf.v);
        if (s) goto exec_err;
        if (IS_BC) {
-               IF_BC(s = zbc_parse_expr(&parse, 0));
+               IF_BC(s = zbc_parse_expr(0));
        } else {
-               IF_DC(s = zdc_parse_exprs_until_eof(&parse));
+               IF_DC(s = zdc_parse_exprs_until_eof());
        }
        if (s) goto exec_err;
-
-       if (parse.l.t.t != BC_LEX_NLINE && parse.l.t.t != BC_LEX_EOF) {
-               s = bc_error("bad read() expression");
+       if (G.prs.lex != XC_LEX_NLINE && G.prs.lex != XC_LEX_EOF) {
+               s = bc_error_at("bad read() expression");
                goto exec_err;
        }
+       xc_parse_push(XC_INST_RET);
 
        ip.func = BC_PROG_READ;
        ip.inst_idx = 0;
-       IF_BC(ip.results_len_before_call = G.prog.results.len;)
-
-       bc_parse_push(&parse, BC_INST_RET);
        bc_vec_push(&G.prog.exestack, &ip);
+
  exec_err:
-       bc_parse_free(&parse);
-       G.prog.file = sv_file;
+       xc_parse_free();
+       G.prs = sv_parse; // struct copy
        bc_vec_free(&buf);
        RETURN_STATUS(s);
 }
-#define zbc_program_read(...) (zbc_program_read(__VA_ARGS__) COMMA_SUCCESS)
-
-static size_t bc_program_index(char *code, size_t *bgn)
-{
-       unsigned char *bytes = (void*)(code + *bgn);
-       unsigned amt;
-       unsigned i;
-       size_t res;
-
-       amt = *bytes++;
-       *bgn += amt + 1;
-
-       amt *= 8;
-       res = 0;
-       for (i = 0; i < amt; i += 8)
-               res |= (size_t)(*bytes++) << i;
-
-       return res;
-}
-
-static char *bc_program_name(char *code, size_t *bgn)
-{
-       size_t i;
-       char *s;
-
-       code += *bgn;
-       s = xmalloc(strchr(code, BC_PARSE_STREND) - code + 1);
-       i = 0;
-       for (;;) {
-               char c = *code++;
-               if (c == BC_PARSE_STREND)
-                       break;
-               s[i++] = c;
-       }
-       s[i] = '\0';
-       *bgn += i + 1;
-
-       return s;
-}
+#define zxc_program_read(...) (zxc_program_read(__VA_ARGS__) COMMA_SUCCESS)
 
-static void bc_program_printString(const char *str)
+static void xc_program_printString(const char *str)
 {
 #if ENABLE_DC
-       if (!str[0]) {
+       if (!str[0] && IS_DC) {
                // Example: echo '[]ap' | dc
                // should print two bytes: 0x00, 0x0A
                bb_putchar('\0');
@@ -5274,46 +5453,27 @@ static void bc_program_printString(const char *str)
        }
 #endif
        while (*str) {
-               int c = *str++;
-               if (c != '\\' || !*str)
-                       bb_putchar(c);
-               else {
+               char c = *str++;
+               if (c == '\\') {
+                       static const char esc[] ALIGN1 = "nabfrt""e\\";
+                       char *n;
+
                        c = *str++;
-                       switch (c) {
-                       case 'a':
-                               bb_putchar('\a');
-                               break;
-                       case 'b':
-                               bb_putchar('\b');
-                               break;
-                       case '\\':
-                       case 'e':
-                               bb_putchar('\\');
-                               break;
-                       case 'f':
-                               bb_putchar('\f');
-                               break;
-                       case 'n':
-                               bb_putchar('\n');
-                               G.prog.nchars = SIZE_MAX;
-                               break;
-                       case 'r':
-                               bb_putchar('\r');
-                               break;
-                       case 'q':
-                               bb_putchar('"');
-                               break;
-                       case 't':
-                               bb_putchar('\t');
-                               break;
-                       default:
-                               // Just print the backslash and following character.
+                       n = strchr(esc, c); // note: if c is NUL, n = \0 at end of esc
+                       if (!n || !c) {
+                               // Just print the backslash and following character
                                bb_putchar('\\');
                                ++G.prog.nchars;
-                               bb_putchar(c);
-                               break;
+                               // But if we're at the end of the string, stop
+                               if (!c) break;
+                       } else {
+                               if (n - esc == 0) // "\n" ?
+                                       G.prog.nchars = SIZE_MAX;
+                               c = "\n\a\b\f\r\t""\\\\""\\"[n - esc];
+                               //   n a b f r t   e \   \<end of line>
                        }
                }
+               putchar(c);
                ++G.prog.nchars;
        }
 }
@@ -5383,7 +5543,9 @@ static void bc_num_printDecimal(BcNum *n)
                bc_num_printHex((size_t) n->num[i], 1, i == rdx);
 }
 
-static BC_STATUS zbc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNumDigitOp print)
+typedef void (*BcNumDigitOp)(size_t, size_t, bool) FAST_FUNC;
+
+static BC_STATUS zxc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNumDigitOp print)
 {
        BcStatus s;
        BcVec stack;
@@ -5449,9 +5611,9 @@ static BC_STATUS zbc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNum
        bc_vec_free(&stack);
        RETURN_STATUS(s);
 }
-#define zbc_num_printNum(...) (zbc_num_printNum(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_num_printNum(...) (zxc_num_printNum(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_num_printBase(BcNum *n)
+static BC_STATUS zxc_num_printBase(BcNum *n)
 {
        BcStatus s;
        size_t width;
@@ -5465,7 +5627,7 @@ static BC_STATUS zbc_num_printBase(BcNum *n)
 
        n->neg = false;
 
-       if (G.prog.ob_t <= BC_NUM_MAX_IBASE) {
+       if (G.prog.ob_t <= 16) {
                width = 1;
                print = bc_num_printHex;
        } else {
@@ -5480,14 +5642,14 @@ static BC_STATUS zbc_num_printBase(BcNum *n)
                print = bc_num_printDigits;
        }
 
-       s = zbc_num_printNum(n, G.prog.ob_t, width, print);
+       s = zxc_num_printNum(n, G.prog.ob_t, width, print);
        n->neg = neg;
 
        RETURN_STATUS(s);
 }
-#define zbc_num_printBase(...) (zbc_num_printBase(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_num_printBase(...) (zxc_num_printBase(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_num_print(BcNum *n, bool newline)
+static BC_STATUS zxc_num_print(BcNum *n, bool newline)
 {
        BcStatus s = BC_STATUS_SUCCESS;
 
@@ -5499,7 +5661,7 @@ static BC_STATUS zbc_num_print(BcNum *n, bool newline)
        } else if (G.prog.ob_t == 10)
                bc_num_printDecimal(n);
        else
-               s = zbc_num_printBase(n);
+               s = zxc_num_printBase(n);
 
        if (newline) {
                bb_putchar('\n');
@@ -5508,107 +5670,116 @@ static BC_STATUS zbc_num_print(BcNum *n, bool newline)
 
        RETURN_STATUS(s);
 }
-#define zbc_num_print(...) (zbc_num_print(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_num_print(...) (zxc_num_print(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_program_print(char inst, size_t idx)
+#if !ENABLE_DC
+// for bc, idx is always 0
+#define xc_program_print(inst, idx) \
+       xc_program_print(inst)
+#endif
+static BC_STATUS xc_program_print(char inst, size_t idx)
 {
        BcStatus s;
        BcResult *r;
        BcNum *num;
-       bool pop = inst != BC_INST_PRINT;
+       IF_NOT_DC(size_t idx = 0);
 
        if (!STACK_HAS_MORE_THAN(&G.prog.results, idx))
                RETURN_STATUS(bc_error_stack_has_too_few_elements());
 
        r = bc_vec_item_rev(&G.prog.results, idx);
-       s = zbc_program_num(r, &num, false);
+#if ENABLE_BC
+       if (inst == XC_INST_PRINT && r->t == BC_RESULT_VOID)
+               // void function's result on stack, ignore
+               RETURN_STATUS(BC_STATUS_SUCCESS);
+#endif
+       s = zxc_program_num(r, &num);
        if (s) RETURN_STATUS(s);
 
        if (BC_PROG_NUM(r, num)) {
-               s = zbc_num_print(num, !pop);
+               s = zxc_num_print(num, /*newline:*/ inst == XC_INST_PRINT);
 #if ENABLE_BC
                if (!s && IS_BC) bc_num_copy(&G.prog.last, num);
 #endif
        } else {
                char *str;
 
-               idx = (r->t == BC_RESULT_STR) ? r->d.id.idx : num->rdx;
-               str = *bc_program_str(idx);
+               idx = (r->t == XC_RESULT_STR) ? r->d.id.idx : num->rdx;
+               str = *xc_program_str(idx);
 
-               if (inst == BC_INST_PRINT_STR) {
-                       for (;;) {
-                               char c = *str++;
-                               if (c == '\0') break;
-                               bb_putchar(c);
-                               ++G.prog.nchars;
-                               if (c == '\n') G.prog.nchars = 0;
-                       }
+               if (inst == XC_INST_PRINT_STR) {
+                       char *nl;
+                       G.prog.nchars += printf("%s", str);
+                       nl = strrchr(str, '\n');
+                       if (nl)
+                               G.prog.nchars = strlen(nl + 1);
                } else {
-                       bc_program_printString(str);
-                       if (inst == BC_INST_PRINT) bb_putchar('\n');
+                       xc_program_printString(str);
+                       if (inst == XC_INST_PRINT)
+                               bb_putchar('\n');
                }
        }
 
-       if (!s && pop) bc_vec_pop(&G.prog.results);
+       if (!s && inst != XC_INST_PRINT) bc_vec_pop(&G.prog.results);
 
        RETURN_STATUS(s);
 }
-#define zbc_program_print(...) (zbc_program_print(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_print(...) (xc_program_print(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_program_negate(void)
+static BC_STATUS zxc_program_negate(void)
 {
        BcStatus s;
        BcResult res, *ptr;
-       BcNum *num = NULL;
+       BcNum *num;
 
-       s = zbc_program_prep(&ptr, &num);
+       s = zxc_program_prep(&ptr, &num);
        if (s) RETURN_STATUS(s);
 
        bc_num_init(&res.d.n, num->len);
        bc_num_copy(&res.d.n, num);
        if (res.d.n.len) res.d.n.neg = !res.d.n.neg;
 
-       bc_program_retire(&res, BC_RESULT_TEMP);
+       xc_program_retire(&res, XC_RESULT_TEMP);
 
        RETURN_STATUS(s);
 }
-#define zbc_program_negate(...) (zbc_program_negate(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_negate(...) (zxc_program_negate(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_program_logical(char inst)
+static BC_STATUS zxc_program_logical(char inst)
 {
        BcStatus s;
        BcResult *opd1, *opd2, res;
        BcNum *n1, *n2;
        ssize_t cond;
 
-       s = zbc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false);
+       s = zxc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false);
        if (s) RETURN_STATUS(s);
 
        bc_num_init_DEF_SIZE(&res.d.n);
 
-       if (inst == BC_INST_BOOL_AND)
+       if (inst == XC_INST_BOOL_AND)
                cond = bc_num_cmp(n1, &G.prog.zero) && bc_num_cmp(n2, &G.prog.zero);
-       else if (inst == BC_INST_BOOL_OR)
+       else if (inst == XC_INST_BOOL_OR)
                cond = bc_num_cmp(n1, &G.prog.zero) || bc_num_cmp(n2, &G.prog.zero);
        else {
                cond = bc_num_cmp(n1, n2);
                switch (inst) {
-               case BC_INST_REL_EQ:
+               case XC_INST_REL_EQ:
                        cond = (cond == 0);
                        break;
-               case BC_INST_REL_LE:
+               case XC_INST_REL_LE:
                        cond = (cond <= 0);
                        break;
-               case BC_INST_REL_GE:
+               case XC_INST_REL_GE:
                        cond = (cond >= 0);
                        break;
-               case BC_INST_REL_LT:
+               case XC_INST_REL_LT:
                        cond = (cond < 0);
                        break;
-               case BC_INST_REL_GT:
+               case XC_INST_REL_GT:
                        cond = (cond > 0);
                        break;
-               default: // = case BC_INST_REL_NE:
+               default: // = case XC_INST_REL_NE:
                        //cond = (cond != 0); - not needed
                        break;
                }
@@ -5617,11 +5788,11 @@ static BC_STATUS zbc_program_logical(char inst)
        if (cond) bc_num_one(&res.d.n);
        //else bc_num_zero(&res.d.n); - already is
 
-       bc_program_binOpRetire(&res);
+       xc_program_binOpRetire(&res);
 
        RETURN_STATUS(s);
 }
-#define zbc_program_logical(...) (zbc_program_logical(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_logical(...) (zxc_program_logical(__VA_ARGS__) COMMA_SUCCESS)
 
 #if ENABLE_DC
 static BC_STATUS zdc_program_assignStr(BcResult *r, BcVec *v, bool push)
@@ -5631,7 +5802,7 @@ static BC_STATUS zdc_program_assignStr(BcResult *r, BcVec *v, bool push)
 
        memset(&n2, 0, sizeof(BcNum));
        n2.rdx = res.d.id.idx = r->d.id.idx;
-       res.t = BC_RESULT_STR;
+       res.t = XC_RESULT_STR;
 
        if (!push) {
                if (!STACK_HAS_MORE_THAN(&G.prog.results, 1))
@@ -5648,103 +5819,137 @@ static BC_STATUS zdc_program_assignStr(BcResult *r, BcVec *v, bool push)
 #define zdc_program_assignStr(...) (zdc_program_assignStr(__VA_ARGS__) COMMA_SUCCESS)
 #endif // ENABLE_DC
 
-static BC_STATUS zbc_program_copyToVar(char *name, bool var)
+static BC_STATUS zxc_program_popResultAndCopyToVar(char *name, BcType t)
 {
        BcStatus s;
        BcResult *ptr, r;
-       BcVec *v;
+       BcVec *vec;
        BcNum *n;
+       bool var = (t == BC_TYPE_VAR);
 
        if (!STACK_HAS_MORE_THAN(&G.prog.results, 0))
                RETURN_STATUS(bc_error_stack_has_too_few_elements());
 
        ptr = bc_vec_top(&G.prog.results);
-       if ((ptr->t == BC_RESULT_ARRAY) != !var)
+       if ((ptr->t == XC_RESULT_ARRAY) == var)
                RETURN_STATUS(bc_error_variable_is_wrong_type());
-       v = bc_program_search(name, var);
+       vec = xc_program_search(name, t);
 
 #if ENABLE_DC
-       if (ptr->t == BC_RESULT_STR && !var)
-               RETURN_STATUS(bc_error_variable_is_wrong_type());
-       if (ptr->t == BC_RESULT_STR)
-               RETURN_STATUS(zdc_program_assignStr(ptr, v, true));
+       if (ptr->t == XC_RESULT_STR) {
+               if (!var)
+                       RETURN_STATUS(bc_error_variable_is_wrong_type());
+               RETURN_STATUS(zdc_program_assignStr(ptr, vec, true));
+       }
 #endif
 
-       s = zbc_program_num(ptr, &n, false);
+       s = zxc_program_num(ptr, &n);
        if (s) RETURN_STATUS(s);
 
        // Do this once more to make sure that pointers were not invalidated.
-       v = bc_program_search(name, var);
+       vec = xc_program_search(name, t);
 
        if (var) {
                bc_num_init_DEF_SIZE(&r.d.n);
                bc_num_copy(&r.d.n, n);
        } else {
+               BcVec *v = (BcVec*) n;
+               bool ref, ref_size;
+
+               ref = (v->size == sizeof(BcVec) && t != BC_TYPE_ARRAY);
+               ref_size = (v->size == sizeof(uint8_t));
+
+               if (ref || (ref_size && t == BC_TYPE_REF)) {
+                       bc_vec_init(&r.d.v, sizeof(uint8_t), NULL);
+                       if (ref) {
+                               size_t vidx, idx;
+                               BcId id;
+
+                               id.name = ptr->d.id.name;
+                               v = xc_program_search(ptr->d.id.name, BC_TYPE_REF);
+
+                               // Make sure the pointer was not invalidated.
+                               vec = xc_program_search(name, t);
+
+                               vidx = bc_map_find_exact(&G.prog.arr_map, &id);
+                               //assert(vidx != BC_VEC_INVALID_IDX);
+                               vidx = ((BcId*) bc_vec_item(&G.prog.arr_map, vidx))->idx;
+                               idx = v->len - 1;
+
+                               bc_vec_pushIndex(&r.d.v, vidx);
+                               bc_vec_pushIndex(&r.d.v, idx);
+                       }
+                       // If we get here, we are copying a ref to a ref.
+                       else bc_vec_npush(&r.d.v, v->len, v->v);
+
+                       // We need to return early.
+                       goto ret;
+               }
+
+               if (ref_size && t != BC_TYPE_REF)
+                       v = xc_program_dereference(v);
+
                bc_array_init(&r.d.v, true);
-               bc_array_copy(&r.d.v, (BcVec *) n);
+               bc_array_copy(&r.d.v, v);
        }
-
-       bc_vec_push(v, &r.d);
+ ret:
+       bc_vec_push(vec, &r.d);
        bc_vec_pop(&G.prog.results);
 
        RETURN_STATUS(s);
 }
-#define zbc_program_copyToVar(...) (zbc_program_copyToVar(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_popResultAndCopyToVar(...) (zxc_program_popResultAndCopyToVar(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_program_assign(char inst)
+static BC_STATUS zxc_program_assign(char inst)
 {
        BcStatus s;
        BcResult *left, *right, res;
-       BcNum *l = NULL, *r = NULL;
-       bool assign = inst == BC_INST_ASSIGN, ib, sc;
+       BcNum *l, *r;
+       bool assign = (inst == XC_INST_ASSIGN);
+       bool ib, sc;
 
-       s = zbc_program_binOpPrep(&left, &l, &right, &r, assign);
+       s = zxc_program_binOpPrep(&left, &l, &right, &r, assign);
        if (s) RETURN_STATUS(s);
 
-       ib = left->t == BC_RESULT_IBASE;
-       sc = left->t == BC_RESULT_SCALE;
+       ib = left->t == XC_RESULT_IBASE;
+       sc = left->t == XC_RESULT_SCALE;
 
 #if ENABLE_DC
-       if (right->t == BC_RESULT_STR) {
+       if (right->t == XC_RESULT_STR) {
                BcVec *v;
 
-               if (left->t != BC_RESULT_VAR)
+               if (left->t != XC_RESULT_VAR)
                        RETURN_STATUS(bc_error_variable_is_wrong_type());
-               v = bc_program_search(left->d.id.name, true);
+               v = xc_program_search(left->d.id.name, BC_TYPE_VAR);
 
                RETURN_STATUS(zdc_program_assignStr(right, v, false));
        }
 #endif
 
-       if (left->t == BC_RESULT_CONSTANT || left->t == BC_RESULT_TEMP)
-               RETURN_STATUS(bc_error("bad assignment:"
-                               " left side must be variable"
-                               " or array element"
-               )); // note: shared string
+       if (left->t == XC_RESULT_CONSTANT
+        || left->t == XC_RESULT_TEMP
+       IF_BC(|| left->t == BC_RESULT_VOID)
+       ) {
+               RETURN_STATUS(bc_error_bad_assignment());
+       }
 
 #if ENABLE_BC
-       if (inst == BC_INST_ASSIGN_DIVIDE && !bc_num_cmp(r, &G.prog.zero))
-               RETURN_STATUS(bc_error("divide by zero"));
-
        if (assign)
                bc_num_copy(l, r);
        else {
                s = BC_STATUS_SUCCESS;
-               IF_ERROR_RETURN_POSSIBLE(s =) zbc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, G.prog.scale);
+               IF_ERROR_RETURN_POSSIBLE(s =) zxc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, G.prog.scale);
        }
        if (s) RETURN_STATUS(s);
 #else
        bc_num_copy(l, r);
 #endif
 
-       if (ib || sc || left->t == BC_RESULT_OBASE) {
+       if (ib || sc || left->t == XC_RESULT_OBASE) {
                static const char *const msg[] = {
-                       "bad ibase; must be [2,16]",                 //BC_RESULT_IBASE
-                       "bad scale; must be [0,"BC_MAX_SCALE_STR"]", //BC_RESULT_SCALE
-                       NULL, //can't happen                         //BC_RESULT_LAST
-                       NULL, //can't happen                         //BC_RESULT_CONSTANT
-                       NULL, //can't happen                         //BC_RESULT_ONE
-                       "bad obase; must be [2,"BC_MAX_OBASE_STR"]", //BC_RESULT_OBASE
+                       "bad ibase; must be [2,16]",                 //XC_RESULT_IBASE
+                       "bad obase; must be [2,"BC_MAX_OBASE_STR"]", //XC_RESULT_OBASE
+                       "bad scale; must be [0,"BC_MAX_SCALE_STR"]", //XC_RESULT_SCALE
                };
                size_t *ptr;
                size_t max;
@@ -5752,7 +5957,7 @@ static BC_STATUS zbc_program_assign(char inst)
 
                s = zbc_num_ulong(l, &val);
                if (s) RETURN_STATUS(s);
-               s = left->t - BC_RESULT_IBASE;
+               s = left->t - XC_RESULT_IBASE;
                if (sc) {
                        max = BC_MAX_SCALE;
                        ptr = &G.prog.scale;
@@ -5772,29 +5977,29 @@ static BC_STATUS zbc_program_assign(char inst)
 
        bc_num_init(&res.d.n, l->len);
        bc_num_copy(&res.d.n, l);
-       bc_program_binOpRetire(&res);
+       xc_program_binOpRetire(&res);
 
        RETURN_STATUS(s);
 }
-#define zbc_program_assign(...) (zbc_program_assign(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_assign(...) (zxc_program_assign(__VA_ARGS__) COMMA_SUCCESS)
 
 #if !ENABLE_DC
-#define bc_program_pushVar(code, bgn, pop, copy) \
-       bc_program_pushVar(code, bgn)
+#define xc_program_pushVar(code, bgn, pop, copy) \
+       xc_program_pushVar(code, bgn)
 // for bc, 'pop' and 'copy' are always false
 #endif
-static BC_STATUS bc_program_pushVar(char *code, size_t *bgn,
+static BC_STATUS xc_program_pushVar(char *code, size_t *bgn,
                                    bool pop, bool copy)
 {
        BcResult r;
-       char *name = bc_program_name(code, bgn);
+       char *name = xc_program_name(code, bgn);
 
-       r.t = BC_RESULT_VAR;
+       r.t = XC_RESULT_VAR;
        r.d.id.name = name;
 
 #if ENABLE_DC
        if (pop || copy) {
-               BcVec *v = bc_program_search(name, true);
+               BcVec *v = xc_program_search(name, BC_TYPE_VAR);
                BcNum *num = bc_vec_top(v);
 
                free(name);
@@ -5803,11 +6008,11 @@ static BC_STATUS bc_program_pushVar(char *code, size_t *bgn,
                }
 
                if (!BC_PROG_STR(num)) {
-                       r.t = BC_RESULT_TEMP;
+                       r.t = XC_RESULT_TEMP;
                        bc_num_init_DEF_SIZE(&r.d.n);
                        bc_num_copy(&r.d.n, num);
                } else {
-                       r.t = BC_RESULT_STR;
+                       r.t = XC_RESULT_STR;
                        r.d.id.idx = num->rdx;
                }
 
@@ -5819,7 +6024,7 @@ static BC_STATUS bc_program_pushVar(char *code, size_t *bgn,
 
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
-#define zbc_program_pushVar(...) (bc_program_pushVar(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_pushVar(...) (xc_program_pushVar(__VA_ARGS__) COMMA_SUCCESS)
 
 static BC_STATUS zbc_program_pushArray(char *code, size_t *bgn, char inst)
 {
@@ -5827,16 +6032,16 @@ static BC_STATUS zbc_program_pushArray(char *code, size_t *bgn, char inst)
        BcResult r;
        BcNum *num;
 
-       r.d.id.name = bc_program_name(code, bgn);
+       r.d.id.name = xc_program_name(code, bgn);
 
-       if (inst == BC_INST_ARRAY) {
-               r.t = BC_RESULT_ARRAY;
+       if (inst == XC_INST_ARRAY) {
+               r.t = XC_RESULT_ARRAY;
                bc_vec_push(&G.prog.results, &r);
        } else {
                BcResult *operand;
                unsigned long temp;
 
-               s = zbc_program_prep(&operand, &num);
+               s = zxc_program_prep(&operand, &num);
                if (s) goto err;
                s = zbc_num_ulong(num, &temp);
                if (s) goto err;
@@ -5847,7 +6052,7 @@ static BC_STATUS zbc_program_pushArray(char *code, size_t *bgn, char inst)
                }
 
                r.d.id.idx = (size_t) temp;
-               bc_program_retire(&r, BC_RESULT_ARRAY_ELEM);
+               xc_program_retire(&r, XC_RESULT_ARRAY_ELEM);
        }
  err:
        if (s) free(r.d.id.name);
@@ -5860,25 +6065,25 @@ static BC_STATUS zbc_program_incdec(char inst)
 {
        BcStatus s;
        BcResult *ptr, res, copy;
-       BcNum *num = NULL;
+       BcNum *num;
        char inst2 = inst;
 
-       s = zbc_program_prep(&ptr, &num);
+       s = zxc_program_prep(&ptr, &num);
        if (s) RETURN_STATUS(s);
 
        if (inst == BC_INST_INC_POST || inst == BC_INST_DEC_POST) {
-               copy.t = BC_RESULT_TEMP;
+               copy.t = XC_RESULT_TEMP;
                bc_num_init(&copy.d.n, num->len);
                bc_num_copy(&copy.d.n, num);
        }
 
        res.t = BC_RESULT_ONE;
-       inst = inst == BC_INST_INC_PRE || inst == BC_INST_INC_POST ?
-                  BC_INST_ASSIGN_PLUS :
-                  BC_INST_ASSIGN_MINUS;
+       inst = (inst == BC_INST_INC_PRE || inst == BC_INST_INC_POST)
+                       ? BC_INST_ASSIGN_PLUS
+                       : BC_INST_ASSIGN_MINUS;
 
        bc_vec_push(&G.prog.results, &res);
-       s = zbc_program_assign(inst);
+       s = zxc_program_assign(inst);
        if (s) RETURN_STATUS(s);
 
        if (inst2 == BC_INST_INC_POST || inst2 == BC_INST_DEC_POST) {
@@ -5893,14 +6098,12 @@ static BC_STATUS zbc_program_call(char *code, size_t *idx)
 {
        BcInstPtr ip;
        size_t i, nparams;
-       BcFunc *func;
        BcId *a;
-       BcResult *arg;
+       BcFunc *func;
 
-       nparams = bc_program_index(code, idx);
-       ip.inst_idx = 0;
-       ip.func = bc_program_index(code, idx);
-       func = bc_program_func(ip.func);
+       nparams = xc_program_index(code, idx);
+       ip.func = xc_program_index(code, idx);
+       func = xc_program_func(ip.func);
 
        if (func->code.len == 0) {
                RETURN_STATUS(bc_error("undefined function"));
@@ -5908,18 +6111,24 @@ static BC_STATUS zbc_program_call(char *code, size_t *idx)
        if (nparams != func->nparams) {
                RETURN_STATUS(bc_error_fmt("function has %u parameters, but called with %u", func->nparams, nparams));
        }
-       ip.results_len_before_call = G.prog.results.len - nparams;
+       ip.inst_idx = 0;
 
        for (i = 0; i < nparams; ++i) {
+               BcResult *arg;
                BcStatus s;
+               bool arr;
 
                a = bc_vec_item(&func->autos, nparams - 1 - i);
                arg = bc_vec_top(&G.prog.results);
 
-               if ((!a->idx) != (arg->t == BC_RESULT_ARRAY) || arg->t == BC_RESULT_STR)
-                       RETURN_STATUS(bc_error_variable_is_wrong_type());
+               arr = (a->idx == BC_TYPE_ARRAY || a->idx == BC_TYPE_REF);
 
-               s = zbc_program_copyToVar(a->name, a->idx);
+               if (arr != (arg->t == XC_RESULT_ARRAY) // array/variable mismatch
+               // || arg->t == XC_RESULT_STR - impossible, f("str") is not a legal syntax (strings are not bc expressions)
+               ) {
+                       RETURN_STATUS(bc_error_variable_is_wrong_type());
+               }
+               s = zxc_program_popResultAndCopyToVar(a->name, (BcType) a->idx);
                if (s) RETURN_STATUS(s);
        }
 
@@ -5927,12 +6136,13 @@ static BC_STATUS zbc_program_call(char *code, size_t *idx)
        for (; i < func->autos.len; i++, a++) {
                BcVec *v;
 
-               v = bc_program_search(a->name, a->idx);
-               if (a->idx) {
+               v = xc_program_search(a->name, (BcType) a->idx);
+               if (a->idx == BC_TYPE_VAR) {
                        BcNum n2;
                        bc_num_init_DEF_SIZE(&n2);
                        bc_vec_push(v, &n2);
                } else {
+                       //assert(a->idx == BC_TYPE_ARRAY);
                        BcVec v2;
                        bc_array_init(&v2, true);
                        bc_vec_push(v, &v2);
@@ -5953,49 +6163,51 @@ static BC_STATUS zbc_program_return(char inst)
        size_t i;
        BcInstPtr *ip = bc_vec_top(&G.prog.exestack);
 
-       if (!STACK_HAS_EQUAL_OR_MORE_THAN(&G.prog.results, ip->results_len_before_call + (inst == BC_INST_RET)))
-               RETURN_STATUS(bc_error_stack_has_too_few_elements());
-
-       f = bc_program_func(ip->func);
-       res.t = BC_RESULT_TEMP;
+       f = xc_program_func(ip->func);
 
-       if (inst == BC_INST_RET) {
+       res.t = XC_RESULT_TEMP;
+       if (inst == XC_INST_RET) {
+               // bc needs this for e.g. RESULT_CONSTANT ("return 5")
+               // because bc constants are per-function.
+               // TODO: maybe avoid if value is already RESULT_TEMP?
                BcStatus s;
                BcNum *num;
                BcResult *operand = bc_vec_top(&G.prog.results);
 
-               s = zbc_program_num(operand, &num, false);
+               s = zxc_program_num(operand, &num);
                if (s) RETURN_STATUS(s);
                bc_num_init(&res.d.n, num->len);
                bc_num_copy(&res.d.n, num);
+               bc_vec_pop(&G.prog.results);
        } else {
+               if (f->voidfunc)
+                       res.t = BC_RESULT_VOID;
                bc_num_init_DEF_SIZE(&res.d.n);
                //bc_num_zero(&res.d.n); - already is
        }
+       bc_vec_push(&G.prog.results, &res);
+
+       bc_vec_pop(&G.prog.exestack);
 
        // We need to pop arguments as well, so this takes that into account.
        a = (void*)f->autos.v;
        for (i = 0; i < f->autos.len; i++, a++) {
                BcVec *v;
-               v = bc_program_search(a->name, a->idx);
+               v = xc_program_search(a->name, (BcType) a->idx);
                bc_vec_pop(v);
        }
 
-       bc_vec_npop(&G.prog.results, G.prog.results.len - ip->results_len_before_call);
-       bc_vec_push(&G.prog.results, &res);
-       bc_vec_pop(&G.prog.exestack);
-
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
 #define zbc_program_return(...) (zbc_program_return(__VA_ARGS__) COMMA_SUCCESS)
 #endif // ENABLE_BC
 
-static unsigned long bc_program_scale(BcNum *n)
+static unsigned long xc_program_scale(BcNum *n)
 {
        return (unsigned long) n->rdx;
 }
 
-static unsigned long bc_program_len(BcNum *n)
+static unsigned long xc_program_len(BcNum *n)
 {
        size_t len = n->len;
 
@@ -6008,19 +6220,19 @@ static unsigned long bc_program_len(BcNum *n)
        return len;
 }
 
-static BC_STATUS zbc_program_builtin(char inst)
+static BC_STATUS zxc_program_builtin(char inst)
 {
        BcStatus s;
        BcResult *opnd;
-       BcNum *num = NULL;
+       BcNum *num;
        BcResult res;
-       bool len = inst == BC_INST_LENGTH;
+       bool len = (inst == XC_INST_LENGTH);
 
        if (!STACK_HAS_MORE_THAN(&G.prog.results, 0))
                RETURN_STATUS(bc_error_stack_has_too_few_elements());
        opnd = bc_vec_top(&G.prog.results);
 
-       s = zbc_program_num(opnd, &num, false);
+       s = zxc_program_num(opnd, &num);
        if (s) RETURN_STATUS(s);
 
 #if ENABLE_DC
@@ -6030,40 +6242,40 @@ static BC_STATUS zbc_program_builtin(char inst)
 
        bc_num_init_DEF_SIZE(&res.d.n);
 
-       if (inst == BC_INST_SQRT)
+       if (inst == XC_INST_SQRT)
                s = zbc_num_sqrt(num, &res.d.n, G.prog.scale);
 #if ENABLE_BC
-       else if (len != 0 && opnd->t == BC_RESULT_ARRAY) {
+       else if (len != 0 && opnd->t == XC_RESULT_ARRAY) {
                bc_num_ulong2num(&res.d.n, (unsigned long) ((BcVec *) num)->len);
        }
 #endif
 #if ENABLE_DC
        else if (len != 0 && !BC_PROG_NUM(opnd, num)) {
                char **str;
-               size_t idx = opnd->t == BC_RESULT_STR ? opnd->d.id.idx : num->rdx;
+               size_t idx = opnd->t == XC_RESULT_STR ? opnd->d.id.idx : num->rdx;
 
-               str = bc_program_str(idx);
+               str = xc_program_str(idx);
                bc_num_ulong2num(&res.d.n, strlen(*str));
        }
 #endif
        else {
-               bc_num_ulong2num(&res.d.n, len ? bc_program_len(num) : bc_program_scale(num));
+               bc_num_ulong2num(&res.d.n, len ? xc_program_len(num) : xc_program_scale(num));
        }
 
-       bc_program_retire(&res, BC_RESULT_TEMP);
+       xc_program_retire(&res, XC_RESULT_TEMP);
 
        RETURN_STATUS(s);
 }
-#define zbc_program_builtin(...) (zbc_program_builtin(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_builtin(...) (zxc_program_builtin(__VA_ARGS__) COMMA_SUCCESS)
 
 #if ENABLE_DC
 static BC_STATUS zdc_program_divmod(void)
 {
        BcStatus s;
        BcResult *opd1, *opd2, res, res2;
-       BcNum *n1, *n2 = NULL;
+       BcNum *n1, *n2;
 
-       s = zbc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false);
+       s = zxc_program_binOpPrep(&opd1, &n1, &opd2, &n2, false);
        if (s) RETURN_STATUS(s);
 
        bc_num_init_DEF_SIZE(&res.d.n);
@@ -6072,8 +6284,8 @@ static BC_STATUS zdc_program_divmod(void)
        s = zbc_num_divmod(n1, n2, &res2.d.n, &res.d.n, G.prog.scale);
        if (s) goto err;
 
-       bc_program_binOpRetire(&res2);
-       res.t = BC_RESULT_TEMP;
+       xc_program_binOpRetire(&res2);
+       res.t = XC_RESULT_TEMP;
        bc_vec_push(&G.prog.results, &res);
 
        RETURN_STATUS(s);
@@ -6092,23 +6304,23 @@ static BC_STATUS zdc_program_modexp(void)
 
        if (!STACK_HAS_MORE_THAN(&G.prog.results, 2))
                RETURN_STATUS(bc_error_stack_has_too_few_elements());
-       s = zbc_program_binOpPrep(&r2, &n2, &r3, &n3, false);
+       s = zxc_program_binOpPrep(&r2, &n2, &r3, &n3, false);
        if (s) RETURN_STATUS(s);
 
        r1 = bc_vec_item_rev(&G.prog.results, 2);
-       s = zbc_program_num(r1, &n1, false);
+       s = zxc_program_num(r1, &n1);
        if (s) RETURN_STATUS(s);
        if (!BC_PROG_NUM(r1, n1))
                RETURN_STATUS(bc_error_variable_is_wrong_type());
 
        // Make sure that the values have their pointers updated, if necessary.
-       if (r1->t == BC_RESULT_VAR || r1->t == BC_RESULT_ARRAY_ELEM) {
+       if (r1->t == XC_RESULT_VAR || r1->t == XC_RESULT_ARRAY_ELEM) {
                if (r1->t == r2->t) {
-                       s = zbc_program_num(r2, &n2, false);
+                       s = zxc_program_num(r2, &n2);
                        if (s) RETURN_STATUS(s);
                }
                if (r1->t == r3->t) {
-                       s = zbc_program_num(r3, &n3, false);
+                       s = zxc_program_num(r3, &n3);
                        if (s) RETURN_STATUS(s);
                }
        }
@@ -6118,7 +6330,7 @@ static BC_STATUS zdc_program_modexp(void)
        if (s) goto err;
 
        bc_vec_pop(&G.prog.results);
-       bc_program_binOpRetire(&res);
+       xc_program_binOpRetire(&res);
 
        RETURN_STATUS(s);
  err:
@@ -6132,7 +6344,7 @@ static void dc_program_stackLen(void)
        BcResult res;
        size_t len = G.prog.results.len;
 
-       res.t = BC_RESULT_TEMP;
+       res.t = XC_RESULT_TEMP;
 
        bc_num_init_DEF_SIZE(&res.d.n);
        bc_num_ulong2num(&res.d.n, len);
@@ -6148,16 +6360,16 @@ static BC_STATUS zdc_program_asciify(void)
        char *str;
        char c;
        size_t idx;
-       unsigned long val;
 
        if (!STACK_HAS_MORE_THAN(&G.prog.results, 0))
                RETURN_STATUS(bc_error_stack_has_too_few_elements());
-       r = bc_vec_top(&G.prog.results);
 
-       s = zbc_program_num(r, &num, false);
+       r = bc_vec_top(&G.prog.results);
+       s = zxc_program_num(r, &num);
        if (s) RETURN_STATUS(s);
 
        if (BC_PROG_NUM(r, num)) {
+               unsigned long val;
                BcNum strmb;
                BcDig strmb_digs[ULONG_NUM_BUFSIZE];
 
@@ -6168,8 +6380,8 @@ static BC_STATUS zdc_program_asciify(void)
                strmb.cap = ARRAY_SIZE(strmb_digs);
                strmb.num = strmb_digs;
                bc_num_ulong2num(&strmb, 0x100);
-               s = zbc_num_mod(&n, &strmb, &n, 0);
 
+               s = zbc_num_mod(&n, &strmb, &n, 0);
                if (s) goto num_err;
                s = zbc_num_ulong(&n, &val);
                if (s) goto num_err;
@@ -6179,8 +6391,8 @@ static BC_STATUS zdc_program_asciify(void)
                bc_num_free(&n);
        } else {
                char *sp;
-               idx = (r->t == BC_RESULT_STR) ? r->d.id.idx : num->rdx;
-               sp = *bc_program_str(idx);
+               idx = (r->t == XC_RESULT_STR) ? r->d.id.idx : num->rdx;
+               sp = *xc_program_str(idx);
                c = sp[0];
        }
 
@@ -6193,9 +6405,13 @@ static BC_STATUS zdc_program_asciify(void)
        str = xzalloc(2);
        str[0] = c;
        //str[1] = '\0'; - already is
-       bc_vec_push(&G.prog.strs, &str);
+       idx = bc_vec_push(&G.prog.strs, &str);
+       // Add an empty function so that if zdc_program_execStr ever needs to
+       // parse the string into code (from the 'x' command) there's somewhere
+       // to store the bytecode.
+       xc_program_add_fn();
  dup:
-       res.t = BC_RESULT_STR;
+       res.t = XC_RESULT_STR;
        res.d.id.idx = idx;
        bc_result_pop_and_push(&res);
 
@@ -6217,15 +6433,15 @@ static BC_STATUS zdc_program_printStream(void)
                RETURN_STATUS(bc_error_stack_has_too_few_elements());
        r = bc_vec_top(&G.prog.results);
 
-       s = zbc_program_num(r, &n, false);
+       s = zxc_program_num(r, &n);
        if (s) RETURN_STATUS(s);
 
        if (BC_PROG_NUM(r, n)) {
-               s = zbc_num_printNum(n, 0x100, 1, dc_num_printChar);
+               s = zxc_num_printNum(n, 0x100, 1, dc_num_printChar);
        } else {
                char *str;
-               idx = (r->t == BC_RESULT_STR) ? r->d.id.idx : n->rdx;
-               str = *bc_program_str(idx);
+               idx = (r->t == XC_RESULT_STR) ? r->d.id.idx : n->rdx;
+               str = *xc_program_str(idx);
                fputs(str, stdout);
        }
 
@@ -6237,10 +6453,10 @@ static BC_STATUS zdc_program_nquit(void)
 {
        BcStatus s;
        BcResult *opnd;
-       BcNum *num = NULL;
+       BcNum *num;
        unsigned long val;
 
-       s = zbc_program_prep(&opnd, &num);
+       s = zxc_program_prep(&opnd, &num);
        if (s) RETURN_STATUS(s);
        s = zbc_num_ulong(num, &val);
        if (s) RETURN_STATUS(s);
@@ -6276,13 +6492,13 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond)
                BcNum *n = n; // for compiler
                bool exec;
                char *name;
-               char *then_name = bc_program_name(code, bgn);
+               char *then_name = xc_program_name(code, bgn);
                char *else_name = NULL;
 
-               if (code[*bgn] == BC_PARSE_STREND)
+               if (code[*bgn] == '\0')
                        (*bgn) += 1;
                else
-                       else_name = bc_program_name(code, bgn);
+                       else_name = xc_program_name(code, bgn);
 
                exec = r->d.n.len != 0;
                name = then_name;
@@ -6293,7 +6509,7 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond)
 
                if (exec) {
                        BcVec *v;
-                       v = bc_program_search(name, true);
+                       v = xc_program_search(name, BC_TYPE_VAR);
                        n = bc_vec_top(v);
                }
 
@@ -6308,46 +6524,43 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond)
 
                sidx = n->rdx;
        } else {
-               if (r->t == BC_RESULT_STR) {
+               if (r->t == XC_RESULT_STR) {
                        sidx = r->d.id.idx;
-               } else if (r->t == BC_RESULT_VAR) {
+               } else if (r->t == XC_RESULT_VAR) {
                        BcNum *n;
-                       s = zbc_program_num(r, &n, false);
+                       s = zxc_program_num(r, &n);
                        if (s || !BC_PROG_STR(n)) goto exit;
                        sidx = n->rdx;
                } else
-                       goto exit;
+                       goto exit_nopop;
        }
 
        fidx = sidx + BC_PROG_REQ_FUNCS;
 
-       f = bc_program_func(fidx);
+       f = xc_program_func(fidx);
 
        if (f->code.len == 0) {
-               FILE *sv_input_fp;
-               BcParse prs;
+               BcParse sv_parse;
                char *str;
 
-               bc_parse_create(&prs, fidx);
-               str = *bc_program_str(sidx);
-               s = zbc_parse_text_init(&prs, str);
+               sv_parse = G.prs; // struct copy
+               xc_parse_create(fidx);
+               str = *xc_program_str(sidx);
+               s = zxc_parse_text_init(str);
                if (s) goto err;
 
-               sv_input_fp = G.input_fp;
-               G.input_fp = NULL; // "do not read from input file when <EOL> reached"
-               s = zdc_parse_exprs_until_eof(&prs);
-               G.input_fp = sv_input_fp;
-
+               s = zdc_parse_exprs_until_eof();
                if (s) goto err;
-               if (prs.l.t.t != BC_LEX_EOF) {
+               xc_parse_push(DC_INST_POP_EXEC);
+               if (G.prs.lex != XC_LEX_EOF)
                        s = bc_error_bad_expression();
+               xc_parse_free();
+               G.prs = sv_parse; // struct copy
+               if (s) {
  err:
-                       bc_parse_free(&prs);
                        bc_vec_pop_all(&f->code);
                        goto exit;
                }
-               bc_parse_push(&prs, BC_INST_POP_EXEC);
-               bc_parse_free(&prs);
        }
 
        ip.inst_idx = 0;
@@ -6359,20 +6572,21 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond)
        RETURN_STATUS(BC_STATUS_SUCCESS);
  exit:
        bc_vec_pop(&G.prog.results);
+ exit_nopop:
        RETURN_STATUS(s);
 }
 #define zdc_program_execStr(...) (zdc_program_execStr(__VA_ARGS__) COMMA_SUCCESS)
 #endif // ENABLE_DC
 
-static void bc_program_pushGlobal(char inst)
+static void xc_program_pushGlobal(char inst)
 {
        BcResult res;
        unsigned long val;
 
-       res.t = inst - BC_INST_IBASE + BC_RESULT_IBASE;
-       if (inst == BC_INST_IBASE)
+       res.t = inst - XC_INST_IBASE + XC_RESULT_IBASE;
+       if (inst == XC_INST_IBASE)
                val = (unsigned long) G.prog.ib_t;
-       else if (inst == BC_INST_SCALE)
+       else if (inst == XC_INST_SCALE)
                val = (unsigned long) G.prog.scale;
        else
                val = (unsigned long) G.prog.ob_t;
@@ -6382,12 +6596,11 @@ static void bc_program_pushGlobal(char inst)
        bc_vec_push(&G.prog.results, &res);
 }
 
-static BC_STATUS zbc_program_exec(void)
+static BC_STATUS zxc_program_exec(void)
 {
        BcResult r, *ptr;
-       BcNum *num;
        BcInstPtr *ip = bc_vec_top(&G.prog.exestack);
-       BcFunc *func = bc_program_func(ip->func);
+       BcFunc *func = xc_program_func(ip->func);
        char *code = func->code.v;
 
        dbg_exec("func:%zd bytes:%zd ip:%zd results.len:%d",
@@ -6398,246 +6611,255 @@ static BC_STATUS zbc_program_exec(void)
 
                dbg_exec("inst at %zd:%d results.len:%d", ip->inst_idx - 1, inst, G.prog.results.len);
                switch (inst) {
-#if ENABLE_BC
-                       case BC_INST_JUMP_ZERO: {
-                               bool zero;
-                               dbg_exec("BC_INST_JUMP_ZERO:");
-                               s = zbc_program_prep(&ptr, &num);
-                               if (s) RETURN_STATUS(s);
-                               zero = (bc_num_cmp(num, &G.prog.zero) == 0);
-                               bc_vec_pop(&G.prog.results);
-                               if (!zero) {
-                                       bc_program_index(code, &ip->inst_idx);
-                                       break;
-                               }
-                               // else: fall through
+               case XC_INST_RET:
+                       if (IS_DC) { // end of '?' reached
+                               bc_vec_pop(&G.prog.exestack);
+                               goto read_updated_ip;
                        }
-                       case BC_INST_JUMP: {
-                               size_t idx = bc_program_index(code, &ip->inst_idx);
-                               size_t *addr = bc_vec_item(&func->labels, idx);
-                               dbg_exec("BC_INST_JUMP: to %ld", (long)*addr);
-                               ip->inst_idx = *addr;
+                       // bc: fall through
+#if ENABLE_BC
+               case BC_INST_RET0:
+                       dbg_exec("BC_INST_RET[0]:");
+                       s = zbc_program_return(inst);
+                       goto read_updated_ip;
+               case BC_INST_JUMP_ZERO: {
+                       BcNum *num;
+                       bool zero;
+                       dbg_exec("BC_INST_JUMP_ZERO:");
+                       s = zxc_program_prep(&ptr, &num);
+                       if (s) RETURN_STATUS(s);
+                       zero = (bc_num_cmp(num, &G.prog.zero) == 0);
+                       bc_vec_pop(&G.prog.results);
+                       if (!zero) {
+                               xc_program_index(code, &ip->inst_idx);
                                break;
                        }
-                       case BC_INST_CALL:
-                               dbg_exec("BC_INST_CALL:");
-                               s = zbc_program_call(code, &ip->inst_idx);
-                               goto read_updated_ip;
-                       case BC_INST_INC_PRE:
-                       case BC_INST_DEC_PRE:
-                       case BC_INST_INC_POST:
-                       case BC_INST_DEC_POST:
-                               dbg_exec("BC_INST_INCDEC:");
-                               s = zbc_program_incdec(inst);
-                               break;
-                       case BC_INST_HALT:
-                               dbg_exec("BC_INST_HALT:");
-                               QUIT_OR_RETURN_TO_MAIN;
-                               break;
-                       case BC_INST_RET:
-                       case BC_INST_RET0:
-                               dbg_exec("BC_INST_RET[0]:");
-                               s = zbc_program_return(inst);
-                               goto read_updated_ip;
-                       case BC_INST_BOOL_OR:
-                       case BC_INST_BOOL_AND:
+                       // else: fall through
+               }
+               case BC_INST_JUMP: {
+                       size_t idx = xc_program_index(code, &ip->inst_idx);
+                       size_t *addr = bc_vec_item(&func->labels, idx);
+                       dbg_exec("BC_INST_JUMP: to %ld", (long)*addr);
+                       ip->inst_idx = *addr;
+                       break;
+               }
+               case BC_INST_CALL:
+                       dbg_exec("BC_INST_CALL:");
+                       s = zbc_program_call(code, &ip->inst_idx);
+                       goto read_updated_ip;
+               case BC_INST_INC_PRE:
+               case BC_INST_DEC_PRE:
+               case BC_INST_INC_POST:
+               case BC_INST_DEC_POST:
+                       dbg_exec("BC_INST_INCDEC:");
+                       s = zbc_program_incdec(inst);
+                       break;
+               case BC_INST_HALT:
+                       dbg_exec("BC_INST_HALT:");
+                       QUIT_OR_RETURN_TO_MAIN;
+                       break;
+               case XC_INST_BOOL_OR:
+               case XC_INST_BOOL_AND:
 #endif // ENABLE_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:
-                               dbg_exec("BC_INST_BOOL:");
-                               s = zbc_program_logical(inst);
-                               break;
-                       case BC_INST_READ:
-                               dbg_exec("BC_INST_READ:");
-                               s = zbc_program_read();
-                               goto read_updated_ip;
-                       case BC_INST_VAR:
-                               dbg_exec("BC_INST_VAR:");
-                               s = zbc_program_pushVar(code, &ip->inst_idx, false, false);
-                               break;
-                       case BC_INST_ARRAY_ELEM:
-                       case BC_INST_ARRAY:
-                               dbg_exec("BC_INST_ARRAY[_ELEM]:");
-                               s = zbc_program_pushArray(code, &ip->inst_idx, inst);
-                               break;
-                       case BC_INST_LAST:
-//TODO: this can't happen on dc, right?
-                               dbg_exec("BC_INST_LAST:");
-                               r.t = BC_RESULT_LAST;
-                               bc_vec_push(&G.prog.results, &r);
-                               break;
-                       case BC_INST_IBASE:
-                       case BC_INST_SCALE:
-                       case BC_INST_OBASE:
-                               dbg_exec("BC_INST_internalvar:");
-                               bc_program_pushGlobal(inst);
-                               break;
-                       case BC_INST_SCALE_FUNC:
-                       case BC_INST_LENGTH:
-                       case BC_INST_SQRT:
-                               dbg_exec("BC_INST_builtin:");
-                               s = zbc_program_builtin(inst);
-                               break;
-                       case BC_INST_NUM:
-                               dbg_exec("BC_INST_NUM:");
-                               r.t = BC_RESULT_CONSTANT;
-                               r.d.id.idx = bc_program_index(code, &ip->inst_idx);
-                               bc_vec_push(&G.prog.results, &r);
-                               break;
-                       case BC_INST_POP:
-                               dbg_exec("BC_INST_POP:");
-                               if (!STACK_HAS_MORE_THAN(&G.prog.results, 0))
-                                       s = bc_error_stack_has_too_few_elements();
-                               else
-                                       bc_vec_pop(&G.prog.results);
-                               break;
-                       case BC_INST_PRINT:
-                       case BC_INST_PRINT_POP:
-                       case BC_INST_PRINT_STR:
-                               dbg_exec("BC_INST_PRINTxyz:");
-                               s = zbc_program_print(inst, 0);
-                               break;
-                       case BC_INST_STR:
-                               dbg_exec("BC_INST_STR:");
-                               r.t = BC_RESULT_STR;
-                               r.d.id.idx = bc_program_index(code, &ip->inst_idx);
-                               bc_vec_push(&G.prog.results, &r);
-                               break;
-                       case BC_INST_POWER:
-                       case BC_INST_MULTIPLY:
-                       case BC_INST_DIVIDE:
-                       case BC_INST_MODULUS:
-                       case BC_INST_PLUS:
-                       case BC_INST_MINUS:
-                               dbg_exec("BC_INST_binaryop:");
-                               s = zbc_program_op(inst);
-                               break;
-                       case BC_INST_BOOL_NOT:
-                               dbg_exec("BC_INST_BOOL_NOT:");
-                               s = zbc_program_prep(&ptr, &num);
-                               if (s) RETURN_STATUS(s);
-                               bc_num_init_DEF_SIZE(&r.d.n);
-                               if (!bc_num_cmp(num, &G.prog.zero))
-                                       bc_num_one(&r.d.n);
-                               //else bc_num_zero(&r.d.n); - already is
-                               bc_program_retire(&r, BC_RESULT_TEMP);
-                               break;
-                       case BC_INST_NEG:
-                               dbg_exec("BC_INST_NEG:");
-                               s = zbc_program_negate();
-                               break;
+               case XC_INST_REL_EQ:
+               case XC_INST_REL_LE:
+               case XC_INST_REL_GE:
+               case XC_INST_REL_NE:
+               case XC_INST_REL_LT:
+               case XC_INST_REL_GT:
+                       dbg_exec("BC_INST_BOOL:");
+                       s = zxc_program_logical(inst);
+                       break;
+               case XC_INST_READ:
+                       dbg_exec("XC_INST_READ:");
+                       s = zxc_program_read();
+                       goto read_updated_ip;
+               case XC_INST_VAR:
+                       dbg_exec("XC_INST_VAR:");
+                       s = zxc_program_pushVar(code, &ip->inst_idx, false, false);
+                       break;
+               case XC_INST_ARRAY_ELEM:
+               case XC_INST_ARRAY:
+                       dbg_exec("XC_INST_ARRAY[_ELEM]:");
+                       s = zbc_program_pushArray(code, &ip->inst_idx, inst);
+                       break;
 #if ENABLE_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:
+               case BC_INST_LAST:
+                       dbg_exec("BC_INST_LAST:");
+                       r.t = BC_RESULT_LAST;
+                       bc_vec_push(&G.prog.results, &r);
+                       break;
 #endif
-                       case BC_INST_ASSIGN:
-                               dbg_exec("BC_INST_ASSIGNxyz:");
-                               s = zbc_program_assign(inst);
-                               break;
+               case XC_INST_IBASE:
+               case XC_INST_OBASE:
+               case XC_INST_SCALE:
+                       dbg_exec("XC_INST_internalvar(%d):", inst - XC_INST_IBASE);
+                       xc_program_pushGlobal(inst);
+                       break;
+               case XC_INST_SCALE_FUNC:
+               case XC_INST_LENGTH:
+               case XC_INST_SQRT:
+                       dbg_exec("BC_INST_builtin:");
+                       s = zxc_program_builtin(inst);
+                       break;
+               case XC_INST_NUM:
+                       dbg_exec("XC_INST_NUM:");
+                       r.t = XC_RESULT_CONSTANT;
+                       r.d.id.idx = xc_program_index(code, &ip->inst_idx);
+                       bc_vec_push(&G.prog.results, &r);
+                       break;
+               case XC_INST_POP:
+                       dbg_exec("XC_INST_POP:");
+                       if (!STACK_HAS_MORE_THAN(&G.prog.results, 0))
+                               s = bc_error_stack_has_too_few_elements();
+                       else
+                               bc_vec_pop(&G.prog.results);
+                       break;
+               case XC_INST_PRINT:
+               case XC_INST_PRINT_POP:
+               case XC_INST_PRINT_STR:
+                       dbg_exec("XC_INST_PRINTxyz(%d):", inst - XC_INST_PRINT);
+                       s = zxc_program_print(inst, 0);
+                       break;
+               case XC_INST_STR:
+                       dbg_exec("XC_INST_STR:");
+                       r.t = XC_RESULT_STR;
+                       r.d.id.idx = xc_program_index(code, &ip->inst_idx);
+                       bc_vec_push(&G.prog.results, &r);
+                       break;
+               case XC_INST_POWER:
+               case XC_INST_MULTIPLY:
+               case XC_INST_DIVIDE:
+               case XC_INST_MODULUS:
+               case XC_INST_PLUS:
+               case XC_INST_MINUS:
+                       dbg_exec("BC_INST_binaryop:");
+                       s = zxc_program_op(inst);
+                       break;
+               case XC_INST_BOOL_NOT: {
+                       BcNum *num;
+                       dbg_exec("XC_INST_BOOL_NOT:");
+                       s = zxc_program_prep(&ptr, &num);
+                       if (s) RETURN_STATUS(s);
+                       bc_num_init_DEF_SIZE(&r.d.n);
+                       if (bc_num_cmp(num, &G.prog.zero) == 0)
+                               bc_num_one(&r.d.n);
+                       //else bc_num_zero(&r.d.n); - already is
+                       xc_program_retire(&r, XC_RESULT_TEMP);
+                       break;
+               }
+               case XC_INST_NEG:
+                       dbg_exec("XC_INST_NEG:");
+                       s = zxc_program_negate();
+                       break;
+#if ENABLE_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
+               case XC_INST_ASSIGN:
+                       dbg_exec("BC_INST_ASSIGNxyz:");
+                       s = zxc_program_assign(inst);
+                       break;
 #if ENABLE_DC
-                       case BC_INST_POP_EXEC:
-                               dbg_exec("BC_INST_POP_EXEC:");
-                               bc_vec_pop(&G.prog.exestack);
-                               goto read_updated_ip;
-                       case BC_INST_MODEXP:
-                               dbg_exec("BC_INST_MODEXP:");
-                               s = zdc_program_modexp();
-                               break;
-                       case BC_INST_DIVMOD:
-                               dbg_exec("BC_INST_DIVMOD:");
-                               s = zdc_program_divmod();
-                               break;
-                       case BC_INST_EXECUTE:
-                       case BC_INST_EXEC_COND:
-                               dbg_exec("BC_INST_EXEC[_COND]:");
-                               s = zdc_program_execStr(code, &ip->inst_idx, inst == BC_INST_EXEC_COND);
-                               goto read_updated_ip;
-                       case BC_INST_PRINT_STACK: {
-                               size_t idx;
-                               dbg_exec("BC_INST_PRINT_STACK:");
-                               for (idx = 0; idx < G.prog.results.len; ++idx) {
-                                       s = zbc_program_print(BC_INST_PRINT, idx);
-                                       if (s) break;
-                               }
-                               break;
-                       }
-                       case BC_INST_CLEAR_STACK:
-                               dbg_exec("BC_INST_CLEAR_STACK:");
-                               bc_vec_pop_all(&G.prog.results);
-                               break;
-                       case BC_INST_STACK_LEN:
-                               dbg_exec("BC_INST_STACK_LEN:");
-                               dc_program_stackLen();
-                               break;
-                       case BC_INST_DUPLICATE:
-                               dbg_exec("BC_INST_DUPLICATE:");
-                               if (!STACK_HAS_MORE_THAN(&G.prog.results, 0))
-                                       RETURN_STATUS(bc_error_stack_has_too_few_elements());
-                               ptr = bc_vec_top(&G.prog.results);
-                               dc_result_copy(&r, ptr);
-                               bc_vec_push(&G.prog.results, &r);
-                               break;
-                       case BC_INST_SWAP: {
-                               BcResult *ptr2;
-                               dbg_exec("BC_INST_SWAP:");
-                               if (!STACK_HAS_MORE_THAN(&G.prog.results, 1))
-                                       RETURN_STATUS(bc_error_stack_has_too_few_elements());
-                               ptr = bc_vec_item_rev(&G.prog.results, 0);
-                               ptr2 = bc_vec_item_rev(&G.prog.results, 1);
-                               memcpy(&r, ptr, sizeof(BcResult));
-                               memcpy(ptr, ptr2, sizeof(BcResult));
-                               memcpy(ptr2, &r, sizeof(BcResult));
-                               break;
-                       }
-                       case BC_INST_ASCIIFY:
-                               dbg_exec("BC_INST_ASCIIFY:");
-                               s = zdc_program_asciify();
-                               break;
-                       case BC_INST_PRINT_STREAM:
-                               dbg_exec("BC_INST_STREAM:");
-                               s = zdc_program_printStream();
-                               break;
-                       case BC_INST_LOAD:
-                       case BC_INST_PUSH_VAR: {
-                               bool copy = inst == BC_INST_LOAD;
-                               s = zbc_program_pushVar(code, &ip->inst_idx, true, copy);
-                               break;
-                       }
-                       case BC_INST_PUSH_TO_VAR: {
-                               char *name = bc_program_name(code, &ip->inst_idx);
-                               s = zbc_program_copyToVar(name, true);
-                               free(name);
-                               break;
+               case DC_INST_POP_EXEC:
+                       dbg_exec("DC_INST_POP_EXEC:");
+                       bc_vec_pop(&G.prog.exestack);
+                       goto read_updated_ip;
+               case DC_INST_MODEXP:
+                       dbg_exec("DC_INST_MODEXP:");
+                       s = zdc_program_modexp();
+                       break;
+               case DC_INST_DIVMOD:
+                       dbg_exec("DC_INST_DIVMOD:");
+                       s = zdc_program_divmod();
+                       break;
+               case DC_INST_EXECUTE:
+               case DC_INST_EXEC_COND:
+                       dbg_exec("DC_INST_EXEC[_COND]:");
+                       s = zdc_program_execStr(code, &ip->inst_idx, inst == DC_INST_EXEC_COND);
+                       goto read_updated_ip;
+               case DC_INST_PRINT_STACK: {
+                       size_t idx;
+                       dbg_exec("DC_INST_PRINT_STACK:");
+                       for (idx = 0; idx < G.prog.results.len; ++idx) {
+                               s = zxc_program_print(XC_INST_PRINT, idx);
+                               if (s) break;
                        }
-                       case BC_INST_QUIT:
-                               dbg_exec("BC_INST_QUIT:");
-                               if (G.prog.exestack.len <= 2)
-                                       QUIT_OR_RETURN_TO_MAIN;
-                               bc_vec_npop(&G.prog.exestack, 2);
-                               goto read_updated_ip;
-                       case BC_INST_NQUIT:
-                               dbg_exec("BC_INST_NQUIT:");
-                               s = zdc_program_nquit();
-                               //goto read_updated_ip; - just fall through to it
+                       break;
+               }
+               case DC_INST_CLEAR_STACK:
+                       dbg_exec("DC_INST_CLEAR_STACK:");
+                       bc_vec_pop_all(&G.prog.results);
+                       break;
+               case DC_INST_STACK_LEN:
+                       dbg_exec("DC_INST_STACK_LEN:");
+                       dc_program_stackLen();
+                       break;
+               case DC_INST_DUPLICATE:
+                       dbg_exec("DC_INST_DUPLICATE:");
+                       if (!STACK_HAS_MORE_THAN(&G.prog.results, 0))
+                               RETURN_STATUS(bc_error_stack_has_too_few_elements());
+                       ptr = bc_vec_top(&G.prog.results);
+                       dc_result_copy(&r, ptr);
+                       bc_vec_push(&G.prog.results, &r);
+                       break;
+               case DC_INST_SWAP: {
+                       BcResult *ptr2;
+                       dbg_exec("DC_INST_SWAP:");
+                       if (!STACK_HAS_MORE_THAN(&G.prog.results, 1))
+                               RETURN_STATUS(bc_error_stack_has_too_few_elements());
+                       ptr = bc_vec_item_rev(&G.prog.results, 0);
+                       ptr2 = bc_vec_item_rev(&G.prog.results, 1);
+                       memcpy(&r, ptr, sizeof(BcResult));
+                       memcpy(ptr, ptr2, sizeof(BcResult));
+                       memcpy(ptr2, &r, sizeof(BcResult));
+                       break;
+               }
+               case DC_INST_ASCIIFY:
+                       dbg_exec("DC_INST_ASCIIFY:");
+                       s = zdc_program_asciify();
+                       break;
+               case DC_INST_PRINT_STREAM:
+                       dbg_exec("DC_INST_PRINT_STREAM:");
+                       s = zdc_program_printStream();
+                       break;
+               case DC_INST_LOAD:
+               case DC_INST_PUSH_VAR: {
+                       bool copy = inst == DC_INST_LOAD;
+                       s = zxc_program_pushVar(code, &ip->inst_idx, true, copy);
+                       break;
+               }
+               case DC_INST_PUSH_TO_VAR: {
+                       char *name = xc_program_name(code, &ip->inst_idx);
+                       s = zxc_program_popResultAndCopyToVar(name, BC_TYPE_VAR);
+                       free(name);
+                       break;
+               }
+               case DC_INST_QUIT:
+                       dbg_exec("DC_INST_QUIT:");
+                       if (G.prog.exestack.len <= 2)
+                               QUIT_OR_RETURN_TO_MAIN;
+                       bc_vec_npop(&G.prog.exestack, 2);
+                       goto read_updated_ip;
+               case DC_INST_NQUIT:
+                       dbg_exec("DC_INST_NQUIT:");
+                       s = zdc_program_nquit();
+                       //goto read_updated_ip; - just fall through to it
 #endif // ENABLE_DC
  read_updated_ip:
-                               // Instruction stack has changed, read new pointers
-                               ip = bc_vec_top(&G.prog.exestack);
-                               func = bc_program_func(ip->func);
-                               code = func->code.v;
-                               dbg_exec("func:%zd bytes:%zd ip:%zd", ip->func, func->code.len, ip->inst_idx);
+                       // Instruction stack has changed, read new pointers
+                       ip = bc_vec_top(&G.prog.exestack);
+                       func = xc_program_func(ip->func);
+                       code = func->code.v;
+                       dbg_exec("func:%zd bytes:%zd ip:%zd", ip->func, func->code.len, ip->inst_idx);
                }
 
                if (s || G_interrupt) {
-                       bc_program_reset();
+                       xc_program_reset();
                        RETURN_STATUS(s);
                }
 
@@ -6646,9 +6868,9 @@ static BC_STATUS zbc_program_exec(void)
 
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
-#define zbc_program_exec(...) (zbc_program_exec(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_exec(...) (zxc_program_exec(__VA_ARGS__) COMMA_SUCCESS)
 
-static unsigned bc_vm_envLen(const char *var)
+static unsigned xc_vm_envLen(const char *var)
 {
        char *lenv;
        unsigned len;
@@ -6664,49 +6886,62 @@ static unsigned bc_vm_envLen(const char *var)
        return len;
 }
 
-static BC_STATUS zbc_vm_process(const char *text)
+static BC_STATUS zxc_vm_process(const char *text)
 {
        BcStatus s;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
-       s = zbc_parse_text_init(&G.prs, text); // does the first zbc_lex_next()
+       s = zxc_parse_text_init(text); // does the first zxc_lex_next()
        if (s) RETURN_STATUS(s);
 
-       while (G.prs.l.t.t != BC_LEX_EOF) {
+       while (G.prs.lex != XC_LEX_EOF) {
                BcInstPtr *ip;
                BcFunc *f;
 
-               dbg_lex("%s:%d G.prs.l.t.t:%d, parsing...", __func__, __LINE__, G.prs.l.t.t);
+               dbg_lex("%s:%d G.prs.lex:%d, parsing...", __func__, __LINE__, G.prs.lex);
                if (IS_BC) {
-// FIXME: "eating" of stmt delimiters is coded inconsistently
-// (sometimes zbc_parse_stmt() eats the delimiter, sometimes don't),
-// which causes bugs such as "print 1 print 2" erroneously accepted,
-// or "print 1 else 2" detecting parse error only after executing
-// "print 1" part.
-                       IF_BC(s = zbc_parse_stmt_or_funcdef(&G.prs));
+#if ENABLE_BC
+                       s = zbc_parse_stmt_or_funcdef();
+                       if (s) goto err;
+
+                       // Check that next token is a correct stmt delimiter -
+                       // disallows "print 1 print 2" and such.
+                       if (G.prs.lex != BC_LEX_SCOLON
+                        && G.prs.lex != XC_LEX_NLINE
+                        && G.prs.lex != XC_LEX_EOF
+                       ) {
+                               bc_error_at("bad statement terminator");
+                               goto err;
+                       }
+                       // The above logic is fragile. Check these examples:
+                       // - interactive read() still works
+#endif
                } else {
-                       IF_DC(s = zdc_parse_expr(&G.prs));
+#if ENABLE_DC
+                       s = zdc_parse_expr();
+#endif
                }
                if (s || G_interrupt) {
-                       bc_parse_reset(&G.prs); // includes bc_program_reset()
+ err:
+                       xc_parse_reset(); // includes xc_program_reset()
                        RETURN_STATUS(BC_STATUS_FAILURE);
                }
 
                dbg_lex("%s:%d executing...", __func__, __LINE__);
-               s = zbc_program_exec();
+               s = zxc_program_exec();
                if (s) {
-                       bc_program_reset();
+                       xc_program_reset();
                        break;
                }
 
                ip = (void*)G.prog.exestack.v;
 #if SANITY_CHECKS
                if (G.prog.exestack.len != 1) // should have only main's IP
-                       bb_error_msg_and_die("BUG:call stack");
+                       bb_simple_error_msg_and_die("BUG:call stack");
                if (ip->func != BC_PROG_MAIN)
-                       bb_error_msg_and_die("BUG:not MAIN");
+                       bb_simple_error_msg_and_die("BUG:not MAIN");
 #endif
-               f = bc_program_func_BC_PROG_MAIN();
+               f = xc_program_func_BC_PROG_MAIN();
                // bc discards strings, constants and code after each
                // top-level statement in the "main program".
                // This prevents "yes 1 | bc" from growing its memory
@@ -6720,18 +6955,14 @@ static BC_STATUS zbc_vm_process(const char *text)
                if (IS_BC) {
 #if SANITY_CHECKS
                        if (G.prog.results.len != 0) // should be empty
-                               bb_error_msg_and_die("BUG:data stack");
+                               bb_simple_error_msg_and_die("BUG:data stack");
 #endif
                        IF_BC(bc_vec_pop_all(&f->strs);)
                        IF_BC(bc_vec_pop_all(&f->consts);)
+                       // We are at SCOLON/NLINE, skip it:
+                       s = zxc_lex_next();
+                       if (s) goto err;
                } else {
-                       // Most of dc parsing assumes all whitespace,
-                       // including '\n', is eaten.
-                       if (G.prs.l.t.t == BC_LEX_NLINE) {
-                               s = zbc_lex_next(&G.prs.l);
-                               if (s) RETURN_STATUS(s);
-                       }
-
                        if (G.prog.results.len == 0
                         && G.prog.vars.len == 0
                        ) {
@@ -6756,49 +6987,51 @@ static BC_STATUS zbc_vm_process(const char *text)
        dbg_lex_done("%s:%d done", __func__, __LINE__);
        RETURN_STATUS(s);
 }
-#define zbc_vm_process(...) (zbc_vm_process(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_vm_process(...) (zxc_vm_process(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_vm_execute_FILE(FILE *fp, const char *filename)
+static BC_STATUS zxc_vm_execute_FILE(FILE *fp, const char *filename)
 {
        // So far bc/dc have no way to include a file from another file,
-       // therefore we know G.prog.file == NULL on entry
+       // therefore we know G.prs.lex_filename == NULL on entry
        //const char *sv_file;
        BcStatus s;
 
-       G.prog.file = filename;
-       G.input_fp = fp;
-       bc_lex_file(&G.prs.l);
+       G.prs.lex_filename = filename;
+       G.prs.lex_input_fp = fp;
+       G.err_line = G.prs.lex_line = 1;
+       dbg_lex("p->lex_line reset to 1");
 
        do {
-               s = zbc_vm_process("");
+               s = zxc_vm_process("");
                // We do not stop looping on errors here if reading stdin.
                // Example: start interactive bc and enter "return".
                // It should say "'return' not in a function"
                // but should not exit.
-       } while (G.input_fp == stdin);
-       G.prog.file = NULL;
+       } while (G.prs.lex_input_fp == stdin);
+       G.prs.lex_filename = NULL;
        RETURN_STATUS(s);
 }
-#define zbc_vm_execute_FILE(...) (zbc_vm_execute_FILE(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_vm_execute_FILE(...) (zxc_vm_execute_FILE(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_vm_file(const char *file)
+static BC_STATUS zxc_vm_file(const char *file)
 {
        BcStatus s;
        FILE *fp;
 
        fp = xfopen_for_read(file);
-       s = zbc_vm_execute_FILE(fp, file);
+       s = zxc_vm_execute_FILE(fp, file);
        fclose(fp);
 
        RETURN_STATUS(s);
 }
-#define zbc_vm_file(...) (zbc_vm_file(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_vm_file(...) (zxc_vm_file(__VA_ARGS__) COMMA_SUCCESS)
 
 #if ENABLE_BC
 static void bc_vm_info(void)
 {
        printf("%s "BB_VER"\n"
-               "Copyright (c) 2018 Gavin D. Howard and contributors\n"
+               "Adapted from https://github.com/gavinhoward/bc\n"
+               "Original code (c) 2018 Gavin D. Howard and contributors\n"
        , applet_name);
 }
 
@@ -7037,7 +7270,7 @@ static const char bc_lib[] ALIGN1 = {
 };
 #endif // ENABLE_BC
 
-static BC_STATUS zbc_vm_exec(void)
+static BC_STATUS zxc_vm_exec(void)
 {
        char **fname;
        BcStatus s;
@@ -7048,8 +7281,7 @@ static BC_STATUS zbc_vm_exec(void)
                // We know that internal library is not buggy,
                // thus error checking is normally disabled.
 # define DEBUG_LIB 0
-               bc_lex_file(&G.prs.l);
-               s = zbc_vm_process(bc_lib);
+               s = zxc_vm_process(bc_lib);
                if (DEBUG_LIB && s) RETURN_STATUS(s);
        }
 #endif
@@ -7057,7 +7289,7 @@ static BC_STATUS zbc_vm_exec(void)
        s = BC_STATUS_SUCCESS;
        fname = (void*)G.files.v;
        for (i = 0; i < G.files.len; i++) {
-               s = zbc_vm_file(*fname++);
+               s = zxc_vm_file(*fname++);
                if (ENABLE_FEATURE_CLEAN_UP && !G_ttyin && s) {
                        // Debug config, non-interactive mode:
                        // return all the way back to main.
@@ -7068,14 +7300,14 @@ static BC_STATUS zbc_vm_exec(void)
        }
 
        if (IS_BC || (option_mask32 & BC_FLAG_I))
-               s = zbc_vm_execute_FILE(stdin, /*filename:*/ NULL);
+               s = zxc_vm_execute_FILE(stdin, /*filename:*/ NULL);
 
        RETURN_STATUS(s);
 }
-#define zbc_vm_exec(...) (zbc_vm_exec(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_vm_exec(...) (zxc_vm_exec(__VA_ARGS__) COMMA_SUCCESS)
 
 #if ENABLE_FEATURE_CLEAN_UP
-static void bc_program_free(void)
+static void xc_program_free(void)
 {
        bc_vec_free(&G.prog.fns);
        IF_BC(bc_vec_free(&G.prog.fn_map);)
@@ -7088,21 +7320,13 @@ static void bc_program_free(void)
        bc_vec_free(&G.prog.results);
        bc_vec_free(&G.prog.exestack);
        IF_BC(bc_num_free(&G.prog.last);)
-       IF_BC(bc_num_free(&G.prog.zero);)
+       //IF_BC(bc_num_free(&G.prog.zero);)
        IF_BC(bc_num_free(&G.prog.one);)
        bc_vec_free(&G.input_buffer);
 }
-
-static void bc_vm_free(void)
-{
-       bc_vec_free(&G.files);
-       bc_program_free();
-       bc_parse_free(&G.prs);
-       free(G.env_args);
-}
 #endif
 
-static void bc_program_init(void)
+static void xc_program_init(void)
 {
        BcInstPtr ip;
 
@@ -7116,7 +7340,7 @@ static void bc_program_init(void)
        IF_BC(bc_num_init_DEF_SIZE(&G.prog.last);)
        //IF_BC(bc_num_zero(&G.prog.last);) - already is
 
-       bc_num_init_DEF_SIZE(&G.prog.zero);
+       //bc_num_init_DEF_SIZE(&G.prog.zero); - not needed
        //bc_num_zero(&G.prog.zero); - already is
 
        IF_BC(bc_num_init_DEF_SIZE(&G.prog.one);)
@@ -7132,8 +7356,8 @@ static void bc_program_init(void)
                IF_BC(bc_program_addFunc(xstrdup("1"))); // func #1: for read()
        } else {
                // in dc, functions have no names
-               bc_program_add_fn();
-               bc_program_add_fn();
+               xc_program_add_fn();
+               xc_program_add_fn();
        }
 
        bc_vec_init(&G.prog.vars, sizeof(BcVec), bc_vec_free);
@@ -7151,22 +7375,22 @@ static void bc_program_init(void)
        bc_char_vec_init(&G.input_buffer);
 }
 
-static int bc_vm_init(const char *env_len)
+static int xc_vm_init(const char *env_len)
 {
+       G.prog.len = xc_vm_envLen(env_len);
 #if ENABLE_FEATURE_EDITING
        G.line_input_state = new_line_input_t(DO_HISTORY);
 #endif
-       G.prog.len = bc_vm_envLen(env_len);
-
        bc_vec_init(&G.files, sizeof(char *), NULL);
+
+       xc_program_init();
        IF_BC(if (IS_BC) bc_vm_envArgs();)
-       bc_program_init();
-       bc_parse_create(&G.prs, BC_PROG_MAIN);
+       xc_parse_create(BC_PROG_MAIN);
 
 //TODO: in GNU bc, the check is (isatty(0) && isatty(1)),
 //-i option unconditionally enables this regardless of isatty():
        if (isatty(0)) {
-#if ENABLE_FEATURE_BC_SIGNALS
+#if ENABLE_FEATURE_BC_INTERACTIVE
                G_ttyin = 1;
                // With SA_RESTART, most system calls will restart
                // (IOW: they won't fail with EINTR).
@@ -7193,13 +7417,17 @@ static int bc_vm_init(const char *env_len)
        return 0; // "not a tty"
 }
 
-static BcStatus bc_vm_run(void)
+static BcStatus xc_vm_run(void)
 {
-       BcStatus st = zbc_vm_exec();
+       BcStatus st = zxc_vm_exec();
 #if ENABLE_FEATURE_CLEAN_UP
        if (G_exiting) // it was actually "halt" or "quit"
                st = EXIT_SUCCESS;
-       bc_vm_free();
+
+       bc_vec_free(&G.files);
+       xc_program_free();
+       xc_parse_free();
+       free(G.env_args);
 # if ENABLE_FEATURE_EDITING
        free_line_input_t(G.line_input_state);
 # endif
@@ -7217,14 +7445,14 @@ int bc_main(int argc UNUSED_PARAM, char **argv)
 
        INIT_G();
 
-       is_tty = bc_vm_init("BC_LINE_LENGTH");
+       is_tty = xc_vm_init("BC_LINE_LENGTH");
 
        bc_args(argv);
 
        if (is_tty && !(option_mask32 & BC_FLAG_Q))
                bc_vm_info();
 
-       return bc_vm_run();
+       return xc_vm_run();
 }
 #endif
 
@@ -7246,7 +7474,7 @@ int dc_main(int argc UNUSED_PARAM, char **argv)
        //      |123\    |
        //      |456     |
        // Do the same, or it's a bug?
-       bc_vm_init("DC_LINE_LENGTH");
+       xc_vm_init("DC_LINE_LENGTH");
 
        // Run -e'SCRIPT' and -fFILE in order of appearance, then handle FILEs
        noscript = BC_FLAG_I;
@@ -7257,12 +7485,12 @@ int dc_main(int argc UNUSED_PARAM, char **argv)
                switch (n) {
                case 'e':
                        noscript = 0;
-                       n = zbc_vm_process(optarg);
+                       n = zxc_vm_process(optarg);
                        if (n) return n;
                        break;
                case 'f':
                        noscript = 0;
-                       n = zbc_vm_file(optarg);
+                       n = zxc_vm_file(optarg);
                        if (n) return n;
                        break;
                case 'x':
@@ -7281,8 +7509,8 @@ int dc_main(int argc UNUSED_PARAM, char **argv)
 
        option_mask32 |= noscript; // set BC_FLAG_I if we need to interpret stdin
 
-       return bc_vm_run();
+       return xc_vm_run();
 }
 #endif
 
-#endif // not DC_SMALL
+#endif // DC_BIG