bc: support ibase up to 36 (GNU compat)
[oweals/busybox.git] / miscutils / bc.c
index ec2f8613351e99c87b21a72e452fee94cafc5bfc..798bc0a3e072aa82e48d5019f1bfb249bb6107d2 100644 (file)
@@ -1,11 +1,23 @@
 /* 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: GNU extensions:
+// support "define void f()..."
+// support "define f(*param[])" - "pass array by reference" syntax
+
+#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 +97,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:     "\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"
 #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(...) \
@@ -214,8 +207,8 @@ typedef enum BcStatus {
        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 +230,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
@@ -435,7 +428,7 @@ typedef enum BcLexType {
        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 5D
+       BC_LEX_LBRACKET, // [] are 0x5B and 0x5D
        BC_LEX_COMMA,
        BC_LEX_RBRACKET, // must be LBRACKET+2: code uses (c - '[' + BC_LEX_LBRACKET)
 
@@ -452,10 +445,10 @@ typedef enum BcLexType {
        BC_LEX_KEY_FOR,
        BC_LEX_KEY_HALT,
        // 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_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,
-       IF_BC(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_PRINT,
@@ -517,31 +510,31 @@ 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("obase"   , 1), // 8
-       BC_LEX_KW_ENTRY("if"      , 1), // 9
-       BC_LEX_KW_ENTRY("last"    , 0), // 10
-       BC_LEX_KW_ENTRY("length"  , 1), // 11
-       BC_LEX_KW_ENTRY("limits"  , 0), // 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
+#undef LEX_KW_ENTRY
 #define STRING_else  (bc_lex_kws[4].name8)
 #define STRING_for   (bc_lex_kws[5].name8)
 #define STRING_if    (bc_lex_kws[9].name8)
@@ -569,7 +562,7 @@ 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.
@@ -609,7 +602,7 @@ static ALWAYS_INLINE long lex_allowed_in_bc_expr(unsigned i)
 
 // This is an array of data for operators that correspond to
 // [XC_LEX_1st_op...] token types.
-static const uint8_t bc_parse_ops[] = {
+static const uint8_t bc_ops_prec_and_assoc[] ALIGN1 = {
 #define OP(p,l) ((int)(l) * 0x10 + (p))
        OP(1, false), // neg
        OP(6, true ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true  ), OP( 6, true ), // == <= >= != < >
@@ -623,60 +616,60 @@ static const uint8_t bc_parse_ops[] = {
        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 //BcLexType - should be this type
 uint8_t
-dc_char_to_LEX[] = {
-       /* %&'( */
+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 */
+       // 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 */
+       // 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 */
+       // 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,
-       /* QRSTUVWXY */
+       // QRSTUVWXY
        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, XC_LEX_INVALID,
-       /* Z[\] */
+       // Z[\]
        DC_LEX_LENGTH, XC_LEX_INVALID, XC_LEX_INVALID, XC_LEX_INVALID,
-       /* ^_` */
+       // ^_`
        XC_LEX_OP_POWER, XC_LEX_NEG, XC_LEX_INVALID,
-       /* abcdefgh */
+       // 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 */
+       // 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 */
+       // 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 */
+       // 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_LEX_to_INST[] = { // starts at XC_LEX_OP_POWER       // corresponding XC/DC_LEX_xyz:
-       XC_INST_POWER,       XC_INST_MULTIPLY,          // OP_POWER     OP_MULTIPLY
-       XC_INST_DIVIDE,      XC_INST_MODULUS,           // OP_DIVIDE    OP_MODULUS
-       XC_INST_PLUS,        XC_INST_MINUS,             // OP_PLUS      OP_MINUS
+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
@@ -691,55 +684,40 @@ dc_LEX_to_INST[] = { // starts at XC_LEX_OP_POWER       // corresponding XC/DC_L
        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,            // OP_MODEXP    OP_DIVMOD
-       DC_INST_INVALID,     DC_INST_INVALID,           // COLON        ELSE
-       DC_INST_EXECUTE,                                // EXECUTE
-       DC_INST_PRINT_STACK, DC_INST_CLEAR_STACK,       // PRINT_STACK  CLEAR_STACK
-       DC_INST_STACK_LEN,   DC_INST_DUPLICATE,         // STACK_LEVEL  DUPLICATE
-       DC_INST_SWAP,        XC_INST_POP,               // SWAP         POP
-       DC_INST_ASCIIFY,     DC_INST_PRINT_STREAM,      // ASCIIFY      PRINT_STREAM
-       DC_INST_INVALID,     DC_INST_INVALID,           // STORE_IBASE  STORE_OBASE
-       DC_INST_INVALID,     DC_INST_INVALID,           // STORE_SCALE  LOAD
-       DC_INST_INVALID,     DC_INST_INVALID,           // LOAD_POP     STORE_PUSH
-       XC_INST_PRINT,       DC_INST_NQUIT,             // PRINT_POP    NQUIT
-       XC_INST_SCALE_FUNC,                             // SCALE_FACTOR
+       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;
-       smallint lex;      // was BcLexType
-       smallint lex_last; // was BcLexType
-       BcVec  lex_buf;
-} 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 {
@@ -765,33 +743,78 @@ 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)
+struct globals {
+       BcParse prs; // first member is most used
+
+       // 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 input_buffer;
+
+       IF_FEATURE_BC_INTERACTIVE(smallint ttyin;)
+       IF_FEATURE_CLEAN_UP(smallint exiting;)
+
+       BcProgram prog;
+
+       BcVec files;
+
+       char *env_args;
+
+#if ENABLE_FEATURE_EDITING
+       line_input_t *line_input_state;
+#endif
+} FIX_ALIASING;
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+#define FREE_G() do { \
+       FREE_PTR_TO_GLOBALS(); \
+} while (0)
+#define G_posix (ENABLE_BC && (option_mask32 & BC_FLAG_S))
+#define G_warn  (ENABLE_BC && (option_mask32 & BC_FLAG_W))
+#define G_exreg (ENABLE_DC && (option_mask32 & DC_FLAG_X))
+#if ENABLE_FEATURE_BC_INTERACTIVE
+# define G_interrupt bb_got_signal
+# define G_ttyin     G.ttyin
+#else
+# define G_interrupt 0
+# define G_ttyin     0
+#endif
+#if ENABLE_FEATURE_CLEAN_UP
+# define G_exiting G.exiting
+#else
+# define G_exiting 0
 #endif
+#define IS_BC (ENABLE_BC && (!ENABLE_DC || applet_name[0] == 'b'))
+#define IS_DC (ENABLE_DC && (!ENABLE_BC || applet_name[0] != 'b'))
 
-#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))
+#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)
-#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_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)
@@ -827,52 +850,6 @@ typedef struct BcProgram {
 #endif
 #define BC_MAX_NUM_STR BC_MAX_STRING_STR
 
-struct globals {
-       IF_FEATURE_BC_SIGNALS(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;
-
-#if ENABLE_FEATURE_EDITING
-       line_input_t *line_input_state;
-#endif
-} FIX_ALIASING;
-#define G (*ptr_to_globals)
-#define INIT_G() do { \
-       SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
-} while (0)
-#define FREE_G() do { \
-       FREE_PTR_TO_GLOBALS(); \
-} while (0)
-#define G_posix (ENABLE_BC && (option_mask32 & BC_FLAG_S))
-#define G_warn  (ENABLE_BC && (option_mask32 & BC_FLAG_W))
-#define G_exreg (ENABLE_DC && (option_mask32 & DC_FLAG_X))
-#if ENABLE_FEATURE_BC_SIGNALS
-# define G_interrupt bb_got_signal
-# define G_ttyin     G.ttyin
-#else
-# define G_interrupt 0
-# define G_ttyin     0
-#endif
-#if ENABLE_FEATURE_CLEAN_UP
-# define G_exiting G.exiting
-#else
-# define G_exiting 0
-#endif
-#define IS_BC (ENABLE_BC && (!ENABLE_DC || applet_name[0] == 'b'))
-#define IS_DC (ENABLE_DC && (!ENABLE_BC || applet_name[0] != 'b'))
-
 // 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".
@@ -884,7 +861,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
@@ -904,6 +881,9 @@ struct globals {
 // Utility routines
 //
 
+#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();
@@ -914,14 +894,11 @@ static void fflush_and_check(void)
 #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)
 {
@@ -931,16 +908,18 @@ static void quit(void)
        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:%u", applet_name, G.prs.lex_filename, 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;
        }
@@ -1027,12 +1006,12 @@ static BC_STATUS zbc_POSIX_does_not_allow(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_STATUS(zbc_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));
 }
 #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_STATUS(zbc_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
@@ -1151,23 +1130,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;
@@ -1190,23 +1152,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) {
@@ -1217,7 +1179,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) {
@@ -1662,7 +1624,7 @@ static FAST_FUNC BC_STATUS zbc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scal
        RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_p, a->len * b->len + 1));
 }
 
-static const BcNumBinaryOp zbc_program_ops[] = {
+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)
@@ -1860,7 +1822,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;
@@ -2027,7 +1989,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
@@ -2195,6 +2157,7 @@ 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;
+       BcDig half_digs[1];
        size_t pow, len, digs, digs1, resrdx, req, times = 0;
        ssize_t cmp = 1, cmp1 = SSIZE_MAX, cmp2 = SSIZE_MAX;
 
@@ -2219,10 +2182,11 @@ static BC_STATUS zbc_num_sqrt(BcNum *a, BcNum *restrict b, size_t 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);
@@ -2285,7 +2249,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);
@@ -2323,7 +2286,7 @@ 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[2];
+       BcDig two_digs[1];
 
        if (c->len == 0)
                RETURN_STATUS(bc_error("divide by zero"));
@@ -2506,13 +2469,13 @@ static int bad_input_byte(char c)
        return 0;
 }
 
-// Note: it _appends_ data from fp to vec.
-static void bc_read_line(BcVec *vec, FILE *fp)
+static void xc_read_line(BcVec *vec, FILE *fp)
 {
  again:
+       bc_vec_pop_all(vec);
        fflush_and_check();
 
-#if ENABLE_FEATURE_BC_SIGNALS
+#if ENABLE_FEATURE_BC_INTERACTIVE
        if (G_interrupt) { // ^C was pressed
  intr:
                if (fp != stdin) {
@@ -2549,7 +2512,7 @@ static void bc_read_line(BcVec *vec, FILE *fp)
                        if (!c) break;
                        if (bad_input_byte(c)) goto again;
                }
-               bc_vec_concat(vec, line_buf);
+               bc_vec_string(vec, n, line_buf);
 #  undef line_buf
        } else
 # endif
@@ -2557,13 +2520,12 @@ static void bc_read_line(BcVec *vec, FILE *fp)
        {
                int c;
                bool bad_chars = 0;
-               size_t len = vec->len;
 
                do {
-#if ENABLE_FEATURE_BC_SIGNALS
+#if ENABLE_FEATURE_BC_INTERACTIVE
                        if (G_interrupt) {
                                // ^C was pressed: ignore entire line, get another one
-                               vec->len = len;
+                               bc_vec_pop_all(vec);
                                goto intr;
                        }
 #endif
@@ -2580,12 +2542,11 @@ static void bc_read_line(BcVec *vec, FILE *fp)
 
                if (bad_chars) {
                        // Bad chars on this line
-                       if (!G.prog.file) { // stdin
+                       if (!G.prs.lex_filename) { // 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);
+                       bb_perror_msg_and_die("file '%s' is not text", G.prs.lex_filename);
                }
                bc_vec_pushZeroByte(vec);
        }
@@ -2595,13 +2556,16 @@ static void bc_read_line(BcVec *vec, FILE *fp)
 // Parsing routines
 //
 
-static bool bc_num_strValid(const char *val, size_t base)
+// "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)
 {
-       BcDig b;
-       bool radix;
-
-       b = (BcDig)(base <= 10 ? base + '0' : base - 10 + 'A');
-       radix = false;
+       bool radix = false;
        for (;;) {
                BcDig c = *val++;
                if (c == '\0')
@@ -2611,7 +2575,7 @@ static bool bc_num_strValid(const char *val, size_t base)
                        radix = true;
                        continue;
                }
-               if (c < '0' || c >= b || (c > '9' && c < 'A'))
+               if ((c < '0' || c > '9') && (c < 'A' || c > 'Z'))
                        return false;
        }
        return true;
@@ -2628,7 +2592,7 @@ static void bc_num_parseDecimal(BcNum *n, const char *val)
        if (len == 0)
                return;
 
-       bc_num_expand(n, len);
+       bc_num_expand(n, len + 1); // +1 for e.g. "A" converting into 10
 
        ptr = strchr(val, '.');
 
@@ -2639,10 +2603,25 @@ static void bc_num_parseDecimal(BcNum *n, const char *val)
        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 (;;) {
-                               n->num[n->len] = val[i] - '0';
-                               ++n->len;
+                               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;
@@ -2658,32 +2637,33 @@ static void bc_num_parseDecimal(BcNum *n, const char *val)
 static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
 {
        BcStatus s;
-       BcNum temp, mult, result;
+       BcNum mult, result;
+       BcNum temp;
        BcNum base;
+       BcDig temp_digs[ULONG_NUM_BUFSIZE];
        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;
-       }
+       size_t digits;
 
-       bc_num_init_DEF_SIZE(&temp);
        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;
+
                c = *val++;
                if (c == '\0') goto int_err;
                if (c == '.') break;
 
-               v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
+               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;
@@ -2698,11 +2678,14 @@ static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
 
        digits = 0;
        for (;;) {
+               unsigned v;
+
                c = *val++;
                if (c == '\0') break;
                digits++;
 
-               v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
+               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;
@@ -2727,129 +2710,181 @@ static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
        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)
+static BC_STATUS zxc_num_parse(BcNum *n, const char *val, unsigned base_t)
 {
-       if (!bc_num_strValid(val, 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++;
+       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)
+       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);
 
        RETURN_STATUS(BC_STATUS_SUCCESS);
 }
-#define zbc_num_parse(...) (zbc_num_parse(__VA_ARGS__) COMMA_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->lex = XC_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->lex = XC_LEX_WHITESPACE;
+       BcParse *p = &G.prs;
+
+       p->lex = XC_LEX_WHITESPACE;
        for (;;) {
-               char c = l->buf[l->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;
                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->lex = XC_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++;
+                       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->lex_buf);
-       bc_vec_expand(&l->lex_buf, 1 + len);
-       bc_vec_push(&l->lex_buf, &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->lex_buf, 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->lex_buf);
-
+       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->lex = XC_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;
@@ -2863,185 +2898,82 @@ 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->lex_buf, 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->lex_buf);
-}
-
-static void bc_lex_free(BcLex *l)
-{
-       bc_vec_free(&l->lex_buf);
-}
-
-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->lex_last = l->lex;
-       if (l->lex_last == XC_LEX_EOF) RETURN_STATUS(bc_error("end of file"));
-
-       l->line += l->newline;
-       G.err_line = l->line;
-       l->newline = false;
+       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 XC_LEX_WHITESPACE tokens and eaten here.
        s = BC_STATUS_SUCCESS;
        do {
-               if (l->i == l->len) {
-                       l->lex = XC_LEX_EOF;
-                       if (!G.input_fp)
-                               RETURN_STATUS(BC_STATUS_SUCCESS);
-                       if (!bc_lex_more_input(l)) {
-                               G.input_fp = NULL;
+               if (*p->lex_inbuf == '\0') {
+                       p->lex = XC_LEX_EOF;
+                       if (peek_inbuf() == '\0')
                                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->lex == XC_LEX_WHITESPACE);
-       dbg_lex("l->lex from string:%d", l->lex);
+       } 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->lex == XC_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->lex = l->lex_last = XC_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;
@@ -3056,298 +2988,312 @@ 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->lex = BC_LEX_KEY_1st_keyword + i;
-               if (!bc_lex_kws_POSIX(i)) {
+               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->lex_buf.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 = zbc_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->lex = XC_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++;
+               bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf);
+               p->lex_inbuf++;
        }
-       bc_vec_string(&l->lex_buf, 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->lex = (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->lex = XC_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"));
                }
-               nls += (c == '\n');
+               if (c == '\n')
+                       p->lex_line++;
        }
+       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->lex = XC_LEX_EOF;
-//                     l->newline = true;
-//                     break;
-               case '\n':
-                       l->lex = XC_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, XC_LEX_OP_REL_NE, BC_LEX_OP_BOOL_NOT);
-                       if (l->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(l);
-                       break;
-               case '#':
-                       s = zbc_POSIX_does_not_allow("'#' script comments");
+//     case '\0': // probably never reached
+//             p->lex_inbuf--;
+//             p->lex = XC_LEX_EOF;
+//             break;
+       case '\n':
+               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);
-                       bc_lex_lineComment(l);
-                       break;
-               case '%':
-                       bc_lex_assign(l, BC_LEX_OP_ASSIGN_MODULUS, XC_LEX_OP_MODULUS);
-                       break;
-               case '&':
-                       c2 = l->buf[l->i];
-                       if (c2 == '&') {
-                               s = zbc_POSIX_does_not_allow_bool_ops_this_is_bad("&&");
-                               if (s) RETURN_STATUS(s);
-                               ++l->i;
-                               l->lex = BC_LEX_OP_BOOL_AND;
-                       } else {
-                               l->lex = XC_LEX_INVALID;
-                               s = bc_error_bad_character('&');
-                       }
-                       break;
-               case '(':
-               case ')':
-                       l->lex = (BcLexType)(c - '(' + BC_LEX_LPAREN);
-                       break;
-               case '*':
-                       bc_lex_assign(l, BC_LEX_OP_ASSIGN_MULTIPLY, XC_LEX_OP_MULTIPLY);
-                       break;
-               case '+':
-                       c2 = l->buf[l->i];
-                       if (c2 == '+') {
-                               ++l->i;
-                               l->lex = BC_LEX_OP_INC;
-                       } else
-                               bc_lex_assign(l, BC_LEX_OP_ASSIGN_PLUS, XC_LEX_OP_PLUS);
-                       break;
-               case ',':
-                       l->lex = BC_LEX_COMMA;
-                       break;
-               case '-':
-                       c2 = l->buf[l->i];
-                       if (c2 == '-') {
-                               ++l->i;
-                               l->lex = BC_LEX_OP_DEC;
-                       } else
-                               bc_lex_assign(l, BC_LEX_OP_ASSIGN_MINUS, XC_LEX_OP_MINUS);
-                       break;
-               case '.':
-                       if (isdigit(l->buf[l->i]))
-                               s = zbc_lex_number(l, c);
-                       else {
-                               l->lex = BC_LEX_KEY_LAST;
-                               s = zbc_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, 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':
-                       s = zbc_lex_number(l, c);
-                       break;
-               case ';':
-                       l->lex = BC_LEX_SCOLON;
-                       break;
-               case '<':
-                       bc_lex_assign(l, XC_LEX_OP_REL_LE, XC_LEX_OP_REL_LT);
-                       break;
-               case '=':
-                       bc_lex_assign(l, XC_LEX_OP_REL_EQ, BC_LEX_OP_ASSIGN);
-                       break;
-               case '>':
-                       bc_lex_assign(l, XC_LEX_OP_REL_GE, XC_LEX_OP_REL_GT);
-                       break;
-               case '[':
-               case ']':
-                       l->lex = (BcLexType)(c - '[' + BC_LEX_LBRACKET);
-                       break;
-               case '\\':
-                       if (l->buf[l->i] == '\n') {
-                               l->lex = XC_LEX_WHITESPACE;
-                               ++l->i;
-                       } else
-                               s = bc_error_bad_character(c);
-                       break;
-               case '^':
-                       bc_lex_assign(l, 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(l);
-                       break;
-               case '{':
-               case '}':
-                       l->lex = (BcLexType)(c - '{' + BC_LEX_LBRACE);
-                       break;
-               case '|':
-                       c2 = l->buf[l->i];
-                       if (c2 == '|') {
-                               s = zbc_POSIX_does_not_allow_bool_ops_this_is_bad("||");
-                               if (s) RETURN_STATUS(s);
-                               ++l->i;
-                               l->lex = BC_LEX_OP_BOOL_OR;
-                       } else {
-                               l->lex = XC_LEX_INVALID;
-                               s = bc_error_bad_character(c);
-                       }
-                       break;
-               default:
-                       l->lex = XC_LEX_INVALID;
+               }
+               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);
@@ -3356,166 +3302,155 @@ 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 (G_exreg && isspace(l->buf[l->i])) {
-               bc_lex_whitespace(l); // eats whitespace (but not newline)
-               l->i++; // bc_lex_name() expects this
-               bc_lex_name(l);
+       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(&l->lex_buf);
-               bc_vec_push(&l->lex_buf, &l->buf[l->i++]);
-               bc_vec_pushZeroByte(&l->lex_buf);
-               l->lex = XC_LEX_NAME;
+               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->lex = XC_LEX_STR;
-       bc_vec_pop_all(&l->lex_buf);
+       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;
-               }
-               bc_vec_push(&l->lex_buf, &l->buf[i]);
-               i++;
-       }
-       i++;
-
-       bc_vec_pushZeroByte(&l->lex_buf);
-       // 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"]"));
+               if (c == '[') depth++;
+               if (c == ']')
+                       if (--depth == 0)
+                               break;
+               if (c == '\n')
+                       p->lex_line++;
+               bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf);
+               p->lex_inbuf++;
        }
+       bc_vec_pushZeroByte(&p->lex_strnumbuf);
+       p->lex_inbuf++; // skip trailing ']'
 
-       l->i = i;
-       l->line += nls;
-       G.err_line = l->line;
-
+       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[] = {
+       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,
        };
 
+       BcParse *p = &G.prs;
        BcStatus s;
        char c, c2;
        size_t i;
 
        for (i = 0; i < ARRAY_SIZE(dc_lex_regs); ++i) {
-               if (l->lex_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 = l->buf[l->i++];
+       c = eat_inbuf();
        if (c >= '%' && c <= '~'
-        && (l->lex = dc_char_to_LEX[c - '%']) != XC_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->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.
-                       l->lex = XC_LEX_NLINE;
-                       l->newline = true;
-                       break;
-               case '\t':
-               case '\v':
-               case '\f':
-               case '\r':
-               case ' ':
-                       l->newline = 0; // was (c == '\n')
-                       bc_lex_whitespace(l);
-                       break;
-               case '!':
-                       c2 = l->buf[l->i];
-                       if (c2 == '=')
-                               l->lex = XC_LEX_OP_REL_NE;
-                       else if (c2 == '<')
-                               l->lex = XC_LEX_OP_REL_LE;
-                       else if (c2 == '>')
-                               l->lex = XC_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->lex = XC_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++;
+               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);
@@ -3523,70 +3458,96 @@ 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(char 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, 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 xc_parse_pushIndex(size_t idx)
 {
        size_t mask;
        unsigned amt;
 
        dbg_lex("%s:%d pushing index %zd", __func__, __LINE__, idx);
+       if (idx < SMALL_INDEX_LIMIT) {
+               goto push_idx;
+       }
+
        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);
+       xc_parse_push((SMALL_INDEX_LIMIT - 1) + amt);
 
        while (idx != 0) {
-               bc_parse_push(p, (unsigned char)idx);
+ push_idx:
+               xc_parse_push((unsigned char)idx);
                idx >>= 8;
        }
 }
 
 #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_push(BC_INST_JUMP);
+       xc_parse_pushIndex(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_push(BC_INST_JUMP_ZERO);
+       xc_parse_pushIndex(idx);
 }
 
-static BC_STATUS zbc_parse_pushSTR(BcParse *p)
+static BC_STATUS zbc_parse_pushSTR(void)
 {
-       char *str = xstrdup(p->l.lex_buf.v);
+       BcParse *p = &G.prs;
+       char *str = xstrdup(p->lex_strnumbuf.v);
 
-       bc_parse_push(p, XC_INST_STR);
-       bc_parse_pushIndex(p, p->func->strs.len);
+       xc_parse_push(XC_INST_STR);
+       xc_parse_pushIndex(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.lex_buf.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
@@ -3594,21 +3555,22 @@ static void bc_parse_pushNUM(BcParse *p)
 #else // DC
        size_t idx = bc_vec_push(&G.prog.consts, &num);
 #endif
-       bc_parse_push(p, XC_INST_NUM);
-       bc_parse_pushIndex(p, idx);
+       xc_parse_push(XC_INST_NUM);
+       xc_parse_pushIndex(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;
@@ -3616,55 +3578,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.lex = XC_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;
@@ -3695,11 +3659,11 @@ 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;
@@ -3711,55 +3675,56 @@ static size_t bc_program_addFunc(char *name)
 // first in the expr enum. Note: This only works for binary operators.
 #define BC_TOKEN_2_INST(t) ((char) ((t) - XC_LEX_OP_POWER + XC_INST_POWER))
 
-static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags);
+static BcStatus bc_parse_expr_empty_ok(uint8_t flags);
 
-static BC_STATUS zbc_parse_expr(BcParse *p, uint8_t flags)
+static BC_STATUS zbc_parse_expr(uint8_t flags)
 {
        BcStatus s;
 
-       s = bc_parse_expr_empty_ok(p, flags);
+       s = bc_parse_expr_empty_ok(flags);
        if (s == BC_STATUS_PARSE_EMPTY_EXP)
                RETURN_STATUS(bc_error("empty expression"));
        RETURN_STATUS(s);
 }
 #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.lex == XC_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 - XC_LEX_1st_op);
-       bool left = bc_parse_op_LEFT(type - XC_LEX_1st_op);
+       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 - XC_LEX_1st_op);
+               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 != XC_LEX_NEG);
        }
@@ -3767,8 +3732,9 @@ static void bc_parse_operator(BcParse *p, BcLexType type, size_t start,
        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)
@@ -3776,7 +3742,7 @@ 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 != XC_LEX_NEG);
@@ -3792,53 +3758,55 @@ static BC_STATUS zbc_parse_rightParen(BcParse *p, size_t ops_bgn, size_t *nexs)
 }
 #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.lex:%d", __func__, __LINE__, p->l.lex);
+       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.lex != 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.lex != BC_LEX_COMMA) {
-                               if (p->l.lex == 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_push(BC_INST_CALL);
+       xc_parse_pushIndex(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.lex != BC_LEX_RPAREN) {
+       if (p->lex != BC_LEX_RPAREN) {
                s = bc_error_bad_token();
                goto err;
        }
@@ -3853,29 +3821,30 @@ 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.lex_buf.v);
-       s = zbc_lex_next(&p->l);
+       name = xstrdup(p->lex_strnumbuf.v);
+       s = zxc_lex_next();
        if (s) goto err;
 
-       if (p->l.lex == 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.lex == BC_LEX_RBRACKET) {
+               if (p->lex == BC_LEX_RBRACKET) {
                        if (!(flags & BC_PARSE_ARRAY)) {
                                s = bc_error_bad_expression();
                                goto err;
@@ -3884,25 +3853,25 @@ static BC_STATUS zbc_parse_name(BcParse *p, BcInst *type, uint8_t flags)
                } else {
                        *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.lex == 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 = XC_INST_VAR;
-               bc_parse_push(p, XC_INST_VAR);
-               bc_parse_pushName(p, name);
+               xc_parse_push(XC_INST_VAR);
+               xc_parse_pushName(name);
                free(name);
        }
 
@@ -3913,82 +3882,85 @@ 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.lex != 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.lex != 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, XC_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.lex != 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.lex != 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) ? XC_INST_LENGTH : XC_INST_SQRT;
-       bc_parse_push(p, *prev);
+       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.lex != BC_LEX_LPAREN) {
+       if (p->lex != BC_LEX_LPAREN) {
                *type = XC_INST_SCALE;
-               bc_parse_push(p, XC_INST_SCALE);
+               xc_parse_push(XC_INST_SCALE);
                RETURN_STATUS(BC_STATUS_SUCCESS);
        }
 
        *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.lex != BC_LEX_RPAREN)
+       if (p->lex != BC_LEX_RPAREN)
                RETURN_STATUS(bc_error_bad_token());
-       bc_parse_push(p, XC_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, bool *paren_expr,
+                               size_t *nexprs, uint8_t flags)
 {
+       BcParse *p = &G.prs;
        BcStatus s;
        BcLexType type;
        char inst;
@@ -3998,45 +3970,45 @@ static BC_STATUS zbc_parse_incdec(BcParse *p, BcInst *prev, bool *paren_expr,
         || etype == XC_INST_SCALE || etype == BC_INST_LAST
         || etype == XC_INST_IBASE || etype == XC_INST_OBASE
        ) {
-               *prev = inst = BC_INST_INC_POST + (p->l.lex != 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.lex != BC_LEX_OP_INC);
+               *prev = inst = BC_INST_INC_PRE + (p->lex != BC_LEX_OP_INC);
                *paren_expr = true;
 
-               s = zbc_lex_next(&p->l);
+               s = zxc_lex_next();
                if (s) RETURN_STATUS(s);
-               type = p->l.lex;
+               type = p->lex;
 
                // Because we parse the next part of the expression
                // right here, we need to increment this.
                *nexprs = *nexprs + 1;
 
                switch (type) {
-                       case XC_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 + XC_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.lex == BC_LEX_LPAREN)
-                                       s = bc_error_bad_token();
-                               else
-                                       bc_parse_push(p, XC_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);
@@ -4061,14 +4033,15 @@ static int ok_in_expr(BcInst p)
 #define BC_PARSE_LEAF(p, rparen) ((rparen) || ok_in_expr(p))
 #endif
 
-static BC_STATUS zbc_parse_minus(BcParse *p, BcInst *prev, size_t ops_bgn,
-                               bool rparen, size_t *nexprs)
+static BC_STATUS zbc_parse_minus(BcInst *prev, size_t ops_bgn,
+                               bool rparen, 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 = BC_PARSE_LEAF(etype, rparen) ? XC_LEX_OP_MINUS : XC_LEX_NEG;
@@ -4079,29 +4052,30 @@ static BC_STATUS zbc_parse_minus(BcParse *p, BcInst *prev, size_t ops_bgn,
        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.lex;
+               type = p->lex;
                if (type == XC_LEX_STR) {
-                       s = zbc_parse_pushSTR(p);
+                       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, XC_INST_PRINT_POP);
-               if (p->l.lex != BC_LEX_COMMA)
+               xc_parse_push(XC_INST_PRINT_POP);
+               if (p->lex != BC_LEX_COMMA)
                        break;
        }
 
@@ -4109,33 +4083,34 @@ 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.lex;
+       t = p->lex;
        if (t == XC_LEX_NLINE || t == BC_LEX_SCOLON)
-               bc_parse_push(p, BC_INST_RET0);
+               xc_parse_push(BC_INST_RET0);
        else {
                bool paren = (t == BC_LEX_LPAREN);
-               s = bc_parse_expr_empty_ok(p, 0);
+               s = bc_parse_expr_empty_ok(0);
                if (s == BC_STATUS_PARSE_EMPTY_EXP) {
-                       bc_parse_push(p, BC_INST_RET0);
-                       s = zbc_lex_next(&p->l);
+                       xc_parse_push(BC_INST_RET0);
+                       s = zxc_lex_next();
                }
                if (s) RETURN_STATUS(s);
 
-               if (!paren || p->l.lex_last != BC_LEX_RPAREN) {
+               if (!paren || p->lex_last != BC_LEX_RPAREN) {
                        s = zbc_POSIX_requires("parentheses around return expressions");
                        if (s) RETURN_STATUS(s);
                }
 
-               bc_parse_push(p, XC_INST_RET);
+               xc_parse_push(XC_INST_RET);
        }
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
@@ -4143,73 +4118,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.lex != 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.lex != 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.lex:%d", __func__, __LINE__, p->l.lex);
-       if (p->l.lex == 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.lex != 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);
@@ -4219,20 +4197,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.lex != 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);
@@ -4241,29 +4219,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.lex:%d", __func__, __LINE__, p->l.lex);
-       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.lex != 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.lex != BC_LEX_SCOLON) {
-               s = zbc_parse_expr(p, 0);
-               bc_parse_push(p, XC_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 = zbc_POSIX_does_not_allow_empty_X_expression_in_for("init");
                if (s) RETURN_STATUS(s);
        }
 
-       if (p->l.lex != 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);
@@ -4271,53 +4250,53 @@ static BC_STATUS zbc_parse_for(BcParse *p)
        body_idx = update_idx + 1;
        exit_idx = body_idx + 1;
 
-       if (p->l.lex != 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.lex_buf, 1, "1");
-               bc_parse_pushNUM(p);
+               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.lex != 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.lex != 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.lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
-               bc_parse_push(p, XC_INST_POP);
+               if (p->lex != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
+               xc_parse_push(XC_INST_POP);
        } else {
                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);
@@ -4326,9 +4305,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) {
@@ -4338,16 +4317,9 @@ 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);
 
-       bc_parse_pushJUMP(p, i);
-
-       s = zbc_lex_next(&p->l);
-       if (s) RETURN_STATUS(s);
-
-       if (p->l.lex != BC_LEX_SCOLON && p->l.lex != XC_LEX_NLINE)
-               RETURN_STATUS(bc_error_bad_token());
-
-       RETURN_STATUS(zbc_lex_next(&p->l));
+       RETURN_STATUS(zxc_lex_next());
 }
 #define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__) COMMA_SUCCESS)
 
@@ -4360,7 +4332,7 @@ static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var)
        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"));
+                       RETURN_STATUS(bc_error("duplicate function parameter or auto name"));
        }
 
        a.idx = var;
@@ -4372,57 +4344,58 @@ static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var)
 }
 #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;
        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->l.lex != XC_LEX_NAME)
+       if (p->lex != XC_LEX_NAME)
                RETURN_STATUS(bc_error("bad function definition"));
 
-       name = xstrdup(p->l.lex_buf.v);
+       name = xstrdup(p->lex_strnumbuf.v);
        p->fidx = bc_program_addFunc(name);
-       p->func = bc_program_func(p->fidx);
+       p->func = xc_program_func(p->fidx);
 
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
-       if (p->l.lex != BC_LEX_LPAREN)
+       if (p->lex != BC_LEX_LPAREN)
                RETURN_STATUS(bc_error("bad function definition"));
-       s = zbc_lex_next(&p->l);
+       s = zxc_lex_next();
        if (s) RETURN_STATUS(s);
 
-       while (p->l.lex != BC_LEX_RPAREN) {
-               if (p->l.lex != XC_LEX_NAME)
+       while (p->lex != BC_LEX_RPAREN) {
+               if (p->lex != XC_LEX_NAME)
                        RETURN_STATUS(bc_error("bad function definition"));
 
                ++p->func->nparams;
 
-               name = xstrdup(p->l.lex_buf.v);
-               s = zbc_lex_next(&p->l);
+               name = xstrdup(p->lex_strnumbuf.v);
+               s = zxc_lex_next();
                if (s) goto err;
 
-               var = p->l.lex != BC_LEX_LBRACKET;
+               var = p->lex != BC_LEX_LBRACKET;
 
                if (!var) {
-                       s = zbc_lex_next(&p->l);
+                       s = zxc_lex_next();
                        if (s) goto err;
 
-                       if (p->l.lex != BC_LEX_RBRACKET) {
+                       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;
                }
 
-               comma = p->l.lex == 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;
                }
 
@@ -4432,31 +4405,31 @@ static BC_STATUS zbc_parse_funcdef(BcParse *p)
 
        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.lex != BC_LEX_LBRACE) {
+       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.lex != BC_LEX_LBRACE)
+       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);
@@ -4467,50 +4440,51 @@ 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;
        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);
 
        for (;;) {
                bool var;
 
-               if (p->l.lex != XC_LEX_NAME)
+               if (p->lex != XC_LEX_NAME)
                        RETURN_STATUS(bc_error("bad 'auto' syntax"));
 
-               name = xstrdup(p->l.lex_buf.v);
-               s = zbc_lex_next(&p->l);
+               name = xstrdup(p->lex_strnumbuf.v);
+               s = zxc_lex_next();
                if (s) goto err;
 
-               var = (p->l.lex != BC_LEX_LBRACKET);
+               var = (p->lex != BC_LEX_LBRACKET);
                if (!var) {
-                       s = zbc_lex_next(&p->l);
+                       s = zxc_lex_next();
                        if (s) goto err;
 
-                       if (p->l.lex != BC_LEX_RBRACKET) {
+                       if (p->lex != BC_LEX_RBRACKET) {
                                s = bc_error("bad 'auto' syntax");
                                goto err;
                        }
-                       s = zbc_lex_next(&p->l);
+                       s = zxc_lex_next();
                        if (s) goto err;
                }
 
                s = zbc_func_insert(p->func, name, var);
                if (s) goto err;
 
-               if (p->l.lex == XC_LEX_NLINE
-                || p->l.lex == BC_LEX_SCOLON
-               //|| p->l.lex == BC_LEX_RBRACE // allow "define f() {auto a}"
+               if (p->lex == XC_LEX_NLINE
+                || p->lex == BC_LEX_SCOLON
+               //|| p->lex == BC_LEX_RBRACE // allow "define f() {auto a}"
                ) {
                        break;
                }
-               if (p->l.lex != BC_LEX_COMMA)
+               if (p->lex != BC_LEX_COMMA)
                        RETURN_STATUS(bc_error("bad 'auto' syntax"));
-               s = zbc_lex_next(&p->l); // skip comma
+               s = zxc_lex_next(); // skip comma
                if (s) RETURN_STATUS(s);
        }
 
@@ -4524,112 +4498,113 @@ 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.lex:%d", __func__, __LINE__, p->l.lex);
+       dbg_lex_enter("%s:%d entered, p->lex:%d", __func__, __LINE__, p->lex);
 
-       if (p->l.lex == XC_LEX_NLINE) {
+       if (p->lex == XC_LEX_NLINE) {
                dbg_lex_done("%s:%d done (seen XC_LEX_NLINE)", __func__, __LINE__);
-               RETURN_STATUS(zbc_lex_next(&p->l));
+               RETURN_STATUS(zxc_lex_next());
        }
-       if (p->l.lex == 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(zxc_lex_next());
        }
 
-       if (p->l.lex == 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.lex == XC_LEX_NLINE);
-               if (auto_allowed && p->l.lex == 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.lex != 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);
                }
-               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.lex:%d", __func__, __LINE__, p->l.lex);
-       switch (p->l.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(p, BC_PARSE_PRINT);
-                       break;
-               case XC_LEX_STR:
-                       s = zbc_parse_pushSTR(p);
-                       bc_parse_push(p, XC_INST_PRINT_STR);
-                       break;
-               case BC_LEX_KEY_BREAK:
-               case BC_LEX_KEY_CONTINUE:
-                       s = zbc_parse_break_or_continue(p, p->l.lex);
-                       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_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 = 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__);
@@ -4637,19 +4612,20 @@ 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.lex == XC_LEX_EOF)
+       if (p->lex == XC_LEX_EOF)
                s = bc_error("end of file");
-       else if (p->l.lex == BC_LEX_KEY_DEFINE) {
-               dbg_lex("%s:%d p->l.lex:BC_LEX_KEY_DEFINE", __func__, __LINE__);
-               s = zbc_parse_funcdef(p);
+       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.lex:%d (not BC_LEX_KEY_DEFINE)", __func__, __LINE__, p->l.lex);
-               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__);
@@ -4658,15 +4634,16 @@ 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)
+static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
 {
+       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, assign, bin_last;
 
        dbg_lex_enter("%s:%d entered", __func__, __LINE__);
-       paren_first = (p->l.lex == BC_LEX_LPAREN);
+       paren_first = (p->lex == BC_LEX_LPAREN);
        nparens = nrelops = 0;
        paren_expr = rprn = assign = false;
        bin_last = true;
@@ -4674,7 +4651,7 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags)
        for (;;) {
                bool get_token;
                BcStatus s;
-               BcLexType t = p->l.lex;
+               BcLexType t = p->lex;
 
                if (!lex_allowed_in_bc_expr(t))
                        break;
@@ -4683,156 +4660,168 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags)
                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 = bin_last = false;
-                               //get_token = false; - already is
-                               break;
-                       case XC_LEX_OP_MINUS:
-                               s = zbc_parse_minus(p, &prev, ops_bgn, rprn, &nexprs);
-                               rprn = false;
-                               //get_token = false; - already is
-                               bin_last = (prev == XC_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 != 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 bc_error("bad assignment:"
-                                               " left side must be variable"
-                                               " or array element"
-                                       ); // note: shared string
-                               }
-                       // 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:
-                               if (((t == BC_LEX_OP_BOOL_NOT) != bin_last)
-                                || (t != BC_LEX_OP_BOOL_NOT && prev == XC_INST_BOOL_NOT)
-                               ) {
-                                       return 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(p, t, ops_bgn, &nexprs);
-                               s = zbc_lex_next(&p->l);
-                               rprn = false;
-                               //get_token = false; - already is
-                               bin_last = (t != BC_LEX_OP_BOOL_NOT);
-                               break;
-                       case BC_LEX_LPAREN:
-                               if (BC_PARSE_LEAF(prev, rprn))
-                                       return bc_error_bad_expression();
-                               bc_vec_push(&p->ops, &t);
-                               nparens++;
-                               get_token = true;
-                               paren_expr = false;
-                               rprn = bin_last = false;
-                               break;
-                       case BC_LEX_RPAREN:
-                               if (bin_last || prev == XC_INST_BOOL_NOT)
-                                       return bc_error_bad_expression();
-                               if (nparens == 0) {
-                                       goto exit_loop;
-                               }
-                               if (!paren_expr) {
-                                       dbg_lex_done("%s:%d done (returning EMPTY_EXP)", __func__, __LINE__);
-                                       return BC_STATUS_PARSE_EMPTY_EXP;
-                               }
-                               s = zbc_parse_rightParen(p, ops_bgn, &nexprs);
-                               nparens--;
-                               get_token = true;
-                               paren_expr = rprn = true;
-                               bin_last = false;
-                               break;
-                       case XC_LEX_NAME:
-                               if (BC_PARSE_LEAF(prev, rprn))
-                                       return bc_error_bad_expression();
-                               s = zbc_parse_name(p, &prev, flags & ~BC_PARSE_NOCALL);
-                               paren_expr = true;
-                               rprn = bin_last = false;
-                               //get_token = false; - already is
-                               nexprs++;
-                               break;
-                       case XC_LEX_NUMBER:
-                               if (BC_PARSE_LEAF(prev, rprn))
-                                       return bc_error_bad_expression();
-                               bc_parse_pushNUM(p);
-                               prev = XC_INST_NUM;
-                               get_token = true;
-                               paren_expr = true;
-                               rprn = bin_last = false;
-                               nexprs++;
-                               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 + XC_INST_IBASE);
-                               bc_parse_push(p, (char) prev);
-                               get_token = true;
-                               paren_expr = 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 = bin_last = false;
-                               //get_token = false; - already is
-                               nexprs++;
-                               break;
-                       case BC_LEX_KEY_READ:
-                               if (BC_PARSE_LEAF(prev, rprn))
-                                       return bc_error_bad_expression();
-                               s = zbc_parse_read(p);
-                               prev = XC_INST_READ;
-                               paren_expr = true;
-                               rprn = bin_last = false;
-                               //get_token = false; - already is
-                               nexprs++;
-                               break;
-                       case BC_LEX_KEY_SCALE:
-                               if (BC_PARSE_LEAF(prev, rprn))
-                                       return bc_error_bad_expression();
-                               s = zbc_parse_scale(p, &prev, flags);
-                               prev = XC_INST_SCALE;
-                               paren_expr = true;
-                               rprn = bin_last = false;
-                               //get_token = false; - already is
-                               nexprs++;
-                               break;
-                       default:
-                               return bc_error_bad_token();
+               case BC_LEX_OP_INC:
+               case BC_LEX_OP_DEC:
+                       dbg_lex("%s:%d LEX_OP_INC/DEC", __func__, __LINE__);
+                       s = zbc_parse_incdec(&prev, &paren_expr, &nexprs, flags);
+                       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, &nexprs);
+                       rprn = false;
+                       //get_token = false; - already is
+                       bin_last = (prev == XC_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:
+                       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 bc_error("bad assignment:"
+                                       " left side must be variable"
+                                       " or array element"
+                               ); // note: shared string
+                       }
+               // 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) != bin_last)
+                        || (t != BC_LEX_OP_BOOL_NOT && prev == XC_INST_BOOL_NOT)
+                       ) {
+                               return 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);
+                       s = zxc_lex_next();
+                       rprn = false;
+                       //get_token = false; - already is
+                       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, rprn))
+                               return bc_error_bad_expression();
+                       bc_vec_push(&p->ops, &t);
+                       nparens++;
+                       get_token = true;
+                       paren_expr = false;
+                       rprn = bin_last = false;
+                       break;
+               case BC_LEX_RPAREN:
+                       dbg_lex("%s:%d LEX_RPAREN", __func__, __LINE__);
+                       if (bin_last || prev == XC_INST_BOOL_NOT)
+                               return bc_error_bad_expression();
+                       if (nparens == 0) {
+                               goto exit_loop;
+                       }
+                       if (!paren_expr) {
+                               dbg_lex_done("%s:%d done (returning EMPTY_EXP)", __func__, __LINE__);
+                               return BC_STATUS_PARSE_EMPTY_EXP;
+                       }
+                       s = zbc_parse_rightParen(ops_bgn, &nexprs);
+                       nparens--;
+                       get_token = true;
+                       paren_expr = rprn = true;
+                       bin_last = false;
+                       break;
+               case XC_LEX_NAME:
+                       dbg_lex("%s:%d LEX_NAME", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, rprn))
+                               return bc_error_bad_expression();
+                       s = zbc_parse_name(&prev, flags & ~BC_PARSE_NOCALL);
+                       paren_expr = true;
+                       rprn = 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, rprn))
+                               return bc_error_bad_expression();
+                       xc_parse_pushNUM();
+                       prev = XC_INST_NUM;
+                       get_token = true;
+                       paren_expr = 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, rprn))
+                               return bc_error_bad_expression();
+                       prev = (char) (t - BC_LEX_KEY_IBASE + XC_INST_IBASE);
+                       xc_parse_push((char) prev);
+                       get_token = true;
+                       paren_expr = 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, rprn))
+                               return bc_error_bad_expression();
+                       s = zbc_parse_builtin(t, flags, &prev);
+                       get_token = true;
+                       paren_expr = true;
+                       rprn = bin_last = false;
+                       nexprs++;
+                       break;
+               case BC_LEX_KEY_READ:
+                       dbg_lex("%s:%d LEX_READ", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, rprn))
+                               return bc_error_bad_expression();
+                       s = zbc_parse_read();
+                       prev = XC_INST_READ;
+                       get_token = true;
+                       paren_expr = true;
+                       rprn = bin_last = false;
+                       nexprs++;
+                       break;
+               case BC_LEX_KEY_SCALE:
+                       dbg_lex("%s:%d LEX_SCALE", __func__, __LINE__);
+                       if (BC_PARSE_LEAF(prev, rprn))
+                               return bc_error_bad_expression();
+                       s = zbc_parse_scale(&prev, flags);
+                       prev = XC_INST_SCALE;
+                       //get_token = false; - already is
+                       paren_expr = true;
+                       rprn = bin_last = false;
+                       nexprs++;
+                       break;
+               default:
+                       return bc_error_bad_token();
                }
 
                if (s || G_interrupt) // error, or ^C: stop parsing
                        return BC_STATUS_FAILURE;
                if (get_token) {
-                       s = zbc_lex_next(&p->l);
+                       s = zxc_lex_next();
                        if (s) return s;
                }
        }
@@ -4845,7 +4834,7 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags)
                if (top == BC_LEX_LPAREN || top == BC_LEX_RPAREN)
                        return 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 != XC_LEX_NEG);
                bc_vec_pop(&p->ops);
@@ -4866,8 +4855,8 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags)
 
        if (flags & BC_PARSE_PRINT) {
                if (paren_first || !assign)
-                       bc_parse_push(p, XC_INST_PRINT);
-               bc_parse_push(p, XC_INST_POP);
+                       xc_parse_push(XC_INST_PRINT);
+               xc_parse_push(XC_INST_POP);
        }
 
        dbg_lex_done("%s:%d done", __func__, __LINE__);
@@ -4878,88 +4867,91 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags)
 
 #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.lex != XC_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.lex_buf.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.lex_buf.v);
-       bc_parse_push(p, XC_INST_STR);
-       bc_parse_pushIndex(p, len);
+       str = xstrdup(p->lex_strnumbuf.v);
+       xc_parse_push(XC_INST_STR);
+       xc_parse_pushIndex(len);
        bc_vec_push(&G.prog.strs, &str);
 
        // Explanation needed here
-       bc_program_add_fn();
-       p->func = bc_program_func(p->fidx);
+       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, DC_INST_SWAP);
-               bc_parse_push(p, XC_INST_ASSIGN);
-               bc_parse_push(p, XC_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, DC_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.lex == DC_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;
@@ -4969,95 +4961,97 @@ static BC_STATUS zdc_parse_token(BcParse *p, BcLexType t)
        s = BC_STATUS_SUCCESS;
        get_token = true;
        switch (t) {
-               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(p, 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(p, 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(p);
-                       break;
-               case XC_LEX_NEG:
-                       dbg_lex("%s:%d LEX_NEG", __func__, __LINE__);
-                       s = zbc_lex_next(&p->l);
-                       if (s) RETURN_STATUS(s);
-                       if (p->l.lex != XC_LEX_NUMBER)
-                               RETURN_STATUS(bc_error_bad_token());
-                       bc_parse_pushNUM(p);
-                       bc_parse_push(p, XC_INST_NEG);
-                       break;
-               case XC_LEX_NUMBER:
-                       dbg_lex("%s:%d LEX_NUMBER", __func__, __LINE__);
-                       bc_parse_pushNUM(p);
-                       break;
-               case DC_LEX_READ:
-                       dbg_lex("%s:%d LEX_KEY_READ", __func__, __LINE__);
-                       bc_parse_push(p, 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(p, 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(p, 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(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)
 {
+       BcParse *p = &G.prs;
        int i;
 
-       i = (int)p->l.lex - (int)XC_LEX_OP_POWER;
+       i = (int)p->lex - (int)XC_LEX_OP_POWER;
        if (i >= 0) {
                BcInst inst = dc_LEX_to_INST[i];
                if (inst != DC_INST_INVALID) {
-                       bc_parse_push(p, inst);
-                       RETURN_STATUS(zbc_lex_next(&p->l));
+                       xc_parse_push(inst);
+                       RETURN_STATUS(zxc_lex_next());
                }
        }
-       RETURN_STATUS(zdc_parse_token(p, p->l.lex));
+       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.lex:%d", __func__, __LINE__, p->l.lex);
-       while (p->l.lex != XC_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);
        }
 
@@ -5072,10 +5066,14 @@ static BC_STATUS zdc_parse_exprs_until_eof(BcParse *p)
 // 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 BcVec* bc_program_search(char *id, bool var)
+static BcVec* xc_program_search(char *id, bool var)
 {
        BcId e, *ptr;
        BcVec *v, *map;
@@ -5101,77 +5099,77 @@ static BcVec* bc_program_search(char *id, bool var)
 }
 
 // 'num' need not be initialized on entry
-static BC_STATUS zbc_program_num(BcResult *r, BcNum **num, bool hex)
+static BC_STATUS zxc_program_num(BcResult *r, BcNum **num)
 {
        switch (r->t) {
-               case XC_RESULT_STR:
-               case XC_RESULT_TEMP:
-               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;
-                       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 = XC_RESULT_TEMP;
-                       break;
-               }
-               case XC_RESULT_VAR:
-               case XC_RESULT_ARRAY:
-               case XC_RESULT_ARRAY_ELEM: {
-                       BcVec *v;
+       case XC_RESULT_STR:
+       case XC_RESULT_TEMP:
+       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;
 
-                       v = bc_program_search(r->d.id.name, r->t == XC_RESULT_VAR);
+               str = *xc_program_const(r->d.id.idx);
+               len = strlen(str);
 
-                       if (r->t == XC_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;
+               bc_num_init(&r->d.n, len);
+
+               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: {
+               BcVec *v;
+               void *p;
+               v = xc_program_search(r->d.id.name, r->t == XC_RESULT_VAR);
+// dc variables are all stacks, so here we have this:
+               p = bc_vec_top(v);
+// TODO: eliminate these stacks for bc-only config?
+               if (r->t == XC_RESULT_ARRAY_ELEM) {
+                       v = p;
+                       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 = 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);
+       default:
+               // Testing the theory that dc does not reach LAST/ONE
+               bb_error_msg_and_die("BUG:%d", r->t);
 #endif
        }
 
        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))
@@ -5180,19 +5178,18 @@ 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 == XC_RESULT_IBASE || lt == XC_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 == XC_RESULT_VAR || lt == XC_RESULT_ARRAY_ELEM)) {
-               s = zbc_program_num(*l, ln, false);
+               s = zxc_program_num(*l, ln);
                if (s) RETURN_STATUS(s);
        }
 
@@ -5203,9 +5200,9 @@ static BC_STATUS zbc_program_binOpPrep(BcResult **l, BcNum **ln,
 
        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 = XC_RESULT_TEMP;
        bc_vec_pop(&G.prog.results);
@@ -5213,7 +5210,7 @@ static void bc_program_binOpRetire(BcResult *r)
 }
 
 // Note: *r and *n need not be initialized by caller
-static BC_STATUS zbc_program_prep(BcResult **r, BcNum **n)
+static BC_STATUS zxc_program_prep(BcResult **r, BcNum **n)
 {
        BcStatus s;
 
@@ -5221,7 +5218,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)))
@@ -5229,86 +5226,82 @@ 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;
 
-       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 - XC_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.lex != XC_LEX_NLINE && parse.l.lex != XC_LEX_EOF) {
+       if (G.prs.lex != XC_LEX_NLINE && G.prs.lex != XC_LEX_EOF) {
                s = bc_error("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, XC_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)
+#define zxc_program_read(...) (zxc_program_read(__VA_ARGS__) COMMA_SUCCESS)
 
-static size_t bc_program_index(char *code, size_t *bgn)
+static size_t xc_program_index(char *code, size_t *bgn)
 {
        unsigned char *bytes = (void*)(code + *bgn);
        unsigned amt;
@@ -5316,37 +5309,32 @@ static size_t bc_program_index(char *code, size_t *bgn)
        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;
 
-       amt *= 8;
        res = 0;
-       for (i = 0; i < amt; i += 8)
+       i = 0;
+       do {
                res |= (size_t)(*bytes++) << i;
+               i += 8;
+       } while (--amt != 0);
 
        return res;
 }
 
-static char *bc_program_name(char *code, size_t *bgn)
+static char *xc_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;
+       *bgn += strlen(code) + 1;
 
-       return s;
+       return xstrdup(code);
 }
 
-static void bc_program_printString(const char *str)
+static void xc_program_printString(const char *str)
 {
 #if ENABLE_DC
        if (!str[0]) {
@@ -5468,7 +5456,7 @@ static void bc_num_printDecimal(BcNum *n)
 
 typedef void (*BcNumDigitOp)(size_t, size_t, bool) FAST_FUNC;
 
-static BC_STATUS zbc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNumDigitOp print)
+static BC_STATUS zxc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNumDigitOp print)
 {
        BcStatus s;
        BcVec stack;
@@ -5534,9 +5522,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;
@@ -5550,7 +5538,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 {
@@ -5565,14 +5553,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;
 
@@ -5584,7 +5572,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');
@@ -5593,9 +5581,9 @@ 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)
+static BC_STATUS zxc_program_print(char inst, size_t idx)
 {
        BcStatus s;
        BcResult *r;
@@ -5606,11 +5594,11 @@ static BC_STATUS zbc_program_print(char inst, size_t 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);
+       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, !pop);
 #if ENABLE_BC
                if (!s && IS_BC) bc_num_copy(&G.prog.last, num);
 #endif
@@ -5618,7 +5606,7 @@ static BC_STATUS zbc_program_print(char inst, size_t idx)
                char *str;
 
                idx = (r->t == XC_RESULT_STR) ? r->d.id.idx : num->rdx;
-               str = *bc_program_str(idx);
+               str = *xc_program_str(idx);
 
                if (inst == XC_INST_PRINT_STR) {
                        for (;;) {
@@ -5629,7 +5617,7 @@ static BC_STATUS zbc_program_print(char inst, size_t idx)
                                if (c == '\n') G.prog.nchars = 0;
                        }
                } else {
-                       bc_program_printString(str);
+                       xc_program_printString(str);
                        if (inst == XC_INST_PRINT) bb_putchar('\n');
                }
        }
@@ -5638,35 +5626,35 @@ static BC_STATUS zbc_program_print(char inst, size_t idx)
 
        RETURN_STATUS(s);
 }
-#define zbc_program_print(...) (zbc_program_print(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_print(...) (zxc_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;
 
-       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, XC_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);
@@ -5702,11 +5690,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)
@@ -5733,7 +5721,7 @@ 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_copyToVar(char *name, bool var)
 {
        BcStatus s;
        BcResult *ptr, r;
@@ -5746,7 +5734,7 @@ static BC_STATUS zbc_program_copyToVar(char *name, bool var)
        ptr = bc_vec_top(&G.prog.results);
        if ((ptr->t == XC_RESULT_ARRAY) != !var)
                RETURN_STATUS(bc_error_variable_is_wrong_type());
-       v = bc_program_search(name, var);
+       v = xc_program_search(name, var);
 
 #if ENABLE_DC
        if (ptr->t == XC_RESULT_STR && !var)
@@ -5755,11 +5743,11 @@ static BC_STATUS zbc_program_copyToVar(char *name, bool var)
                RETURN_STATUS(zdc_program_assignStr(ptr, v, 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);
+       v = xc_program_search(name, var);
 
        if (var) {
                bc_num_init_DEF_SIZE(&r.d.n);
@@ -5774,9 +5762,9 @@ static BC_STATUS zbc_program_copyToVar(char *name, bool var)
 
        RETURN_STATUS(s);
 }
-#define zbc_program_copyToVar(...) (zbc_program_copyToVar(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_copyToVar(...) (zxc_program_copyToVar(__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;
@@ -5784,7 +5772,7 @@ static BC_STATUS zbc_program_assign(char inst)
        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 == XC_RESULT_IBASE;
@@ -5796,7 +5784,7 @@ static BC_STATUS zbc_program_assign(char inst)
 
                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, true);
 
                RETURN_STATUS(zdc_program_assignStr(right, v, false));
        }
@@ -5816,7 +5804,7 @@ static BC_STATUS zbc_program_assign(char inst)
                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
@@ -5855,29 +5843,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 = 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, true);
                BcNum *num = bc_vec_top(v);
 
                free(name);
@@ -5902,7 +5890,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)
 {
@@ -5910,7 +5898,7 @@ 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 == XC_INST_ARRAY) {
                r.t = XC_RESULT_ARRAY;
@@ -5919,7 +5907,7 @@ static BC_STATUS zbc_program_pushArray(char *code, size_t *bgn, char inst)
                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;
@@ -5930,7 +5918,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, XC_RESULT_ARRAY_ELEM);
+               xc_program_retire(&r, XC_RESULT_ARRAY_ELEM);
        }
  err:
        if (s) free(r.d.id.name);
@@ -5946,7 +5934,7 @@ static BC_STATUS zbc_program_incdec(char inst)
        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) {
@@ -5961,7 +5949,7 @@ static BC_STATUS zbc_program_incdec(char inst)
                        : 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) {
@@ -5980,10 +5968,10 @@ static BC_STATUS zbc_program_call(char *code, size_t *idx)
        BcId *a;
        BcResult *arg;
 
-       nparams = bc_program_index(code, idx);
+       nparams = xc_program_index(code, idx);
        ip.inst_idx = 0;
-       ip.func = bc_program_index(code, idx);
-       func = bc_program_func(ip.func);
+       ip.func = xc_program_index(code, idx);
+       func = xc_program_func(ip.func);
 
        if (func->code.len == 0) {
                RETURN_STATUS(bc_error("undefined function"));
@@ -6002,7 +5990,7 @@ static BC_STATUS zbc_program_call(char *code, size_t *idx)
                if ((!a->idx) != (arg->t == XC_RESULT_ARRAY) || arg->t == XC_RESULT_STR)
                        RETURN_STATUS(bc_error_variable_is_wrong_type());
 
-               s = zbc_program_copyToVar(a->name, a->idx);
+               s = zxc_program_copyToVar(a->name, a->idx);
                if (s) RETURN_STATUS(s);
        }
 
@@ -6010,7 +5998,7 @@ 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);
+               v = xc_program_search(a->name, a->idx);
                if (a->idx) {
                        BcNum n2;
                        bc_num_init_DEF_SIZE(&n2);
@@ -6039,7 +6027,7 @@ static BC_STATUS zbc_program_return(char inst)
        if (!STACK_HAS_EQUAL_OR_MORE_THAN(&G.prog.results, ip->results_len_before_call + (inst == XC_INST_RET)))
                RETURN_STATUS(bc_error_stack_has_too_few_elements());
 
-       f = bc_program_func(ip->func);
+       f = xc_program_func(ip->func);
        res.t = XC_RESULT_TEMP;
 
        if (inst == XC_INST_RET) {
@@ -6047,7 +6035,7 @@ static BC_STATUS zbc_program_return(char inst)
                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);
@@ -6060,7 +6048,7 @@ static BC_STATUS zbc_program_return(char inst)
        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, a->idx);
                bc_vec_pop(v);
        }
 
@@ -6073,12 +6061,12 @@ static BC_STATUS zbc_program_return(char inst)
 #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;
 
@@ -6091,7 +6079,7 @@ 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;
@@ -6103,7 +6091,7 @@ static BC_STATUS zbc_program_builtin(char inst)
                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
@@ -6125,19 +6113,19 @@ static BC_STATUS zbc_program_builtin(char inst)
                char **str;
                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, XC_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)
@@ -6146,7 +6134,7 @@ static BC_STATUS zdc_program_divmod(void)
        BcResult *opd1, *opd2, res, res2;
        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);
@@ -6155,7 +6143,7 @@ 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);
+       xc_program_binOpRetire(&res2);
        res.t = XC_RESULT_TEMP;
        bc_vec_push(&G.prog.results, &res);
 
@@ -6175,11 +6163,11 @@ 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());
@@ -6187,11 +6175,11 @@ static BC_STATUS zdc_program_modexp(void)
        // Make sure that the values have their pointers updated, if necessary.
        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);
                }
        }
@@ -6201,7 +6189,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:
@@ -6234,9 +6222,9 @@ static BC_STATUS zdc_program_asciify(void)
 
        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)) {
@@ -6251,8 +6239,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;
@@ -6263,7 +6251,7 @@ static BC_STATUS zdc_program_asciify(void)
        } else {
                char *sp;
                idx = (r->t == XC_RESULT_STR) ? r->d.id.idx : num->rdx;
-               sp = *bc_program_str(idx);
+               sp = *xc_program_str(idx);
                c = sp[0];
        }
 
@@ -6300,15 +6288,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 == XC_RESULT_STR) ? r->d.id.idx : n->rdx;
-               str = *bc_program_str(idx);
+               str = *xc_program_str(idx);
                fputs(str, stdout);
        }
 
@@ -6323,7 +6311,7 @@ static BC_STATUS zdc_program_nquit(void)
        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);
@@ -6359,13 +6347,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;
@@ -6376,7 +6364,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, true);
                        n = bc_vec_top(v);
                }
 
@@ -6395,7 +6383,7 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond)
                        sidx = r->d.id.idx;
                } 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
@@ -6404,33 +6392,30 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond)
 
        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.lex != XC_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, DC_INST_POP_EXEC);
-               bc_parse_free(&prs);
        }
 
        ip.inst_idx = 0;
@@ -6447,7 +6432,7 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond)
 #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;
@@ -6465,11 +6450,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;
        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",
@@ -6481,249 +6466,249 @@ 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: {
-                               BcNum *num;
-                               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 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;
+               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 XC_INST_RET:
-                       case BC_INST_RET0:
-                               dbg_exec("BC_INST_RET[0]:");
-                               s = zbc_program_return(inst);
-                               goto read_updated_ip;
-                       case XC_INST_BOOL_OR:
-                       case XC_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_RET:
+               case BC_INST_RET0:
+                       dbg_exec("BC_INST_RET[0]:");
+                       s = zbc_program_return(inst);
+                       goto read_updated_ip;
+               case XC_INST_BOOL_OR:
+               case XC_INST_BOOL_AND:
 #endif // ENABLE_BC
-                       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 = zbc_program_logical(inst);
-                               break;
-                       case XC_INST_READ:
-                               dbg_exec("XC_INST_READ:");
-                               s = zbc_program_read();
-                               goto read_updated_ip;
-                       case XC_INST_VAR:
-                               dbg_exec("XC_INST_VAR:");
-                               s = zbc_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;
+               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_LAST:
-                               dbg_exec("BC_INST_LAST:");
-                               r.t = BC_RESULT_LAST;
-                               bc_vec_push(&G.prog.results, &r);
-                               break;
+               case BC_INST_LAST:
+                       dbg_exec("BC_INST_LAST:");
+                       r.t = BC_RESULT_LAST;
+                       bc_vec_push(&G.prog.results, &r);
+                       break;
 #endif
-                       case XC_INST_IBASE:
-                       case XC_INST_OBASE:
-                       case XC_INST_SCALE:
-                               dbg_exec("XC_INST_internalvar(%d):", inst - XC_INST_IBASE);
-                               bc_program_pushGlobal(inst);
-                               break;
-                       case XC_INST_SCALE_FUNC:
-                       case XC_INST_LENGTH:
-                       case XC_INST_SQRT:
-                               dbg_exec("BC_INST_builtin:");
-                               s = zbc_program_builtin(inst);
-                               break;
-                       case XC_INST_NUM:
-                               dbg_exec("XC_INST_NUM:");
-                               r.t = XC_RESULT_CONSTANT;
-                               r.d.id.idx = bc_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:");
-                               s = zbc_program_print(inst, 0);
-                               break;
-                       case XC_INST_STR:
-                               dbg_exec("XC_INST_STR:");
-                               r.t = XC_RESULT_STR;
-                               r.d.id.idx = bc_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 = zbc_program_op(inst);
-                               break;
-                       case XC_INST_BOOL_NOT: {
-                               BcNum *num;
-                               dbg_exec("XC_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) == 0)
-                                       bc_num_one(&r.d.n);
-                               //else bc_num_zero(&r.d.n); - already is
-                               bc_program_retire(&r, XC_RESULT_TEMP);
-                               break;
-                       }
-                       case XC_INST_NEG:
-                               dbg_exec("XC_INST_NEG:");
-                               s = zbc_program_negate();
-                               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:");
+                       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:
+               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 = zbc_program_assign(inst);
-                               break;
+               case XC_INST_ASSIGN:
+                       dbg_exec("BC_INST_ASSIGNxyz:");
+                       s = zxc_program_assign(inst);
+                       break;
 #if ENABLE_DC
-                       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 = zbc_program_print(XC_INST_PRINT, idx);
-                                       if (s) break;
-                               }
-                               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 = zbc_program_pushVar(code, &ip->inst_idx, true, copy);
-                               break;
-                       }
-                       case DC_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 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
+                       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_copyToVar(name, true);
+                       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);
                }
 
@@ -6732,9 +6717,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;
@@ -6750,47 +6735,74 @@ 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.lex != XC_LEX_EOF) {
+ IF_BC(check_eof:)
+       while (G.prs.lex != XC_LEX_EOF) {
                BcInstPtr *ip;
                BcFunc *f;
 
-               dbg_lex("%s:%d G.prs.l.lex:%d, parsing...", __func__, __LINE__, G.prs.l.lex);
+               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
+                       if (G.prs.lex == BC_LEX_SCOLON
+                        || G.prs.lex == XC_LEX_NLINE
+                       ) {
+                               s = zxc_lex_next();
+                               if (s) goto err;
+                               goto check_eof;
+                       }
+
+                       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
+                       ) {
+                               const char *err_at;
+//TODO: commonalize for other parse errors:
+                               err_at = G.prs.lex_next_at ? G.prs.lex_next_at : "UNKNOWN";
+                               bc_error_fmt("bad statement terminator at '%.*s'",
+                                       (int)(strchrnul(err_at, '\n') - err_at),
+                                       err_at
+                               );
+                               goto err;
+                       }
+                       // The above logic is fragile. Check these examples:
+                       // - interactive read() still works
+#endif
                } else {
+#if ENABLE_DC
                        // Most of dc parsing assumes all whitespace,
                        // including '\n', is eaten.
-                       while (G.prs.l.lex == XC_LEX_NLINE) {
-                               s = zbc_lex_next(&G.prs.l);
+                       while (G.prs.lex == XC_LEX_NLINE) {
+                               s = zxc_lex_next();
                                if (s) goto err;
-                               if (G.prs.l.lex == XC_LEX_EOF)
+                               if (G.prs.lex == XC_LEX_EOF)
                                        goto done;
                        }
-                       IF_DC(s = zdc_parse_expr(&G.prs));
+                       s = zdc_parse_expr();
+#endif
                }
                if (s || G_interrupt) {
  err:
-                       bc_parse_reset(&G.prs); // includes bc_program_reset()
+                       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;
                }
 
@@ -6801,7 +6813,7 @@ static BC_STATUS zbc_vm_process(const char *text)
                if (ip->func != BC_PROG_MAIN)
                        bb_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
@@ -6840,53 +6852,54 @@ static BC_STATUS zbc_vm_process(const char *text)
                bc_vec_pop_all(&f->code);
                ip->inst_idx = 0;
        }
- done:
+ IF_DC(done:)
        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;
 
        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);
 }
 
@@ -7125,7 +7138,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;
@@ -7136,8 +7149,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
@@ -7145,7 +7157,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.
@@ -7156,14 +7168,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);)
@@ -7176,21 +7188,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;
 
@@ -7204,7 +7208,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);)
@@ -7220,8 +7224,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);
@@ -7239,22 +7243,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).
@@ -7281,13 +7285,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
@@ -7305,14 +7313,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
 
@@ -7334,7 +7342,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;
@@ -7345,12 +7353,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':
@@ -7369,8 +7377,9 @@ 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
+