aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorGavin Howard <gavin@yzena.com>2021-07-14 00:21:27 -0600
committerGavin Howard <gavin@yzena.com>2021-07-14 00:21:27 -0600
commitf378c8b80af62743d75a959af1643e4f4a9762a2 (patch)
treea9ddd09cfec47d0f759877dd303bd4cd78248b9c /include
parent4aafbe313576f7f4c37482702d4b76cc987ac8a5 (diff)
downloadplatform_external_bc-f378c8b80af62743d75a959af1643e4f4a9762a2.tar.gz
platform_external_bc-f378c8b80af62743d75a959af1643e4f4a9762a2.tar.bz2
platform_external_bc-f378c8b80af62743d75a959af1643e4f4a9762a2.zip
Do a lot of documentation work
Signed-off-by: Gavin Howard <gavin@yzena.com>
Diffstat (limited to 'include')
-rw-r--r--include/bc.h4
-rw-r--r--include/num.h12
-rw-r--r--include/status.h272
-rw-r--r--include/vector.h286
-rw-r--r--include/vm.h476
5 files changed, 1017 insertions, 33 deletions
diff --git a/include/bc.h b/include/bc.h
index 1096772b..fb70ad24 100644
--- a/include/bc.h
+++ b/include/bc.h
@@ -144,8 +144,8 @@ void bc_lex_token(BcLex *l);
#define BC_PARSE_FLAG_FUNC (UINTMAX_C(1)<<2)
#define BC_PARSE_FUNC(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_FUNC)
-// This flag is set if the parser is parsing a body, whether of a function, an
-// if statement, or a loop.
+// This flag is set if the parser is expecting to parse a body, whether of a
+// function, an if statement, or a loop.
#define BC_PARSE_FLAG_BODY (UINTMAX_C(1)<<3)
#define BC_PARSE_BODY(p) (BC_PARSE_TOP_FLAG(p) & BC_PARSE_FLAG_BODY)
diff --git a/include/num.h b/include/num.h
index eb9f73d2..6a7239eb 100644
--- a/include/num.h
+++ b/include/num.h
@@ -706,23 +706,25 @@ size_t bc_num_placesReq(const BcNum *a, const BcNum *b, size_t scale);
#endif // BC_ENABLE_EXTRA_MATH
/**
- * Truncate @a n *by* @a places decimal places.
+ * Truncate @a n *by* @a places decimal places. This only extends places *after*
+ * the decimal point.
* @param n The number to truncate.
* @param places The number of places to truncate @a n by.
*/
void bc_num_truncate(BcNum *restrict n, size_t places);
/**
- * Extend @a n *by* @a places decimal places.
+ * Extend @a n *by* @a places decimal places. This only extends places *after*
+ * the decimal point.
* @param n The number to truncate.
* @param places The number of places to extend @a n by.
*/
void bc_num_extend(BcNum *restrict n, size_t places);
/**
- * Shifts @a n right by @a places decimal places. This is a helper for
- * bc_num_rshift(), and would be static to src/num.c, except that src/library.c
- * uses it for efficiency when executing its frand.
+ * Shifts @a n right by @a places decimal places. This is the workhorse of the
+ * right shift operator, and would be static to src/num.c, except that
+ * src/library.c uses it for efficiency when executing its frand.
* @param n The number to shift right.
* @param places The number of decimal places to shift @a n right by.
*/
diff --git a/include/status.h b/include/status.h
index 9d9ea5a9..49b41eef 100644
--- a/include/status.h
+++ b/include/status.h
@@ -38,6 +38,7 @@
#include <stdint.h>
+// This is used by configure.sh to test for OpenBSD.
#ifdef BC_TEST_OPENBSD
#ifdef __OpenBSD__
#error On OpenBSD without _BSD_SOURCE
@@ -52,6 +53,7 @@
#define DC_ENABLED (1)
#endif // DC_ENABLED
+// This is error checking for fuzz builds.
#if BC_ENABLE_AFL
#ifndef __AFL_HAVE_MANUAL_CONTROL
#error Must compile with afl-clang-fast or afl-clang-lto for fuzzing
@@ -62,23 +64,54 @@
#define BC_ENABLE_MEMCHECK (0)
#endif // BC_ENABLE_MEMCHECK
+/**
+ * Mark a variable as unused.
+ * @param e The variable to mark as unused.
+ */
#define BC_UNUSED(e) ((void) (e))
+// If users want, they can define this to something like __builtin_expect(e, 1).
+// It might give a performance improvement.
#ifndef BC_LIKELY
+
+/**
+ * Mark a branch expression as likely.
+ * @param e The expression to mark as likely.
+ */
#define BC_LIKELY(e) (e)
+
#endif // BC_LIKELY
+// If users want, they can define this to something like __builtin_expect(e, 0).
+// It might give a performance improvement.
#ifndef BC_UNLIKELY
+
+/**
+ * Mark a branch expression as unlikely.
+ * @param e The expression to mark as unlikely.
+ */
#define BC_UNLIKELY(e) (e)
+
#endif // BC_UNLIKELY
+/**
+ * Mark a branch expression as an error, if true.
+ * @param e The expression to mark as an error, if true.
+ */
#define BC_ERR(e) BC_UNLIKELY(e)
+
+/**
+ * Mark a branch expression as not an error, if true.
+ * @param e The expression to mark as not an error, if true.
+ */
#define BC_NO_ERR(s) BC_LIKELY(s)
+// Disable extra debug code by default.
#ifndef BC_DEBUG_CODE
#define BC_DEBUG_CODE (0)
#endif // BC_DEBUG_CODE
+// We want to be able to use _Noreturn on C11 compilers.
#if __STDC_VERSION__ >= 201100L
#include <stdnoreturn.h>
#define BC_NORETURN _Noreturn
@@ -158,10 +191,12 @@
#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
#endif // SIG_ATOMIC_MAX
+// Yes, this has to be here.
#include <bcl.h>
#if BC_ENABLED
+// All of these set defaults for settings.
#ifndef BC_DEFAULT_BANNER
#define BC_DEFAULT_BANNER (0)
#endif // BC_DEFAULT_BANNER
@@ -182,6 +217,7 @@
#if DC_ENABLED
+// All of these set defaults for settings.
#ifndef DC_DEFAULT_SIGINT_RESET
#define DC_DEFAULT_SIGINT_RESET (1)
#endif // DC_DEFAULT_SIGINT_RESET
@@ -200,103 +236,245 @@
#endif // DC_ENABLED
+/// Statuses, which mark either which category of error happened, or some other
+/// status that matters.
typedef enum BcStatus {
+ /// Normal status.
BC_STATUS_SUCCESS = 0,
+
+ /// Math error.
BC_STATUS_ERROR_MATH,
+
+ /// Parse (and lex) error.
BC_STATUS_ERROR_PARSE,
+
+ /// Runtime error.
BC_STATUS_ERROR_EXEC,
+
+ /// Fatal error.
BC_STATUS_ERROR_FATAL,
+
+ /// EOF status.
BC_STATUS_EOF,
+
+ /// Quit status. This means that bc/dc is in the process of quitting.
BC_STATUS_QUIT,
} BcStatus;
+/// Errors, which are more specific errors.
typedef enum BcErr {
+ // Math errors.
+
+ /// Negative number used when not allowed.
BC_ERR_MATH_NEGATIVE,
+
+ /// Non-integer used when not allowed.
BC_ERR_MATH_NON_INTEGER,
+
+ /// Conversion to a hardware integer would overflow.
BC_ERR_MATH_OVERFLOW,
+
+ /// Divide by zero.
BC_ERR_MATH_DIVIDE_BY_ZERO,
+ // Fatal errors.
+
+ /// An allocation or reallocation failed.
BC_ERR_FATAL_ALLOC_ERR,
+
+ /// I/O failure.
BC_ERR_FATAL_IO_ERR,
+
+ /// File error, such as permissions or file does not exist.
BC_ERR_FATAL_FILE_ERR,
+
+ /// Attempted to read a directory as a file error.
BC_ERR_FATAL_PATH_DIR,
+
+ /// Invalid option error.
BC_ERR_FATAL_OPTION,
+
+ /// Option with required argument not given an argument.
BC_ERR_FATAL_OPTION_NO_ARG,
+
+ /// Option with no argument given an argument.
BC_ERR_FATAL_OPTION_ARG,
+ // Runtime errors.
+
+ /// Invalid ibase value.
BC_ERR_EXEC_IBASE,
+
+ /// Invalid obase value.
BC_ERR_EXEC_OBASE,
+
+ /// Invalid scale value.
BC_ERR_EXEC_SCALE,
+
+ /// Invalid expression parsed by read().
BC_ERR_EXEC_READ_EXPR,
+
+ /// read() used within an expression given to a read() call.
BC_ERR_EXEC_REC_READ,
+
+ /// Type error.
BC_ERR_EXEC_TYPE,
+ /// Stack has too few elements error.
BC_ERR_EXEC_STACK,
+
+ /// Register stack has too few elements error.
BC_ERR_EXEC_STACK_REGISTER,
+ /// Wrong number of arguments error.
BC_ERR_EXEC_PARAMS,
+
+ /// Undefined function error.
BC_ERR_EXEC_UNDEF_FUNC,
+
+ /// Void value used in an expression error.
BC_ERR_EXEC_VOID_VAL,
+ // Parse (and lex errors).
+
+ /// EOF encountered when not expected error.
BC_ERR_PARSE_EOF,
+
+ /// Invalid character error.
BC_ERR_PARSE_CHAR,
+
+ /// Invalid string (no ending quote) error.
BC_ERR_PARSE_STRING,
+
+ /// Invalid comment (no end found) error.
BC_ERR_PARSE_COMMENT,
+
+ /// Invalid token encountered error.
BC_ERR_PARSE_TOKEN,
+
#if BC_ENABLED
+
+ /// Invalid expression error.
BC_ERR_PARSE_EXPR,
+
+ /// Expression is empty error.
BC_ERR_PARSE_EMPTY_EXPR,
+
+ /// Print statement is invalid error.
BC_ERR_PARSE_PRINT,
+
+ /// Function definition is invalid error.
BC_ERR_PARSE_FUNC,
+
+ /// Assignment is invalid error.
BC_ERR_PARSE_ASSIGN,
+
+ /// No auto identifiers given for an auto statement error.
BC_ERR_PARSE_NO_AUTO,
+
+ /// Duplicate local (parameter or auto) error.
BC_ERR_PARSE_DUP_LOCAL,
+
+ /// Invalid block (within braces) error.
BC_ERR_PARSE_BLOCK,
+
+ /// Invalid return statement for void functions.
BC_ERR_PARSE_RET_VOID,
+
+ /// Reference attached to a variable, not an array, error.
BC_ERR_PARSE_REF_VAR,
+ // POSIX-only errors.
+
+ /// Name length greater than 1 error.
BC_ERR_POSIX_NAME_LEN,
+
+ /// Non-POSIX comment used error.
BC_ERR_POSIX_COMMENT,
+
+ /// Non-POSIX keyword error.
BC_ERR_POSIX_KW,
+
+ /// Non-POSIX . (last) error.
BC_ERR_POSIX_DOT,
+
+ /// Non-POSIX return error.
BC_ERR_POSIX_RET,
+
+ /// Non-POSIX boolean operator used error.
BC_ERR_POSIX_BOOL,
+
+ /// POSIX relation operator used outside if, while, or for statements error.
BC_ERR_POSIX_REL_POS,
+
+ /// Multiple POSIX relation operators used in an if, while, or for statement
+ /// error.
BC_ERR_POSIX_MULTIREL,
+
+ /// Empty statements in POSIX for loop error.
BC_ERR_POSIX_FOR,
+
+ /// Non-POSIX exponential (scientific or engineering) number used error.
BC_ERR_POSIX_EXP_NUM,
+
+ /// Non-POSIX array reference error.
BC_ERR_POSIX_REF,
+
+ /// Non-POSIX void error.
BC_ERR_POSIX_VOID,
+
+ /// Non-POSIX brace position used error.
BC_ERR_POSIX_BRACE,
+
#endif // BC_ENABLED
+ // Number of elements.
BC_ERR_NELEMS,
#if BC_ENABLED
+
+ /// A marker for the start of POSIX errors.
BC_ERR_POSIX_START = BC_ERR_POSIX_NAME_LEN,
+
+ /// A marker for the end of POSIX errors.
BC_ERR_POSIX_END = BC_ERR_POSIX_BRACE,
+
#endif // BC_ENABLED
} BcErr;
// The indices of each category of error in bc_errs[], and used in bc_err_ids[]
// to associate actual errors with their categories.
+
+/// Math error category.
#define BC_ERR_IDX_MATH (0)
+
+/// Parse (and lex) error category.
#define BC_ERR_IDX_PARSE (1)
+
+/// Runtime error category.
#define BC_ERR_IDX_EXEC (2)
+
+/// Fatal error category.
#define BC_ERR_IDX_FATAL (3)
+
+/// Number of categories.
#define BC_ERR_IDX_NELEMS (4)
// If bc is enabled, we add an extra category for POSIX warnings.
#if BC_ENABLED
+
+/// POSIX warning category.
#define BC_ERR_IDX_WARN (BC_ERR_IDX_NELEMS)
+
#endif // BC_ENABLED
-// BC_JMP is what to use when activating an "exception", i.e., a longjmp(). With
-// debug code, it will print the name of the function it jumped from.
+/// Do a longjmp(). This is what to use when activating an "exception", i.e., a
+/// longjmp(). With debug code, it will print the name of the function it jumped
+/// from.
#if BC_DEBUG_CODE
#define BC_JMP bc_vm_jmp(__func__)
#else // BC_DEBUG_CODE
@@ -311,17 +489,34 @@ typedef enum BcErr {
#define BC_NO_SIG_EXC \
BC_LIKELY(vm.status == (sig_atomic_t) BC_STATUS_SUCCESS && !vm.sig)
-// These two assert whether signals are locked or not, respectively. There are
-// non-async-signal-safe functions in bc, and they *must* have signals locked.
-// Other functions are expected to *not* have signals locked, for reasons. So
-// these are pre-built asserts (no-ops in non-debug mode) that check that
-// signals are locked or not.
#ifndef NDEBUG
+
+/// Assert that signals are locked. There are non-async-signal-safe functions in
+/// bc, and they *must* have signals locked. Other functions are expected to
+/// *not* have signals locked, for reasons. So this is a pre-built assert
+/// (no-op in non-debug mode) that check that signals are locked.
#define BC_SIG_ASSERT_LOCKED do { assert(vm.sig_lock); } while (0)
+
+/// Assert that signals are unlocked. There are non-async-signal-safe functions
+/// in bc, and they *must* have signals locked. Other functions are expected to
+/// *not* have signals locked, for reasons. So this is a pre-built assert
+/// (no-op in non-debug mode) that check that signals are unlocked.
#define BC_SIG_ASSERT_NOT_LOCKED do { assert(vm.sig_lock == 0); } while (0)
+
#else // NDEBUG
+
+/// Assert that signals are locked. There are non-async-signal-safe functions in
+/// bc, and they *must* have signals locked. Other functions are expected to
+/// *not* have signals locked, for reasons. So this is a pre-built assert
+/// (no-op in non-debug mode) that check that signals are locked.
#define BC_SIG_ASSERT_LOCKED
+
+/// Assert that signals are unlocked. There are non-async-signal-safe functions
+/// in bc, and they *must* have signals locked. Other functions are expected to
+/// *not* have signals locked, for reasons. So this is a pre-built assert
+/// (no-op in non-debug mode) that check that signals are unlocked.
#define BC_SIG_ASSERT_NOT_LOCKED
+
#endif // NDEBUG
/// Locks signals.
@@ -377,10 +572,15 @@ typedef enum BcErr {
if (!(v) && vm.sig) BC_JMP; \
} while (0)
-/// Sets a jump, and sets it up as well so that if a longjmp() happens, bc will
-/// immediately goto a label where some cleanup code is. This one assumes that
-/// signals are not locked and will lock them, set the jump, and unlock them.
-/// Setting the jump also includes pushing the jmp_buf onto the jmp_buf stack.
+/**
+ * Sets a jump, and sets it up as well so that if a longjmp() happens, bc will
+ * immediately goto a label where some cleanup code is. This one assumes that
+ * signals are not locked and will lock them, set the jump, and unlock them.
+ * Setting the jump also includes pushing the jmp_buf onto the jmp_buf stack.
+ * This grows the jmp_bufs vector first to prevent a fatal error from happening
+ * after the setjmp().
+ * param l The label to jump to on a longjmp().
+ */
#define BC_SETJMP(l) \
do { \
sigjmp_buf sjb; \
@@ -437,24 +637,64 @@ typedef enum BcErr {
/// Stops a stack unwinding. Technically, a stack unwinding needs to be done
/// manually, but it will always be done unless certain flags are cleared. This
/// clears the flags.
-#define BC_LONGJMP_STOP \
- do { \
- vm.sig_pop = 0; \
- vm.sig = 0; \
+#define BC_LONGJMP_STOP \
+ do { \
+ vm.sig_pop = 0; \
+ vm.sig = 0; \
} while (0)
// Various convenience macros for calling the bc's error handling routine.
#if BC_ENABLE_LIBRARY
+
+/**
+ * Call bc's error handling routine.
+ * @param e The error.
+ * @param l The line of the script that the error happened.
+ * @param ... Extra arguments for error messages as necessary.
+ */
#define bc_error(e, l, ...) (bc_vm_handleError((e)))
+
+/**
+ * Call bc's error handling routine.
+ * @param e The error.
+ */
#define bc_err(e) (bc_vm_handleError((e)))
+
+/**
+ * Call bc's error handling routine.
+ * @param e The error.
+ */
#define bc_verr(e, ...) (bc_vm_handleError((e)))
+
#else // BC_ENABLE_LIBRARY
+
+/**
+ * Call bc's error handling routine.
+ * @param e The error.
+ * @param l The line of the script that the error happened.
+ * @param ... Extra arguments for error messages as necessary.
+ */
#define bc_error(e, l, ...) (bc_vm_handleError((e), (l), __VA_ARGS__))
+
+/**
+ * Call bc's error handling routine.
+ * @param e The error.
+ */
#define bc_err(e) (bc_vm_handleError((e), 0))
+
+/**
+ * Call bc's error handling routine.
+ * @param e The error.
+ */
#define bc_verr(e, ...) (bc_vm_handleError((e), 0, __VA_ARGS__))
+
#endif // BC_ENABLE_LIBRARY
-/// Returns true if status s is an error, false otherwise.
+/**
+ * Returns true if status @a s is an error, false otherwise.
+ * @param s The status to test.
+ * @return True if @a s is an error, false otherwise.
+ */
#define BC_STATUS_IS_ERROR(s) \
((s) >= BC_STATUS_ERROR_MATH && (s) <= BC_STATUS_ERROR_FATAL)
diff --git a/include/vector.h b/include/vector.h
index 05509202..e832a24e 100644
--- a/include/vector.h
+++ b/include/vector.h
@@ -42,108 +42,384 @@
#include <status.h>
+/// An invalid index for a map to mark when an item does not exist.
#define BC_VEC_INVALID_IDX (SIZE_MAX)
+
+/// The starting capacity for vectors. This is based on the minimum allocation
+/// for 64-bit systems.
#define BC_VEC_START_CAP (UINTMAX_C(1)<<5)
+/// An alias.
typedef unsigned char uchar;
-typedef void (*BcVecFree)(void*);
+/**
+ * A destructor. Frees the object that @a ptr points to. This is used by vectors
+ * to free the memory they own.
+ * @param ptr Pointer to the data to free.
+ */
+typedef void (*BcVecFree)(void *ptr);
// Forward declaration.
struct BcId;
#if BC_LONG_BIT >= 64
+
+/// An integer to shrink the size of a vector by using these instead of size_t.
typedef uint32_t BcSize;
+
#else // BC_LONG_BIT >= 64
+
+/// An integer to shrink the size of a vector by using these instead of size_t.
typedef uint16_t BcSize;
+
#endif // BC_LONG_BIT >= 64
+/// An enum of all of the destructors. We use an enum to save space.
typedef enum BcDtorType {
+
+ /// No destructor needed.
BC_DTOR_NONE,
+
+ /// Vector destructor.
BC_DTOR_VEC,
+
+ /// BcNum destructor.
BC_DTOR_NUM,
+
#if !BC_ENABLE_LIBRARY
+
#ifndef NDEBUG
+
+ /// BcFunc destructor.
BC_DTOR_FUNC,
+
#endif // NDEBUG
+
+ /// BcSlab destructor.
BC_DTOR_SLAB,
+
+ /// BcConst destructor.
BC_DTOR_CONST,
+
+ /// BcResult destructor.
BC_DTOR_RESULT,
+
#if BC_ENABLE_HISTORY
+
+ /// String destructor for history, which is *special*.
BC_DTOR_HISTORY_STRING,
+
#endif // BC_ENABLE_HISTORY
#else // !BC_ENABLE_LIBRARY
+
+ /// Destructor for bcl numbers.
BC_DTOR_BCL_NUM,
+
#endif // !BC_ENABLE_LIBRARY
+
} BcDtorType;
+/// The actual vector struct.
typedef struct BcVec {
+
+ /// The vector array itself. This uses a char* because it is compatible with
+ /// pointers of all other types, and I can do pointer arithmetic on it.
char *restrict v;
+
+ /// The length of the vector, which is how many items actually exist.
size_t len;
+
+ /// The capacity of the vector, which is how many items can fit in the
+ /// current allocation.
size_t cap;
+
+ /// The size of the items in the vector, as returned by sizeof().
BcSize size;
+
+ /// The destructor as a BcDtorType enum.
BcSize dtor;
+
} BcVec;
+/**
+ * Initializes a vector.
+ * @param v The vector to initialize.
+ * @param esize The size of the elements, as returned by sizeof().
+ * @param dtor The destructor of the elements, as a BcDtorType enum.
+ */
void bc_vec_init(BcVec *restrict v, size_t esize, BcDtorType dtor);
+
+/**
+ * Expands the vector to have a capacity of @a req items, if it doesn't have
+ * enough already.
+ * @param v The vector to expand.
+ * @param req The requested capacity.
+ */
void bc_vec_expand(BcVec *restrict v, size_t req);
+
+/**
+ * Grow a vector by at least @a n elements.
+ * @param v The vector to grow.
+ * @param n The number of elements to grow the vector by.
+ */
void bc_vec_grow(BcVec *restrict v, size_t n);
+/**
+ * Pops @a n items off the back of the vector. The vector must have at least
+ * @a n elements.
+ * @param v The vector to pop off of.
+ * @param n The number of elements to pop off.
+ */
void bc_vec_npop(BcVec *restrict v, size_t n);
+
+/**
+ * Pops @a n items, starting at index @a idx, off the vector. The vector must
+ * have at least @a n elements after the @a idx index. Any remaining elements at
+ * the end are moved up to fill the hole.
+ * @param v The vector to pop off of.
+ * @param n The number of elements to pop off.
+ * @param idx The index to start popping at.
+ */
void bc_vec_npopAt(BcVec *restrict v, size_t n, size_t idx);
+/**
+ * Pushes one item on the back of the vector. It does a memcpy(), but it assumes
+ * that the vector takes ownership of the data.
+ * @param v The vector to push onto.
+ * @param data A pointer to the data to push.
+ */
void bc_vec_push(BcVec *restrict v, const void *data);
+
+/**
+ * Pushes @a n items on the back of the vector. It does a memcpy(), but it
+ * assumes that the vector takes ownership of the data.
+ * @param v The vector to push onto.
+ * @param data A pointer to the elements of data to push.
+ */
void bc_vec_npush(BcVec *restrict v, size_t n, const void *data);
+
+/**
+ * Push an empty element and return a pointer to it. This is done as an
+ * optimization where initializing an item needs a pointer anyway. It removes an
+ * extra memcpy().
+ * @param v The vector to push onto.
+ * @return A pointer to the newly-pushed element.
+ */
void* bc_vec_pushEmpty(BcVec *restrict v);
+
+/**
+ * Pushes a byte onto a bytecode vector. This is a convenience function for the
+ * parsers pushing instructions. The vector must be a bytecode vector.
+ * @param v The vector to push onto.
+ * @param data The byte to push.
+ */
void bc_vec_pushByte(BcVec *restrict v, uchar data);
+
+/**
+ * Pushes and index onto a bytecode vector. The vector must be a bytecode
+ * vector. For more info about why and how this is done, see the development
+ * manual (manuals/development#bytecode-indices).
+ * @param v The vector to push onto.
+ * @param idx The index to push.
+ */
void bc_vec_pushIndex(BcVec *restrict v, size_t idx);
+
+/**
+ * Push an item onto the vector at a certain index. The index must be valid
+ * (either exists or is equal to the length of the vector). The elements at that
+ * index and after are moved back one element and kept in the same order. This
+ * is how the map vectors are kept sorted.
+ * @param v The vector to push onto.
+ * @param data A pointer to the data to push.
+ * @param idx The index to push at.
+ */
void bc_vec_pushAt(BcVec *restrict v, const void *data, size_t idx);
+
+/**
+ * Empties the vector and sets it to the string. The vector must be a valid
+ * vector and must have chars as its elements.
+ * @param v The vector to set to the string.
+ * @param len The length of the string. This can be less than the actual length
+ * of the string, but must never be more.
+ * @param str The string to push.
+ */
void bc_vec_string(BcVec *restrict v, size_t len, const char *restrict str);
+
+/**
+ * Appends the string to the end of the vector, which must be holding a string
+ * (nul byte-terminated) already.
+ * @param v The vector to append to.
+ * @param str The string to append (by copying).
+ */
void bc_vec_concat(BcVec *restrict v, const char *restrict str);
+
+/**
+ * Empties a vector and pushes a nul-byte at the first index. The vector must be
+ * a char vector.
+ */
void bc_vec_empty(BcVec *restrict v);
#if BC_ENABLE_HISTORY
+
+/**
+ * Replaces an item at a particular index. No elements are moved. The index must
+ * exist.
+ * @param v The vector to replace an item on.
+ * @param idx The index of the item to replace.
+ * @param data The data to replace the item with.
+ */
void bc_vec_replaceAt(BcVec *restrict v, size_t idx, const void *data);
+
#endif // BC_ENABLE_HISTORY
+/**
+ * Returns a pointer to the item in the vector at the index. This is the key
+ * function for vectors. The index must exist.
+ * @param v The vector.
+ * @param idx The index to the item to get a pointer to.
+ * @return A pointer to the item at @a idx.
+ */
void* bc_vec_item(const BcVec *restrict v, size_t idx);
+
+/**
+ * Returns a pointer to the item in the vector at the index, reversed. This is
+ * another key function for vectors. The index must exist.
+ * @param v The vector.
+ * @param idx The index to the item to get a pointer to.
+ * @return A pointer to the item at len - @a idx - 1.
+ */
void* bc_vec_item_rev(const BcVec *restrict v, size_t idx);
+/**
+ * Zeros a vector. The vector must not be allocated.
+ * @param v The vector to clear.
+ */
void bc_vec_clear(BcVec *restrict v);
+/**
+ * Frees a vector and its elements. This is a destructor.
+ * @param vec A vector as a void pointer.
+ */
void bc_vec_free(void *vec);
+/**
+ * Attempts to insert an item into a map and returns true if it succeeded, false
+ * if the item already exists.
+ * @param v The map vector to insert into.
+ * @param name The name of the item to insert. This name is assumed to be owned
+ * by another entity.
+ * @param idx The index of the partner array where the actual item is.
+ * @param i A pointer to an index that will be set to the index of the item
+ * in the map.
+ * @return True if the item was inserted, false if the item already exists.
+ */
bool bc_map_insert(BcVec *restrict v, const char *name,
size_t idx, size_t *restrict i);
+
+/**
+ * Returns the index of the item with @a name in the map, or BC_VEC_INVALID_IDX
+ * if it doesn't exist.
+ * @param v The map vector.
+ * @param name The name of the item to find.
+ * @return The index in the map of the item with @a name, or
+ * BC_VEC_INVALID_IDX if the item does not exist.
+ */
size_t bc_map_index(const BcVec *restrict v, const char *name);
+
#if DC_ENABLED
+
+/**
+ * Returns the name of the item at index @a idx in the map.
+ * @param v The map vector.
+ * @param idx The index.
+ * @return The name of the item at @a idx.
+ */
char* bc_map_name(const BcVec *restrict v, size_t idx);
+
#endif // DC_ENABLED
+/**
+ * Pops one item off of the vector.
+ * @param v The vector to pop one item off of.
+ */
#define bc_vec_pop(v) (bc_vec_npop((v), 1))
+
+/**
+ * Pops all items off of the vector.
+ * @param v The vector to pop all items off of.
+ */
#define bc_vec_popAll(v) (bc_vec_npop((v), (v)->len))
+
+/**
+ * Return a pointer to the last item in the vector, or first if it's being
+ * treated as a stack.
+ * @param v The vector to get the top of stack of.
+ */
#define bc_vec_top(v) (bc_vec_item_rev((v), 0))
+/**
+ * Initializes a vector to serve as a map.
+ * @param v The vector to initialize.
+ */
#define bc_map_init(v) (bc_vec_init((v), sizeof(BcId), BC_DTOR_NONE))
+/// A reference to the array of destructors.
extern const BcVecFree bc_vec_dtors[];
#if !BC_ENABLE_LIBRARY
+/// The allocated size of slabs.
#define BC_SLAB_SIZE (4096)
+/// A slab for allocating strings.
typedef struct BcSlab {
+
+ /// The actual allocation.
char *s;
+
+ /// How many bytes of the slab are taken.
size_t len;
+
} BcSlab;
+/**
+ * Frees a slab. This is a destructor.
+ * @param slab The slab as a void pointer.
+ */
void bc_slab_free(void *slab);
-void bc_slabvec_init(BcVec* v);
-char* bc_slabvec_strdup(BcVec *v, const char *str);
-void bc_slabvec_undo(BcVec *v, size_t len);
-void bc_slabvec_clear(BcVec *v);
+/**
+ * Initializes a slab vector.
+ * @param v The vector to initialize.
+ */
+void bc_slabvec_init(BcVec *restrict v);
+
+/**
+ * Duplicates the string using slabs in the slab vector.
+ * @param v The slab vector.
+ * @param str The string to duplicate.
+ * @return A pointer to the duplicated string, owned by the slab vector.
+ */
+char* bc_slabvec_strdup(BcVec *restrict v, const char *str);
+
+#if BC_ENABLED
+/**
+ * Undoes the last allocation on the slab vector. This allows bc to have a
+ * heap-based stacks for strings. This is used by the bc parser.
+ */
+void bc_slabvec_undo(BcVec *restrict v, size_t len);
+
+#endif // BC_ENABLED
+
+
+/**
+ * Clears a slab vector. This deallocates all but the first slab and clears the
+ * first slab.
+ * @param v The slab vector to clear.
+ */
+void bc_slabvec_clear(BcVec *restrict v);
+/// A convenience macro for freeing a vector of slabs.
#define bc_slabvec_free bc_vec_free
#endif // !BC_ENABLE_LIBRARY
diff --git a/include/vm.h b/include/vm.h
index 25c97ebf..a08cc52c 100644
--- a/include/vm.h
+++ b/include/vm.h
@@ -44,9 +44,9 @@
#if BC_ENABLE_NLS
-# ifdef _WIN32
-# error NLS is not supported on Windows.
-# endif // _WIN32
+#ifdef _WIN32
+#error NLS is not supported on Windows.
+#endif // _WIN32
#include <nl_types.h>
@@ -59,19 +59,24 @@
#include <program.h>
#include <history.h>
+// We don't want to include this file for the library because it's unused.
#if !BC_ENABLE_LIBRARY
#include <file.h>
#endif // !BC_ENABLE_LIBRARY
+// This should be obvious. If neither calculator is enabled, barf.
#if !BC_ENABLED && !DC_ENABLED
#error Must define BC_ENABLED, DC_ENABLED, or both
#endif
-// CHAR_BIT must be at least 6.
+// CHAR_BIT must be at least 6, for various reasons. I might want to bump this
+// to 8 in the future.
#if CHAR_BIT < 6
#error CHAR_BIT must be at least 6.
#endif
+// Set defaults.
+//
#ifndef BC_ENABLE_NLS
#define BC_ENABLE_NLS (0)
#endif // BC_ENABLE_NLS
@@ -88,13 +93,32 @@
#undef EXECPREFIX
#endif // _WIN32
+/**
+ * Generate a string from text.
+ * @parm V The text to generate a string for.
+ */
#define GEN_STR(V) #V
+
+/**
+ * Help generate a string from text. The preprocessor requires this two-step
+ * process. Trust me.
+ * @parm V The text to generate a string for.
+ */
#define GEN_STR2(V) GEN_STR(V)
+/// The version as a string. VERSION must be defined previously, usually by the
+/// build system.
#define BC_VERSION GEN_STR2(VERSION)
+
+/// The main executable name as a string. MAINEXEC must be defined previously,
+/// usually by the build system.
#define BC_MAINEXEC GEN_STR2(MAINEXEC)
+
+/// The build type as a string. BUILD_TYPE must be defined previously, usually
+/// by the build system.
#define BC_BUILD_TYPE GEN_STR2(BUILD_TYPE)
+// We only allow an empty executable prefix on Windows.
#ifndef _WIN32
#define BC_EXECPREFIX GEN_STR2(EXECPREFIX)
#else // _WIN32
@@ -104,273 +128,715 @@
#if !BC_ENABLE_LIBRARY
#if DC_ENABLED
+
+/// The flag for the extended register option.
#define DC_FLAG_X (UINTMAX_C(1)<<0)
+
#endif // DC_ENABLED
#if BC_ENABLED
+
+/// The flag for the POSIX warning option.
#define BC_FLAG_W (UINTMAX_C(1)<<1)
+
+/// The flag for the POSIX error option.
#define BC_FLAG_S (UINTMAX_C(1)<<2)
+
+/// The flag for the math library option.
#define BC_FLAG_L (UINTMAX_C(1)<<3)
+
+/// The flag for the global stacks option.
#define BC_FLAG_G (UINTMAX_C(1)<<4)
+
#endif // BC_ENABLED
+/// The flag for quiet, though this one is reversed; the option clears the flag.
#define BC_FLAG_Q (UINTMAX_C(1)<<5)
+
+/// The flag for interactive.
#define BC_FLAG_I (UINTMAX_C(1)<<6)
+
+/// The flag for prompt. This is also reversed; the option clears the flag.
#define BC_FLAG_P (UINTMAX_C(1)<<7)
+
+/// The flag for read prompt. This is also reversed; the option clears the flag.
#define BC_FLAG_R (UINTMAX_C(1)<<8)
+
+/// The flag for stdin being a TTY.
#define BC_FLAG_TTYIN (UINTMAX_C(1)<<10)
+
+/// The flag for TTY mode.
#define BC_FLAG_TTY (UINTMAX_C(1)<<11)
+
+/// The flag for reset on SIGINT.
#define BC_FLAG_SIGINT (UINTMAX_C(1)<<12)
+
+/// A convenience macro for getting the TTYIN flag.
#define BC_TTYIN (vm.flags & BC_FLAG_TTYIN)
+
+/// A convenience macro for getting the TTY flag.
#define BC_TTY (vm.flags & BC_FLAG_TTY)
+
+/// A convenience macro for getting the SIGINT flag.
#define BC_SIGINT (vm.flags & BC_FLAG_SIGINT)
#if BC_ENABLED
+/// A convenience macro for getting the POSIX error flag.
#define BC_S (vm.flags & BC_FLAG_S)
+
+/// A convenience macro for getting the POSIX warning flag.
#define BC_W (vm.flags & BC_FLAG_W)
+
+/// A convenience macro for getting the math library flag.
#define BC_L (vm.flags & BC_FLAG_L)
+
+/// A convenience macro for getting the global stacks flag.
#define BC_G (vm.flags & BC_FLAG_G)
#endif // BC_ENABLED
#if DC_ENABLED
+
+/// A convenience macro for getting the extended register flag.
#define DC_X (vm.flags & DC_FLAG_X)
+
#endif // DC_ENABLED
+/// A convenience macro for getting the interactive flag.
#define BC_I (vm.flags & BC_FLAG_I)
+
+/// A convenience macro for getting the prompt flag.
#define BC_P (vm.flags & BC_FLAG_P)
+
+/// A convenience macro for getting the read prompt flag.
#define BC_R (vm.flags & BC_FLAG_R)
#if BC_ENABLED
+/// A convenience macro for checking if bc is in POSIX mode.
#define BC_IS_POSIX (BC_S || BC_W)
#if DC_ENABLED
+
+/// Returns true if bc is running.
#define BC_IS_BC (vm.name[0] != 'd')
+
+/// Returns true if dc is running.
#define BC_IS_DC (vm.name[0] == 'd')
+
#else // DC_ENABLED
+
+/// Returns true if bc is running.
#define BC_IS_BC (1)
+
+/// Returns true if dc is running.
#define BC_IS_DC (0)
+
#endif // DC_ENABLED
#else // BC_ENABLED
+
+/// A convenience macro for checking if bc is in POSIX mode.
#define BC_IS_POSIX (0)
+
+/// Returns true if bc is running.
#define BC_IS_BC (0)
+
+/// Returns true if dc is running.
#define BC_IS_DC (1)
+
#endif // BC_ENABLED
+/// A convenience macro for checking if the prompt is enabled.
#define BC_PROMPT (BC_P)
#endif // !BC_ENABLE_LIBRARY
+/**
+ * Returns the max of its two arguments. This evaluates arguments twice, so be
+ * careful what args you give it.
+ * @param a The first argument.
+ * @param b The second argument.
+ * @return The max of the two arguments.
+ */
#define BC_MAX(a, b) ((a) > (b) ? (a) : (b))
+
+/**
+ * Returns the min of its two arguments. This evaluates arguments twice, so be
+ * careful what args you give it.
+ * @param a The first argument.
+ * @param b The second argument.
+ * @return The min of the two arguments.
+ */
#define BC_MIN(a, b) ((a) < (b) ? (a) : (b))
+/// Returns the max obase that is allowed.
#define BC_MAX_OBASE ((BcBigDig) (BC_BASE_POW))
+
+/// Returns the max array size that is allowed.
#define BC_MAX_DIM ((BcBigDig) (SIZE_MAX - 1))
+
+/// Returns the max scale that is allowed.
#define BC_MAX_SCALE ((BcBigDig) (BC_NUM_BIGDIG_MAX - 1))
+
+/// Returns the max string length that is allowed.
#define BC_MAX_STRING ((BcBigDig) (BC_NUM_BIGDIG_MAX - 1))
+
+/// Returns the max identifier length that is allowed.
#define BC_MAX_NAME BC_MAX_STRING
+
+/// Returns the max number size that is allowed.
#define BC_MAX_NUM BC_MAX_SCALE
#if BC_ENABLE_EXTRA_MATH
+
+/// Returns the max random integer that can be returned.
#define BC_MAX_RAND ((BcBigDig) (((BcRand) 0) - 1))
+
#endif // BC_ENABLE_EXTRA_MATH
+/// Returns the max exponent that is allowed.
#define BC_MAX_EXP ((ulong) (BC_NUM_BIGDIG_MAX))
+
+/// Returns the max number of variables that is allowed.
#define BC_MAX_VARS ((ulong) (SIZE_MAX - 1))
+/// The size of the global buffer.
#define BC_VM_BUF_SIZE (1<<12)
+
+/// The amount of the global buffer allocated to stdout.
#define BC_VM_STDOUT_BUF_SIZE (1<<11)
+
+/// The amount of the global buffer allocated to stderr.
#define BC_VM_STDERR_BUF_SIZE (1<<10)
+
+/// The amount of the global buffer allocated to stdin.
#define BC_VM_STDIN_BUF_SIZE (BC_VM_STDERR_BUF_SIZE - 1)
+/// The max number of temporary BcNums that can be kept.
#define BC_VM_MAX_TEMPS (1 << 9)
+/// The capacity of the one BcNum, which is a constant.
+#define BC_VM_ONE_CAP (1)
+
+/**
+ * Returns true if a BcResult is safe for garbage collection.
+ * @param r The BcResult to test.
+ * @return True if @a r is safe to garbage collect.
+ */
#define BC_VM_SAFE_RESULT(r) ((r)->t >= BC_RESULT_TEMP)
+/// The invalid locale catalog return value.
#define BC_VM_INVALID_CATALOG ((nl_catd) -1)
+/**
+ * Returns true if the *unsigned* multiplication overflows.
+ * @param a The first operand.
+ * @param b The second operand.
+ * @param r The product.
+ * @return True if the multiplication of @a a and @a b overflows.
+ */
#define BC_VM_MUL_OVERFLOW(a, b, r) \
((r) >= SIZE_MAX || ((a) != 0 && (r) / (a) != (b)))
+/// The global vm struct. This holds all of the global data besides the file
+/// buffers.
typedef struct BcVm {
+ /// The current status. This is volatile sig_atomic_t because it is also
+ /// used in the signal handler. See the development manual
+ /// (manuals/development.md#async-signal-safe-signal-handling) for more
+ /// information.
volatile sig_atomic_t status;
+
+ /// Non-zero if a jump series is in progress and items should be popped off
+ /// the jmp_bufs vector. This is volatile sig_atomic_t because it is also
+ /// used in the signal handler. See the development manual
+ /// (manuals/development.md#async-signal-safe-signal-handling) for more
+ /// information.
volatile sig_atomic_t sig_pop;
#if !BC_ENABLE_LIBRARY
+
+ /// The parser.
BcParse prs;
+
+ /// The program.
BcProgram prog;
+ /// A buffer for lines for stdin.
BcVec line_buf;
+
+ /// A buffer to hold a series of lines from stdin. Sometimes, multiple lines
+ /// are necessary for parsing, such as a comment that spans multiple lines.
BcVec buffer;
+
#endif // !BC_ENABLE_LIBRARY
+ /// A vector of jmp_bufs for doing a jump series. This allows exception-type
+ /// error handling, while allowing me to do cleanup on the way.
BcVec jmp_bufs;
+ /// The number of temps in the temps array.
size_t temps_len;
#if BC_ENABLE_LIBRARY
+ /// The vector of contexts for the library.
BcVec ctxts;
+
+ /// The vector for creating strings to pass to the client.
BcVec out;
+ /// The PRNG.
BcRNG rng;
+ /// The current error.
BclError err;
+
+ /// Whether or not bcl should abort on fatal errors.
bool abrt;
+ /// The number of "references," or times that the library was initialized.
unsigned int refs;
+ /// Non-zero if bcl is running. This is volatile sig_atomic_t because it is
+ /// also used in the signal handler. See the development manual
+ /// (manuals/development.md#async-signal-safe-signal-handling) for more
+ /// information.
volatile sig_atomic_t running;
+
#endif // BC_ENABLE_LIBRARY
#if !BC_ENABLE_LIBRARY
+
+ /// A pointer to the filename of the current file. This is not owned by the
+ /// BcVm struct.
const char* file;
+ /// The message printed when SIGINT happens.
const char *sigmsg;
+
#endif // !BC_ENABLE_LIBRARY
+
+ /// Non-zero when signals are "locked." This is volatile sig_atomic_t
+ /// because it is also used in the signal handler. See the development
+ /// manual (manuals/development.md#async-signal-safe-signal-handling) for
+ /// more information.
volatile sig_atomic_t sig_lock;
+
+ /// Non-zero when a signal has been received, but not acted on. This is
+ /// volatile sig_atomic_t because it is also used in the signal handler. See
+ /// the development manual
+ /// (manuals/development.md#async-signal-safe-signal-handling) for more
+ /// information.
volatile sig_atomic_t sig;
+
#if !BC_ENABLE_LIBRARY
+
+ /// The length of sigmsg.
uchar siglen;
+ /// The instruction used for returning from a read() call.
uchar read_ret;
+
+ /// The flags field used by most macros above.
uint16_t flags;
+ /// The number of characters printed in the current line. This is used
+ /// because bc has a limit of the number of characters it can print per
+ /// line.
uint16_t nchars;
+
+ /// The length of the line we can print. The user can set this if they wish.
uint16_t line_len;
- bool no_exit_exprs;
+ /// True if bc should error if expressions are encountered during option
+ /// parsing, false otherwise.
+ bool no_exprs;
+
+ /// True if bc should exit if expresions are encountered.
bool exit_exprs;
+
+ /// True if EOF was encountered.
bool eof;
+
+ /// True if bc is currently reading from stdin.
bool is_stdin;
+
#endif // !BC_ENABLE_LIBRARY
+ /// An array of maxes for the globals.
BcBigDig maxes[BC_PROG_GLOBALS_LEN + BC_ENABLE_EXTRA_MATH];
#if !BC_ENABLE_LIBRARY
+
+ /// A vector of filenames to process.
BcVec files;
+
+ /// A vector of expressions to process.
BcVec exprs;
+ /// The name of the calculator under use. This is used by BC_IS_BC and
+ /// BC_IS_DC.
const char *name;
+
+ /// The help text for the calculator.
const char *help;
#if BC_ENABLE_HISTORY
+
+ /// The history data.
BcHistory history;
+
#endif // BC_ENABLE_HISTORY
+ /// The function to call to get the next lex token.
BcLexNext next;
+
+ /// The function to call to parse.
BcParseParse parse;
+
+ /// The function to call to parse expressions.
BcParseExpr expr;
+ /// The text to display to label functions in error messages.
const char *func_header;
+ /// The names of the categories of errors.
const char *err_ids[BC_ERR_IDX_NELEMS + BC_ENABLED];
+
+ /// The messages for each error.
const char *err_msgs[BC_ERR_NELEMS];
+ /// The locale.
const char *locale;
+
#endif // !BC_ENABLE_LIBRARY
+ /// The last base used to parse.
BcBigDig last_base;
+
+ /// The last power of last_base used to parse.
BcBigDig last_pow;
+
+ /// The last exponent of base that equals last_pow.
BcBigDig last_exp;
+
+ /// BC_BASE_POW - last_pow.
BcBigDig last_rem;
#if !BC_ENABLE_LIBRARY
+
+ /// A buffer of environment arguments. This is the actual value of the
+ /// environment variable.
char *env_args_buffer;
+
+ /// A vector for environment arguments after parsing.
BcVec env_args;
+
+ /// A BcNum set to constant 0.
+ BcNum zero;
+
+ /// A BcNum set to constant 1.
+ BcNum one;
+
+ // The BcDig array for the zero BcNum.
+ BcDig zero_num[BC_VM_ONE_CAP];
+
+ // The BcDig array for the one BcNum.
+ BcDig one_num[BC_VM_ONE_CAP];
+
#endif // !BC_ENABLE_LIBRARY
+ /// A BcNum holding the max number held by a BcBigDig plus 1.
BcNum max;
+
+ /// A BcNum holding the max number held by a BcBigDig times 2 plus 1.
BcNum max2;
+
+ /// The BcDig array for max.
BcDig max_num[BC_NUM_BIGDIG_LOG10];
+
+ /// The BcDig array for max2.
BcDig max2_num[BC_NUM_BIGDIG_LOG10];
#if !BC_ENABLE_LIBRARY
+
+ /// The stdout file.
BcFile fout;
+
+ /// The stderr file.
BcFile ferr;
#if BC_ENABLE_NLS
+
+ /// The locale catalog.
nl_catd catalog;
+
#endif // BC_ENABLE_NLS
+ /// A pointer to the stdin buffer.
char *buf;
+
+ /// The number of items in the input buffer.
size_t buf_len;
+ /// The slab for constants in the main function. This is separate for
+ /// garbage collection reasons.
BcVec main_const_slab;
+
+ //// The slab for all other strings for the main function.
BcVec main_slabs;
+
#if BC_ENABLED
+
+ /// The slab for function names, strings in other functions, and constants
+ /// in other functions.
BcVec other_slabs;
+
#endif // BC_ENABLED
#endif // !BC_ENABLE_LIBRARY
#if BC_DEBUG_CODE
+
+ /// The depth for BC_FUNC_ENTER and BC_FUNC_EXIT.
size_t func_depth;
+
#endif // BC_DEBUG_CODE
} BcVm;
+/**
+ * Print the copyright banner and help if it's non-NULL.
+ * @param help The help message to print if it's non-NULL.
+ */
void bc_vm_info(const char* const help);
+
+/**
+ * The entrance point for bc/dc together.
+ * @param argc The count of arguments.
+ * @param argv The argument array.
+ */
void bc_vm_boot(int argc, char *argv[]);
+
+/**
+ * Initializes some of the BcVm global.
+ */
void bc_vm_init(void);
+
+/**
+ * Frees the BcVm global.
+ */
void bc_vm_shutdown(void);
+
+/**
+ * Add a temp to the temp array.
+ * @param num The BcDig array to add to the temp array.
+ */
void bc_vm_addTemp(BcDig *num);
+
+/**
+ * Dish out a temp, or NULL if there are none.
+ * @return A temp, or NULL if none exist.
+ */
BcDig* bc_vm_takeTemp(void);
+
+/**
+ * Frees all temporaries.
+ */
void bc_vm_freeTemps(void);
#if !BC_ENABLE_HISTORY
+
+/**
+ * Erases the flush argument if history does not exist because it does not
+ * matter if history does not exist.
+ */
#define bc_vm_putchar(c, t) bc_vm_putchar(c)
+
#endif // !BC_ENABLE_HISTORY
+/**
+ * Print to stdout with limited formating.
+ * @param fmt The format string.
+ */
void bc_vm_printf(const char *fmt, ...);
+
+/**
+ * Puts a char into the stdout buffer.
+ * @param c The character to put on the stdout buffer.
+ * @param type The flush type.
+ */
void bc_vm_putchar(int c, BcFlushType type);
+
+/**
+ * Multiplies @a n and @a size and throws an allocation error if overflow
+ * occurs.
+ * @param n The number of elements.
+ * @param size The size of each element.
+ * @return The product of @a n and @a size.
+ */
size_t bc_vm_arraySize(size_t n, size_t size);
+
+/**
+ * Adds @a a and @a b and throws an error if overflow occurs.
+ * @param a The first operand.
+ * @param b The second operand.
+ * @return The sum of @a a and @a b.
+ */
size_t bc_vm_growSize(size_t a, size_t b);
+
+/**
+ * Allocate @a n bytes and throw an allocation error if allocation fails.
+ * @param n The bytes to allocate.
+ * @return A pointer to the allocated memory.
+ */
void* bc_vm_malloc(size_t n);
+
+/**
+ * Reallocate @a ptr to be @a n bytes and throw an allocation error if
+ * reallocation fails.
+ * @param ptr The pointer to a memory allocation to reallocate.
+ * @param n The bytes to allocate.
+ * @return A pointer to the reallocated memory.
+ */
void* bc_vm_realloc(void *ptr, size_t n);
+
+/**
+ * Allocates space for, and duplicates, @a str.
+ * @param str The string to allocate.
+ * @return The allocated string.
+ */
char* bc_vm_strdup(const char *str);
+/**
+ * Reads a line into BcVm's buffer field.
+ * @param clear True if the buffer should be cleared first, false otherwise.
+ * @return True if a line was read, false otherwise.
+ */
bool bc_vm_readLine(bool clear);
+/**
+ * A convenience and portability function for OpenBSD's pledge().
+ * @param promises The promises to pledge().
+ * @param execpromises The exec promises to pledge().
+ */
void bc_pledge(const char *promises, const char *execpromises);
+/**
+ * Returns the value of an environment variable.
+ * @param var The environment variable.
+ * @return The value of the environment variable.
+ */
char* bc_vm_getenv(const char* var);
+
+/**
+ * Frees an environment variable value.
+ * @param val The value to free.
+ */
void bc_vm_getenvFree(char* val);
#if BC_DEBUG_CODE
+
+/**
+ * Start executing a jump series.
+ * @param f The name of the function that started the jump series.
+ */
void bc_vm_jmp(const char *f);
#else // BC_DEBUG_CODE
+
+/**
+ * Start executing a jump series.
+ */
void bc_vm_jmp(void);
+
#endif // BC_DEBUG_CODE
#if BC_ENABLE_LIBRARY
+
+/**
+ * Handle an error. This is the true error handler. It will start a jump series
+ * if an error occurred. POSIX errors will not cause jumps when warnings are on
+ * or no POSIX errors are enabled.
+ * @param e The error.
+ */
void bc_vm_handleError(BcErr e);
+
+/**
+ * Handle a fatal error.
+ * @param e The error.
+ */
void bc_vm_fatalError(BcErr e);
+
+/**
+ * A function to call at exit.
+ */
void bc_vm_atexit(void);
+
#else // BC_ENABLE_LIBRARY
+
+/**
+ * Handle an error. This is the true error handler. It will start a jump series
+ * if an error occurred. POSIX errors will not cause jumps when warnings are on
+ * or no POSIX errors are enabled.
+ * @param e The error.
+ * @param line The source line where the error occurred.
+ */
void bc_vm_handleError(BcErr e, size_t line, ...);
+
+/**
+ * Handle a fatal error.
+ * @param e The error.
+ */
#if !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
BC_NORETURN
#endif // !BC_ENABLE_LIBRARY && !BC_ENABLE_MEMCHECK
void bc_vm_fatalError(BcErr e);
+
+/**
+ * A function to call at exit.
+ * @param status The exit status.
+ */
int bc_vm_atexit(int status);
#endif // BC_ENABLE_LIBRARY
+/// A reference to the copyright header.
extern const char bc_copyright[];
+
+/// A reference to the format string for source code line printing.
extern const char* const bc_err_line;
+
+/// A reference to the format string for source code function printing.
extern const char* const bc_err_func_header;
+
+/// A reference to the array of default error category names.
extern const char *bc_errs[];
+
+/// A reference to the array of error category indices for each error.
extern const uchar bc_err_ids[];
+
+/// A reference to the array of default error messages.
extern const char* const bc_err_msgs[];
+/// A reference to the pledge() promises at start.
extern const char bc_pledge_start[];
+
+#if BC_ENABLE_HISTORY
+
+/// A reference to the end pledge() promises when using history.
extern const char bc_pledge_end_history[];
+
+#endif // BC_ENABLE_HISTORY
+
+/// A reference to the end pledge() promises when *not* using history.
extern const char bc_pledge_end[];
+/// A reference to the global data.
extern BcVm vm;
+
+/// A reference to the global output buffers.
extern char output_bufs[BC_VM_BUF_SIZE];
#endif // BC_VM_H