mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Toy_VM and Toy_Stack are working and tested, read more
At this point, only a minimal number of operations are working, and after running any kind of source code, the 'result' is simply left on the VM's stack. Still, it's awesome to see it reach this point.
This commit is contained in:
@@ -3,10 +3,17 @@
|
|||||||
//general utilities
|
//general utilities
|
||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
#include "toy_console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
#include "toy_memory.h"
|
||||||
|
|
||||||
//building blocks
|
//building blocks
|
||||||
//
|
#include "toy_value.h"
|
||||||
|
#include "toy_ast.h"
|
||||||
|
#include "toy_routine.h"
|
||||||
|
#include "toy_stack.h"
|
||||||
|
|
||||||
//pipeline
|
//pipeline
|
||||||
#include "toy_lexer.h"
|
#include "toy_lexer.h"
|
||||||
|
#include "toy_parser.h"
|
||||||
|
#include "toy_bytecode.h"
|
||||||
|
#include "toy_vm.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
//defined separately, as compilation can take several seconds, invalidating the comparisons of the given macros
|
//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() {
|
const char* Toy_private_version_build() {
|
||||||
return build;
|
return build;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ void Toy_pushStack(Toy_Stack* stack, Toy_Value value) {
|
|||||||
//expand the capacity if needed
|
//expand the capacity if needed
|
||||||
if (stack->count + 1 > stack->capacity) {
|
if (stack->count + 1 > stack->capacity) {
|
||||||
int oldCapacity = 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);
|
stack->ptr = TOY_GROW_ARRAY(Toy_Value, stack->ptr, oldCapacity, stack->capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
#include "toy_console_colors.h"
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
bool Toy_private_isTruthy(Toy_Value value) {
|
bool Toy_private_isTruthy(Toy_Value value) {
|
||||||
//null is an error
|
//null is an error
|
||||||
if (TOY_VALUE_IS_NULL(value)) {
|
if (TOY_VALUE_IS_NULL(value)) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: 'null' is neither true nor false\n" TOY_CC_RESET);
|
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
|
//only 'false' is falsy
|
||||||
@@ -19,3 +20,46 @@ bool Toy_private_isTruthy(Toy_Value value) {
|
|||||||
//anything else is truthy
|
//anything else is truthy
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,3 +54,5 @@ typedef struct Toy_Value {
|
|||||||
#define TOY_VALUE_IS_TRUTHY(value) Toy_private_isTruthy(value)
|
#define TOY_VALUE_IS_TRUTHY(value) Toy_private_isTruthy(value)
|
||||||
TOY_API bool Toy_private_isTruthy(Toy_Value 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);
|
||||||
|
|||||||
230
source/toy_vm.c
230
source/toy_vm.c
@@ -11,13 +11,13 @@
|
|||||||
|
|
||||||
//utilities
|
//utilities
|
||||||
#define READ_BYTE(vm) \
|
#define READ_BYTE(vm) \
|
||||||
vm->program[vm->programCounter++]
|
vm->routine[vm->routineCounter++]
|
||||||
|
|
||||||
#define READ_INT(vm) \
|
#define READ_INT(vm) \
|
||||||
*((int*)(vm->program + _read_postfix(&(vm->programCounter), 4)))
|
*((int*)(vm->routine + _read_postfix(&(vm->routineCounter), 4)))
|
||||||
|
|
||||||
#define READ_FLOAT(vm) \
|
#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) {
|
static inline int _read_postfix(int* ptr, int amount) {
|
||||||
int ret = *ptr;
|
int ret = *ptr;
|
||||||
@@ -26,8 +26,8 @@ static inline int _read_postfix(int* ptr, int amount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void fix_alignment(Toy_VM* vm) {
|
static inline void fix_alignment(Toy_VM* vm) {
|
||||||
if (vm->programCounter % 4 != 0) {
|
if (vm->routineCounter % 4 != 0) {
|
||||||
vm->programCounter = (4 - vm->programCounter % 4);
|
vm->routineCounter += (4 - vm->routineCounter % 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,79 +157,133 @@ static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) {
|
|||||||
Toy_pushStack(&vm->stack, result);
|
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) {
|
static void process(Toy_VM* vm) {
|
||||||
Toy_OpcodeType opcode = READ_BYTE(vm);
|
while(true) {
|
||||||
|
Toy_OpcodeType opcode = READ_BYTE(vm);
|
||||||
|
|
||||||
switch(opcode) {
|
switch(opcode) {
|
||||||
case TOY_OPCODE_READ:
|
case TOY_OPCODE_READ:
|
||||||
processRead(vm);
|
processRead(vm);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_OPCODE_ADD:
|
case TOY_OPCODE_ADD:
|
||||||
case TOY_OPCODE_SUBTRACT:
|
case TOY_OPCODE_SUBTRACT:
|
||||||
case TOY_OPCODE_MULTIPLY:
|
case TOY_OPCODE_MULTIPLY:
|
||||||
case TOY_OPCODE_DIVIDE:
|
case TOY_OPCODE_DIVIDE:
|
||||||
case TOY_OPCODE_MODULO:
|
case TOY_OPCODE_MODULO:
|
||||||
processArithmetic(vm, opcode);
|
processArithmetic(vm, opcode);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOY_OPCODE_COMPARE_EQUAL:
|
case TOY_OPCODE_COMPARE_EQUAL:
|
||||||
//
|
case TOY_OPCODE_COMPARE_LESS:
|
||||||
// break;
|
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:
|
case TOY_OPCODE_AND:
|
||||||
//
|
case TOY_OPCODE_OR:
|
||||||
// break;
|
case TOY_OPCODE_TRUTHY:
|
||||||
|
case TOY_OPCODE_NEGATE: //TODO: squeeze into !=
|
||||||
|
processLogical(vm, opcode);
|
||||||
|
break;
|
||||||
|
|
||||||
case TOY_OPCODE_COMPARE_LESS_EQUAL:
|
case TOY_OPCODE_LOAD:
|
||||||
//
|
case TOY_OPCODE_LOAD_LONG:
|
||||||
// break;
|
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:
|
case TOY_OPCODE_RETURN: //temp terminator, temp position
|
||||||
//
|
//
|
||||||
// break;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case TOY_OPCODE_COMPARE_GREATER_EQUAL:
|
//prepare for the next instruction
|
||||||
//
|
fix_alignment(vm);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//exposed functions
|
//exposed functions
|
||||||
void Toy_initVM(Toy_VM* vm) {
|
void Toy_initVM(Toy_VM* vm) {
|
||||||
vm->program = NULL;
|
vm->bc = NULL;
|
||||||
vm->programSize = 0;
|
vm->bcSize = 0;
|
||||||
|
|
||||||
|
vm->routine = NULL;
|
||||||
|
vm->routineSize = 0;
|
||||||
|
|
||||||
vm->paramCount = 0;
|
vm->paramCount = 0;
|
||||||
vm->jumpsCount = 0;
|
vm->jumpsCount = 0;
|
||||||
@@ -242,17 +296,47 @@ void Toy_initVM(Toy_VM* vm) {
|
|||||||
vm->dataAddr = 0;
|
vm->dataAddr = 0;
|
||||||
vm->subsAddr = 0;
|
vm->subsAddr = 0;
|
||||||
|
|
||||||
vm->programCounter = 0;
|
vm->routineCounter = 0;
|
||||||
|
|
||||||
//init the scope & stack
|
//init the scope & stack
|
||||||
Toy_initStack(&vm->stack);
|
Toy_initStack(&vm->stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_bindVM(Toy_VM* vm, unsigned char* program) {
|
void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, int bytecodeSize) {
|
||||||
vm->program = program;
|
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
|
//read the header metadata
|
||||||
vm->programSize = READ_INT(vm);
|
vm->routineSize = READ_INT(vm);
|
||||||
vm->paramCount = READ_INT(vm);
|
vm->paramCount = READ_INT(vm);
|
||||||
vm->jumpsCount = READ_INT(vm);
|
vm->jumpsCount = READ_INT(vm);
|
||||||
vm->dataCount = 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->paramAddr = READ_INT(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
vm->codeAddr = READ_INT(vm);
|
vm->codeAddr = READ_INT(vm); //required
|
||||||
|
|
||||||
if (vm->jumpsCount > 0) {
|
if (vm->jumpsCount > 0) {
|
||||||
vm->jumpsAddr = READ_INT(vm);
|
vm->jumpsAddr = READ_INT(vm);
|
||||||
@@ -284,8 +368,8 @@ void Toy_bindVM(Toy_VM* vm, unsigned char* program) {
|
|||||||
void Toy_runVM(Toy_VM* vm) {
|
void Toy_runVM(Toy_VM* vm) {
|
||||||
//TODO: read params into scope
|
//TODO: read params into scope
|
||||||
|
|
||||||
//prep the program counter for execution
|
//prep the routine counter for execution
|
||||||
vm->programCounter = vm->codeAddr;
|
vm->routineCounter = vm->codeAddr;
|
||||||
|
|
||||||
//begin
|
//begin
|
||||||
process(vm);
|
process(vm);
|
||||||
@@ -298,6 +382,6 @@ void Toy_freeVM(Toy_VM* vm) {
|
|||||||
//TODO: clear the scope
|
//TODO: clear the scope
|
||||||
|
|
||||||
//free the bytecode
|
//free the bytecode
|
||||||
TOY_FREE_ARRAY(unsigned char, vm->program, vm->programSize);
|
TOY_FREE_ARRAY(unsigned char, vm->bc, vm->bcSize);
|
||||||
Toy_initVM(vm);
|
Toy_initVM(vm);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,13 @@
|
|||||||
#include "toy_stack.h"
|
#include "toy_stack.h"
|
||||||
|
|
||||||
typedef struct Toy_VM {
|
typedef struct Toy_VM {
|
||||||
//bytecode - raw instructions that are being executed
|
//hold the raw bytecode
|
||||||
unsigned char* program;
|
unsigned char* bc;
|
||||||
int programSize;
|
int bcSize;
|
||||||
|
|
||||||
|
//raw instructions to be executed
|
||||||
|
unsigned char* routine;
|
||||||
|
int routineSize;
|
||||||
|
|
||||||
int paramCount;
|
int paramCount;
|
||||||
int jumpsCount;
|
int jumpsCount;
|
||||||
@@ -20,7 +24,7 @@ typedef struct Toy_VM {
|
|||||||
int dataAddr;
|
int dataAddr;
|
||||||
int subsAddr;
|
int subsAddr;
|
||||||
|
|
||||||
int programCounter;
|
int routineCounter;
|
||||||
|
|
||||||
//scope - block-level key/value pairs
|
//scope - block-level key/value pairs
|
||||||
|
|
||||||
@@ -29,7 +33,10 @@ typedef struct Toy_VM {
|
|||||||
} Toy_VM;
|
} Toy_VM;
|
||||||
|
|
||||||
TOY_API void Toy_initVM(Toy_VM* 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_runVM(Toy_VM* vm);
|
||||||
TOY_API void Toy_freeVM(Toy_VM* vm);
|
TOY_API void Toy_freeVM(Toy_VM* vm);
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
//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) {
|
if (offset % 4 != 0) {
|
||||||
offset += 4 - (offset % 4); //ceil
|
offset += 4 - (offset % 4); //ceil
|
||||||
}
|
}
|
||||||
|
|||||||
179
tests/cases/test_stack.c
Normal file
179
tests/cases/test_stack.c
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
#include "toy_stack.h"
|
||||||
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -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);
|
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
149
tests/cases/test_vm.c
Normal file
149
tests/cases/test_vm.c
Normal file
@@ -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 <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user