#include "toy_vm.h" #include "toy_console_colors.h" #include "toy_lexer.h" #include "toy_parser.h" #include "toy_bytecode.h" #include "toy_print.h" #include #include #include //utils Toy_Bytecode makeBytecodeFromSource(Toy_Bucket** bucketHandle, const char* source) { //did I forget this? Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); Toy_Bytecode bc = Toy_compileBytecode(ast); return bc; } //tests int test_setup_and_teardown(Toy_Bucket** bucketHandle) { //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(bucketHandle, &parser); Toy_Bytecode bc = Toy_compileBytecode(ast); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, bc.ptr); //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.paramSize != 0 || vm.jumpsSize != 0 || vm.dataSize != 0 || vm.subsSize != 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; } int test_simple_execution(Toy_Bucket** bucketHandle) { //test execution { //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(bucketHandle, &parser); Toy_Bytecode bc = Toy_compileBytecode(ast); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, bc.ptr); //run Toy_runVM(&vm); //check the final state of the stack if (vm.stack == NULL || 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: Unexpected result in 'Toy_VM', source: %s\n" TOY_CC_RESET, source); //cleanup and return Toy_freeVM(&vm); return -1; } //teadown Toy_freeVM(&vm); } return 0; } int test_opcode_not_equal(Toy_Bucket** bucketHandle) { //testing a specific opcode; '!=' is compressed into a single word, so lets check it works { //generate bytecode for testing const char* source = "3 != 5;"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); Toy_Bytecode bc = Toy_compileBytecode(ast); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, bc.ptr); //run Toy_runVM(&vm); //check the final state of the stack if (vm.stack == NULL || vm.stack->count != 1 || TOY_VALUE_IS_BOOLEAN( Toy_peekStack(&vm.stack) ) != true || TOY_VALUE_AS_BOOLEAN( Toy_peekStack(&vm.stack) ) != true ) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM', source: %s\n" TOY_CC_RESET, source); //cleanup and return Toy_freeVM(&vm); return -1; } //teadown Toy_freeVM(&vm); } return 0; } static char* callbackUtilReceived = NULL; static void callbackUtil(const char* msg) { if (msg != NULL) { free(callbackUtilReceived); callbackUtilReceived = (char*)malloc(strlen(msg) + 1); strcpy(callbackUtilReceived, msg); } } int test_keywords(Toy_Bucket** bucketHandle) { //test print { //setup Toy_setPrintCallback(callbackUtil); const char* source = "print 42;"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); Toy_Bytecode bc = Toy_compileBytecode(ast); Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, bc.ptr); //run Toy_runVM(&vm); //check the final state of the stack if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "42") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetPrintCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); } //test print with a string { //setup Toy_setPrintCallback(callbackUtil); const char* source = "print \"Hello world!\";"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); Toy_Bytecode bc = Toy_compileBytecode(ast); Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, bc.ptr); //run Toy_runVM(&vm); //check the final state of the stack if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Hello world!") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetPrintCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); } //test print with a string concat { //setup Toy_setPrintCallback(callbackUtil); const char* source = "print \"Hello\" .. \"world!\";"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); Toy_Bytecode bc = Toy_compileBytecode(ast); Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, bc.ptr); //run Toy_runVM(&vm); //check the final state of the stack if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Helloworld!") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetPrintCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); } return 0; } int test_vm_reuse(Toy_Bucket** bucketHandle) { //run code in the same vm multiple times { Toy_setPrintCallback(callbackUtil); Toy_VM vm; Toy_initVM(&vm); //run 1 Toy_Bytecode bc1 = makeBytecodeFromSource(bucketHandle, "print \"Hello world!\";"); Toy_bindVM(&vm, bc1.ptr); Toy_runVM(&vm); Toy_resetVM(&vm); if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Hello world!") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' found in VM reuse run 1\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL"); //cleanup and return free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeBytecode(bc1); Toy_freeVM(&vm); Toy_resetPrintCallback(); return -1; } Toy_freeBytecode(bc1); //run 2 Toy_Bytecode bc2 = makeBytecodeFromSource(bucketHandle, "print \"Hello world!\";"); Toy_bindVM(&vm, bc2.ptr); Toy_runVM(&vm); Toy_resetVM(&vm); if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Hello world!") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' found in VM reuse run 2\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL"); //cleanup and return free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeBytecode(bc2); Toy_freeVM(&vm); Toy_resetPrintCallback(); return -1; } Toy_freeBytecode(bc2); //run 3 Toy_Bytecode bc3 = makeBytecodeFromSource(bucketHandle, "print \"Hello world!\";"); Toy_bindVM(&vm, bc3.ptr); Toy_runVM(&vm); Toy_resetVM(&vm); if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Hello world!") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' found in VM reuse run 3\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL"); //cleanup and return free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeBytecode(bc3); Toy_freeVM(&vm); Toy_resetPrintCallback(); return -1; } Toy_freeBytecode(bc3); //cleanup Toy_freeVM(&vm); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_resetPrintCallback(); } return 0; } int main() { //run each test set, returning the total errors given int total = 0, res = 0; { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_setup_and_teardown(&bucket); Toy_freeBucket(&bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); } total += res; } { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_simple_execution(&bucket); Toy_freeBucket(&bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); } total += res; } { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_opcode_not_equal(&bucket); Toy_freeBucket(&bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); } total += res; } { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_keywords(&bucket); Toy_freeBucket(&bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); } total += res; } { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_vm_reuse(&bucket); Toy_freeBucket(&bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); } total += res; } return total; }