/*
 * *****************************************************************************
 *
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * *****************************************************************************
 *
 * Definitions for bc programs.
 *
 */

#ifndef BC_PROGRAM_H
#define BC_PROGRAM_H

#include <assert.h>
#include <stddef.h>

#include <status.h>
#include <parse.h>
#include <lang.h>
#include <num.h>
#include <rand.h>

/// The index of ibase in the globals array.
#define BC_PROG_GLOBALS_IBASE (0)

/// The index of obase in the globals array.
#define BC_PROG_GLOBALS_OBASE (1)

/// The index of scale in the globals array.
#define BC_PROG_GLOBALS_SCALE (2)

#if BC_ENABLE_EXTRA_MATH

/// The index of the rand max in the maxes array.
#define BC_PROG_MAX_RAND (3)

#endif // BC_ENABLE_EXTRA_MATH

/// The length of the globals array.
#define BC_PROG_GLOBALS_LEN (3 + BC_ENABLE_EXTRA_MATH)

typedef struct BcProgram {

	/// The array of globals values.
	BcBigDig globals[BC_PROG_GLOBALS_LEN];

	/// The array of globals stacks.
	BcVec globals_v[BC_PROG_GLOBALS_LEN];

#if BC_ENABLE_EXTRA_MATH

	/// The pseudo-random number generator.
	BcRNG rng;

#endif // BC_ENABLE_EXTRA_MATH

	/// The results stack.
	BcVec results;

	/// The execution stack.
	BcVec stack;

	/// A pointer to the current function's constants.
	BcVec *consts;

	/// A pointer to the current function's strings.
	BcVec *strs;

	/// The array of functions.
	BcVec fns;

	/// The map of functions to go with fns.
	BcVec fn_map;

	/// The array of variables.
	BcVec vars;

	/// The map of variables to go with vars.
	BcVec var_map;

	/// The array of arrays.
	BcVec arrs;

	/// The map of arrays to go with arrs.
	BcVec arr_map;

#if DC_ENABLED

	/// A vector of tail calls. These are just integers, which are the number of
	/// tail calls that have been executed for each function (string) on the
	/// stack for dc. This is to prevent dc from constantly growing memory use
	/// because of pushing more and more string executions on the stack.
	BcVec tail_calls;

#endif // DC_ENABLED

	/// A BcNum that has the proper base for asciify.
	BcNum strmb;

#if BC_ENABLED

	/// The last printed value for bc.
	BcNum last;

#endif // BC_ENABLED

	// The BcDig array for strmb. This uses BC_NUM_LONG_LOG10 because it is used
	// in bc_num_ulong2num(), which attempts to realloc, unless it is big
	// enough. This is big enough.
	BcDig strmb_num[BC_NUM_BIGDIG_LOG10];

} BcProgram;

/**
 * Returns true if the stack @a s has at least @a n items, false otherwise.
 * @param s  The stack to check.
 * @param n  The number of items the stack must have.
 * @return   True if @a s has at least @a n items, false otherwise.
 */
#define BC_PROG_STACK(s, n) ((s)->len >= ((size_t) (n)))

/**
 * Get a pointer to the top value in a global value stack.
 * @param v  The global value stack.
 * @return   A pointer to the top value in @a v.
 */
#define BC_PROG_GLOBAL_PTR(v) (bc_vec_top(v))

/**
 * Get the top value in a global value stack.
 * @param v  The global value stack.
 * @return   The top value in @a v.
 */
#define BC_PROG_GLOBAL(v) (*((BcBigDig*) BC_PROG_GLOBAL_PTR(v)))

/**
 * Returns the current value of ibase.
 * @param p  The program.
 * @return   The current ibase.
 */
#define BC_PROG_IBASE(p) ((p)->globals[BC_PROG_GLOBALS_IBASE])

/**
 * Returns the current value of obase.
 * @param p  The program.
 * @return   The current obase.
 */
#define BC_PROG_OBASE(p) ((p)->globals[BC_PROG_GLOBALS_OBASE])

/**
 * Returns the current value of scale.
 * @param p  The program.
 * @return   The current scale.
 */
#define BC_PROG_SCALE(p) ((p)->globals[BC_PROG_GLOBALS_SCALE])

/// The index for the main function in the functions array.//
#define BC_PROG_MAIN (0)

/// The index for the read function in the functions array.
#define BC_PROG_READ (1)

/**
 * Retires (completes the execution of) an instruction. Some instructions
 * require special retirement, but most can use this. This basically pops the
 * operands while preserving the result (which we assumed was pushed before the
 * actual operation).
 * @param p     The program.
 * @param nres  The number of results returned by the instruction.
 * @param nops  The number of operands used by the instruction.
 */
#define bc_program_retire(p, nres, nops) \
	(bc_vec_npopAt(&(p)->results, (nops), (p)->results.len - (nres + nops)))

#if DC_ENABLED

/// A constant that tells how many functions are required in dc.
#define BC_PROG_REQ_FUNCS (2)

#if !BC_ENABLED

/// This define disappears the parameter last because for dc only, last is
/// always true.
#define bc_program_copyToVar(p, name, t, last) \
	bc_program_copyToVar(p, name, t)

#endif // !BC_ENABLED

#else // DC_ENABLED

/// This define disappears pop and copy because for bc, 'pop' and 'copy' are
/// always false.
#define bc_program_pushVar(p, code, bgn, pop, copy) \
	bc_program_pushVar(p, code, bgn)

// In debug mode, we want bc to check the stack, but otherwise, we don't because
// the bc language implicitly mandates that the stack should always have enough
// items.
#ifdef NDEBUG
#define BC_PROG_NO_STACK_CHECK
#endif // NDEBUG

#endif // DC_ENABLED

/**
 * Returns true if the BcNum @a n is acting as a string.
 * @param n  The BcNum to test.
 * @return   True if @a n is acting as a string, false otherwise.
 */
#define BC_PROG_STR(n) ((n)->num == NULL && !(n)->cap)

#if BC_ENABLED

/**
 * Returns true if the result @a r and @a n is a number.
 * @param r  The result.
 * @param n  The number corresponding to the result.
 * @return   True if the result holds a number, false otherwise.
 */
#define BC_PROG_NUM(r, n) \
	((r)->t != BC_RESULT_ARRAY && (r)->t != BC_RESULT_STR && !BC_PROG_STR(n))

#else // BC_ENABLED

/**
 * Returns true if the result @a r and @a n is a number.
 * @param r  The result.
 * @param n  The number corresponding to the result.
 * @return   True if the result holds a number, false otherwise.
 */
#define BC_PROG_NUM(r, n) ((r)->t != BC_RESULT_STR && !BC_PROG_STR(n))

#endif // BC_ENABLED

/**
 * This is a function type for unary operations. Currently, these include
 * boolean not, negation, and truncation with extra math.
 * @param r  The BcResult to store the result into.
 * @param n  The parameter to the unary operation.
 */
typedef void (*BcProgramUnary)(BcResult *r, BcNum *n);

/**
 * Initializes the BcProgram.
 * @param p  The program to initialize.
 */
void bc_program_init(BcProgram *p);

#ifndef NDEBUG

/**
 * Frees a BcProgram. This is only used in debug builds because a BcProgram is
 * only freed on program exit, and we don't care about freeing resources on
 * exit.
 * @param p  The program to initialize.
 */
void bc_program_free(BcProgram *p);

#endif // NDEBUG

#if BC_DEBUG_CODE
#if BC_ENABLED && DC_ENABLED

/**
 * Prints the bytecode in a function. This is a debug-only function.
 * @param p  The program.
 */
void bc_program_code(const BcProgram *p);

/**
 * Prints an instruction. This is a debug-only function.
 * @param p  The program.
 * @param code  The bytecode array.
 * @param bgn   A pointer to the current index. It is also updated to the next
 *              index.
 */
void bc_program_printInst(const BcProgram *p, const char *code,
                          size_t *restrict bgn);

/**
 * Prints the stack. This is a debug-only function.
 * @param p  The program.
 */
void bc_program_printStackDebug(BcProgram* p);

#endif // BC_ENABLED && DC_ENABLED
#endif // BC_DEBUG_CODE

/**
 * Returns the index of the variable or array in their respective arrays.
 * @param p    The program.
 * @param id   The BcId of the variable or array.
 * @param var  True if the search should be for a variable, false for an array.
 * @return     The index of the variable or array in the correct array.
 */
size_t bc_program_search(BcProgram *p, const char* id, bool var);

/**
 * Adds a string to a function and returns the string's index in the function.
 * @param p     The program.
 * @param str   The string to add.
 * @param fidx  The index of the function to add to.
 */
size_t bc_program_addString(BcProgram *p, const char *str, size_t fidx);

/**
 * Inserts a function into the program and returns the index of the function in
 * the fns array.
 * @param p     The program.
 * @param name  The name of the function.
 * @return      The index of the function after insertion.
 */
size_t bc_program_insertFunc(BcProgram *p, const char *name);

/**
 * Resets a program, usually because of resetting after an error.
 * @param p  The program to reset.
 */
void bc_program_reset(BcProgram *p);

/**
 * Executes bc or dc code in the BcProgram.
 * @param p  The program.
 */
void bc_program_exec(BcProgram *p);

/**
 * Negates a copy of a BcNum. This is a BcProgramUnary function.
 * @param r  The BcResult to store the result into.
 * @param n  The parameter to the unary operation.
 */
void bc_program_negate(BcResult *r, BcNum *n);

/**
 * Returns a boolean not of a BcNum. This is a BcProgramUnary function.
 * @param r  The BcResult to store the result into.
 * @param n  The parameter to the unary operation.
 */
void bc_program_not(BcResult *r, BcNum *n);

#if BC_ENABLE_EXTRA_MATH

/**
 * Truncates a copy of a BcNum. This is a BcProgramUnary function.
 * @param r  The BcResult to store the result into.
 * @param n  The parameter to the unary operation.
 */
void bc_program_trunc(BcResult *r, BcNum *n);

#endif // BC_ENABLE_EXTRA_MATH

/// A reference to an array of binary operator functions.
extern const BcNumBinaryOp bc_program_ops[];

/// A reference to an array of binary operator allocation request functions.
extern const BcNumBinaryOpReq bc_program_opReqs[];

/// A reference to an array of unary operator functions.
extern const BcProgramUnary bc_program_unarys[];

/// A reference to a filename for command-line expressions.
extern const char bc_program_exprs_name[];

/// A reference to a filename for stdin.
extern const char bc_program_stdin_name[];

/// A reference to the ready message printed on SIGINT.
extern const char bc_program_ready_msg[];

/// A reference to the length of the ready message.
extern const size_t bc_program_ready_msg_len;

/// A reference to an array of escape characters for the print statement.
extern const char bc_program_esc_chars[];

/// A reference to an array of the characters corresponding to the escape
/// characters in bc_program_esc_chars.
extern const char bc_program_esc_seqs[];

#if BC_HAS_COMPUTED_GOTO

#if BC_DEBUG_CODE

#define BC_PROG_JUMP(inst, code, ip)                                 \
	do {                                                             \
		inst = (uchar) (code)[(ip)->idx++];                          \
		bc_file_printf(&vm.ferr, "inst: %s\n", bc_inst_names[inst]); \
		bc_file_flush(&vm.ferr, bc_flush_none);                      \
		goto *bc_program_inst_lbls[inst];                            \
	} while (0)

#else // BC_DEBUG_CODE

#define BC_PROG_JUMP(inst, code, ip)        \
	do {                                    \
		inst = (uchar) (code)[(ip)->idx++]; \
		goto *bc_program_inst_lbls[inst];   \
	} while (0)

#endif // BC_DEBUG_CODE

#define BC_PROG_DIRECT_JUMP(l) goto lbl_ ## l;
#define BC_PROG_LBL(l) lbl_ ## l
#define BC_PROG_FALLTHROUGH

#if BC_C11

#define BC_PROG_LBLS_SIZE (sizeof(bc_program_inst_lbls) / sizeof(void*))
#define BC_PROG_LBLS_ASSERT \
	static_assert(BC_PROG_LBLS_SIZE == BC_INST_INVALID + 1,\
	              "bc_program_inst_lbls[] mismatches the instructions")

#else // BC_C11

#define BC_PROG_LBLS_ASSERT

#endif // BC_C11

#if BC_ENABLED

#if DC_ENABLED

#if BC_ENABLE_EXTRA_MATH

#define BC_PROG_LBLS static const void* const bc_program_inst_lbls[] = { \
	&&lbl_BC_INST_INC,                                                   \
	&&lbl_BC_INST_DEC,                                                   \
	&&lbl_BC_INST_NEG,                                                   \
	&&lbl_BC_INST_BOOL_NOT,                                              \
	&&lbl_BC_INST_TRUNC,                                                 \
	&&lbl_BC_INST_POWER,                                                 \
	&&lbl_BC_INST_MULTIPLY,                                              \
	&&lbl_BC_INST_DIVIDE,                                                \
	&&lbl_BC_INST_MODULUS,                                               \
	&&lbl_BC_INST_PLUS,                                                  \
	&&lbl_BC_INST_MINUS,                                                 \
	&&lbl_BC_INST_PLACES,                                                \
	&&lbl_BC_INST_LSHIFT,                                                \
	&&lbl_BC_INST_RSHIFT,                                                \
	&&lbl_BC_INST_REL_EQ,                                                \
	&&lbl_BC_INST_REL_LE,                                                \
	&&lbl_BC_INST_REL_GE,                                                \
	&&lbl_BC_INST_REL_NE,                                                \
	&&lbl_BC_INST_REL_LT,                                                \
	&&lbl_BC_INST_REL_GT,                                                \
	&&lbl_BC_INST_BOOL_OR,                                               \
	&&lbl_BC_INST_BOOL_AND,                                              \
	&&lbl_BC_INST_ASSIGN_POWER,                                          \
	&&lbl_BC_INST_ASSIGN_MULTIPLY,                                       \
	&&lbl_BC_INST_ASSIGN_DIVIDE,                                         \
	&&lbl_BC_INST_ASSIGN_MODULUS,                                        \
	&&lbl_BC_INST_ASSIGN_PLUS,                                           \
	&&lbl_BC_INST_ASSIGN_MINUS,                                          \
	&&lbl_BC_INST_ASSIGN_PLACES,                                         \
	&&lbl_BC_INST_ASSIGN_LSHIFT,                                         \
	&&lbl_BC_INST_ASSIGN_RSHIFT,                                         \
	&&lbl_BC_INST_ASSIGN,                                                \
	&&lbl_BC_INST_ASSIGN_POWER_NO_VAL,                                   \
	&&lbl_BC_INST_ASSIGN_MULTIPLY_NO_VAL,                                \
	&&lbl_BC_INST_ASSIGN_DIVIDE_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_MODULUS_NO_VAL,                                 \
	&&lbl_BC_INST_ASSIGN_PLUS_NO_VAL,                                    \
	&&lbl_BC_INST_ASSIGN_MINUS_NO_VAL,                                   \
	&&lbl_BC_INST_ASSIGN_PLACES_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_LSHIFT_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_RSHIFT_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_NO_VAL,                                         \
	&&lbl_BC_INST_NUM,                                                   \
	&&lbl_BC_INST_VAR,                                                   \
	&&lbl_BC_INST_ARRAY_ELEM,                                            \
	&&lbl_BC_INST_ARRAY,                                                 \
	&&lbl_BC_INST_ZERO,                                                  \
	&&lbl_BC_INST_ONE,                                                   \
	&&lbl_BC_INST_LAST,                                                  \
	&&lbl_BC_INST_IBASE,                                                 \
	&&lbl_BC_INST_OBASE,                                                 \
	&&lbl_BC_INST_SCALE,                                                 \
	&&lbl_BC_INST_SEED,                                                  \
	&&lbl_BC_INST_LENGTH,                                                \
	&&lbl_BC_INST_SCALE_FUNC,                                            \
	&&lbl_BC_INST_SQRT,                                                  \
	&&lbl_BC_INST_ABS,                                                   \
	&&lbl_BC_INST_IRAND,                                                 \
	&&lbl_BC_INST_ASCIIFY,                                               \
	&&lbl_BC_INST_READ,                                                  \
	&&lbl_BC_INST_RAND,                                                  \
	&&lbl_BC_INST_MAXIBASE,                                              \
	&&lbl_BC_INST_MAXOBASE,                                              \
	&&lbl_BC_INST_MAXSCALE,                                              \
	&&lbl_BC_INST_MAXRAND,                                               \
	&&lbl_BC_INST_PRINT,                                                 \
	&&lbl_BC_INST_PRINT_POP,                                             \
	&&lbl_BC_INST_STR,                                                   \
	&&lbl_BC_INST_PRINT_STR,                                             \
	&&lbl_BC_INST_JUMP,                                                  \
	&&lbl_BC_INST_JUMP_ZERO,                                             \
	&&lbl_BC_INST_CALL,                                                  \
	&&lbl_BC_INST_RET,                                                   \
	&&lbl_BC_INST_RET0,                                                  \
	&&lbl_BC_INST_RET_VOID,                                              \
	&&lbl_BC_INST_HALT,                                                  \
	&&lbl_BC_INST_POP,                                                   \
	&&lbl_BC_INST_SWAP,                                                  \
	&&lbl_BC_INST_MODEXP,                                                \
	&&lbl_BC_INST_DIVMOD,                                                \
	&&lbl_BC_INST_PRINT_STREAM,                                          \
	&&lbl_BC_INST_POP_EXEC,                                              \
	&&lbl_BC_INST_EXECUTE,                                               \
	&&lbl_BC_INST_EXEC_COND,                                             \
	&&lbl_BC_INST_PRINT_STACK,                                           \
	&&lbl_BC_INST_CLEAR_STACK,                                           \
	&&lbl_BC_INST_REG_STACK_LEN,                                         \
	&&lbl_BC_INST_STACK_LEN,                                             \
	&&lbl_BC_INST_DUPLICATE,                                             \
	&&lbl_BC_INST_LOAD,                                                  \
	&&lbl_BC_INST_PUSH_VAR,                                              \
	&&lbl_BC_INST_PUSH_TO_VAR,                                           \
	&&lbl_BC_INST_QUIT,                                                  \
	&&lbl_BC_INST_NQUIT,                                                 \
	&&lbl_BC_INST_EXEC_STACK_LEN,                                        \
	&&lbl_BC_INST_INVALID,                                               \
}

#else // BC_ENABLE_EXTRA_MATH

#define BC_PROG_LBLS static const void* const bc_program_inst_lbls[] = { \
	&&lbl_BC_INST_INC,                                                   \
	&&lbl_BC_INST_DEC,                                                   \
	&&lbl_BC_INST_NEG,                                                   \
	&&lbl_BC_INST_BOOL_NOT,                                              \
	&&lbl_BC_INST_POWER,                                                 \
	&&lbl_BC_INST_MULTIPLY,                                              \
	&&lbl_BC_INST_DIVIDE,                                                \
	&&lbl_BC_INST_MODULUS,                                               \
	&&lbl_BC_INST_PLUS,                                                  \
	&&lbl_BC_INST_MINUS,                                                 \
	&&lbl_BC_INST_REL_EQ,                                                \
	&&lbl_BC_INST_REL_LE,                                                \
	&&lbl_BC_INST_REL_GE,                                                \
	&&lbl_BC_INST_REL_NE,                                                \
	&&lbl_BC_INST_REL_LT,                                                \
	&&lbl_BC_INST_REL_GT,                                                \
	&&lbl_BC_INST_BOOL_OR,                                               \
	&&lbl_BC_INST_BOOL_AND,                                              \
	&&lbl_BC_INST_ASSIGN_POWER,                                          \
	&&lbl_BC_INST_ASSIGN_MULTIPLY,                                       \
	&&lbl_BC_INST_ASSIGN_DIVIDE,                                         \
	&&lbl_BC_INST_ASSIGN_MODULUS,                                        \
	&&lbl_BC_INST_ASSIGN_PLUS,                                           \
	&&lbl_BC_INST_ASSIGN_MINUS,                                          \
	&&lbl_BC_INST_ASSIGN,                                                \
	&&lbl_BC_INST_ASSIGN_POWER_NO_VAL,                                   \
	&&lbl_BC_INST_ASSIGN_MULTIPLY_NO_VAL,                                \
	&&lbl_BC_INST_ASSIGN_DIVIDE_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_MODULUS_NO_VAL,                                 \
	&&lbl_BC_INST_ASSIGN_PLUS_NO_VAL,                                    \
	&&lbl_BC_INST_ASSIGN_MINUS_NO_VAL,                                   \
	&&lbl_BC_INST_ASSIGN_NO_VAL,                                         \
	&&lbl_BC_INST_NUM,                                                   \
	&&lbl_BC_INST_VAR,                                                   \
	&&lbl_BC_INST_ARRAY_ELEM,                                            \
	&&lbl_BC_INST_ARRAY,                                                 \
	&&lbl_BC_INST_ZERO,                                                  \
	&&lbl_BC_INST_ONE,                                                   \
	&&lbl_BC_INST_LAST,                                                  \
	&&lbl_BC_INST_IBASE,                                                 \
	&&lbl_BC_INST_OBASE,                                                 \
	&&lbl_BC_INST_SCALE,                                                 \
	&&lbl_BC_INST_LENGTH,                                                \
	&&lbl_BC_INST_SCALE_FUNC,                                            \
	&&lbl_BC_INST_SQRT,                                                  \
	&&lbl_BC_INST_ABS,                                                   \
	&&lbl_BC_INST_ASCIIFY,                                               \
	&&lbl_BC_INST_READ,                                                  \
	&&lbl_BC_INST_MAXIBASE,                                              \
	&&lbl_BC_INST_MAXOBASE,                                              \
	&&lbl_BC_INST_MAXSCALE,                                              \
	&&lbl_BC_INST_PRINT,                                                 \
	&&lbl_BC_INST_PRINT_POP,                                             \
	&&lbl_BC_INST_STR,                                                   \
	&&lbl_BC_INST_PRINT_STR,                                             \
	&&lbl_BC_INST_JUMP,                                                  \
	&&lbl_BC_INST_JUMP_ZERO,                                             \
	&&lbl_BC_INST_CALL,                                                  \
	&&lbl_BC_INST_RET,                                                   \
	&&lbl_BC_INST_RET0,                                                  \
	&&lbl_BC_INST_RET_VOID,                                              \
	&&lbl_BC_INST_HALT,                                                  \
	&&lbl_BC_INST_POP,                                                   \
	&&lbl_BC_INST_SWAP,                                                  \
	&&lbl_BC_INST_MODEXP,                                                \
	&&lbl_BC_INST_DIVMOD,                                                \
	&&lbl_BC_INST_PRINT_STREAM,                                          \
	&&lbl_BC_INST_POP_EXEC,                                              \
	&&lbl_BC_INST_EXECUTE,                                               \
	&&lbl_BC_INST_EXEC_COND,                                             \
	&&lbl_BC_INST_PRINT_STACK,                                           \
	&&lbl_BC_INST_CLEAR_STACK,                                           \
	&&lbl_BC_INST_REG_STACK_LEN,                                         \
	&&lbl_BC_INST_STACK_LEN,                                             \
	&&lbl_BC_INST_DUPLICATE,                                             \
	&&lbl_BC_INST_LOAD,                                                  \
	&&lbl_BC_INST_PUSH_VAR,                                              \
	&&lbl_BC_INST_PUSH_TO_VAR,                                           \
	&&lbl_BC_INST_QUIT,                                                  \
	&&lbl_BC_INST_NQUIT,                                                 \
	&&lbl_BC_INST_EXEC_STACK_LEN,                                        \
	&&lbl_BC_INST_INVALID,                                               \
}

#endif // BC_ENABLE_EXTRA_MATH

#else // DC_ENABLED

#if BC_ENABLE_EXTRA_MATH

#define BC_PROG_LBLS static const void* const bc_program_inst_lbls[] = { \
	&&lbl_BC_INST_INC,                                                   \
	&&lbl_BC_INST_DEC,                                                   \
	&&lbl_BC_INST_NEG,                                                   \
	&&lbl_BC_INST_BOOL_NOT,                                              \
	&&lbl_BC_INST_TRUNC,                                                 \
	&&lbl_BC_INST_POWER,                                                 \
	&&lbl_BC_INST_MULTIPLY,                                              \
	&&lbl_BC_INST_DIVIDE,                                                \
	&&lbl_BC_INST_MODULUS,                                               \
	&&lbl_BC_INST_PLUS,                                                  \
	&&lbl_BC_INST_MINUS,                                                 \
	&&lbl_BC_INST_PLACES,                                                \
	&&lbl_BC_INST_LSHIFT,                                                \
	&&lbl_BC_INST_RSHIFT,                                                \
	&&lbl_BC_INST_REL_EQ,                                                \
	&&lbl_BC_INST_REL_LE,                                                \
	&&lbl_BC_INST_REL_GE,                                                \
	&&lbl_BC_INST_REL_NE,                                                \
	&&lbl_BC_INST_REL_LT,                                                \
	&&lbl_BC_INST_REL_GT,                                                \
	&&lbl_BC_INST_BOOL_OR,                                               \
	&&lbl_BC_INST_BOOL_AND,                                              \
	&&lbl_BC_INST_ASSIGN_POWER,                                          \
	&&lbl_BC_INST_ASSIGN_MULTIPLY,                                       \
	&&lbl_BC_INST_ASSIGN_DIVIDE,                                         \
	&&lbl_BC_INST_ASSIGN_MODULUS,                                        \
	&&lbl_BC_INST_ASSIGN_PLUS,                                           \
	&&lbl_BC_INST_ASSIGN_MINUS,                                          \
	&&lbl_BC_INST_ASSIGN_PLACES,                                         \
	&&lbl_BC_INST_ASSIGN_LSHIFT,                                         \
	&&lbl_BC_INST_ASSIGN_RSHIFT,                                         \
	&&lbl_BC_INST_ASSIGN,                                                \
	&&lbl_BC_INST_ASSIGN_POWER_NO_VAL,                                   \
	&&lbl_BC_INST_ASSIGN_MULTIPLY_NO_VAL,                                \
	&&lbl_BC_INST_ASSIGN_DIVIDE_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_MODULUS_NO_VAL,                                 \
	&&lbl_BC_INST_ASSIGN_PLUS_NO_VAL,                                    \
	&&lbl_BC_INST_ASSIGN_MINUS_NO_VAL,                                   \
	&&lbl_BC_INST_ASSIGN_PLACES_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_LSHIFT_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_RSHIFT_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_NO_VAL,                                         \
	&&lbl_BC_INST_NUM,                                                   \
	&&lbl_BC_INST_VAR,                                                   \
	&&lbl_BC_INST_ARRAY_ELEM,                                            \
	&&lbl_BC_INST_ARRAY,                                                 \
	&&lbl_BC_INST_ZERO,                                                  \
	&&lbl_BC_INST_ONE,                                                   \
	&&lbl_BC_INST_LAST,                                                  \
	&&lbl_BC_INST_IBASE,                                                 \
	&&lbl_BC_INST_OBASE,                                                 \
	&&lbl_BC_INST_SCALE,                                                 \
	&&lbl_BC_INST_SEED,                                                  \
	&&lbl_BC_INST_LENGTH,                                                \
	&&lbl_BC_INST_SCALE_FUNC,                                            \
	&&lbl_BC_INST_SQRT,                                                  \
	&&lbl_BC_INST_ABS,                                                   \
	&&lbl_BC_INST_IRAND,                                                 \
	&&lbl_BC_INST_ASCIIFY,                                               \
	&&lbl_BC_INST_READ,                                                  \
	&&lbl_BC_INST_RAND,                                                  \
	&&lbl_BC_INST_MAXIBASE,                                              \
	&&lbl_BC_INST_MAXOBASE,                                              \
	&&lbl_BC_INST_MAXSCALE,                                              \
	&&lbl_BC_INST_MAXRAND,                                               \
	&&lbl_BC_INST_PRINT,                                                 \
	&&lbl_BC_INST_PRINT_POP,                                             \
	&&lbl_BC_INST_STR,                                                   \
	&&lbl_BC_INST_PRINT_STR,                                             \
	&&lbl_BC_INST_JUMP,                                                  \
	&&lbl_BC_INST_JUMP_ZERO,                                             \
	&&lbl_BC_INST_CALL,                                                  \
	&&lbl_BC_INST_RET,                                                   \
	&&lbl_BC_INST_RET0,                                                  \
	&&lbl_BC_INST_RET_VOID,                                              \
	&&lbl_BC_INST_HALT,                                                  \
	&&lbl_BC_INST_POP,                                                   \
	&&lbl_BC_INST_SWAP,                                                  \
	&&lbl_BC_INST_MODEXP,                                                \
	&&lbl_BC_INST_DIVMOD,                                                \
	&&lbl_BC_INST_PRINT_STREAM,                                          \
	&&lbl_BC_INST_INVALID,                                               \
}

#else // BC_ENABLE_EXTRA_MATH

#define BC_PROG_LBLS static const void* const bc_program_inst_lbls[] = { \
	&&lbl_BC_INST_INC,                                                   \
	&&lbl_BC_INST_DEC,                                                   \
	&&lbl_BC_INST_NEG,                                                   \
	&&lbl_BC_INST_BOOL_NOT,                                              \
	&&lbl_BC_INST_POWER,                                                 \
	&&lbl_BC_INST_MULTIPLY,                                              \
	&&lbl_BC_INST_DIVIDE,                                                \
	&&lbl_BC_INST_MODULUS,                                               \
	&&lbl_BC_INST_PLUS,                                                  \
	&&lbl_BC_INST_MINUS,                                                 \
	&&lbl_BC_INST_REL_EQ,                                                \
	&&lbl_BC_INST_REL_LE,                                                \
	&&lbl_BC_INST_REL_GE,                                                \
	&&lbl_BC_INST_REL_NE,                                                \
	&&lbl_BC_INST_REL_LT,                                                \
	&&lbl_BC_INST_REL_GT,                                                \
	&&lbl_BC_INST_BOOL_OR,                                               \
	&&lbl_BC_INST_BOOL_AND,                                              \
	&&lbl_BC_INST_ASSIGN_POWER,                                          \
	&&lbl_BC_INST_ASSIGN_MULTIPLY,                                       \
	&&lbl_BC_INST_ASSIGN_DIVIDE,                                         \
	&&lbl_BC_INST_ASSIGN_MODULUS,                                        \
	&&lbl_BC_INST_ASSIGN_PLUS,                                           \
	&&lbl_BC_INST_ASSIGN_MINUS,                                          \
	&&lbl_BC_INST_ASSIGN,                                                \
	&&lbl_BC_INST_ASSIGN_POWER_NO_VAL,                                   \
	&&lbl_BC_INST_ASSIGN_MULTIPLY_NO_VAL,                                \
	&&lbl_BC_INST_ASSIGN_DIVIDE_NO_VAL,                                  \
	&&lbl_BC_INST_ASSIGN_MODULUS_NO_VAL,                                 \
	&&lbl_BC_INST_ASSIGN_PLUS_NO_VAL,                                    \
	&&lbl_BC_INST_ASSIGN_MINUS_NO_VAL,                                   \
	&&lbl_BC_INST_ASSIGN_NO_VAL,                                         \
	&&lbl_BC_INST_NUM,                                                   \
	&&lbl_BC_INST_VAR,                                                   \
	&&lbl_BC_INST_ARRAY_ELEM,                                            \
	&&lbl_BC_INST_ARRAY,                                                 \
	&&lbl_BC_INST_ZERO,                                                  \
	&&lbl_BC_INST_ONE,                                                   \
	&&lbl_BC_INST_LAST,                                                  \
	&&lbl_BC_INST_IBASE,                                                 \
	&&lbl_BC_INST_OBASE,                                                 \
	&&lbl_BC_INST_SCALE,                                                 \
	&&lbl_BC_INST_LENGTH,                                                \
	&&lbl_BC_INST_SCALE_FUNC,                                            \
	&&lbl_BC_INST_SQRT,                                                  \
	&&lbl_BC_INST_ABS,                                                   \
	&&lbl_BC_INST_ASCIIFY,                                               \
	&&lbl_BC_INST_READ,                                                  \
	&&lbl_BC_INST_MAXIBASE,                                              \
	&&lbl_BC_INST_MAXOBASE,                                              \
	&&lbl_BC_INST_MAXSCALE,                                              \
	&&lbl_BC_INST_PRINT,                                                 \
	&&lbl_BC_INST_PRINT_POP,                                             \
	&&lbl_BC_INST_STR,                                                   \
	&&lbl_BC_INST_PRINT_STR,                                             \
	&&lbl_BC_INST_JUMP,                                                  \
	&&lbl_BC_INST_JUMP_ZERO,                                             \
	&&lbl_BC_INST_CALL,                                                  \
	&&lbl_BC_INST_RET,                                                   \
	&&lbl_BC_INST_RET0,                                                  \
	&&lbl_BC_INST_RET_VOID,                                              \
	&&lbl_BC_INST_HALT,                                                  \
	&&lbl_BC_INST_POP,                                                   \
	&&lbl_BC_INST_SWAP,                                                  \
	&&lbl_BC_INST_MODEXP,                                                \
	&&lbl_BC_INST_DIVMOD,                                                \
	&&lbl_BC_INST_PRINT_STREAM,                                          \
	&&lbl_BC_INST_INVALID,                                               \
}

#endif // BC_ENABLE_EXTRA_MATH

#endif // DC_ENABLED

#else // BC_ENABLED

#if BC_ENABLE_EXTRA_MATH

#define BC_PROG_LBLS static const void* const bc_program_inst_lbls[] = { \
	&&lbl_BC_INST_NEG,                                                   \
	&&lbl_BC_INST_BOOL_NOT,                                              \
	&&lbl_BC_INST_TRUNC,                                                 \
	&&lbl_BC_INST_POWER,                                                 \
	&&lbl_BC_INST_MULTIPLY,                                              \
	&&lbl_BC_INST_DIVIDE,                                                \
	&&lbl_BC_INST_MODULUS,                                               \
	&&lbl_BC_INST_PLUS,                                                  \
	&&lbl_BC_INST_MINUS,                                                 \
	&&lbl_BC_INST_PLACES,                                                \
	&&lbl_BC_INST_LSHIFT,                                                \
	&&lbl_BC_INST_RSHIFT,                                                \
	&&lbl_BC_INST_REL_EQ,                                                \
	&&lbl_BC_INST_REL_LE,                                                \
	&&lbl_BC_INST_REL_GE,                                                \
	&&lbl_BC_INST_REL_NE,                                                \
	&&lbl_BC_INST_REL_LT,                                                \
	&&lbl_BC_INST_REL_GT,                                                \
	&&lbl_BC_INST_BOOL_OR,                                               \
	&&lbl_BC_INST_BOOL_AND,                                              \
	&&lbl_BC_INST_ASSIGN_NO_VAL,                                         \
	&&lbl_BC_INST_NUM,                                                   \
	&&lbl_BC_INST_VAR,                                                   \
	&&lbl_BC_INST_ARRAY_ELEM,                                            \
	&&lbl_BC_INST_ARRAY,                                                 \
	&&lbl_BC_INST_ZERO,                                                  \
	&&lbl_BC_INST_ONE,                                                   \
	&&lbl_BC_INST_IBASE,                                                 \
	&&lbl_BC_INST_OBASE,                                                 \
	&&lbl_BC_INST_SCALE,                                                 \
	&&lbl_BC_INST_SEED,                                                  \
	&&lbl_BC_INST_LENGTH,                                                \
	&&lbl_BC_INST_SCALE_FUNC,                                            \
	&&lbl_BC_INST_SQRT,                                                  \
	&&lbl_BC_INST_ABS,                                                   \
	&&lbl_BC_INST_IRAND,                                                 \
	&&lbl_BC_INST_ASCIIFY,                                               \
	&&lbl_BC_INST_READ,                                                  \
	&&lbl_BC_INST_RAND,                                                  \
	&&lbl_BC_INST_MAXIBASE,                                              \
	&&lbl_BC_INST_MAXOBASE,                                              \
	&&lbl_BC_INST_MAXSCALE,                                              \
	&&lbl_BC_INST_MAXRAND,                                               \
	&&lbl_BC_INST_PRINT,                                                 \
	&&lbl_BC_INST_PRINT_POP,                                             \
	&&lbl_BC_INST_STR,                                                   \
	&&lbl_BC_INST_POP,                                                   \
	&&lbl_BC_INST_SWAP,                                                  \
	&&lbl_BC_INST_MODEXP,                                                \
	&&lbl_BC_INST_DIVMOD,                                                \
	&&lbl_BC_INST_PRINT_STREAM,                                          \
	&&lbl_BC_INST_POP_EXEC,                                              \
	&&lbl_BC_INST_EXECUTE,                                               \
	&&lbl_BC_INST_EXEC_COND,                                             \
	&&lbl_BC_INST_PRINT_STACK,                                           \
	&&lbl_BC_INST_CLEAR_STACK,                                           \
	&&lbl_BC_INST_REG_STACK_LEN,                                         \
	&&lbl_BC_INST_STACK_LEN,                                             \
	&&lbl_BC_INST_DUPLICATE,                                             \
	&&lbl_BC_INST_LOAD,                                                  \
	&&lbl_BC_INST_PUSH_VAR,                                              \
	&&lbl_BC_INST_PUSH_TO_VAR,                                           \
	&&lbl_BC_INST_QUIT,                                                  \
	&&lbl_BC_INST_NQUIT,                                                 \
	&&lbl_BC_INST_EXEC_STACK_LEN,                                        \
	&&lbl_BC_INST_INVALID,                                               \
}

#else // BC_ENABLE_EXTRA_MATH

#define BC_PROG_LBLS static const void* const bc_program_inst_lbls[] = { \
	&&lbl_BC_INST_NEG,                                                   \
	&&lbl_BC_INST_BOOL_NOT,                                              \
	&&lbl_BC_INST_POWER,                                                 \
	&&lbl_BC_INST_MULTIPLY,                                              \
	&&lbl_BC_INST_DIVIDE,                                                \
	&&lbl_BC_INST_MODULUS,                                               \
	&&lbl_BC_INST_PLUS,                                                  \
	&&lbl_BC_INST_MINUS,                                                 \
	&&lbl_BC_INST_REL_EQ,                                                \
	&&lbl_BC_INST_REL_LE,                                                \
	&&lbl_BC_INST_REL_GE,                                                \
	&&lbl_BC_INST_REL_NE,                                                \
	&&lbl_BC_INST_REL_LT,                                                \
	&&lbl_BC_INST_REL_GT,                                                \
	&&lbl_BC_INST_BOOL_OR,                                               \
	&&lbl_BC_INST_BOOL_AND,                                              \
	&&lbl_BC_INST_ASSIGN_NO_VAL,                                         \
	&&lbl_BC_INST_NUM,                                                   \
	&&lbl_BC_INST_VAR,                                                   \
	&&lbl_BC_INST_ARRAY_ELEM,                                            \
	&&lbl_BC_INST_ARRAY,                                                 \
	&&lbl_BC_INST_ZERO,                                                  \
	&&lbl_BC_INST_ONE,                                                   \
	&&lbl_BC_INST_IBASE,                                                 \
	&&lbl_BC_INST_OBASE,                                                 \
	&&lbl_BC_INST_SCALE,                                                 \
	&&lbl_BC_INST_LENGTH,                                                \
	&&lbl_BC_INST_SCALE_FUNC,                                            \
	&&lbl_BC_INST_SQRT,                                                  \
	&&lbl_BC_INST_ABS,                                                   \
	&&lbl_BC_INST_ASCIIFY,                                               \
	&&lbl_BC_INST_READ,                                                  \
	&&lbl_BC_INST_MAXIBASE,                                              \
	&&lbl_BC_INST_MAXOBASE,                                              \
	&&lbl_BC_INST_MAXSCALE,                                              \
	&&lbl_BC_INST_PRINT,                                                 \
	&&lbl_BC_INST_PRINT_POP,                                             \
	&&lbl_BC_INST_STR,                                                   \
	&&lbl_BC_INST_POP,                                                   \
	&&lbl_BC_INST_SWAP,                                                  \
	&&lbl_BC_INST_MODEXP,                                                \
	&&lbl_BC_INST_DIVMOD,                                                \
	&&lbl_BC_INST_PRINT_STREAM,                                          \
	&&lbl_BC_INST_POP_EXEC,                                              \
	&&lbl_BC_INST_EXECUTE,                                               \
	&&lbl_BC_INST_EXEC_COND,                                             \
	&&lbl_BC_INST_PRINT_STACK,                                           \
	&&lbl_BC_INST_CLEAR_STACK,                                           \
	&&lbl_BC_INST_REG_STACK_LEN,                                         \
	&&lbl_BC_INST_STACK_LEN,                                             \
	&&lbl_BC_INST_DUPLICATE,                                             \
	&&lbl_BC_INST_LOAD,                                                  \
	&&lbl_BC_INST_PUSH_VAR,                                              \
	&&lbl_BC_INST_PUSH_TO_VAR,                                           \
	&&lbl_BC_INST_QUIT,                                                  \
	&&lbl_BC_INST_NQUIT,                                                 \
	&&lbl_BC_INST_EXEC_STACK_LEN,                                        \
	&&lbl_BC_INST_INVALID,                                               \
}

#endif // BC_ENABLE_EXTRA_MATH

#endif // BC_ENABLED

#else // BC_HAS_COMPUTED_GOTO

#define BC_PROG_JUMP(inst, code, ip) break
#define BC_PROG_DIRECT_JUMP(l)
#define BC_PROG_LBL(l) case l
#define BC_PROG_FALLTHROUGH BC_FALLTHROUGH

#define BC_PROG_LBLS

#endif // BC_HAS_COMPUTED_GOTO

#endif // BC_PROGRAM_H