Move stuff for graff
diff --git a/bc/Makefile b/bc/Makefile
new file mode 100644
index 0000000..9d232a8
--- /dev/null
+++ b/bc/Makefile
@@ -0,0 +1,18 @@
+CFLAGS += -Wall -Wextra -I./include/ -I../arbprec/include -std=c99 -fsanitize=address -fsanitize=undefined -g -O0
+
+BC_OBJ = $(shell for i in *.c ; do printf "%s\n" $${i%.c}.o ; done )
+
+BC_EXEC = bc
+
+all:
+
+ $(MAKE) create_bc
+
+create_bc: $(BC_OBJ)
+
+ $(CC) $(CFLAGS) -o $(BC_EXEC) *.o ../arbprec/libarbprec.a
+
+clean:
+
+ $(RM) $(BC_OBJ)
+ $(RM) $(BC_EXEC)
diff --git a/bc/bc.c b/bc/bc.c
new file mode 100644
index 0000000..76fff7b
--- /dev/null
+++ b/bc/bc.c
@@ -0,0 +1,98 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+
+#include "vm.h"
+
+uint32_t bc_ibase = 10;
+uint32_t bc_obase = 10;
+
+int bc_mathlib = 0;
+int bc_quiet = 0;
+int bc_std = 0;
+int bc_warn = 0;
+
+static const struct option bc_opts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "mathlib", no_argument, &bc_mathlib, 'l' },
+ { "quiet", no_argument, &bc_quiet, 'q' },
+ { "standard", no_argument, &bc_std, 's' },
+ { "version", no_argument, NULL, 'v' },
+ { "warn", no_argument, &bc_warn, 'w' },
+ { 0, 0, 0, 0},
+};
+
+static const char* const bc_short_opts = "hlqsvw";
+
+int main(int argc, char* argv[]) {
+
+ BcStatus status;
+ BcVm vm;
+
+ // Getopt needs this.
+ int opt_idx = 0;
+
+ int c = getopt_long(argc, argv, bc_short_opts, bc_opts, &opt_idx);
+
+ while (c != -1) {
+
+ switch (c) {
+
+ case 0:
+ // This is the case when a long option is
+ // found, so we don't need to do anything.
+ break;
+
+ case 'h':
+ // TODO: Print help.
+ break;
+
+ case 'l':
+ bc_mathlib = 'l';
+ break;
+
+ case 'q':
+ bc_quiet = 'q';
+ break;
+
+ case 's':
+ bc_std = 's';
+ break;
+
+ case 'v':
+ // TODO: Print version.
+ break;
+
+ case 'w':
+ bc_warn = 'w';
+ break;
+
+ case '?':
+ // Getopt printed an error message, but we should exit.
+ default:
+ exit(BC_STATUS_INVALID_OPTION);
+ break;
+ }
+
+ // Get the next option.
+ c = getopt_long(argc, argv, bc_short_opts, bc_opts, &opt_idx);
+ }
+
+ uint32_t num_files = argc - optind;
+ const char** file_names = (const char**) (argv + optind);
+
+ status = bc_vm_init(&vm, num_files, file_names);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_vm_exec(&vm);
+
+ bc_vm_free(&vm);
+
+ return status;
+}
diff --git a/bc/include/bc/bc.h b/bc/include/bc/bc.h
new file mode 100644
index 0000000..7be978c
--- /dev/null
+++ b/bc/include/bc/bc.h
@@ -0,0 +1,39 @@
+#ifndef BC_H
+#define BC_H
+
+#include <stdint.h>
+
+typedef enum BcStatus {
+
+ BC_STATUS_SUCCESS,
+
+ BC_STATUS_INVALID_OPTION,
+
+ BC_STATUS_MALLOC_FAIL,
+ BC_STATUS_INVALID_PARAM,
+
+ BC_STATUS_VM_FILE_ERR,
+ BC_STATUS_VM_FILE_READ_ERR,
+ BC_STATUS_VM_DIVIDE_BY_ZERO,
+ BC_STATUS_VM_NEG_SQRT,
+ BC_STATUS_VM_MISMATCHED_PARAMS,
+ BC_STATUS_VM_UNDEFINED_FUNC,
+
+ BC_STATUS_LEX_INVALID_TOKEN,
+ BC_STATUS_LEX_NO_STRING_END,
+ BC_STATUS_LEX_NO_COMMENT_END,
+ BC_STATUS_LEX_EOF,
+
+ BC_STATUS_PARSE_INVALID_TOKEN,
+ BC_STATUS_PARSE_INVALID_EXPR,
+ BC_STATUS_PARSE_INVALID_PRINT,
+ BC_STATUS_PARSE_INVALID_FUNC,
+ BC_STATUS_PARSE_EOF,
+ BC_STATUS_PARSE_BUG,
+
+} BcStatus;
+
+void bc_error(BcStatus status);
+void bc_error_file(const char* file, uint32_t line, BcStatus status);
+
+#endif // BC_H
diff --git a/bc/include/bc/lex.h b/bc/include/bc/lex.h
new file mode 100644
index 0000000..062b499
--- /dev/null
+++ b/bc/include/bc/lex.h
@@ -0,0 +1,124 @@
+#ifndef BC_LEX_H
+#define BC_LEX_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "bc.h"
+
+#define BC_LEX_GEN_ENUM(ENUM) ENUM,
+#define BC_LEX_GEN_STR(STRING) #STRING,
+
+// BC_LEX_OP_NEGATE is not used in lexing;
+// it is only for parsing.
+#define BC_LEX_TOKEN_FOREACH(TOKEN) \
+ TOKEN(BC_LEX_OP_INC) \
+ TOKEN(BC_LEX_OP_DEC) \
+ \
+ TOKEN(BC_LEX_OP_POWER) \
+ \
+ TOKEN(BC_LEX_OP_MULTIPLY) \
+ TOKEN(BC_LEX_OP_DIVIDE) \
+ TOKEN(BC_LEX_OP_MODULUS) \
+ \
+ TOKEN(BC_LEX_OP_PLUS) \
+ TOKEN(BC_LEX_OP_MINUS) \
+ \
+ TOKEN(BC_LEX_OP_ASSIGN) \
+ TOKEN(BC_LEX_OP_ASSIGN_PLUS) \
+ TOKEN(BC_LEX_OP_ASSIGN_MINUS) \
+ TOKEN(BC_LEX_OP_ASSIGN_MULTIPLY) \
+ TOKEN(BC_LEX_OP_ASSIGN_DIVIDE) \
+ TOKEN(BC_LEX_OP_ASSIGN_MODULUS) \
+ TOKEN(BC_LEX_OP_ASSIGN_POWER) \
+ \
+ TOKEN(BC_LEX_OP_REL_EQUAL) \
+ TOKEN(BC_LEX_OP_REL_LESS_EQ) \
+ TOKEN(BC_LEX_OP_REL_GREATER_EQ) \
+ TOKEN(BC_LEX_OP_REL_NOT_EQ) \
+ TOKEN(BC_LEX_OP_REL_LESS) \
+ TOKEN(BC_LEX_OP_REL_GREATER) \
+ \
+ TOKEN(BC_LEX_OP_BOOL_NOT) \
+ \
+ TOKEN(BC_LEX_OP_BOOL_OR) \
+ TOKEN(BC_LEX_OP_BOOL_AND) \
+ \
+ TOKEN(BC_LEX_OP_NEGATE) \
+ \
+ TOKEN(BC_LEX_NEWLINE) \
+ \
+ TOKEN(BC_LEX_WHITESPACE) \
+ \
+ TOKEN(BC_LEX_LEFT_PAREN) \
+ TOKEN(BC_LEX_RIGHT_PAREN) \
+ \
+ TOKEN(BC_LEX_LEFT_BRACKET) \
+ TOKEN(BC_LEX_RIGHT_BRACKET) \
+ \
+ TOKEN(BC_LEX_LEFT_BRACE) \
+ TOKEN(BC_LEX_RIGHT_BRACE) \
+ \
+ TOKEN(BC_LEX_COMMA) \
+ TOKEN(BC_LEX_SEMICOLON) \
+ \
+ TOKEN(BC_LEX_STRING) \
+ TOKEN(BC_LEX_NAME) \
+ TOKEN(BC_LEX_NUMBER) \
+ \
+ TOKEN(BC_LEX_KEY_AUTO) \
+ TOKEN(BC_LEX_KEY_BREAK) \
+ TOKEN(BC_LEX_KEY_CONTINUE) \
+ TOKEN(BC_LEX_KEY_DEFINE) \
+ TOKEN(BC_LEX_KEY_ELSE) \
+ TOKEN(BC_LEX_KEY_FOR) \
+ TOKEN(BC_LEX_KEY_HALT) \
+ TOKEN(BC_LEX_KEY_IBASE) \
+ TOKEN(BC_LEX_KEY_IF) \
+ TOKEN(BC_LEX_KEY_LAST) \
+ TOKEN(BC_LEX_KEY_LENGTH) \
+ TOKEN(BC_LEX_KEY_LIMITS) \
+ TOKEN(BC_LEX_KEY_OBASE) \
+ TOKEN(BC_LEX_KEY_PRINT) \
+ TOKEN(BC_LEX_KEY_QUIT) \
+ TOKEN(BC_LEX_KEY_READ) \
+ TOKEN(BC_LEX_KEY_RETURN) \
+ TOKEN(BC_LEX_KEY_SCALE) \
+ TOKEN(BC_LEX_KEY_SQRT) \
+ TOKEN(BC_LEX_KEY_WHILE) \
+ \
+ TOKEN(BC_LEX_EOF) \
+ \
+ TOKEN(BC_LEX_INVALID) \
+
+typedef enum BcLexTokenType {
+ BC_LEX_TOKEN_FOREACH(BC_LEX_GEN_ENUM)
+} BcLexTokenType;
+
+typedef struct BcLexToken {
+
+ BcLexTokenType type;
+ char* string;
+
+} BcLexToken;
+
+typedef struct BcLex {
+
+ const char* buffer;
+ size_t idx;
+ uint32_t line;
+ bool newline;
+ const char* file;
+ size_t len;
+
+} BcLex;
+
+BcStatus bc_lex_init(BcLex* lex);
+
+BcStatus bc_lex_text(BcLex* lex, const char* text);
+
+BcStatus bc_lex_next(BcLex* lex, BcLexToken* token);
+
+BcStatus bc_lex_printToken(BcLexToken* token);
+
+#endif // BC_LEX_H
diff --git a/bc/include/bc/parse.h b/bc/include/bc/parse.h
new file mode 100644
index 0000000..5fcbf69
--- /dev/null
+++ b/bc/include/bc/parse.h
@@ -0,0 +1,106 @@
+#ifndef BC_PARSE_H
+#define BC_PARSE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "stack.h"
+#include "program.h"
+#include "lex.h"
+
+#define BC_PARSE_TOP_FLAG(parse) \
+ (*((uint8_t*) bc_stack_top(&(parse)->flag_stack)))
+
+#define BC_PARSE_FLAG_FUNC_INNER (0x0001)
+
+#define BC_PARSE_FUNC_INNER(parse) \
+ (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_FUNC_INNER)
+
+#define BC_PARSE_FLAG_FUNC (0x0002)
+
+#define BC_PARSE_FUNC(parse) \
+ (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_FUNC)
+
+#define BC_PARSE_FLAG_HEADER (0x0004)
+
+#define BC_PARSE_HEADER(parse) \
+ (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_HEADER)
+
+#define BC_PARSE_FLAG_AUTO (0x0008)
+
+#define BC_PARSE_AUTO(parse) \
+ (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_AUTO)
+
+#define BC_PARSE_FLAG_FOR_LOOP (0x0010)
+
+#define BC_PARSE_FOR_LOOP(parse) \
+ (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_FOR_LOOP)
+
+#define BC_PARSE_FLAG_WHILE_LOOP (0x0020)
+
+#define BC_PARSE_WHILE_LOOP(parse) \
+ (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_WHILE_LOOP)
+
+#define BC_PARSE_FLAG_LOOP_INNER (0x0040)
+
+#define BC_PARSE_LOOP_INNER(parse) \
+ (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_LOOP_INNER)
+
+#define BC_PARSE_FLAG_IF (0x0080)
+
+#define BC_PARSE_IF(parse) \
+ (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_IF)
+
+#define BC_PARSE_FLAG_ELSE (0x0100)
+
+#define BC_PARSE_ELSE(parse) \
+ (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_ELSE)
+
+#define BC_PARSE_CAN_EXEC(parse) \
+ (!(BC_PARSE_TOP_FLAG(parse) & (BC_PARSE_FLAG_FUNC_INNER | \
+ BC_PARSE_FLAG_FUNC | \
+ BC_PARSE_FLAG_HEADER | \
+ BC_PARSE_FLAG_FOR_LOOP | \
+ BC_PARSE_FLAG_WHILE_LOOP | \
+ BC_PARSE_FLAG_LOOP_INNER | \
+ BC_PARSE_FLAG_IF)))
+
+// We can calculate the conversion between tokens and exprs
+// by subtracting the position of the first operator in the
+// lex enum and adding the position of the first in the expr
+// enum. WARNING: This only works for binary operators.
+#define BC_PARSE_TOKEN_TO_EXPR(type) ((type) - BC_LEX_OP_POWER + BC_EXPR_POWER)
+
+typedef struct BcOp {
+
+ uint8_t prec;
+ bool left;
+
+} BcOp;
+
+typedef struct BcParse {
+
+ BcProgram* program;
+ BcLex lex;
+ BcLexToken token;
+
+ BcStack flag_stack;
+
+ BcStack ctx_stack;
+
+ BcFunc* func;
+
+ BcStmt partial;
+
+ uint32_t num_braces;
+
+} BcParse;
+
+BcStatus bc_parse_init(BcParse* parse, BcProgram* program);
+BcStatus bc_parse_text(BcParse* parse, const char* text);
+
+BcStatus bc_parse_parse(BcParse* parse, BcProgram* program);
+
+void bc_parse_free(BcParse* parse);
+
+#endif // BC_PARSE_H
diff --git a/bc/include/bc/program.h b/bc/include/bc/program.h
new file mode 100644
index 0000000..d518f67
--- /dev/null
+++ b/bc/include/bc/program.h
@@ -0,0 +1,245 @@
+#ifndef BC_PROGRAM_H
+#define BC_PROGRAM_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <arbprec/arbprec.h>
+
+#include "segarray.h"
+#include "bc.h"
+
+#define BC_PROGRAM_MAX_STMTS (128)
+
+#define BC_PROGRAM_DEF_SIZE (16)
+
+typedef enum BcExprType {
+
+ BC_EXPR_INC_PRE,
+ BC_EXPR_DEC_PRE,
+
+ BC_EXPR_INC_POST,
+ BC_EXPR_DEC_POST,
+
+ BC_EXPR_NEGATE,
+
+ BC_EXPR_POWER,
+
+ BC_EXPR_MULTIPLY,
+ BC_EXPR_DIVIDE,
+ BC_EXPR_MODULUS,
+
+ BC_EXPR_PLUS,
+ BC_EXPR_MINUS,
+
+ BC_EXPR_ASSIGN,
+ BC_EXPR_ASSIGN_PLUS,
+ BC_EXPR_ASSIGN_MINUS,
+ BC_EXPR_ASSIGN_MULTIPLY,
+ BC_EXPR_ASSIGN_DIVIDE,
+ BC_EXPR_ASSIGN_MODULUS,
+ BC_EXPR_ASSIGN_POWER,
+
+ BC_EXPR_REL_EQUAL,
+ BC_EXPR_REL_LESS_EQ,
+ BC_EXPR_REL_GREATER_EQ,
+ BC_EXPR_REL_NOT_EQ,
+ BC_EXPR_REL_LESS,
+ BC_EXPR_REL_GREATER,
+
+ BC_EXPR_BOOL_NOT,
+
+ BC_EXPR_BOOL_OR,
+ BC_EXPR_BOOL_AND,
+
+ BC_EXPR_NUMBER,
+ BC_EXPR_VAR,
+ BC_EXPR_ARRAY_ELEM,
+
+ BC_EXPR_FUNC_CALL,
+
+ BC_EXPR_SCALE,
+ BC_EXPR_SCALE_FUNC,
+ BC_EXPR_IBASE,
+ BC_EXPR_OBASE,
+ BC_EXPR_LAST,
+ BC_EXPR_LENGTH,
+ BC_EXPR_READ,
+ BC_EXPR_SQRT,
+
+ BC_EXPR_PRINT,
+
+} BcExprType;
+
+typedef enum BcStmtType {
+
+ BC_STMT_EXPR,
+
+ BC_STMT_STRING,
+
+ BC_STMT_BREAK,
+ BC_STMT_CONTINUE,
+
+ BC_STMT_HALT,
+
+ BC_STMT_RETURN,
+
+ BC_STMT_IF,
+ BC_STMT_WHILE,
+
+ BC_STMT_LIST,
+
+} BcStmtType;
+
+typedef struct BcCall {
+
+ char* name;
+ BcSegArray params;
+
+} BcCall;
+
+typedef struct BcExpr {
+
+ BcExprType type;
+ union {
+ char* string;
+ BcStack* expr_stack;
+ BcCall* call;
+ };
+
+} BcExpr;
+
+typedef struct BcStmtList BcStmtList;
+
+typedef struct BcIf {
+
+ BcStack cond;
+ BcStmtList* then_list;
+ BcStmtList* else_list;
+
+} BcIf;
+
+typedef struct BcWhile {
+
+ BcStack cond;
+ BcStmtList* body;
+
+} BcWhile;
+
+typedef struct BcFor {
+
+ BcStmtList* body;
+ BcStack cond;
+ BcStack update;
+ BcStack init;
+
+} BcFor;
+
+typedef union BcStmtData {
+
+ char* string;
+ BcStmtList* list;
+ BcStack* expr_stack;
+ BcIf* if_stmt;
+ BcWhile* while_stmt;
+ BcFor* for_stmt;
+
+} BcStmtData;
+
+typedef struct BcStmt {
+
+ BcStmtType type;
+ BcStmtData data;
+
+} BcStmt;
+
+typedef struct BcStmtList {
+
+ struct BcStmtList* next;
+
+ uint32_t num_stmts;
+
+ BcStmt stmts[BC_PROGRAM_MAX_STMTS];
+
+} BcStmtList;
+
+typedef struct BcAuto {
+
+ char* name;
+ bool var;
+
+} BcAuto;
+
+typedef struct BcFunc {
+
+ char* name;
+
+ BcStmtList* first;
+
+ BcStmtList* cur;
+
+ BcAuto* params;
+ uint32_t num_params;
+ uint32_t param_cap;
+
+ BcAuto* autos;
+ uint32_t num_autos;
+ uint32_t auto_cap;
+
+} BcFunc;
+
+typedef struct BcVar {
+
+ char* name;
+
+ fxdpnt* data;
+
+} BcVar;
+
+/**
+ * An array.
+ */
+typedef struct BcArray {
+
+ char* name;
+
+ BcSegArray array;
+
+} BcArray;
+
+typedef struct BcProgram {
+
+ const char* file;
+
+ BcStmtList* first;
+
+ BcStmtList* cur;
+
+ BcSegArray funcs;
+
+ BcSegArray vars;
+
+ BcSegArray arrays;
+
+} BcProgram;
+
+BcStatus bc_program_init(BcProgram* p, const char* file);
+BcStatus bc_program_list_insert(BcStmtList* list, BcStmt* stmt);
+BcStatus bc_program_func_add(BcProgram* p, BcFunc* func);
+BcStatus bc_program_var_add(BcProgram* p, BcVar* var);
+BcStatus bc_program_array_add(BcProgram* p, BcArray* array);
+BcStatus bc_program_exec(BcProgram* p);
+void bc_program_free(BcProgram* program);
+
+BcStmtList* bc_program_list_create();
+void bc_program_list_free(BcStmtList* list);
+
+BcStatus bc_program_func_init(BcFunc* func, char* name);
+BcStatus bc_program_func_insertParam(BcFunc* func, char* name, bool var);
+BcStatus bc_program_func_insertAuto(BcFunc* func, char* name, bool var);
+BcStatus bc_program_var_init(BcVar* var, char* name);
+BcStatus bc_program_array_init(BcArray* array, char* name);
+
+BcStatus bc_program_stmt_init(BcStmt* stmt);
+
+#endif // BC_PROGRAM_H
diff --git a/bc/include/bc/segarray.h b/bc/include/bc/segarray.h
new file mode 100644
index 0000000..7b72f9f
--- /dev/null
+++ b/bc/include/bc/segarray.h
@@ -0,0 +1,55 @@
+#ifndef BC_SEGARRAY_H
+#define BC_SEGARRAY_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "stack.h"
+
+#define BC_SEGARRAY_NUM_ARRAYS (32)
+
+#define BC_SEGARRAY_SEG_POWER (10)
+
+#define BC_SEGARRAY_SEG_SIZE (1 << BC_SEGARRAY_SEG_POWER)
+
+#define BC_SEGARRAY_SEG_LAST (BC_SEGARRAY_SEG_SIZE - 1)
+
+#define BC_SEGARRAY_SEG_IDX2_MASK (BC_SEGARRAY_SEG_SIZE - 1)
+
+#define BC_SEGARRAY_SEG_IDX1_MASK (~BC_SEGARRAY_SEG_IDX2_MASK)
+
+#define BC_SEGARRAY_IDX1(idx) \
+ (((idx) & BC_SEGARRAY_SEG_IDX1_MASK) >> BC_SEGARRAY_SEG_POWER)
+
+#define BC_SEGARRAY_IDX2(idx) ((idx) & BC_SEGARRAY_SEG_IDX2_MASK)
+
+#define BC_SEGARRAY_MAX (BC_SEGARRAY_NUM_ARRAYS << (BC_SEGARRAY_SEG_POWER))
+
+typedef void (*BcSegArrayFreeFunc)(void*);
+typedef int (*BcSegArrayCmpFunc)(void*, void*);
+
+typedef struct BcSegArray {
+
+ size_t esize;
+ uint32_t num;
+ uint32_t num_ptrs;
+ uint32_t ptr_cap;
+ uint8_t** ptrs;
+ BcSegArrayCmpFunc cmp;
+ BcSegArrayFreeFunc sfree;
+
+} BcSegArray;
+
+BcStatus bc_segarray_init(BcSegArray* sa, size_t esize, BcSegArrayFreeFunc sfree, BcSegArrayCmpFunc cmp);
+
+BcStatus bc_segarray_add(BcSegArray* sa, void* data);
+
+void* bc_segarray_item(BcSegArray* sa, uint32_t idx);
+
+void* bc_segarray_item2(BcSegArray* sa, uint32_t idx1, uint32_t idx2);
+
+uint32_t bc_segarray_find(BcSegArray* sa, void* data);
+
+void bc_segarray_free(BcSegArray* sa);
+
+#endif // BC_SEGARRAY_H
diff --git a/bc/include/bc/stack.h b/bc/include/bc/stack.h
new file mode 100644
index 0000000..c7a87bf
--- /dev/null
+++ b/bc/include/bc/stack.h
@@ -0,0 +1,32 @@
+#ifndef BC_STACK_H
+#define BC_STACK_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "bc.h"
+
+#define BC_STACK_START (16)
+
+typedef struct BcStack {
+
+ size_t size;
+ uint8_t* stack;
+ uint32_t len;
+ uint32_t cap;
+
+} BcStack;
+
+BcStatus bc_stack_init(BcStack* stack, size_t esize);
+
+BcStatus bc_stack_push(BcStack* stack, void* data);
+
+void* bc_stack_top(BcStack* stack);
+
+void* bc_stack_item(BcStack* stack, uint32_t idx);
+
+BcStatus bc_stack_pop(BcStack* stack);
+
+void bc_stack_free(void* stack);
+
+#endif // BC_STACK_H
diff --git a/bc/include/bc/vm.h b/bc/include/bc/vm.h
new file mode 100644
index 0000000..e1f5976
--- /dev/null
+++ b/bc/include/bc/vm.h
@@ -0,0 +1,28 @@
+#ifndef BC_VM_H
+#define BC_VM_H
+
+#include "program.h"
+#include "stack.h"
+#include "parse.h"
+
+#define BC_VM_BUF_SIZE (1024)
+
+typedef struct BcVm {
+
+ BcProgram program;
+ BcParse parse;
+
+ BcStack ctx_stack;
+
+ int filec;
+ const char** filev;
+
+} BcVm;
+
+BcStatus bc_vm_init(BcVm* vm, int filec, const char* filev[]);
+
+BcStatus bc_vm_exec(BcVm* vm);
+
+void bc_vm_free(BcVm* vm);
+
+#endif // BC_VM_H
diff --git a/bc/src/error.c b/bc/src/error.c
new file mode 100644
index 0000000..75f9129
--- /dev/null
+++ b/bc/src/error.c
@@ -0,0 +1,90 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "bc.h"
+
+static const char* const bc_err_types[] = {
+
+ NULL,
+
+ "bc",
+ "bc",
+ "bc",
+
+ "runtime",
+ "runtime",
+ "runtime",
+ "runtime",
+ "runtime",
+ "runtime",
+
+ "lex",
+ "lex",
+ "lex",
+ "lex",
+
+ "parse",
+ "parse",
+ "parse",
+ "parse",
+ "parse",
+ "parse",
+
+};
+
+static const char* const bc_err_descs[] = {
+
+ NULL,
+
+ "invalid option",
+
+ "memory allocation error",
+ "invalid parameter",
+
+ "couldn't open file"
+ "file read error",
+ "divide by zero",
+ "negative square root",
+ "mismatched parameters",
+ "undefined function",
+
+ "invalid token",
+ "string end could not be found",
+ "comment end could not be found",
+ "end of file",
+
+ "invalid token",
+ "invalid expression",
+ "invalid print statement",
+ "invalid function definition",
+ "end of file",
+ "bug in parser",
+
+};
+
+void bc_error(BcStatus status) {
+
+ if (!status) {
+ return;
+ }
+
+ fprintf(stderr, "%s error: %s\n", bc_err_types[status], bc_err_descs[status]);
+}
+
+void bc_error_file(const char* file, uint32_t line, BcStatus status) {
+
+ if (!status || !file) {
+ return;
+ }
+
+ fprintf(stderr, "%s error: %s\n", bc_err_types[status], bc_err_descs[status]);
+ fprintf(stderr, " %s", file);
+
+ if (line) {
+ fprintf(stderr, ":%d\n", line);
+ }
+ else {
+ fputc('\n', stderr);
+ }
+}
diff --git a/bc/src/lex.c b/bc/src/lex.c
new file mode 100644
index 0000000..ca7a6c5
--- /dev/null
+++ b/bc/src/lex.c
@@ -0,0 +1,819 @@
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bc.h"
+#include "lex.h"
+
+static const char* const token_type_strs[] = {
+ BC_LEX_TOKEN_FOREACH(BC_LEX_GEN_STR)
+};
+
+static const char* const keywords[] = {
+
+ "auto",
+ "break",
+ "continue",
+ "define",
+ "else",
+ "for",
+ "halt",
+ "ibase",
+ "if",
+ "last",
+ "length",
+ "limits",
+ "obase",
+ "print",
+ "quit",
+ "read",
+ "return",
+ "scale",
+ "sqrt",
+ "while",
+
+};
+
+static const uint32_t keyword_lens[] = {
+
+ 4, // auto
+ 5, // break
+ 8, // continue
+ 6, // define
+ 4, // else
+ 3, // for
+ 4, // halt
+ 5, // ibase
+ 2, // if
+ 4, // last
+ 6, // length
+ 6, // limits
+ 5, // obase
+ 5, // print
+ 4, // quit
+ 4, // read
+ 6, // return
+ 5, // scale
+ 4, // sqrt
+ 5, // while
+
+};
+
+static BcStatus bc_lex_token(BcLex* lex, BcLexToken* token);
+static BcStatus bc_lex_whitespace(BcLex* lex, BcLexToken* token);
+static BcStatus bc_lex_string(BcLex* lex, BcLexToken* token);
+static BcStatus bc_lex_comment(BcLex* lex, BcLexToken* token);
+static BcStatus bc_lex_number(BcLex* lex, BcLexToken* token, char start);
+static BcStatus bc_lex_name(BcLex* lex, BcLexToken* token);
+
+BcStatus bc_lex_printToken(BcLexToken* token) {
+
+ // Print the type.
+ printf("<%s", token_type_strs[token->type]);
+
+ // Figure out if more needs to be done.
+ switch (token->type) {
+
+ case BC_LEX_STRING:
+ case BC_LEX_NAME:
+ case BC_LEX_NUMBER:
+ printf(":%s", token->string);
+ break;
+
+ default:
+ break;
+ }
+
+ // Print the end.
+ putchar('>');
+ putchar('\n');
+
+ return BC_STATUS_SUCCESS;
+}
+
+BcStatus bc_lex_init(BcLex* lex) {
+
+ if (lex == NULL ) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ lex->line = 1;
+ lex->newline = false;
+
+ return BC_STATUS_SUCCESS;
+}
+
+BcStatus bc_lex_text(BcLex* lex, const char* text) {
+
+ if (lex == NULL || text == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ lex->buffer = text;
+ lex->idx = 0;
+ lex->len = strlen(text);
+
+ return BC_STATUS_SUCCESS;
+}
+
+BcStatus bc_lex_next(BcLex* lex, BcLexToken* token) {
+
+ BcStatus status;
+
+ if (lex == NULL || token == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ if (lex->idx == lex->len) {
+ token->type = BC_LEX_EOF;
+ return BC_STATUS_LEX_EOF;
+ }
+
+ if (lex->newline) {
+ ++lex->line;
+ lex->newline = false;
+ }
+
+ // Loop until failure or we don't have whitespace. This
+ // is so the parser doesn't get inundated with whitespace.
+ do {
+ status = bc_lex_token(lex, token);
+ } while (!status && token->type == BC_LEX_WHITESPACE);
+
+ return status;
+}
+
+static BcStatus bc_lex_token(BcLex* lex, BcLexToken* token) {
+
+ // We want to make sure this is cleared.
+ BcStatus status = BC_STATUS_SUCCESS;
+
+ // Get the character.
+ char c = lex->buffer[lex->idx];
+
+ // Increment the index.
+ ++lex->idx;
+
+ char c2;
+
+ // This is the workhorse of the lexer.
+ switch (c) {
+
+ case '\0':
+ {
+ token->type = BC_LEX_EOF;
+ break;
+ }
+
+ case '\t':
+ {
+ status = bc_lex_whitespace(lex, token);
+ break;
+ }
+
+ case '\n':
+ {
+ lex->newline = true;
+ token->type = BC_LEX_NEWLINE;
+ break;
+ }
+
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ {
+ status = bc_lex_whitespace(lex, token);
+ break;
+ }
+
+ case '!':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // The only valid way to have a bang is a NOT_EQ token.
+ // If it's not valid, we do not want to increment twice.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_REL_NOT_EQ;
+ }
+ else {
+ token->type = BC_LEX_OP_BOOL_NOT;
+ }
+
+ break;
+ }
+
+ case '"':
+ {
+ status = bc_lex_string(lex, token);
+ break;
+ }
+
+ case '%':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or as an assignment.
+ // If it's an assignment, we need to increment the index.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_ASSIGN_MODULUS;
+ }
+ else {
+ token->type = BC_LEX_OP_MODULUS;
+ }
+
+ break;
+ }
+
+ case '&':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or as an assignment.
+ // If it's an assignment, we need to increment the index.
+ if (c2 == '&') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_BOOL_AND;
+ }
+ else {
+ token->type = BC_LEX_INVALID;
+ status = BC_STATUS_LEX_INVALID_TOKEN;
+ }
+
+ break;
+ }
+
+ case '(':
+ {
+ token->type = BC_LEX_LEFT_PAREN;
+ break;
+ }
+
+ case ')':
+ {
+ token->type = BC_LEX_RIGHT_PAREN;
+ break;
+ }
+
+ case '*':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or as an assignment.
+ // If it's an assignment, we need to increment the index.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_ASSIGN_MULTIPLY;
+ }
+ else {
+ token->type = BC_LEX_OP_MULTIPLY;
+ }
+
+ break;
+ }
+
+ case '+':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or as an assignment.
+ // If it's an assignment, we need to increment the index.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_ASSIGN_PLUS;
+ }
+ else if (c2 == '+') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_INC;
+ }
+ else {
+ token->type = BC_LEX_OP_PLUS;
+ }
+
+ break;
+ }
+
+ case ',':
+ {
+ token->type = BC_LEX_COMMA;
+ break;
+ }
+
+ case '-':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or as an assignment.
+ // If it's an assignment, we need to increment the index.
+ // We also need to handle numbers.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_ASSIGN_MINUS;
+ }
+ else if (c2 == '-') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_DEC;
+ }
+ else {
+ token->type = BC_LEX_OP_MINUS;
+ }
+
+ break;
+ }
+
+ case '.':
+ {
+ status = bc_lex_number(lex, token, c);
+ break;
+ }
+
+ case '/':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or as an assignment.
+ // If it's an assignment, we need to increment the index.
+ // We also need to handle comments.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_ASSIGN_DIVIDE;
+ }
+ else if (c2 == '*') {
+ status = bc_lex_comment(lex, token);
+ }
+ else {
+ token->type = BC_LEX_OP_DIVIDE;
+ }
+
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ status = bc_lex_number(lex, token, c);
+ break;
+ }
+
+ case ';':
+ {
+ token->type = BC_LEX_SEMICOLON;
+ break;
+ }
+
+ case '<':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or with an equals.
+ // If with an equals, we need to increment the index.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_REL_LESS_EQ;
+ }
+ else {
+ token->type = BC_LEX_OP_REL_LESS;
+ }
+
+ break;
+ }
+
+ case '=':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or with another equals.
+ // If with another equals, we need to increment the index.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_REL_EQUAL;
+ }
+ else {
+ token->type = BC_LEX_OP_ASSIGN;
+ }
+
+ break;
+ }
+
+ case '>':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or with an equals.
+ // If with an equals, we need to increment the index.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_REL_GREATER_EQ;
+ }
+ else {
+ token->type = BC_LEX_OP_REL_GREATER;
+ }
+
+ break;
+ }
+
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ {
+ status = bc_lex_number(lex, token, c);
+ break;
+ }
+
+ case '[':
+ {
+ token->type = BC_LEX_LEFT_BRACKET;
+ break;
+ }
+
+ case '\\':
+ {
+ status = bc_lex_whitespace(lex, token);
+ break;
+ }
+
+ case ']':
+ {
+ token->type = BC_LEX_RIGHT_BRACKET;
+ break;
+ }
+
+ case '^':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or as an assignment.
+ // If it's an assignment, we need to increment the index.
+ if (c2 == '=') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_ASSIGN_POWER;
+ }
+ else {
+ token->type = BC_LEX_OP_POWER;
+ }
+
+ break;
+ }
+
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ {
+ status = bc_lex_name(lex, token);
+ break;
+ }
+
+ case '{':
+ {
+ token->type = BC_LEX_LEFT_BRACE;
+ break;
+ }
+
+ case '|':
+ {
+ // Get the next character.
+ c2 = lex->buffer[lex->idx];
+
+ // This character can either be alone or as an assignment.
+ // If it's an assignment, we need to increment the index.
+ if (c2 == '|') {
+ ++lex->idx;
+ token->type = BC_LEX_OP_BOOL_OR;
+ }
+ else {
+ token->type = BC_LEX_INVALID;
+ status = BC_STATUS_LEX_INVALID_TOKEN;
+ }
+
+ break;
+ }
+
+ case '}':
+ {
+ token->type = BC_LEX_RIGHT_BRACE;
+ break;
+ }
+
+ default:
+ {
+ // All other characters are invalid.
+ token->type = BC_LEX_INVALID;
+ status = BC_STATUS_LEX_INVALID_TOKEN;
+ break;
+ }
+ }
+
+ return status;
+}
+
+static BcStatus bc_lex_whitespace(BcLex* lex, BcLexToken* token) {
+
+ // Set the token type.
+ token->type = BC_LEX_WHITESPACE;
+
+ // Get the character.
+ char c = lex->buffer[lex->idx];
+
+ // Eat all whitespace (and non-newline) characters.
+ while ((isspace(c) && c != '\n') || c == '\\') {
+ ++lex->idx;
+ c = lex->buffer[lex->idx];
+ }
+
+ return BC_STATUS_SUCCESS;
+}
+
+static BcStatus bc_lex_string(BcLex* lex, BcLexToken* token) {
+
+ // Set the token type.
+ token->type = BC_LEX_STRING;
+
+ // Get the starting index and character.
+ size_t i = lex->idx;
+ char c = lex->buffer[i];
+
+ // Find the end of the string, one way or the other.
+ while (c != '"' && c != '\0') {
+ c = lex->buffer[++i];
+ }
+
+ // If we have reached the end of the buffer, complain.
+ if (c == '\0') {
+ lex->idx = i;
+ return BC_STATUS_LEX_NO_STRING_END;
+ }
+
+ // Calculate the length of the string.
+ size_t len = i - lex->idx;
+
+ // Figure out the number of backslash newlines in a string.
+ size_t backslashes = 0;
+ for (size_t j = lex->idx; j < i; ++j) {
+ c = lex->buffer[j];
+ backslashes += c == '\\' && lex->buffer[j + 1] == '\n' ? 1 : 0;
+ }
+
+ // Allocate the string.
+ token->string = malloc(len - backslashes + 1);
+
+ // Check for error.
+ if (token->string == NULL) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ // The copy start and the number of backslash
+ // hits. These are for the upcoming loop.
+ const char* start = lex->buffer + lex->idx;
+ size_t hits = 0;
+
+ // Copy the string.
+ for (size_t j = 0; j < len; ++j) {
+
+ // Get the character.
+ char c = start[j];
+
+ // If we have hit a backslash, skip it.
+ if (hits < backslashes && c == '\\' && start[j + 1] == '\n') {
+ ++hits;
+ continue;
+ }
+
+ // Copy the character.
+ token->string[j - hits] = c;
+ }
+
+ // Make sure to set the null character.
+ token->string[len] = '\0';
+
+ // Set the index. We need to go one
+ // past because of the closing quote.
+ lex->idx = i + 1;
+
+ return BC_STATUS_SUCCESS;
+}
+
+static BcStatus bc_lex_comment(BcLex* lex, BcLexToken* token) {
+
+ // Set the token type.
+ token->type = BC_LEX_WHITESPACE;
+
+ // Increment the index.
+ ++lex->idx;
+
+ // Get the starting index and character.
+ size_t i = lex->idx;
+ const char* buffer = lex->buffer;
+ char c = buffer[i];
+
+ // The end condition.
+ int end = 0;
+
+ // Loop until we have found the end.
+ while (!end) {
+
+ // Find the end of the string, one way or the other.
+ while (c != '*' && c != '\0') {
+ c = buffer[++i];
+ }
+
+ // If we've reached the end of the string,
+ // but not the comment, complain.
+ if (c == '\0' || buffer[i + 1] == '\0') {
+ lex->idx = i;
+ return BC_STATUS_LEX_NO_COMMENT_END;
+ }
+
+ // If we've reached the end, set the end.
+ end = buffer[i + 1] == '/';
+ i += end ? 0 : 1;
+ }
+
+ // Set the index. Plus 2 is to get past the comment end.
+ lex->idx = i + 2;
+
+ return BC_STATUS_SUCCESS;
+}
+
+static BcStatus bc_lex_number(BcLex* lex, BcLexToken* token, char start) {
+
+ // Set the token type.
+ token->type = BC_LEX_NUMBER;
+
+ // Whether or not we already have passed a decimal point.
+ int point = start == '.';
+
+ // Get a pointer to the place in the buffer.
+ const char* buffer = lex->buffer + lex->idx;
+
+ // Cache these for the upcoming loop.
+ size_t backslashes = 0;
+ size_t i = 0;
+ char c = buffer[i];
+
+ // Find the end of the number.
+ while (c && (isdigit(c) || (c >= 'A' && c <= 'F') || (c == '.' && !point) ||
+ (c == '\\' && buffer[i + 1] == '\n')))
+ {
+ // If we ran into a backslash, handle it.
+ if (c == '\\') {
+ ++i;
+ backslashes += 1;
+ }
+
+ // Increment and get the character.
+ c = buffer[++i];
+ }
+
+ // Calculate the length of the string.
+ size_t len = i + 1;
+
+ // Allocate the string.
+ token->string = malloc(len - backslashes + 1);
+
+ // Check for error.
+ if (token->string == NULL) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ // Set the starting character.
+ token->string[0] = start;
+
+ // The copy start and the number of backslash
+ // hits. These are for the upcoming loop.
+ const char* buf = buffer - 1;
+ size_t hits = 0;
+
+ // Copy the string.
+ for (size_t j = 1; j < len; ++j) {
+
+ // Get the character.
+ char c = buf[j];
+
+ // If we have hit a backslash, skip it.
+ // We don't have to check for a newline
+ // because it's guaranteed.
+ if (hits < backslashes && c == '\\') {
+ ++hits;
+ ++j;
+ continue;
+ }
+
+ // Copy the character.
+ token->string[j - (hits * 2)] = c;
+ }
+
+ // Make sure to set the null character.
+ token->string[len] = '\0';
+
+ // Set the index. We need to go one
+ // past because of the closing quote.
+ lex->idx += i;
+
+ return BC_STATUS_SUCCESS;
+}
+
+static BcStatus bc_lex_name(BcLex* lex, BcLexToken* token) {
+
+ // Get a pointer to the place in the buffer. We subtract
+ // one because the index is already incremented.
+ const char* buffer = lex->buffer + lex->idx - 1;
+
+ // Loop through the keywords.
+ for (uint32_t i = 0; i < sizeof(keywords) / sizeof(char*); ++i) {
+
+ // If a keyword matches, set it, increment, and return.
+ if (!strncmp(buffer, keywords[i], keyword_lens[i])) {
+
+ // We just need to add the starting
+ // index of keyword token types.
+ token->type = BC_LEX_KEY_AUTO + i;
+
+ // We need to minus one because the
+ // index has already been incremented.
+ lex->idx += keyword_lens[i] - 1;
+
+ return BC_STATUS_SUCCESS;
+ }
+ }
+
+ // Set the type.
+ token->type = BC_LEX_NAME;
+
+ // These are for the next loop.
+ size_t i = 0;
+ char c = buffer[i];
+
+ // Find the end of the name.
+ while (islower(c) || isdigit(c) || c == '_') {
+ ++i;
+ c = buffer[i];
+ }
+
+ // Malloc the name.
+ token->string = malloc(i + 1);
+
+ // Check for error.
+ if (token->string == NULL) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ // Copy the string.
+ strncpy(token->string, buffer, i);
+ token->string[i] = '\0';
+
+ // Increment the index. It is minus one
+ // because it has already been incremented.
+ lex->idx += i - 1;
+
+ return BC_STATUS_SUCCESS;
+}
diff --git a/bc/src/parse.c b/bc/src/parse.c
new file mode 100644
index 0000000..3753641
--- /dev/null
+++ b/bc/src/parse.c
@@ -0,0 +1,1720 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "lex.h"
+#include "parse.h"
+
+// This is an array that corresponds to token types. An entry is
+// true if the token is valid in an expression, false otherwise.
+static const bool bc_token_exprs[] = {
+
+ true,
+ true,
+
+ true,
+
+ true,
+ true,
+ true,
+
+ true,
+ true,
+
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+
+ true,
+
+ true,
+ true,
+
+ false,
+
+ false,
+
+ true,
+ true,
+
+ false,
+ false,
+
+ false,
+ false,
+
+ false,
+ false,
+
+ false,
+ true,
+ true,
+
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ true,
+ true,
+ true,
+ true,
+ false,
+ false,
+ true,
+ false,
+ true,
+ true,
+ false,
+
+ false,
+
+ false,
+};
+
+// This is an array of data for operators that correspond to token types.
+// The last corresponds to BC_PARSE_OP_NEGATE_IDX since it doesn't have
+// its own token type (it is the same token at the binary minus operator).
+static const BcOp bc_ops[] = {
+
+ { 0, false },
+ { 0, false },
+
+ { 2, false },
+
+ { 3, true },
+ { 3, true },
+ { 3, true },
+
+ { 4, true },
+ { 4, true },
+
+ { 5, false },
+ { 5, false },
+ { 5, false },
+ { 5, false },
+ { 5, false },
+ { 5, false },
+ { 5, false },
+
+ { 6, true },
+ { 6, true },
+ { 6, true },
+ { 6, true },
+ { 6, true },
+ { 6, true },
+
+ { 7, false },
+
+ { 8, true },
+ { 8, true },
+
+ { 1, false }
+
+};
+
+static BcStatus bc_parse_func(BcParse* parse, BcProgram* program);
+static BcStatus bc_parse_funcStart(BcParse* parse, BcFunc* func);
+static BcStatus bc_parse_semicolonList(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_semicolonListEnd(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_stmt(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_expr(BcParse* parse, BcStack** exprs);
+static BcStatus bc_parse_operator(BcStack* exs, BcStack* ops,BcLexTokenType t,
+ uint32_t* num_exprs);
+static BcStatus bc_parse_rightParen(BcStack* exs, BcStack* ops, uint32_t* nexs);
+static BcStatus bc_parse_expr_name(BcParse* parse, BcStack* exprs,
+ BcExprType* type);
+static BcStatus bc_parse_call(BcParse* parse, BcExpr* expr);
+static BcStatus bc_parse_params(BcParse* parse, BcExpr* expr);
+static BcStatus bc_parse_read(BcParse* parse, BcStack* exprs);
+static BcStatus bc_parse_builtin(BcParse* parse, BcStack* exs, BcExprType type);
+static BcStatus bc_parse_scale(BcParse* parse, BcStack* exs, BcExprType* type);
+static BcStatus bc_parse_incdec(BcParse* parse, BcStack* exs, BcExprType* prev);
+static BcStatus bc_parse_minus(BcParse* parse, BcStack* exs, BcStack* ops,
+ BcExprType* prev, bool rparen, uint32_t* nexprs);
+static BcStatus bc_parse_string(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_string(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_string(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_return(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_print(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_if(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_while(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_for(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_loop(BcParse* parse, BcStmtList* list);
+static BcStatus bc_parse_startBody(BcParse* parse, BcStmtList** new_list,
+ uint16_t flags);
+static BcStatus bc_parse_loopExit(BcParse* parse, BcStmtList* list,
+ BcLexTokenType type);
+static BcStatus bc_parse_rightBrace(BcParse* parse, BcStmtList* list);
+
+BcStatus bc_parse_init(BcParse* parse, BcProgram* program) {
+
+ if (parse == NULL || program == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ parse->program = program;
+
+ BcStatus status = bc_stack_init(&parse->flag_stack, sizeof(uint16_t));
+
+ if (status != BC_STATUS_SUCCESS) {
+ return status;
+ }
+
+ status = bc_stack_init(&parse->ctx_stack, sizeof(BcStmtList*));
+
+ if (status != BC_STATUS_SUCCESS) {
+ return status;
+ }
+
+ uint16_t flags = 0;
+
+ status = bc_stack_push(&parse->flag_stack, &flags);
+ if (status != BC_STATUS_SUCCESS) {
+ return status;
+ }
+
+ status = bc_stack_push(&parse->ctx_stack, &program->first);
+
+ if (status) {
+ return status;
+ }
+
+ parse->func = NULL;
+ parse->num_braces = 0;
+
+ return bc_lex_init(&parse->lex);
+}
+
+BcStatus bc_parse_text(BcParse* parse, const char* text) {
+
+ if (parse == NULL || text == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ return bc_lex_text(&parse->lex, text);
+}
+
+BcStatus bc_parse_parse(BcParse* parse, BcProgram* program) {
+
+ BcStmtList** ptr;
+
+ if (parse == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ BcStatus status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status != BC_STATUS_SUCCESS) {
+ return status;
+ }
+
+ switch (parse->token.type) {
+
+ case BC_LEX_NEWLINE:
+ {
+ // We don't do anything if there is a newline.
+ break;
+ }
+
+ case BC_LEX_KEY_DEFINE:
+ {
+ if (!BC_PARSE_CAN_EXEC(parse)) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ status = bc_parse_func(parse, program);
+
+ break;
+ }
+
+ case BC_LEX_EOF:
+ {
+ status = BC_STATUS_PARSE_EOF;
+ break;
+ }
+
+ default:
+ {
+ ptr = bc_stack_top(&parse->ctx_stack);
+ status = bc_parse_stmt(parse, *ptr);
+ break;
+ }
+ }
+
+ return status;
+}
+
+void bc_parse_free(BcParse* parse) {
+
+ if (!parse) {
+ return;
+ }
+
+ bc_stack_free(&parse->flag_stack);
+ bc_stack_free(&parse->ctx_stack);
+
+ switch (parse->token.type) {
+
+ case BC_LEX_STRING:
+ case BC_LEX_NAME:
+ case BC_LEX_NUMBER:
+ {
+ if (parse->token.string) {
+ free(parse->token.string);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ // We don't have have to free anything.
+ break;
+ }
+ }
+}
+
+static BcStatus bc_parse_func(BcParse* parse, BcProgram* program) {
+
+ BcLexTokenType type;
+ BcStatus status;
+ BcFunc func;
+ bool comma;
+ uint16_t flags;
+ char* name;
+ bool var;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_NAME) {
+ return BC_STATUS_PARSE_INVALID_FUNC;
+ }
+
+ status = bc_program_func_init(&func, parse->token.string);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_LEFT_PAREN) {
+ return BC_STATUS_PARSE_INVALID_FUNC;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ comma = false;
+
+ type = parse->token.type;
+
+ while (!status && type != BC_LEX_RIGHT_PAREN) {
+
+ if (type != BC_LEX_NAME) {
+ return BC_STATUS_PARSE_INVALID_FUNC;
+ }
+
+ name = parse->token.string;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ break;
+ }
+
+ if (parse->token.type == BC_LEX_LEFT_BRACKET) {
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ break;
+ }
+
+ if (parse->token.type != BC_LEX_RIGHT_BRACKET) {
+ return BC_STATUS_PARSE_INVALID_FUNC;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ break;
+ }
+
+ var = false;
+ }
+ else {
+ var = true;
+ }
+
+ if (parse->token.type == BC_LEX_COMMA) {
+ comma = true;
+ status = bc_lex_next(&parse->lex, &parse->token);
+ }
+ else {
+ comma = false;
+ }
+
+ status = bc_program_func_insertParam(&func, name, var);
+
+ if (status) {
+ return status;
+ }
+
+ type = parse->token.type;
+ }
+
+ if (status) {
+ return status;
+ }
+
+ if (comma) {
+ return BC_STATUS_PARSE_INVALID_FUNC;
+ }
+
+ flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_HEADER;
+ status = bc_stack_push(&parse->flag_stack, &flags);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_stack_push(&parse->ctx_stack, &func.first);
+
+ parse->func = bc_segarray_item(&program->funcs, program->funcs.num - 1);
+
+ if (!parse->func) {
+ return BC_STATUS_PARSE_BUG;
+ }
+
+ return BC_STATUS_SUCCESS;
+}
+
+static BcStatus bc_parse_funcStart(BcParse* parse, BcFunc* func) {
+
+ // TODO: Write this function.
+
+}
+
+static BcStatus bc_parse_semicolonList(BcParse* parse, BcStmtList* list) {
+
+ BcStatus status = BC_STATUS_SUCCESS;
+
+ switch (parse->token.type) {
+
+ case BC_LEX_OP_INC:
+ case BC_LEX_OP_DEC:
+ case BC_LEX_OP_MINUS:
+ case BC_LEX_OP_BOOL_NOT:
+ case BC_LEX_NEWLINE:
+ case BC_LEX_LEFT_PAREN:
+ {
+ status = bc_parse_stmt(parse, list);
+ break;
+ }
+
+ case BC_LEX_SEMICOLON:
+ {
+ status = bc_parse_semicolonListEnd(parse, list);
+ break;
+ }
+
+ case BC_LEX_STRING:
+ case BC_LEX_NAME:
+ case BC_LEX_NUMBER:
+ case BC_LEX_KEY_BREAK:
+ case BC_LEX_KEY_CONTINUE:
+ case BC_LEX_KEY_FOR:
+ case BC_LEX_KEY_HALT:
+ case BC_LEX_KEY_IBASE:
+ case BC_LEX_KEY_IF:
+ case BC_LEX_KEY_LAST:
+ case BC_LEX_KEY_LENGTH:
+ case BC_LEX_KEY_OBASE:
+ case BC_LEX_KEY_PRINT:
+ {
+ status = bc_parse_stmt(parse, list);
+ break;
+ }
+
+ case BC_LEX_KEY_LIMITS:
+ {
+ // TODO: Print limits and get next token.
+ // Limit is a compile-time command.
+ break;
+ }
+
+ case BC_LEX_KEY_QUIT:
+ {
+ // Quit is a compile-time command,
+ // so we just do it.
+ exit(status);
+ break;
+ }
+
+ case BC_LEX_KEY_READ:
+ case BC_LEX_KEY_RETURN:
+ case BC_LEX_KEY_SCALE:
+ case BC_LEX_KEY_SQRT:
+ case BC_LEX_KEY_WHILE:
+ {
+ status = bc_parse_stmt(parse, list);
+ break;
+ }
+
+ case BC_LEX_EOF:
+ {
+ if (parse->ctx_stack.len > 0) {
+ status = BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ status = BC_STATUS_PARSE_INVALID_TOKEN;
+ break;
+ }
+ }
+
+ return status;
+}
+
+static BcStatus bc_parse_semicolonListEnd(BcParse* parse, BcStmtList* list) {
+
+ BcStatus status;
+
+ if (parse->token.type == BC_LEX_SEMICOLON) {
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_parse_semicolonList(parse, list);
+ }
+ else if (parse->token.type == BC_LEX_NEWLINE) {
+ status = BC_STATUS_SUCCESS;
+ }
+ else {
+ status = BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ return status;
+}
+
+static BcStatus bc_parse_stmt(BcParse* parse, BcStmtList* list) {
+
+ BcStatus status;
+ BcStmt stmt;
+ uint16_t* flag_ptr;
+
+ status = BC_STATUS_SUCCESS;
+
+ switch (parse->token.type) {
+
+ case BC_LEX_OP_INC:
+ case BC_LEX_OP_DEC:
+ case BC_LEX_OP_MINUS:
+ case BC_LEX_OP_BOOL_NOT:
+ case BC_LEX_LEFT_PAREN:
+ case BC_LEX_NAME:
+ case BC_LEX_NUMBER:
+ case BC_LEX_KEY_IBASE:
+ case BC_LEX_KEY_LAST:
+ case BC_LEX_KEY_LENGTH:
+ case BC_LEX_KEY_OBASE:
+ case BC_LEX_KEY_READ:
+ case BC_LEX_KEY_SCALE:
+ case BC_LEX_KEY_SQRT:
+ {
+ status = bc_parse_expr(parse, &stmt.data.expr_stack);
+ break;
+ }
+
+ case BC_LEX_NEWLINE:
+ {
+ // Do nothing.
+ break;
+ }
+
+ case BC_LEX_LEFT_BRACE:
+ {
+ stmt.type = BC_STMT_LIST;
+ ++parse->num_braces;
+
+ if (BC_PARSE_HEADER(parse)) {
+
+ flag_ptr = bc_stack_top(&parse->flag_stack);
+ *flag_ptr = *flag_ptr & ~(BC_PARSE_FLAG_HEADER);
+
+ if (BC_PARSE_FUNC_INNER(parse)) {
+ status = bc_parse_funcStart(parse, parse->func);
+ }
+ else if (BC_PARSE_FOR_LOOP(parse)) {
+ status = bc_parse_for(parse, list);
+ }
+ else if (BC_PARSE_WHILE_LOOP(parse)) {
+ status = bc_parse_while(parse, list);
+ }
+ else if (BC_PARSE_IF(parse)) {
+ status = bc_parse_if(parse, list);
+ }
+ }
+ else {
+ status = bc_parse_startBody(parse, &stmt.data.list, 0);
+ }
+
+ break;
+ }
+
+ case BC_LEX_RIGHT_BRACE:
+ {
+ status = bc_parse_rightBrace(parse, list);
+ break;
+ }
+
+ case BC_LEX_STRING:
+ {
+ status = bc_parse_string(parse, list);
+ break;
+ }
+
+ case BC_LEX_KEY_BREAK:
+ case BC_LEX_KEY_CONTINUE:
+ {
+ status = bc_parse_loopExit(parse, list, parse->token.type);
+ break;
+ }
+
+ case BC_LEX_KEY_FOR:
+ {
+ status = bc_parse_for(parse, list);
+ break;
+ }
+
+ case BC_LEX_KEY_HALT:
+ {
+ BcStmt stmt;
+
+ stmt.type = BC_STMT_HALT;
+ bc_program_list_insert(list, &stmt);
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_parse_semicolonListEnd(parse, list);
+
+ break;
+ }
+
+ case BC_LEX_KEY_IF:
+ {
+ status = bc_parse_if(parse, list);
+ break;
+ }
+
+ case BC_LEX_KEY_PRINT:
+ {
+ status = bc_parse_print(parse, list);
+ break;
+ }
+
+ case BC_LEX_KEY_RETURN:
+ {
+ status = bc_parse_return(parse, list);
+ break;
+ }
+
+ case BC_LEX_KEY_WHILE:
+ {
+ status = bc_parse_while(parse, list);
+ break;
+ }
+
+ default:
+ {
+ status = BC_STATUS_PARSE_INVALID_TOKEN;
+ break;
+ }
+ }
+
+ return status;
+}
+
+static BcStatus bc_parse_expr(BcParse* parse, BcStack** exprs) {
+
+ BcStatus status;
+ BcStack ops;
+ BcExpr expr;
+ uint32_t nexprs;
+ uint32_t num_parens;
+ bool paren_first;
+ bool paren_expr;
+ bool rparen;
+ bool done;
+ BcExprType prev;
+ BcLexTokenType type;
+ BcLexTokenType* ptr;
+ BcLexTokenType top;
+ BcStack* stack;
+
+ prev = BC_EXPR_PRINT;
+
+ paren_first = parse->token.type == BC_LEX_LEFT_PAREN;
+ num_parens = paren_first ? 1 : 0;
+
+ stack = malloc(sizeof(BcStack));
+
+ if (!stack) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ status = bc_stack_init(stack, sizeof(BcExpr));
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_stack_init(&ops, sizeof(BcLexTokenType));
+
+ if (status) {
+ return status;
+ }
+
+ nexprs = 0;
+ paren_expr = false;
+ rparen = false;
+ done = false;
+
+ type = parse->token.type;
+
+ while (!status && !done && bc_token_exprs[type]) {
+
+ switch (type) {
+
+ case BC_LEX_OP_INC:
+ case BC_LEX_OP_DEC:
+ {
+ status = bc_parse_incdec(parse, stack, &prev);
+ rparen = false;
+ break;
+ }
+
+ case BC_LEX_OP_MINUS:
+ {
+ status = bc_parse_minus(parse, stack, &ops, &prev,
+ rparen, &nexprs);
+ rparen = false;
+ break;
+ }
+
+ case BC_LEX_OP_POWER:
+ case BC_LEX_OP_MULTIPLY:
+ case BC_LEX_OP_DIVIDE:
+ case BC_LEX_OP_MODULUS:
+ case BC_LEX_OP_PLUS:
+ case BC_LEX_OP_ASSIGN:
+ case BC_LEX_OP_ASSIGN_PLUS:
+ case BC_LEX_OP_ASSIGN_MINUS:
+ case BC_LEX_OP_ASSIGN_MULTIPLY:
+ case BC_LEX_OP_ASSIGN_DIVIDE:
+ case BC_LEX_OP_ASSIGN_MODULUS:
+ case BC_LEX_OP_ASSIGN_POWER:
+ case BC_LEX_OP_REL_EQUAL:
+ case BC_LEX_OP_REL_LESS_EQ:
+ case BC_LEX_OP_REL_GREATER_EQ:
+ case BC_LEX_OP_REL_NOT_EQ:
+ case BC_LEX_OP_REL_LESS:
+ case BC_LEX_OP_REL_GREATER:
+ case BC_LEX_OP_BOOL_NOT:
+ // TODO: Handle these specially.
+ // We may not have to though...
+ case BC_LEX_OP_BOOL_OR:
+ case BC_LEX_OP_BOOL_AND:
+ {
+ prev = BC_PARSE_TOKEN_TO_EXPR(type);
+ status = bc_parse_operator(stack, &ops, type, &nexprs);
+ rparen = false;
+ break;
+ }
+
+ case BC_LEX_LEFT_PAREN:
+ {
+ ++num_parens;
+ paren_expr = false;
+ rparen = false;
+ status = bc_stack_push(&ops, &type);
+ break;
+ }
+
+ case BC_LEX_RIGHT_PAREN:
+ {
+ if (num_parens == 0) {
+ status = nexprs != 1 ? BC_STATUS_PARSE_INVALID_EXPR :
+ BC_STATUS_SUCCESS;
+ done = true;
+ }
+ else if (!paren_expr) {
+ return BC_STATUS_PARSE_INVALID_EXPR;
+ }
+
+ paren_expr = true;
+ rparen = true;
+
+ status = bc_parse_rightParen(stack, &ops, &nexprs);
+
+ break;
+ }
+
+ case BC_LEX_NAME:
+ {
+ paren_expr = true;
+ rparen = false;
+ status = bc_parse_expr_name(parse, stack, &prev);
+ ++nexprs;
+ break;
+ }
+
+ case BC_LEX_NUMBER:
+ {
+ expr.type = BC_EXPR_NUMBER;
+ expr.string = parse->token.string;
+ status = bc_stack_push(stack, &expr);
+
+ paren_expr = true;
+ rparen = false;
+ ++nexprs;
+ prev = BC_EXPR_NUMBER;
+
+ break;
+ }
+
+ case BC_LEX_KEY_IBASE:
+ {
+ expr.type = BC_EXPR_IBASE;
+ status = bc_stack_push(stack, &expr);
+
+ paren_expr = true;
+ rparen = false;
+ ++nexprs;
+ prev = BC_EXPR_IBASE;
+
+ break;
+ }
+
+ case BC_LEX_KEY_LENGTH:
+ {
+ status = bc_parse_builtin(parse, stack, BC_EXPR_LENGTH);
+ paren_expr = true;
+ rparen = false;
+ ++nexprs;
+ prev = BC_EXPR_LENGTH;
+ break;
+ }
+
+ case BC_LEX_KEY_OBASE:
+ {
+ expr.type = BC_EXPR_OBASE;
+ status = bc_stack_push(stack, &expr);
+
+ paren_expr = true;
+ rparen = false;
+ ++nexprs;
+ prev = BC_EXPR_OBASE;
+
+ break;
+ }
+
+ case BC_LEX_KEY_READ:
+ {
+ status = bc_parse_read(parse, stack);
+ paren_expr = true;
+ rparen = false;
+ ++nexprs;
+ prev = BC_EXPR_READ;
+ break;
+ }
+
+ case BC_LEX_KEY_SCALE:
+ {
+ status = bc_parse_scale(parse, stack, &prev);
+ paren_expr = true;
+ rparen = false;
+ ++nexprs;
+ prev = BC_EXPR_SCALE;
+ break;
+ }
+
+ case BC_LEX_KEY_SQRT:
+ {
+ status = bc_parse_builtin(parse, stack, BC_EXPR_SQRT);
+ paren_expr = true;
+ rparen = false;
+ ++nexprs;
+ prev = BC_EXPR_SQRT;
+ break;
+ }
+
+ default:
+ {
+ status = BC_STATUS_PARSE_INVALID_TOKEN;
+ break;
+ }
+ }
+
+ type = parse->token.type;
+ }
+
+ if (status) {
+ return status;
+ }
+
+ while (!status && ops.len > 0) {
+
+ ptr = bc_stack_top(&ops);
+ top = *ptr;
+
+ if (top == BC_LEX_LEFT_PAREN || top == BC_LEX_RIGHT_PAREN) {
+ return BC_STATUS_PARSE_INVALID_EXPR;
+ }
+
+ expr.type = BC_PARSE_TOKEN_TO_EXPR(top);
+
+ status = bc_stack_push(stack, &expr);
+
+ if (status) {
+ return status;
+ }
+
+ nexprs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_OP_NEGATE ? 1 : 0;
+
+ status = bc_stack_pop(&ops);
+ }
+
+ if (nexprs != 1) {
+ return BC_STATUS_PARSE_INVALID_EXPR;
+ }
+
+ expr.type = ((BcExpr*) bc_stack_top(stack))->type;
+
+ if (expr.type < BC_EXPR_ASSIGN ||
+ expr.type > BC_EXPR_ASSIGN_POWER ||
+ paren_first)
+ {
+ expr.type = BC_EXPR_PRINT;
+ status = bc_stack_push(stack, &expr);
+ }
+
+ *exprs = stack;
+
+ return status;
+}
+
+static BcStatus bc_parse_operator(BcStack* exs, BcStack* ops, BcLexTokenType t,
+ uint32_t* num_exprs)
+{
+ BcExpr expr;
+ BcStatus status;
+ BcLexTokenType top;
+ BcLexTokenType* ptr;
+ uint8_t lp;
+ uint8_t rp;
+ bool rleft;
+
+ rp = bc_ops[t].prec;
+ rleft = bc_ops[t].left;
+
+ if (ops->len != 0) {
+
+ ptr = bc_stack_top(ops);
+ top = *ptr;
+ lp = bc_ops[top].prec;
+
+ while (top != BC_LEX_LEFT_PAREN && (lp < rp || (lp == rp && rleft))) {
+
+ expr.type = BC_PARSE_TOKEN_TO_EXPR(top);
+
+ status = bc_stack_push(exs, &expr);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_stack_pop(ops);
+
+ if (status) {
+ return status;
+ }
+
+ *num_exprs -= top != BC_LEX_OP_BOOL_NOT &&
+ top != BC_LEX_OP_NEGATE ? 1 : 0;
+
+ if (ops->len == 0) {
+ break;
+ }
+
+ ptr = bc_stack_top(ops);
+ top = *ptr;
+ lp = bc_ops[top].prec;
+ }
+ }
+
+ return bc_stack_push(ops, &t);
+}
+
+static BcStatus bc_parse_rightParen(BcStack* exs, BcStack* ops, uint32_t* nexs)
+{
+ BcStatus status;
+ BcExpr expr;
+ BcLexTokenType top;
+ BcLexTokenType* ptr;
+
+ if (ops->len == 0) {
+ return BC_STATUS_PARSE_INVALID_EXPR;
+ }
+
+ ptr = bc_stack_top(ops);
+ top = *ptr;
+
+ while (top != BC_LEX_LEFT_PAREN) {
+
+ expr.type = BC_PARSE_TOKEN_TO_EXPR(top);
+
+ status = bc_stack_push(exs, &expr);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_stack_pop(ops);
+
+ if (status) {
+ return status;
+ }
+
+ *nexs -= top != BC_LEX_OP_BOOL_NOT &&
+ top != BC_LEX_OP_NEGATE ? 1 : 0;
+
+ if (ops->len == 0) {
+ return BC_STATUS_PARSE_INVALID_EXPR;
+ }
+
+ ptr = bc_stack_top(ops);
+ top = *ptr;
+ }
+
+ return bc_stack_pop(ops);
+}
+
+static BcStatus bc_parse_expr_name(BcParse* parse, BcStack* exprs,
+ BcExprType* type)
+{
+ BcStatus status;
+ BcStack* stack;
+ BcExpr expr;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type == BC_LEX_LEFT_BRACKET) {
+
+ expr.type = BC_EXPR_ARRAY_ELEM;
+ *type = BC_EXPR_ARRAY_ELEM;
+ expr.string = parse->token.string;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_parse_expr(parse, &stack);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_RIGHT_BRACKET) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+ }
+ else if (parse->token.type == BC_LEX_LEFT_PAREN) {
+ *type = BC_EXPR_FUNC_CALL;
+ status = bc_parse_call(parse, &expr);
+ }
+ else {
+ expr.type = BC_EXPR_VAR;
+ *type = BC_EXPR_VAR;
+ expr.string = parse->token.string;
+ }
+
+ return bc_stack_push(exprs, &expr);
+}
+
+static BcStatus bc_parse_call(BcParse* parse, BcExpr* expr) {
+
+ BcStatus status;
+
+ expr->type = BC_EXPR_FUNC_CALL;
+ expr->call->name = parse->token.string;
+
+ status = bc_parse_params(parse, expr);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_RIGHT_PAREN) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ return bc_lex_next(&parse->lex, &parse->token);
+}
+
+static BcStatus bc_parse_params(BcParse* parse, BcExpr* expr) {
+
+ BcStack* exprs;
+ BcStatus status;
+
+ status = bc_segarray_init(&expr->call->params, sizeof(BcStack),
+ bc_stack_free, NULL);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type == BC_LEX_RIGHT_PAREN) {
+ return BC_STATUS_SUCCESS;
+ }
+
+ do {
+
+ status = bc_parse_expr(parse, &exprs);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_segarray_add(&expr->call->params, &exprs);
+
+ } while (!status && parse->token.type == BC_LEX_COMMA);
+
+ return status;
+}
+
+static BcStatus bc_parse_read(BcParse* parse, BcStack* exprs) {
+
+ BcStatus status;
+ BcExpr expr;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_LEFT_PAREN) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_LEFT_PAREN) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ expr.type = BC_EXPR_READ;
+
+ status = bc_stack_push(exprs, &expr);
+
+ if (status) {
+ return status;
+ }
+
+ return bc_lex_next(&parse->lex, &parse->token);
+}
+
+static BcStatus bc_parse_builtin(BcParse* parse, BcStack* exs, BcExprType type)
+{
+ BcStatus status;
+ BcExpr expr;
+
+ expr.type = type;
+
+ expr.expr_stack = malloc(sizeof(BcStack));
+
+ if (!expr.expr_stack) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ status = bc_stack_init(expr.expr_stack, sizeof(BcExpr));
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_LEFT_PAREN) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_parse_expr(parse, &expr.expr_stack);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_RIGHT_PAREN) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ status = bc_stack_push(exs, &expr);
+
+ if (status) {
+ return status;
+ }
+
+ return bc_lex_next(&parse->lex, &parse->token);
+}
+
+static BcStatus bc_parse_scale(BcParse* parse, BcStack* exs, BcExprType* type) {
+
+ BcStatus status;
+ BcExpr expr;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_LEFT_PAREN) {
+
+ expr.type = BC_EXPR_SCALE;
+ *type = BC_EXPR_SCALE;
+
+ return bc_stack_push(exs, &expr);
+ }
+
+ expr.type = BC_EXPR_SCALE_FUNC;
+ *type = BC_EXPR_SCALE_FUNC;
+
+ expr.expr_stack = malloc(sizeof(BcStack));
+
+ if (!expr.expr_stack) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ status = bc_stack_init(expr.expr_stack, sizeof(BcExpr));
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_parse_expr(parse, &expr.expr_stack);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_RIGHT_PAREN) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ status = bc_stack_push(exs, &expr);
+
+ if (status) {
+ return status;
+ }
+
+ return bc_lex_next(&parse->lex, &parse->token);
+}
+
+static BcStatus bc_parse_incdec(BcParse* parse, BcStack* exs, BcExprType* prev)
+{
+ BcStatus status;
+ BcLexTokenType type;
+ BcExpr expr;
+ BcExprType etype;
+
+ etype = *prev;
+
+ if (etype == BC_EXPR_VAR || etype == BC_EXPR_ARRAY_ELEM ||
+ etype == BC_EXPR_SCALE || etype == BC_EXPR_LAST ||
+ etype == BC_EXPR_IBASE || etype == BC_EXPR_OBASE)
+ {
+ expr.type = parse->token.type == BC_LEX_OP_INC ?
+ BC_EXPR_INC_POST : BC_EXPR_DEC_POST;
+ *prev = expr.type;
+
+ status = bc_stack_push(exs, &expr);
+ }
+ else {
+
+ expr.type = parse->token.type == BC_LEX_OP_INC ?
+ BC_EXPR_INC_PRE : BC_EXPR_DEC_PRE;
+ *prev = expr.type;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ type = parse->token.type;
+
+ if (type != BC_LEX_NAME && type != BC_LEX_KEY_SCALE &&
+ type != BC_LEX_KEY_LAST && type != BC_LEX_KEY_IBASE &&
+ type != BC_LEX_KEY_OBASE)
+ {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ status = bc_stack_push(exs, &expr);
+ }
+
+ if (status) {
+ return status;
+ }
+
+ return bc_lex_next(&parse->lex, &parse->token);
+}
+
+static BcStatus bc_parse_minus(BcParse* parse, BcStack* exs, BcStack* ops,
+ BcExprType* prev, bool rparen, uint32_t* nexprs)
+{
+ BcStatus status;
+ BcLexTokenType type;
+ BcExprType etype;
+
+ etype = *prev;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ type = parse->token.type;
+
+ if (type != BC_LEX_NAME && type != BC_LEX_NUMBER &&
+ type != BC_LEX_KEY_SCALE && type != BC_LEX_KEY_LAST &&
+ type != BC_LEX_KEY_IBASE && type != BC_LEX_KEY_OBASE &&
+ type != BC_LEX_LEFT_PAREN)
+ {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ type = rparen || etype == BC_EXPR_NUMBER ||
+ etype == BC_EXPR_VAR || etype == BC_EXPR_ARRAY_ELEM ||
+ etype == BC_EXPR_SCALE || etype == BC_EXPR_LAST ||
+ etype == BC_EXPR_IBASE || etype == BC_EXPR_OBASE ?
+ BC_LEX_OP_MINUS : BC_LEX_OP_NEGATE;
+
+ *prev = BC_PARSE_TOKEN_TO_EXPR(type);
+
+ if (type == BC_LEX_OP_MINUS) {
+ status = bc_parse_operator(exs, ops, type, nexprs);
+ }
+ else {
+
+ // We can just push onto the op stack because this is the largest
+ // precedence operator that gets pushed. Inc/dec does not.
+ status = bc_stack_push(ops, &type);
+ }
+
+ return status;
+}
+
+static BcStatus bc_parse_string(BcParse* parse, BcStmtList* list) {
+
+ BcStatus status;
+ BcStmt stmt;
+
+ stmt.type = BC_STMT_STRING;
+ stmt.data.string = parse->token.string;
+
+ status = bc_program_list_insert(list, &stmt);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ return bc_parse_semicolonListEnd(parse, list);
+}
+
+static BcStatus bc_parse_return(BcParse* parse, BcStmtList* list) {
+
+ BcStatus status;
+ BcStmt stmt;
+ BcExpr expr;
+
+ if (BC_PARSE_FUNC(parse)) {
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ stmt.type = BC_STMT_RETURN;
+
+ if (parse->token.type == BC_LEX_NEWLINE ||
+ parse->token.type == BC_LEX_SEMICOLON)
+ {
+ stmt.data.expr_stack = malloc(sizeof(BcStack));
+
+ if (!stmt.data.expr_stack) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ status = bc_stack_init(stmt.data.expr_stack, sizeof(BcExpr));
+
+ if (status) {
+ return status;
+ }
+
+ expr.type = BC_EXPR_NUMBER;
+ expr.string = NULL;
+
+ status = bc_stack_push(stmt.data.expr_stack, &expr);
+
+ if (status) {
+ return status;
+ }
+
+ bc_program_list_insert(list, &stmt);
+ }
+ else {
+ status = bc_parse_expr(parse, &stmt.data.expr_stack);
+ }
+ }
+ else {
+ status = BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ return status;
+}
+
+static BcStatus bc_parse_print(BcParse* parse, BcStmtList* list) {
+
+ BcStatus status;
+ BcLexTokenType type;
+ BcStack* exprs;
+ BcExpr expr;
+ BcStmt stmt;
+ bool comma;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ type = parse->token.type;
+
+ if (type == BC_LEX_SEMICOLON || type == BC_LEX_NEWLINE) {
+ return BC_STATUS_PARSE_INVALID_PRINT;
+ }
+
+ comma = false;
+
+ while (!status && type != BC_LEX_SEMICOLON && type != BC_LEX_NEWLINE) {
+
+ if (type == BC_LEX_STRING) {
+
+ status = bc_parse_string(parse, list);
+
+ if (status) {
+ return status;
+ }
+
+ stmt.type = BC_STMT_STRING;
+ stmt.data.string = parse->token.string;
+ }
+ else {
+
+ status = bc_parse_expr(parse, &exprs);
+
+ if (status) {
+ return status;
+ }
+
+ expr.type = BC_EXPR_PRINT;
+ status = bc_stack_push(exprs, &expr);
+
+ stmt.type = BC_STMT_EXPR;
+
+ stmt.data.expr_stack = exprs;
+ }
+
+ status = bc_program_list_insert(list, &stmt);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type == BC_LEX_COMMA) {
+ comma = true;
+ status = bc_lex_next(&parse->lex, &parse->token);
+ }
+ else {
+ comma = false;
+ }
+
+ type = parse->token.type;
+ }
+
+ if (status) {
+ return status;
+ }
+
+ if (comma) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ return bc_lex_next(&parse->lex, &parse->token);
+}
+
+static BcStatus bc_parse_if(BcParse* parse, BcStmtList* list) {
+
+ // TODO: Write this function.
+
+ BcStatus status;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+}
+
+static BcStatus bc_parse_while(BcParse* parse, BcStmtList* list) {
+
+ // TODO: Write this function.
+
+}
+
+static BcStatus bc_parse_for(BcParse* parse, BcStmtList* list) {
+
+ // TODO: Write this function.
+
+}
+
+static BcStatus bc_parse_loop(BcParse* parse, BcStmtList* list) {
+
+ // TODO: Write this function.
+
+}
+
+static BcStatus bc_parse_startBody(BcParse* parse, BcStmtList** new_list,
+ uint16_t flags)
+{
+ BcStatus status;
+ BcStmtList* list;
+ uint16_t* flag_ptr;
+
+ list = bc_program_list_create();
+
+ if (!list) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ status = bc_stack_push(&parse->ctx_stack, &list);
+
+ if (status) {
+ return status;
+ }
+
+ flag_ptr = bc_stack_top(&parse->flag_stack);
+
+ if (!flag_ptr) {
+ return BC_STATUS_PARSE_BUG;
+ }
+
+ flags |= (*flag_ptr & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FOR_LOOP |
+ BC_PARSE_FLAG_WHILE_LOOP));
+
+ status = bc_stack_push(&parse->flag_stack, &flags);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type == BC_LEX_LEFT_BRACE) {
+
+ ++parse->num_braces;
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_parse_stmt(parse, list);
+ }
+ else {
+ status = bc_parse_stmt(parse, list);
+ }
+
+ if (status) {
+ bc_program_list_free(list);
+ }
+ else {
+ *new_list = list;
+ }
+
+ return status;
+}
+
+static BcStatus bc_parse_loopExit(BcParse* parse, BcStmtList* list,
+ BcLexTokenType type)
+{
+ BcStatus status;
+ BcStmt stmt;
+
+ if (!BC_PARSE_FOR_LOOP(parse) && !BC_PARSE_WHILE_LOOP(parse)) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ stmt.type = type == BC_LEX_KEY_BREAK ? BC_STMT_BREAK : BC_STMT_CONTINUE;
+
+ status = bc_program_list_insert(list, &stmt);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type != BC_LEX_SEMICOLON &&
+ parse->token.type != BC_LEX_NEWLINE)
+ {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ return bc_lex_next(&parse->lex, &parse->token);
+}
+
+static BcStatus bc_parse_rightBrace(BcParse* parse, BcStmtList* list) {
+
+ BcStatus status;
+ BcStmtList** list_ptr;
+
+ if (parse->ctx_stack.len <= 1 || parse->num_braces == 0) {
+ return BC_STATUS_PARSE_INVALID_TOKEN;
+ }
+
+ if (parse->ctx_stack.len != parse->flag_stack.len) {
+ return BC_STATUS_PARSE_BUG;
+ }
+
+ if (BC_PARSE_IF(parse)) {
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ if (parse->token.type == BC_LEX_KEY_ELSE) {
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+
+ list_ptr = &parse->partial.data.if_stmt->else_list;
+
+ status = bc_parse_startBody(parse, list_ptr, BC_PARSE_FLAG_ELSE);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_lex_next(&parse->lex, &parse->token);
+
+ if (status) {
+ return status;
+ }
+ }
+ }
+
+ status = bc_stack_pop(&parse->flag_stack);
+
+ if (status) {
+ return status;
+ }
+
+ return bc_stack_pop(&parse->ctx_stack);
+}
diff --git a/bc/src/program.c b/bc/src/program.c
new file mode 100644
index 0000000..bb282a2
--- /dev/null
+++ b/bc/src/program.c
@@ -0,0 +1,471 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <arbprec/arbprec.h>
+
+#include "program.h"
+
+static BcStatus bc_program_list_expand(BcStmtList* list);
+
+static int bc_program_func_cmp(void* func1, void* func2);
+static void bc_program_func_free(void* func);
+static int bc_program_var_cmp(void* var1, void* var2);
+static void bc_program_var_free(void* var);
+static int bc_program_array_cmp(void* array1, void* array2);
+static void bc_program_array_free(void* array);
+static void bc_program_stmt_free(BcStmt* stmt);
+
+static void bc_program_num_init(fxdpnt* num);
+
+BcStatus bc_program_init(BcProgram* p, const char* file) {
+
+ BcStatus st;
+
+ if (p == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ p->file = file;
+
+ p->first = bc_program_list_create();
+
+ if (!p->first) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ p->cur = p->first;
+
+ st = bc_segarray_init(&p->funcs, sizeof(BcFunc), bc_program_func_free,
+ bc_program_func_cmp);
+
+ if (st) {
+ goto func_err;
+ }
+
+ st = bc_segarray_init(&p->vars, sizeof(BcVar), bc_program_var_free,
+ bc_program_var_cmp);
+
+ if (st) {
+ goto var_err;
+ }
+
+ st = bc_segarray_init(&p->arrays, sizeof(BcArray), bc_program_array_free,
+ bc_program_array_cmp);
+
+ if (st) {
+ goto array_err;
+ }
+
+ return st;
+
+array_err:
+
+ bc_segarray_free(&p->vars);
+
+var_err:
+
+ bc_segarray_free(&p->funcs);
+
+func_err:
+
+ bc_program_list_free(p->first);
+ p->first = NULL;
+ p->cur = NULL;
+
+ return st;
+}
+
+BcStatus bc_program_func_add(BcProgram* p, BcFunc* func) {
+ assert(p && func);
+ return bc_segarray_add(&p->funcs, func);
+}
+
+BcStatus bc_program_var_add(BcProgram* p, BcVar* var) {
+ assert(p && var);
+ return bc_segarray_add(&p->vars, var);
+}
+
+BcStatus bc_program_array_add(BcProgram* p, BcArray* array) {
+ assert(p && array);
+ return bc_segarray_add(&p->arrays, array);
+}
+
+BcStatus bc_program_exec(BcProgram* p) {
+ // TODO: Write this function.
+}
+
+void bc_program_free(BcProgram* p) {
+
+ if (p == NULL) {
+ return;
+ }
+
+ BcStmtList* temp;
+ BcStmtList* cur = p->first;
+
+ while (cur != NULL) {
+ temp = cur->next;
+ bc_program_list_free(cur);
+ cur = temp;
+ }
+
+ p->cur = NULL;
+ p->first = NULL;
+
+ uint32_t num = p->funcs.num;
+ for (uint32_t i = 0; i < num; ++i) {
+ bc_program_func_free(bc_segarray_item(&p->funcs, i));
+ }
+
+ num = p->vars.num;
+ for (uint32_t i = 0; i < num; ++i) {
+ bc_program_var_free(bc_segarray_item(&p->vars, i));
+ }
+
+ num = p->arrays.num;
+ for (uint32_t i = 0; i < num; ++i) {
+ bc_program_array_free(bc_segarray_item(&p->arrays, i));
+ }
+}
+
+BcStmtList* bc_program_list_create() {
+
+ BcStmtList* list;
+
+ list = malloc(sizeof(BcStmtList));
+
+ if (list == NULL) {
+ return NULL;
+ }
+
+ list->next = NULL;
+ list->num_stmts = 0;
+
+ return list;
+}
+
+BcStatus bc_program_list_insert(BcStmtList* list, BcStmt* stmt) {
+
+ if (list == NULL || stmt == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ // Find the end list.
+ while (list->num_stmts == BC_PROGRAM_MAX_STMTS && list->next) {
+ list = list->next;
+ }
+
+ if (list->num_stmts == BC_PROGRAM_MAX_STMTS) {
+
+ BcStatus status = bc_program_list_expand(list);
+
+ if (status != BC_STATUS_SUCCESS) {
+ return status;
+ }
+
+ list = list->next;
+ }
+
+ memcpy(list->stmts + list->num_stmts, stmt, sizeof(BcStmt));
+ ++list->num_stmts;
+
+ return BC_STATUS_SUCCESS;
+}
+
+void bc_program_list_free(BcStmtList* list) {
+
+ BcStmtList* temp;
+ uint32_t num;
+ BcStmt* stmts;
+
+ if (list == NULL) {
+ return;
+ }
+
+ do {
+
+ temp = list->next;
+
+ num = list->num_stmts;
+ stmts = list->stmts;
+
+ for (uint32_t i = 0; i < num; ++i) {
+ bc_program_stmt_free(stmts + i);
+ }
+
+ free(list);
+
+ list = temp;
+
+ } while (list);
+}
+
+static BcStatus bc_program_list_expand(BcStmtList* list) {
+
+ if (list == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ BcStmtList* next = bc_program_list_create();
+
+ if (next == NULL) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ list->next = next;
+
+ return BC_STATUS_SUCCESS;
+}
+
+BcStatus bc_program_func_init(BcFunc* func, char* name) {
+
+ BcStatus status;
+
+ status = BC_STATUS_SUCCESS;
+
+ if (!func || !name) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ func->num_params = 0;
+ func->num_autos = 0;
+
+ func->first = bc_program_list_create();
+
+ if (!func->first) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ func->name = name;
+ func->cur = func->first;
+
+ func->param_cap = BC_PROGRAM_DEF_SIZE;
+ func->params = malloc(sizeof(BcAuto) * BC_PROGRAM_DEF_SIZE);
+
+ if (!func->params) {
+ goto param_err;
+ }
+
+ func->auto_cap = BC_PROGRAM_DEF_SIZE;
+ func->autos = malloc(sizeof(BcAuto) * BC_PROGRAM_DEF_SIZE);
+
+ if (!func->autos) {
+ func->auto_cap = 0;
+ goto auto_err;
+ }
+
+ return BC_STATUS_SUCCESS;
+
+auto_err:
+
+ free(func->params);
+ func->param_cap = 0;
+
+param_err:
+
+ bc_program_list_free(func->first);
+ func->first = func->cur = NULL;
+
+ return status;
+}
+
+BcStatus bc_program_func_insertParam(BcFunc* func, char* name, bool var) {
+
+ BcAuto* params;
+ size_t new_cap;
+
+ if (!func || !name) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ if (func->num_params == func->param_cap) {
+
+ new_cap = sizeof(BcAuto) * (func->param_cap + BC_PROGRAM_DEF_SIZE);
+ params = realloc(func->params, new_cap);
+
+ if (!params) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ func->param_cap = new_cap;
+ }
+
+ func->params[func->num_params].name = name;
+ func->params[func->num_params].var = var;
+
+ ++func->num_params;
+
+ return BC_STATUS_SUCCESS;
+}
+
+BcStatus bc_program_func_insertAuto(BcFunc* func, char* name, bool var) {
+
+ BcAuto* autos;
+ size_t new_cap;
+
+ if (!func || !name) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ if (func->num_autos == func->auto_cap) {
+
+ new_cap = sizeof(BcAuto) * (func->auto_cap + BC_PROGRAM_DEF_SIZE);
+ autos = realloc(func->autos, new_cap);
+
+ if (!autos) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ func->auto_cap = new_cap;
+ }
+
+ func->autos[func->num_autos].name = name;
+ func->autos[func->num_autos].var = var;
+
+ ++func->num_autos;
+
+ return BC_STATUS_SUCCESS;
+}
+
+static int bc_program_func_cmp(void* func1, void* func2) {
+
+ assert(func1 && func2);
+
+ BcFunc* f1 = (BcFunc*) func1;
+ char* f2name = (char*) func2;
+
+ return strcmp(f1->name, f2name);
+}
+
+static void bc_program_func_free(void* func) {
+
+ BcFunc* f;
+
+ f = (BcFunc*) func;
+
+ if (f == NULL) {
+ return;
+ }
+
+ free(f->name);
+
+ bc_program_list_free(f->first);
+
+ f->name = NULL;
+ f->first = NULL;
+ f->cur = NULL;
+
+ free(f->params);
+ free(f->autos);
+
+ f->params = NULL;
+ f->num_params = 0;
+ f->param_cap = 0;
+ f->autos = NULL;
+ f->num_autos = 0;
+ f->auto_cap = 0;
+}
+
+BcStatus bc_program_var_init(BcVar* var, char* name) {
+
+ if (!var || !name) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ var->name = name;
+
+ var->data = arb_alloc(16);
+
+ return BC_STATUS_SUCCESS;
+}
+
+static int bc_program_var_cmp(void* var1, void* var2) {
+
+ assert(var1 && var2);
+
+ BcFunc* v1 = (BcFunc*) var1;
+ char* v2name = (char*) var2;
+
+ return strcmp(v1->name, v2name);
+}
+
+static void bc_program_var_free(void* var) {
+
+ BcVar* v;
+
+ v = (BcVar*) var;
+
+ if (v == NULL) {
+ return;
+ }
+
+ free(v->name);
+ arb_free(v->data);
+
+ v->name = NULL;
+ v->data = NULL;
+}
+
+BcStatus bc_program_array_init(BcArray* array, char* name) {
+
+ if (!array || !name) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ array->name = name;
+
+ return bc_segarray_init(&array->array, sizeof(fxdpnt),
+ (BcSegArrayFreeFunc) arb_free, NULL);
+}
+
+static int bc_program_array_cmp(void* array1, void* array2) {
+
+ assert(array1 && array2);
+
+ BcFunc* a1 = (BcFunc*) array1;
+ char* a2name = (char*) array2;
+
+ return strcmp(a1->name, a2name);
+}
+
+static void bc_program_array_free(void* array) {
+
+ BcArray* a;
+
+ a = (BcArray*) array;
+
+ if (a == NULL) {
+ return;
+ }
+
+ free(a->name);
+ a->name = NULL;
+
+ bc_segarray_free(&a->array);
+}
+
+BcStatus bc_program_stmt_init(BcStmt* stmt) {
+ // TODO: Write this function.
+}
+
+static void bc_program_stmt_free(BcStmt* stmt) {
+ // TODO: Write this function.
+}
+
+static void bc_program_num_init(fxdpnt* num) {
+
+ num->number = arb_calloc(1, sizeof(ARBT) * BC_PROGRAM_DEF_SIZE);
+ num->sign = '+';
+
+ // FIXME: this should likely be "len"
+ // Look at arb_alloc().
+ num->lp = 0;
+
+ num->rp = 0;
+ num->allocated = BC_PROGRAM_DEF_SIZE;
+ //num->len = len;
+ num->len = 0;
+ num->chunk = 4;
+}
diff --git a/bc/src/segarray.c b/bc/src/segarray.c
new file mode 100644
index 0000000..428a3bd
--- /dev/null
+++ b/bc/src/segarray.c
@@ -0,0 +1,275 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "segarray.h"
+#include "program.h"
+
+static BcStatus bc_segarray_addUnsorted(BcSegArray* sa, void* data) ;
+static BcStatus bc_segarray_addSorted(BcSegArray* sa, void* data);
+static BcStatus bc_segarray_addArray(BcSegArray* sa, uint32_t idx);
+static uint32_t bc_segarray_findIndex(BcSegArray* sa, void* data);
+static inline void bc_segarray_moveLast(BcSegArray* sa, uint32_t end1);
+static void bc_segarray_move(BcSegArray* sa, uint32_t idx1,
+ uint32_t start, uint32_t num_elems);
+
+BcStatus bc_segarray_init(BcSegArray* sa, size_t esize, BcSegArrayFreeFunc sfree, BcSegArrayCmpFunc cmp) {
+
+ if (sa == NULL || esize == 0) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ sa->esize = esize;
+ sa->cmp = cmp;
+ sa->sfree = sfree;
+ sa->num = 0;
+
+ sa->ptrs = malloc(sizeof(uint8_t*) * BC_SEGARRAY_NUM_ARRAYS);
+ if (!sa->ptrs) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ sa->num_ptrs = 0;
+ sa->ptr_cap = BC_SEGARRAY_NUM_ARRAYS;
+
+ return bc_segarray_addArray(sa, 0);
+}
+
+BcStatus bc_segarray_add(BcSegArray* sa, void* data) {
+
+ BcStatus status;
+
+ if (sa == NULL || data == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ if (sa->cmp) {
+ status = bc_segarray_addSorted(sa, data);
+ }
+ else {
+ status = bc_segarray_addUnsorted(sa, data);
+ }
+
+ return status;
+}
+
+void* bc_segarray_item(BcSegArray* sa, uint32_t idx) {
+ return bc_segarray_item2(sa, BC_SEGARRAY_IDX1(idx), BC_SEGARRAY_IDX2(idx));
+}
+
+void* bc_segarray_item2(BcSegArray* sa, uint32_t idx1, uint32_t idx2) {
+
+ uint32_t num = sa->num;
+ uint32_t num1 = BC_SEGARRAY_IDX1(num);
+ uint32_t num2 = BC_SEGARRAY_IDX2(num);
+ if (sa == NULL || idx1 > num1 || (idx1 == num1 && idx2 >= num2)) {
+ return NULL;
+ }
+
+ uint8_t* ptr = sa->ptrs[idx1];
+
+ if (ptr == NULL) {
+ return NULL;
+ }
+
+ return ptr + sa->esize * idx2;
+}
+
+uint32_t bc_segarray_find(BcSegArray* sa, void* data) {
+
+ if (!sa) {
+ return (uint32_t) -1;
+ }
+
+ uint32_t idx = bc_segarray_findIndex(sa, data);
+
+ if (!sa->cmp(bc_segarray_item(sa, idx), data)) {
+ return idx;
+ }
+
+ return (uint32_t) -1;
+}
+
+void bc_segarray_free(BcSegArray* sa) {
+
+ BcSegArrayFreeFunc sfree;
+ uint32_t num;
+
+ if (sa == NULL) {
+ return;
+ }
+
+ sfree = sa->sfree;
+
+ if (sfree) {
+
+ num = sa->num;
+ for (uint32_t i = 0; i < num; ++i) {
+ sfree(bc_segarray_item(sa, i));
+ }
+ }
+
+ num = BC_SEGARRAY_IDX1(sa->num - 1);
+ for (uint32_t i = num - 1; i < num; --i) {
+ free(sa->ptrs[i]);
+ }
+
+ free(sa->ptrs);
+ sa->ptrs = NULL;
+
+ sa->esize = 0;
+ sa->num = 0;
+ sa->num_ptrs = 0;
+ sa->cmp = NULL;
+ sa->sfree = NULL;
+}
+
+static BcStatus bc_segarray_addUnsorted(BcSegArray* sa, void* data) {
+
+ BcStatus status;
+
+ uint32_t end = sa->num;
+ uint32_t end1 = BC_SEGARRAY_IDX1(end);
+ uint32_t end2 = BC_SEGARRAY_IDX2(end);
+
+ if (end2 == 0 && end1 > 0) {
+
+ status = bc_segarray_addArray(sa, end1);
+
+ if (status != BC_STATUS_SUCCESS) {
+ return status;
+ }
+ }
+
+ uint8_t* ptr = sa->ptrs[end1];
+ memcpy(ptr + sa->esize * end2, data, sa->esize);
+
+ ++sa->num;
+
+ return BC_STATUS_SUCCESS;
+}
+
+static BcStatus bc_segarray_addSorted(BcSegArray* sa, void* data) {
+
+ BcStatus status;
+
+ uint32_t end = sa->num;
+ uint32_t end1 = BC_SEGARRAY_IDX1(end);
+ uint32_t end2 = BC_SEGARRAY_IDX2(end);
+
+ uint32_t idx = bc_segarray_findIndex(sa, data);
+ idx = idx == (uint32_t) -1 ? end : idx;
+ uint32_t idx1 = BC_SEGARRAY_IDX1(idx);
+ uint32_t idx2 = BC_SEGARRAY_IDX2(idx);
+
+ if (end2 == 0 && end1 > 0) {
+
+ status = bc_segarray_addArray(sa, end1);
+
+ if (status != BC_STATUS_SUCCESS) {
+ return status;
+ }
+
+ if ((end1 != 0 && end2 == 0) && idx != end) {
+ bc_segarray_moveLast(sa, end1);
+ }
+ }
+
+ if (end1 > 0) {
+
+ uint32_t move_idx = end2 == 0 ? end1 - 1 : end1;
+ while (move_idx > idx1) {
+ bc_segarray_move(sa, move_idx, 0, BC_SEGARRAY_SEG_LAST);
+ bc_segarray_moveLast(sa, move_idx);
+ --move_idx;
+ }
+ }
+
+ if (idx2 != BC_SEGARRAY_SEG_LAST) {
+ bc_segarray_move(sa, idx1, idx2, BC_SEGARRAY_SEG_LAST - idx2);
+ }
+
+ uint8_t* ptr = sa->ptrs[idx1];
+ memcpy(ptr + sa->esize * idx2, data, sa->esize);
+
+ ++sa->num;
+
+ return BC_STATUS_SUCCESS;
+}
+
+static BcStatus bc_segarray_addArray(BcSegArray* sa, uint32_t idx) {
+
+ if (sa->num_ptrs == sa->ptr_cap) {
+
+ uint32_t new_cap = sa->ptr_cap + BC_SEGARRAY_NUM_ARRAYS;
+ uint8_t** temp = realloc(sa->ptrs, sizeof(uint8_t*) * new_cap);
+
+ if (!temp) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ sa->ptrs = temp;
+ sa->ptr_cap = new_cap;
+ }
+
+ uint8_t* ptr = malloc(sa->esize * BC_SEGARRAY_SEG_SIZE);
+
+ if (ptr == NULL) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ sa->ptrs[idx] = ptr;
+ ++sa->num_ptrs;
+
+ return BC_STATUS_SUCCESS;
+}
+
+static uint32_t bc_segarray_findIndex(BcSegArray* sa, void* data) {
+
+ BcSegArrayCmpFunc cmp = sa->cmp;
+
+ uint32_t low = 0;
+ uint32_t high = sa->num;
+
+ while (low < high) {
+
+ uint32_t mid = (low + high) / 2;
+
+ uint8_t* ptr = bc_segarray_item(sa, mid);
+
+ if (cmp(ptr, data) > 0) {
+ high = mid;
+ }
+ else {
+ low = mid + 1;
+ }
+ }
+
+ return low;
+}
+
+static void bc_segarray_moveLast(BcSegArray* sa, uint32_t end1) {
+ uint8_t* dest = sa->ptrs[end1];
+ uint8_t* src = sa->ptrs[end1 - 1] + sa->esize * BC_SEGARRAY_SEG_LAST;
+ memcpy(dest, src, sa->esize);
+}
+
+static void bc_segarray_move(BcSegArray* sa, uint32_t idx1, uint32_t start, uint32_t num_elems) {
+
+ assert(num_elems < BC_SEGARRAY_SEG_SIZE &&
+ start + num_elems < BC_SEGARRAY_SEG_SIZE);
+
+ uint8_t* ptr = sa->ptrs[idx1];
+
+ size_t esize = sa->esize;
+
+ size_t move_size = esize * num_elems;
+
+ uint8_t* src = ptr + esize * start;
+ uint8_t* dest = src + esize;
+
+ memmove(dest, src, move_size);
+}
diff --git a/bc/src/stack.c b/bc/src/stack.c
new file mode 100644
index 0000000..8d6b568
--- /dev/null
+++ b/bc/src/stack.c
@@ -0,0 +1,132 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "stack.h"
+
+static BcStatus bc_stack_expand(BcStack* stack);
+
+BcStatus bc_stack_init(BcStack* stack, size_t esize) {
+
+ // Check for invalid params.
+ if (stack == NULL || esize == 0) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ // Set the fields.
+ stack->size = esize;
+ stack->cap = BC_STACK_START;
+ stack->len = 0;
+
+ // Allocate the array.
+ stack->stack = malloc(esize * BC_STACK_START);
+
+ // Check for error.
+ if (stack->stack == NULL) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ return BC_STATUS_SUCCESS;
+}
+
+BcStatus bc_stack_push(BcStack* stack, void* data) {
+
+ // Check for invalid params.
+ if (stack == NULL || data == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ // Check if we need to expand.
+ if (stack->len == stack->cap) {
+
+ // Expand the stack.
+ BcStatus status = bc_stack_expand(stack);
+
+ // Check for error.
+ if (status != BC_STATUS_SUCCESS) {
+ return status;
+ }
+ }
+
+ // Copy the data.
+ size_t size = stack->size;
+ memmove(stack->stack + (size * stack->len), data, size);
+
+ // Increment the length.
+ ++stack->len;
+
+ return BC_STATUS_SUCCESS;
+}
+
+void* bc_stack_top(BcStack* stack) {
+
+ // Check for invalid state.
+ if (stack == NULL || stack->len == 0) {
+ return NULL;
+ }
+
+ // Calculate the return pointer.
+ return stack->stack + stack->size * (stack->len - 1);
+}
+
+void* bc_stack_item(BcStack* stack, uint32_t idx) {
+
+ // Check for invalid state.
+ if (stack == NULL || stack->len == 0 || idx >= stack->len) {
+ return NULL;
+ }
+
+ // Calculate the return pointer.
+ return stack->stack + stack->size * (stack->len - idx - 1);
+}
+
+BcStatus bc_stack_pop(BcStack* stack) {
+
+ // Check for invalid params.
+ if (stack == NULL) {
+ return BC_STATUS_INVALID_PARAM;
+ }
+
+ // Decrement the length.
+ --stack->len;
+
+ return BC_STATUS_SUCCESS;
+}
+
+void bc_stack_free(void* stack) {
+
+ BcStack* s;
+
+ s = (BcStack*) stack;
+
+ // Check for NULL.
+ if (s == NULL) {
+ return;
+ }
+
+ // Free the stack.
+ free(s->stack);
+
+ // Zero the fields.
+ s->size = 0;
+ s->stack = NULL;
+ s->len = 0;
+ s->cap = 0;
+}
+
+static BcStatus bc_stack_expand(BcStack* stack) {
+
+ // Realloc.
+ uint8_t* ptr = realloc(stack->stack, stack->size * (stack->cap + BC_STACK_START));
+
+ // Check for error.
+ if (ptr == NULL) {
+ return BC_STATUS_MALLOC_FAIL;
+ }
+
+ // Assign the fields.
+ stack->stack = ptr;
+ stack->cap += BC_STACK_START;
+
+ return BC_STATUS_SUCCESS;
+}
diff --git a/bc/src/vm.c b/bc/src/vm.c
new file mode 100644
index 0000000..e799259
--- /dev/null
+++ b/bc/src/vm.c
@@ -0,0 +1,265 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "vm.h"
+
+static BcStatus bc_vm_execFile(BcVm* vm, int idx);
+static BcStatus bc_vm_execStdin(BcVm* vm);
+
+BcStatus bc_vm_init(BcVm* vm, int filec, const char* filev[]) {
+
+ vm->filec = filec;
+ vm->filev = filev;
+
+ return bc_stack_init(&vm->ctx_stack, sizeof(BcStmtList*));
+}
+
+BcStatus bc_vm_exec(BcVm* vm) {
+
+ BcStatus status;
+ int num_files;
+
+ status = BC_STATUS_SUCCESS;
+
+ num_files = vm->filec;
+
+ for (int i = 0; !status && i < num_files; ++i) {
+ status = bc_vm_execFile(vm, i);
+ }
+
+ if (status) {
+ return status;
+ }
+
+ return bc_vm_execStdin(vm);
+}
+
+void bc_vm_free(BcVm* vm) {
+ bc_stack_free(&vm->ctx_stack);
+ bc_parse_free(&vm->parse);
+ bc_program_free(&vm->program);
+}
+
+static BcStatus bc_vm_execFile(BcVm* vm, int idx) {
+
+ BcStatus status;
+ FILE* f;
+ size_t size;
+ size_t read_size;
+
+ status = bc_program_init(&vm->program, vm->filev[idx]);
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_parse_init(&vm->parse, &vm->program);
+
+ if (status) {
+ goto parse_err;
+ }
+
+ f = fopen(vm->filev[idx], "r");
+
+ if (!f) {
+ status = BC_STATUS_VM_FILE_ERR;
+ goto file_err;
+ }
+
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+
+ fseek(f, 0, SEEK_SET);
+
+ char* data = malloc(size + 1);
+
+ if (!data) {
+ status = BC_STATUS_MALLOC_FAIL;
+ goto data_err;
+ }
+
+ read_size = fread(data, 1, size, f);
+
+ if (read_size != size) {
+ status = BC_STATUS_VM_FILE_READ_ERR;
+ goto read_err;
+ }
+
+ data[size] = '\0';
+
+ fclose(f);
+
+ bc_parse_text(&vm->parse, data);
+
+ status = bc_parse_parse(&vm->parse, &vm->program);
+
+ while (!status) {
+
+ status = bc_program_exec(&vm->program);
+
+ if (status) {
+ bc_error(status);
+ break;
+ }
+
+ status = bc_parse_parse(&vm->parse, &vm->program);
+
+ if (status && (status != BC_STATUS_LEX_EOF ||
+ status != BC_STATUS_PARSE_EOF))
+ {
+ bc_error_file(vm->program.file, vm->parse.lex.line, status);
+ }
+ }
+
+ bc_program_free(&vm->program);
+ bc_parse_free(&vm->parse);
+
+ free(data);
+
+ return status;
+
+read_err:
+
+ free(data);
+
+data_err:
+
+ fclose(f);
+
+file_err:
+
+ bc_parse_free(&vm->parse);
+
+parse_err:
+
+ bc_program_free(&vm->program);
+
+ return status;
+}
+
+static BcStatus bc_vm_execStdin(BcVm* vm) {
+
+ BcStatus status;
+ char* buf;
+ char* buffer;
+ char* temp;
+ size_t n;
+ size_t bufn;
+ size_t slen;
+ size_t total_len;
+
+ status = bc_program_init(&vm->program, "-");
+
+ if (status) {
+ return status;
+ }
+
+ status = bc_parse_init(&vm->parse, &vm->program);
+
+ if (status) {
+ goto parse_err;
+ }
+
+ n = BC_VM_BUF_SIZE;
+ bufn = BC_VM_BUF_SIZE;
+ buffer = malloc(BC_VM_BUF_SIZE + 1);
+
+ if (!buffer) {
+ status = BC_STATUS_MALLOC_FAIL;
+ goto buffer_err;
+ }
+
+ buf = malloc(BC_VM_BUF_SIZE + 1);
+
+ if (!buf) {
+ status = BC_STATUS_MALLOC_FAIL;
+ goto buf_err;
+ }
+
+ // The following loop is complicated because the vm tries
+ // not to send any lines that end with a backslash to the
+ // parser. The reason for that is because the parser treats
+ // a backslash newline combo as whitespace, per the bc
+ // spec. Thus, the parser will expect more stuff.
+ while (!status && getline(&buf, &bufn, stdin) != -1) {
+
+ size_t len;
+
+ len = strlen(buf);
+ slen = strlen(buffer);
+ total_len = slen + len;
+
+ if (len > 1) {
+
+ if (buf[len - 2] == '\\') {
+
+ if (total_len > n) {
+
+ temp = realloc(buffer, total_len + 1);
+
+ if (!temp) {
+ status = BC_STATUS_MALLOC_FAIL;
+ goto exit_err;
+ }
+
+ buffer = temp;
+ n = slen + len;
+ }
+
+ strcat(buffer, buf);
+
+ continue;
+ }
+ }
+
+ if (total_len > n) {
+
+ temp = realloc(buffer, total_len + 1);
+
+ if (!temp) {
+ status = BC_STATUS_MALLOC_FAIL;
+ goto exit_err;
+ }
+
+ buffer = temp;
+ n = slen + len;
+ }
+
+ strcat(buffer, buf);
+
+ status = bc_parse_text(&vm->parse, buffer);
+
+ if (status) {
+ goto exit_err;
+ }
+
+ status = bc_parse_parse(&vm->parse, &vm->program);
+
+ if (status) {
+ goto exit_err;
+ }
+
+ status = bc_program_exec(&vm->program);
+
+ buffer[0] = '\0';
+ }
+
+exit_err:
+
+ free(buf);
+
+buf_err:
+
+ free(buffer);
+
+buffer_err:
+
+ bc_parse_free(&vm->parse);
+
+parse_err:
+
+ bc_program_free(&vm->program);
+
+ return status;
+}
diff --git a/bc/tests/segarray_test.c b/bc/tests/segarray_test.c
new file mode 100644
index 0000000..00e695c
--- /dev/null
+++ b/bc/tests/segarray_test.c
@@ -0,0 +1,83 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../bc.h"
+#include "../segarray.h"
+#include "../program.h"
+
+int bc_var_cmp(void* var1, void* var2) {
+
+ BcVar* v1 = (BcVar*) var1;
+ BcVar* v2 = (BcVar*) var2;
+
+ return strcmp(v1->name, v2->name);
+}
+
+int main(int argc, char* argv[]) {
+
+ BcStatus status;
+ BcSegArray sa;
+
+ if (argc < 3) {
+ fprintf(stderr, "Need an input file and an output file\n"
+ " Usage: test <input_file> <output_file>");
+ exit(BC_STATUS_INVALID_OPTION);
+ }
+
+ status = bc_segarray_init(&sa, sizeof(BcVar), bc_var_cmp);
+
+ if (status) {
+ return status;
+ }
+
+ FILE* in = fopen(argv[1], "r");
+
+ if (!in) {
+ BC_STATUS_VM_FILE_ERR;
+ }
+
+ char* buffer = NULL;
+ size_t n = 0;
+
+ while (getline(&buffer, &n, in) != -1) {
+
+ BcVar var;
+
+ var.name = buffer;
+ var.data = NULL;
+
+ status = bc_segarray_add(&sa, &var);
+
+ if (status) {
+ return status;
+ }
+
+ buffer = NULL;
+ n = 0;
+ }
+
+ fclose(in);
+
+ FILE* out = fopen(argv[2], "w");
+
+ if (!out) {
+ BC_STATUS_VM_FILE_ERR;
+ }
+
+ for (uint32_t i = 0; i < sa.num; ++i) {
+
+ BcVar* v = bc_segarray_item(&sa, i);
+
+ if (!v) {
+ return 1;
+ }
+
+ fprintf(out, "%s", v->name);
+ }
+
+ fclose(out);
+
+ return BC_STATUS_SUCCESS;
+}