diff --git a/source/toy.h b/source/toy.h index 4bc7e1b..96db27c 100644 --- a/source/toy.h +++ b/source/toy.h @@ -3,10 +3,17 @@ //general utilities #include "toy_common.h" #include "toy_console_colors.h" +#include "toy_memory.h" //building blocks -// +#include "toy_value.h" +#include "toy_ast.h" +#include "toy_routine.h" +#include "toy_stack.h" //pipeline #include "toy_lexer.h" +#include "toy_parser.h" +#include "toy_bytecode.h" +#include "toy_vm.h" diff --git a/source/toy_common.c b/source/toy_common.c index f96c347..4cc0e58 100644 --- a/source/toy_common.c +++ b/source/toy_common.c @@ -1,7 +1,7 @@ #include "toy_common.h" //defined separately, as compilation can take several seconds, invalidating the comparisons of the given macros -static const char* build = __DATE__ " " __TIME__ ", Toy branch 'dev'"; +static const char* build = __DATE__ " " __TIME__ ", incomplete Toy v2.x"; const char* Toy_private_version_build() { return build; diff --git a/source/toy_stack.c b/source/toy_stack.c index 776dc44..7d35155 100644 --- a/source/toy_stack.c +++ b/source/toy_stack.c @@ -40,7 +40,7 @@ void Toy_pushStack(Toy_Stack* stack, Toy_Value value) { //expand the capacity if needed if (stack->count + 1 > stack->capacity) { int oldCapacity = stack->capacity; - stack->capacity = TOY_GROW_CAPACITY(stack->capacity); + stack->capacity = stack->capacity < MIN_SIZE ? MIN_SIZE : stack->capacity * 2; //similar to TOY_GROW_CAPACITY, with a bigger initial size stack->ptr = TOY_GROW_ARRAY(Toy_Value, stack->ptr, oldCapacity, stack->capacity); } diff --git a/source/toy_value.c b/source/toy_value.c index e6e1791..94ed704 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -3,12 +3,13 @@ #include "toy_console_colors.h" #include +#include bool Toy_private_isTruthy(Toy_Value value) { //null is an error if (TOY_VALUE_IS_NULL(value)) { fprintf(stderr, TOY_CC_ERROR "ERROR: 'null' is neither true nor false\n" TOY_CC_RESET); - return false; + exit(-1); //TODO: return false or exit()? } //only 'false' is falsy @@ -18,4 +19,47 @@ bool Toy_private_isTruthy(Toy_Value value) { //anything else is truthy return true; -} \ No newline at end of file +} + +bool Toy_private_isEqual(Toy_Value left, Toy_Value right) { + //temp check + if (right.type > TOY_VALUE_FLOAT) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown types %d and %d in equality\n" TOY_CC_RESET, left.type, right.type); + exit(-1); + } + + switch(left.type) { + case TOY_VALUE_NULL: + return TOY_VALUE_IS_NULL(right); + + case TOY_VALUE_BOOLEAN: + return TOY_VALUE_IS_BOOLEAN(right) && TOY_VALUE_AS_BOOLEAN(left) == TOY_VALUE_AS_BOOLEAN(right); + + case TOY_VALUE_INTEGER: + if (TOY_VALUE_AS_INTEGER(right)) { + return TOY_VALUE_AS_INTEGER(left) == TOY_VALUE_AS_INTEGER(right); + } + if (TOY_VALUE_AS_FLOAT(right)) { + return TOY_VALUE_AS_INTEGER(left) == TOY_VALUE_AS_FLOAT(right); + } + return false; + + case TOY_VALUE_FLOAT: + if (TOY_VALUE_AS_FLOAT(right)) { + return TOY_VALUE_AS_FLOAT(left) == TOY_VALUE_AS_FLOAT(right); + } + if (TOY_VALUE_AS_INTEGER(right)) { + return TOY_VALUE_AS_FLOAT(left) == TOY_VALUE_AS_INTEGER(right); + } + return false; + + case TOY_VALUE_STRING: + case TOY_VALUE_ARRAY: + case TOY_VALUE_DICTIONARY: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + default: + fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown types %d and %d in equality\n" TOY_CC_RESET, left.type, right.type); + exit(-1); + } +} diff --git a/source/toy_value.h b/source/toy_value.h index b0e696c..81a59b4 100644 --- a/source/toy_value.h +++ b/source/toy_value.h @@ -54,3 +54,5 @@ typedef struct Toy_Value { #define TOY_VALUE_IS_TRUTHY(value) Toy_private_isTruthy(value) TOY_API bool Toy_private_isTruthy(Toy_Value value); +#define TOY_VALUE_IS_EQUAL(left, right) Toy_private_isEqual(left, right) +TOY_API bool Toy_private_isEqual(Toy_Value left, Toy_Value right); diff --git a/source/toy_vm.c b/source/toy_vm.c index 42d05af..9d8c24b 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -11,13 +11,13 @@ //utilities #define READ_BYTE(vm) \ - vm->program[vm->programCounter++] + vm->routine[vm->routineCounter++] #define READ_INT(vm) \ - *((int*)(vm->program + _read_postfix(&(vm->programCounter), 4))) + *((int*)(vm->routine + _read_postfix(&(vm->routineCounter), 4))) #define READ_FLOAT(vm) \ - *((float*)(vm->program + _read_postfix(&(vm->programCounter), 4))) + *((float*)(vm->routine + _read_postfix(&(vm->routineCounter), 4))) static inline int _read_postfix(int* ptr, int amount) { int ret = *ptr; @@ -26,8 +26,8 @@ static inline int _read_postfix(int* ptr, int amount) { } static inline void fix_alignment(Toy_VM* vm) { - if (vm->programCounter % 4 != 0) { - vm->programCounter = (4 - vm->programCounter % 4); + if (vm->routineCounter % 4 != 0) { + vm->routineCounter += (4 - vm->routineCounter % 4); } } @@ -157,79 +157,133 @@ static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) { Toy_pushStack(&vm->stack, result); } +static void processComparison(Toy_VM* vm, Toy_OpcodeType opcode) { + Toy_Value right = Toy_popStack(&vm->stack); + Toy_Value left = Toy_popStack(&vm->stack); + + //most things can be equal, so handle it separately + if (opcode == TOY_OPCODE_COMPARE_EQUAL) { + Toy_pushStack(&vm->stack, TOY_VALUE_TO_BOOLEAN(TOY_VALUE_IS_EQUAL(left, right)) ); + return; + } + + //coerce ints into floats if needed + if (TOY_VALUE_IS_INTEGER(left) && TOY_VALUE_IS_FLOAT(right)) { + left = TOY_VALUE_TO_FLOAT( (float)TOY_VALUE_AS_INTEGER(left) ); + } + else + if (TOY_VALUE_IS_FLOAT(left) && TOY_VALUE_IS_INTEGER(right)) { + right = TOY_VALUE_TO_FLOAT( (float)TOY_VALUE_AS_INTEGER(right) ); + } + + //other opcodes + if (opcode == TOY_OPCODE_COMPARE_LESS) { + Toy_pushStack(&vm->stack, TOY_VALUE_TO_BOOLEAN(TOY_VALUE_IS_FLOAT(left) ? TOY_VALUE_AS_FLOAT(left) < TOY_VALUE_AS_FLOAT(right) : TOY_VALUE_AS_INTEGER(left) < TOY_VALUE_AS_INTEGER(right)) ); + } + else if (opcode == TOY_OPCODE_COMPARE_LESS_EQUAL) { + Toy_pushStack(&vm->stack, TOY_VALUE_TO_BOOLEAN(TOY_VALUE_IS_FLOAT(left) ? TOY_VALUE_AS_FLOAT(left) <= TOY_VALUE_AS_FLOAT(right) : TOY_VALUE_AS_INTEGER(left) <= TOY_VALUE_AS_INTEGER(right)) ); + } + else if (opcode == TOY_OPCODE_COMPARE_GREATER) { + Toy_pushStack(&vm->stack, TOY_VALUE_TO_BOOLEAN(TOY_VALUE_IS_FLOAT(left) ? TOY_VALUE_AS_FLOAT(left) > TOY_VALUE_AS_FLOAT(right) : TOY_VALUE_AS_INTEGER(left) > TOY_VALUE_AS_INTEGER(right)) ); + } + else if (opcode == TOY_OPCODE_COMPARE_GREATER_EQUAL) { + Toy_pushStack(&vm->stack, TOY_VALUE_TO_BOOLEAN(TOY_VALUE_IS_FLOAT(left) ? TOY_VALUE_AS_FLOAT(left) >= TOY_VALUE_AS_FLOAT(right) : TOY_VALUE_AS_INTEGER(left) >= TOY_VALUE_AS_INTEGER(right)) ); + } + else { + fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid opcode %d passed to processComparison, exiting\n" TOY_CC_RESET, opcode); + exit(-1); + } +} + +static void processLogical(Toy_VM* vm, Toy_OpcodeType opcode) { + if (opcode == TOY_OPCODE_AND) { + Toy_Value right = Toy_popStack(&vm->stack); + Toy_Value left = Toy_popStack(&vm->stack); + + Toy_pushStack(&vm->stack, TOY_VALUE_TO_BOOLEAN( TOY_VALUE_IS_TRUTHY(left) && TOY_VALUE_IS_TRUTHY(right) )); + } + else if (opcode == TOY_OPCODE_OR) { + Toy_Value right = Toy_popStack(&vm->stack); + Toy_Value left = Toy_popStack(&vm->stack); + + Toy_pushStack(&vm->stack, TOY_VALUE_TO_BOOLEAN( TOY_VALUE_IS_TRUTHY(left) || TOY_VALUE_IS_TRUTHY(right) )); + } + else if (opcode == TOY_OPCODE_TRUTHY) { + Toy_Value top = Toy_popStack(&vm->stack); + + Toy_pushStack(&vm->stack, TOY_VALUE_TO_BOOLEAN( TOY_VALUE_IS_TRUTHY(top) )); + } + else if (opcode == TOY_OPCODE_NEGATE) { + Toy_Value top = Toy_popStack(&vm->stack); + + Toy_pushStack(&vm->stack, TOY_VALUE_TO_BOOLEAN( !TOY_VALUE_IS_TRUTHY(top) )); + } + else { + fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid opcode %d passed to processLogical, exiting\n" TOY_CC_RESET, opcode); + exit(-1); + } +} + static void process(Toy_VM* vm) { - Toy_OpcodeType opcode = READ_BYTE(vm); + while(true) { + Toy_OpcodeType opcode = READ_BYTE(vm); - switch(opcode) { - case TOY_OPCODE_READ: - processRead(vm); - break; + switch(opcode) { + case TOY_OPCODE_READ: + processRead(vm); + break; - case TOY_OPCODE_ADD: - case TOY_OPCODE_SUBTRACT: - case TOY_OPCODE_MULTIPLY: - case TOY_OPCODE_DIVIDE: - case TOY_OPCODE_MODULO: - processArithmetic(vm, opcode); - break; + case TOY_OPCODE_ADD: + case TOY_OPCODE_SUBTRACT: + case TOY_OPCODE_MULTIPLY: + case TOY_OPCODE_DIVIDE: + case TOY_OPCODE_MODULO: + processArithmetic(vm, opcode); + break; - case TOY_OPCODE_COMPARE_EQUAL: - // - // break; + case TOY_OPCODE_COMPARE_EQUAL: + case TOY_OPCODE_COMPARE_LESS: + case TOY_OPCODE_COMPARE_LESS_EQUAL: + case TOY_OPCODE_COMPARE_GREATER: + case TOY_OPCODE_COMPARE_GREATER_EQUAL: + processComparison(vm, opcode); + break; - case TOY_OPCODE_COMPARE_LESS: - // - // break; + case TOY_OPCODE_AND: + case TOY_OPCODE_OR: + case TOY_OPCODE_TRUTHY: + case TOY_OPCODE_NEGATE: //TODO: squeeze into != + processLogical(vm, opcode); + break; - case TOY_OPCODE_COMPARE_LESS_EQUAL: - // - // break; + case TOY_OPCODE_LOAD: + case TOY_OPCODE_LOAD_LONG: + case TOY_OPCODE_DECLARE: + case TOY_OPCODE_ASSIGN: + case TOY_OPCODE_ACCESS: + case TOY_OPCODE_PASS: + case TOY_OPCODE_ERROR: + case TOY_OPCODE_EOF: + fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid opcode %d found, exiting\n" TOY_CC_RESET, opcode); + exit(-1); - case TOY_OPCODE_COMPARE_GREATER: - // - // break; + case TOY_OPCODE_RETURN: //temp terminator, temp position + // + return; + } - case TOY_OPCODE_COMPARE_GREATER_EQUAL: - // - // break; - - case TOY_OPCODE_AND: - // - // break; - - case TOY_OPCODE_OR: - // - // break; - - case TOY_OPCODE_TRUTHY: - // - // break; - - case TOY_OPCODE_NEGATE: //TODO: squeeze into != - // - // break; - - case TOY_OPCODE_LOAD: - case TOY_OPCODE_LOAD_LONG: - case TOY_OPCODE_DECLARE: - case TOY_OPCODE_ASSIGN: - case TOY_OPCODE_ACCESS: - case TOY_OPCODE_PASS: - case TOY_OPCODE_ERROR: - case TOY_OPCODE_EOF: - fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid opcode %d found, exiting\n" TOY_CC_RESET, opcode); - exit(-1); - - case TOY_OPCODE_RETURN: //temp terminator, temp position - // - // return; + //prepare for the next instruction + fix_alignment(vm); } } //exposed functions void Toy_initVM(Toy_VM* vm) { - vm->program = NULL; - vm->programSize = 0; + vm->bc = NULL; + vm->bcSize = 0; + + vm->routine = NULL; + vm->routineSize = 0; vm->paramCount = 0; vm->jumpsCount = 0; @@ -242,17 +296,47 @@ void Toy_initVM(Toy_VM* vm) { vm->dataAddr = 0; vm->subsAddr = 0; - vm->programCounter = 0; + vm->routineCounter = 0; //init the scope & stack Toy_initStack(&vm->stack); } -void Toy_bindVM(Toy_VM* vm, unsigned char* program) { - vm->program = program; +void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, int bytecodeSize) { + if (bytecode[0] != TOY_VERSION_MAJOR || bytecode[1] > TOY_VERSION_MINOR) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Wrong bytecode version found: expected %d.%d.%d found %d.%d.%d, exiting\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, bytecode[0], bytecode[1], bytecode[2]); + exit(-1); + } + + if (bytecode[2] != TOY_VERSION_PATCH) { + fprintf(stderr, TOY_CC_WARN "WARNING: Wrong bytecode version found: expected %d.%d.%d found %d.%d.%d, continuing\n" TOY_CC_RESET, TOY_VERSION_MAJOR, TOY_VERSION_MINOR, TOY_VERSION_PATCH, bytecode[0], bytecode[1], bytecode[2]); + } + + if (strcmp((char*)(bytecode + 3), TOY_VERSION_BUILD) != 0) { + fprintf(stderr, TOY_CC_WARN "WARNING: Wrong bytecode build info found: expected '%s' found '%s', continuing\n" TOY_CC_RESET, TOY_VERSION_BUILD, (char*)(bytecode + 3)); + } + + //offset by the header size + int offset = 3 + strlen(TOY_VERSION_BUILD) + 1; + if (offset % 4 != 0) { + offset += 4 - (offset % 4); //ceil + } + + //delegate + Toy_bindVMToRoutine(vm, bytecode + offset); + + //cache these + vm->bc = bytecode; + vm->bcSize = bytecodeSize; +} + +void Toy_bindVMToRoutine(Toy_VM* vm, unsigned char* routine) { + Toy_initVM(vm); + + vm->routine = routine; //read the header metadata - vm->programSize = READ_INT(vm); + vm->routineSize = READ_INT(vm); vm->paramCount = READ_INT(vm); vm->jumpsCount = READ_INT(vm); vm->dataCount = READ_INT(vm); @@ -263,7 +347,7 @@ void Toy_bindVM(Toy_VM* vm, unsigned char* program) { vm->paramAddr = READ_INT(vm); } - vm->codeAddr = READ_INT(vm); + vm->codeAddr = READ_INT(vm); //required if (vm->jumpsCount > 0) { vm->jumpsAddr = READ_INT(vm); @@ -284,8 +368,8 @@ void Toy_bindVM(Toy_VM* vm, unsigned char* program) { void Toy_runVM(Toy_VM* vm) { //TODO: read params into scope - //prep the program counter for execution - vm->programCounter = vm->codeAddr; + //prep the routine counter for execution + vm->routineCounter = vm->codeAddr; //begin process(vm); @@ -298,6 +382,6 @@ void Toy_freeVM(Toy_VM* vm) { //TODO: clear the scope //free the bytecode - TOY_FREE_ARRAY(unsigned char, vm->program, vm->programSize); + TOY_FREE_ARRAY(unsigned char, vm->bc, vm->bcSize); Toy_initVM(vm); } diff --git a/source/toy_vm.h b/source/toy_vm.h index 6ae8f99..d96d08c 100644 --- a/source/toy_vm.h +++ b/source/toy_vm.h @@ -5,9 +5,13 @@ #include "toy_stack.h" typedef struct Toy_VM { - //bytecode - raw instructions that are being executed - unsigned char* program; - int programSize; + //hold the raw bytecode + unsigned char* bc; + int bcSize; + + //raw instructions to be executed + unsigned char* routine; + int routineSize; int paramCount; int jumpsCount; @@ -20,7 +24,7 @@ typedef struct Toy_VM { int dataAddr; int subsAddr; - int programCounter; + int routineCounter; //scope - block-level key/value pairs @@ -29,7 +33,10 @@ typedef struct Toy_VM { } Toy_VM; TOY_API void Toy_initVM(Toy_VM* vm); -TOY_API void Toy_bindVM(Toy_VM* vm, unsigned char* program); + +TOY_API void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, int bytecodeSize); //process the version data +TOY_API void Toy_bindVMToRoutine(Toy_VM* vm, unsigned char* routine); //process the routine only + TOY_API void Toy_runVM(Toy_VM* vm); TOY_API void Toy_freeVM(Toy_VM* vm); diff --git a/tests/cases/test_bytecode.c b/tests/cases/test_bytecode.c index b2959b5..51b3afc 100644 --- a/tests/cases/test_bytecode.c +++ b/tests/cases/test_bytecode.c @@ -86,7 +86,7 @@ int test_bytecode_from_source(Toy_Bucket* bucket) { } //check contents of the routine (this is copy/pasted from test_routine.c, and tweaked with the offset) - int offset = strlen(TOY_VERSION_BUILD) + 3; + int offset = 3 + strlen(TOY_VERSION_BUILD) + 1; if (offset % 4 != 0) { offset += 4 - (offset % 4); //ceil } diff --git a/tests/cases/test_stack.c b/tests/cases/test_stack.c new file mode 100644 index 0000000..13b2f2b --- /dev/null +++ b/tests/cases/test_stack.c @@ -0,0 +1,179 @@ +#include "toy_stack.h" +#include "toy_console_colors.h" + +#include + +int test_stack_with_init() { + //init and free the stack + { + Toy_Stack stack; + Toy_initStack(&stack); + + //check if it worked + if ( + stack.ptr != NULL || + stack.capacity != 0 || + stack.count != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to init Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + Toy_freeStack(&stack); + } + + //push, peek and pop stack + { + Toy_Stack stack; + Toy_initStack(&stack); + + //check if it worked (push) + Toy_pushStack(&stack, TOY_VALUE_TO_INTEGER(42)); + Toy_pushStack(&stack, TOY_VALUE_TO_INTEGER(69)); + Toy_pushStack(&stack, TOY_VALUE_TO_INTEGER(420)); + if ( + stack.ptr == NULL || + stack.capacity != 64 || + stack.count != 3) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to push Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + //check if it worked (peek) + Toy_Value top1 = Toy_peekStack(&stack); + if ( + TOY_VALUE_IS_INTEGER(top1) != true || + TOY_VALUE_AS_INTEGER(top1) != 420) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to peek Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + //check if it worked (pop) + Toy_Value top2 = Toy_popStack(&stack); + if ( + stack.ptr == NULL || + stack.capacity != 64 || + stack.count != 2 || + TOY_VALUE_IS_INTEGER(top2) != true || + TOY_VALUE_AS_INTEGER(top2) != 420) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to pop Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + //check if it worked (post-pop peek) + Toy_Value top3 = Toy_peekStack(&stack); + if ( + TOY_VALUE_IS_INTEGER(top3) != true || + TOY_VALUE_AS_INTEGER(top3) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to pop then peek Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + Toy_freeStack(&stack); + } + + return 0; +} + +int test_stack_with_preallocate() { + //preallocate and free the stack + { + Toy_Stack stack; + Toy_preallocateStack(&stack); + + //check if it worked + if ( + stack.ptr == NULL || + stack.capacity != 64 || + stack.count != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to preallocate Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + Toy_freeStack(&stack); + } + + //push, peek and pop stack + { + Toy_Stack stack; + Toy_initStack(&stack); + + //check if it worked (push) + Toy_pushStack(&stack, TOY_VALUE_TO_INTEGER(42)); + Toy_pushStack(&stack, TOY_VALUE_TO_INTEGER(69)); + Toy_pushStack(&stack, TOY_VALUE_TO_INTEGER(420)); + if ( + stack.ptr == NULL || + stack.capacity != 64 || + stack.count != 3) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to push Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + //check if it worked (peek) + Toy_Value top1 = Toy_peekStack(&stack); + if ( + TOY_VALUE_IS_INTEGER(top1) != true || + TOY_VALUE_AS_INTEGER(top1) != 420) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to peek Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + //check if it worked (pop) + Toy_Value top2 = Toy_popStack(&stack); + if ( + stack.ptr == NULL || + stack.capacity != 64 || + stack.count != 2 || + TOY_VALUE_IS_INTEGER(top2) != true || + TOY_VALUE_AS_INTEGER(top2) != 420) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to pop Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + //check if it worked (post-pop peek) + Toy_Value top3 = Toy_peekStack(&stack); + if ( + TOY_VALUE_IS_INTEGER(top3) != true || + TOY_VALUE_AS_INTEGER(top3) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to pop then peek Toy_Stack\n" TOY_CC_RESET); + return -1; + } + + Toy_freeStack(&stack); + } + + return 0; +} + +int main() { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + res = test_stack_with_init(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_stack_with_preallocate(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + return total; +} diff --git a/tests/cases/test_value.c b/tests/cases/test_value.c index 8ccaf85..53ffade 100644 --- a/tests/cases/test_value.c +++ b/tests/cases/test_value.c @@ -33,6 +33,23 @@ int main() { } } + //test value equality + { + Toy_Value answer = TOY_VALUE_TO_INTEGER(42); + Toy_Value question = TOY_VALUE_TO_INTEGER(42); + Toy_Value nice = TOY_VALUE_TO_INTEGER(69); + + if (!TOY_VALUE_IS_EQUAL(answer, question)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: equality check failed, expected true\n" TOY_CC_RESET); + return -1; + } + + if (TOY_VALUE_IS_EQUAL(answer, nice)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: equality check failed, expected false\n" TOY_CC_RESET); + return -1; + } + } + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); return 0; } diff --git a/tests/cases/test_vm.c b/tests/cases/test_vm.c new file mode 100644 index 0000000..bb3e470 --- /dev/null +++ b/tests/cases/test_vm.c @@ -0,0 +1,149 @@ +#include "toy_vm.h" +#include "toy_console_colors.h" + +#include "toy_lexer.h" +#include "toy_parser.h" +#include "toy_bytecode.h" + +#include +#include + +//utils +Toy_Bytecode makeBytecodeFromSource(Toy_Bucket** bucket, const char* source) { + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(bucket, &parser); + Toy_Bytecode bc = Toy_compileBytecode(ast); + + return bc; +} + +//tests +int test_setup_and_teardown(Toy_Bucket* bucket) { + //basic init & quit + { + //generate bytecode for testing + const char* source = "(1 + 2) * (3 + 4);"; + + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + Toy_Bytecode bc = Toy_compileBytecode(ast); + + //run the setup + Toy_VM vm; + Toy_initVM(&vm); + Toy_bindVM(&vm, bc.ptr, bc.capacity); + + //check the header size + int headerSize = 3 + strlen(TOY_VERSION_BUILD) + 1; + if (headerSize % 4 != 0) { + headerSize += 4 - (headerSize % 4); //ceil + } + + //check the routine was loaded correctly + if ( + vm.routine - vm.bc != headerSize || + vm.routineSize != 72 || + vm.paramCount != 0 || + vm.jumpsCount != 0 || + vm.dataCount != 0 || + vm.subsCount != 0 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to setup and teadown Toy_VM, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + Toy_freeVM(&vm); + return -1; + } + + //don't run it this time, simply teadown + Toy_freeVM(&vm); + } + + return 0; +} + +//tests +int test_simple_execution(Toy_Bucket* bucket) { + //basic init & quit + { + //generate bytecode for testing + const char* source = "(1 + 2) * (3 + 4);"; + + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + + Toy_Bytecode bc = Toy_compileBytecode(ast); + + //run the setup + Toy_VM vm; + Toy_initVM(&vm); + Toy_bindVM(&vm, bc.ptr, bc.capacity); + + //run + Toy_runVM(&vm); + + //check the final state of the stack + if (vm.stack.count != 1 || + TOY_VALUE_IS_INTEGER( Toy_peekStack(&vm.stack) ) != true || + TOY_VALUE_AS_INTEGER( Toy_peekStack(&vm.stack) ) != 21 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed run the Toy_VM, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + Toy_freeVM(&vm); + return -1; + } + + //teadown + Toy_freeVM(&vm); + } + + return 0; +} + +int main() { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(Toy_Ast, bucket, 32); + res = test_setup_and_teardown(bucket); + TOY_BUCKET_FREE(bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + Toy_Bucket* bucket = NULL; + TOY_BUCKET_INIT(Toy_Ast, bucket, 32); + res = test_simple_execution(bucket); + TOY_BUCKET_FREE(bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + return total; +}