#include "toy_vm.h" #include "toy_console_colors.h" #include "toy_lexer.h" #include "toy_parser.h" #include "toy_module_compiler.h" #include "toy_print.h" #include #include #include //utils unsigned char* makeCodeFromSource(Toy_Bucket** bucketHandle, const char* source) { Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); return Toy_compileModule(ast); } //tests int test_setup_and_teardown(Toy_Bucket** bucketHandle) { //basic init & quit { //generate module 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); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //check the module was loaded correctly if ( vm.codeAddr != 24 || // module header is: total, jumps, prams, data, subs, codeAddr vm.jumpsCount != 0 || vm.paramCount != 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); free(buffer); return -1; } //don't run it this time, simply teadown Toy_freeVM(&vm); free(buffer); } 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); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //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); free(buffer); return -1; } //teadown Toy_freeVM(&vm); free(buffer); } 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); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //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); free(buffer); return -1; } //teadown Toy_freeVM(&vm); free(buffer); } 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_keyword_assert(Toy_Bucket** bucketHandle) { //test assert true { //setup Toy_setAssertFailureCallback(callbackUtil); const char* source = "assert true;"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check if (callbackUtilReceived != NULL) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected assert message '%s' found, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetAssertFailureCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); free(buffer); return -1; } //teadown Toy_resetAssertFailureCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } //test assert false { //setup Toy_setAssertFailureCallback(callbackUtil); const char* source = "assert false;"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "assertion failed") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected assert failure message '%s' found, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetAssertFailureCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); free(buffer); return -1; } //teadown Toy_resetAssertFailureCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } //test assert false with message { //setup Toy_setAssertFailureCallback(callbackUtil); const char* source = "assert false, \"You passed a false to assert\";"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "You passed a false to assert") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected assert failure message '%s' found, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetAssertFailureCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); free(buffer); return -1; } //teadown Toy_resetAssertFailureCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } return 0; } int test_keyword_print(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); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check 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); free(buffer); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } //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); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check 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); free(buffer); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } //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); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check 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); free(buffer); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } return 0; } int test_keyword_ifThenElse(Toy_Bucket** bucketHandle) { //test if-then (truthy) { //setup Toy_setPrintCallback(callbackUtil); const char* source = "if (true) print \"hello world\";"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "hello world") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print in if keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetPrintCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); free(buffer); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } //test if-then (falsy) { //setup Toy_setPrintCallback(callbackUtil); const char* source = "if (false) print \"hello world\";"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check if (callbackUtilReceived != NULL) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print in if keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetPrintCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); free(buffer); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } //test if-then-else (truthy) { //setup Toy_setPrintCallback(callbackUtil); const char* source = "if (true) print \"hello world\"; else print \"failed\";"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "hello world") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print in if keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetPrintCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); free(buffer); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } //test if-then-else (falsy) { //setup Toy_setPrintCallback(callbackUtil); const char* source = "if (false) print \"hello world\"; else print \"failed\";"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "failed") != 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print in if keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); //cleanup and return Toy_resetPrintCallback(); free(callbackUtilReceived); Toy_freeVM(&vm); free(buffer); return -1; } //teadown Toy_resetPrintCallback(); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_freeVM(&vm); free(buffer); } return 0; } int test_scope(Toy_Bucket** bucketHandle) { //test declaration with initial value { //generate bytecode for testing const char* source = "var foobar = 42;"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check Toy_String* key = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_ANY, false); if (vm.stack == NULL || vm.stack->count != 0 || vm.scope == NULL || Toy_isDeclaredScope(vm.scope, key) == false || TOY_VALUE_IS_INTEGER(*Toy_accessScopeAsPointer(vm.scope, key)) != true || TOY_VALUE_AS_INTEGER(*Toy_accessScopeAsPointer(vm.scope, key)) != 42 ) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM' when testing scope, source: %s\n" TOY_CC_RESET, source); //cleanup and return Toy_freeVM(&vm); free(buffer); return -1; } //teadown Toy_freeVM(&vm); free(buffer); } //test declaration with absent value { //generate bytecode for testing const char* source = "var foobar;"; Toy_Lexer lexer; Toy_bindLexer(&lexer, source); Toy_Parser parser; Toy_bindParser(&parser, &lexer); Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); unsigned char* buffer = Toy_compileModule(ast); Toy_Module module = Toy_parseModule(buffer); //run the setup Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, &module, false); //run Toy_runVM(&vm); //check Toy_String* key = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_UNKNOWN, false); if (vm.stack == NULL || vm.stack->count != 0 || vm.scope == NULL || Toy_isDeclaredScope(vm.scope, key) == false || TOY_VALUE_IS_NULL(*Toy_accessScopeAsPointer(vm.scope, key)) != true ) { fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM' when testing scope, source: %s\n" TOY_CC_RESET, source); //cleanup and return Toy_freeVM(&vm); free(buffer); return -1; } //teadown Toy_freeVM(&vm); free(buffer); } 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 unsigned char* buffer1 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";"); Toy_Module module1 = Toy_parseModule(buffer1); Toy_bindVM(&vm, &module1, false); Toy_runVM(&vm); Toy_resetVM(&vm, true); 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; free(buffer1); Toy_freeVM(&vm); Toy_resetPrintCallback(); return -1; } free(buffer1); //run 2 unsigned char* buffer2 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";"); Toy_Module module2 = Toy_parseModule(buffer2); Toy_bindVM(&vm, &module2, true); //preserve during repeated calls Toy_runVM(&vm); Toy_resetVM(&vm, true); 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; free(buffer2); Toy_freeVM(&vm); Toy_resetPrintCallback(); return -1; } free(buffer2); //run 3 unsigned char* buffer3 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";"); Toy_Module module3 = Toy_parseModule(buffer3); Toy_bindVM(&vm, &module3, true); //preserve during repeated calls Toy_runVM(&vm); Toy_resetVM(&vm, true); 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; free(buffer3); Toy_freeVM(&vm); Toy_resetPrintCallback(); return -1; } free(buffer3); //cleanup Toy_freeVM(&vm); free(callbackUtilReceived); callbackUtilReceived = NULL; Toy_resetPrintCallback(); } return 0; } int main(void) { //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_keyword_print(&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_keyword_assert(&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_keyword_ifThenElse(&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_scope(&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; }