mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
893 lines
20 KiB
C
893 lines
20 KiB
C
#include "toy_vm.h"
|
|
#include "toy_console_colors.h"
|
|
|
|
#include "toy_lexer.h"
|
|
#include "toy_parser.h"
|
|
#include "toy_compiler.h"
|
|
#include "toy_print.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
//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_compileToBytecode(ast);
|
|
}
|
|
|
|
//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);
|
|
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, false);
|
|
|
|
//check the bytecode was loaded correctly
|
|
if (
|
|
vm.codeAddr != 24 || // bytecode 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//don't run it this time, simply teadown
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetAssertFailureCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
//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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetAssertFailureCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
//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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetAssertFailureCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetPrintCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
//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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetPrintCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
//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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetPrintCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetPrintCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
//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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetPrintCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
//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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetPrintCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
//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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, 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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_resetPrintCallback();
|
|
free(callbackUtilReceived);
|
|
callbackUtilReceived = NULL;
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, false);
|
|
|
|
//run
|
|
Toy_runVM(&vm);
|
|
|
|
//check
|
|
Toy_String* key = Toy_createStringLength(bucketHandle, "foobar", 6);
|
|
|
|
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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
//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* bytecode = Toy_compileToBytecode(ast);
|
|
|
|
//run the setup
|
|
Toy_VM vm;
|
|
Toy_initVM(&vm);
|
|
Toy_bindVM(&vm, bytecode, false);
|
|
|
|
//run
|
|
Toy_runVM(&vm);
|
|
|
|
//check
|
|
Toy_String* key = Toy_createStringLength(bucketHandle, "foobar", 6);
|
|
|
|
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(bytecode);
|
|
return -1;
|
|
}
|
|
|
|
//teadown
|
|
Toy_freeVM(&vm);
|
|
free(bytecode);
|
|
}
|
|
|
|
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* bytecode1 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
|
|
Toy_bindVM(&vm, bytecode1, 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(bytecode1);
|
|
Toy_freeVM(&vm);
|
|
Toy_resetPrintCallback();
|
|
return -1;
|
|
}
|
|
free(bytecode1);
|
|
|
|
//run 2
|
|
unsigned char* bytecode2 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
|
|
Toy_bindVM(&vm, bytecode2, 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(bytecode2);
|
|
Toy_freeVM(&vm);
|
|
Toy_resetPrintCallback();
|
|
return -1;
|
|
}
|
|
free(bytecode2);
|
|
|
|
//run 3
|
|
unsigned char* bytecode3 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
|
|
Toy_bindVM(&vm, bytecode3, 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(bytecode3);
|
|
Toy_freeVM(&vm);
|
|
Toy_resetPrintCallback();
|
|
return -1;
|
|
}
|
|
free(bytecode3);
|
|
|
|
//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;
|
|
} |