mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Functions are working, tests incomplete
This required a massive cross-cutting rework to the scope system, multiple subtle bugfixes and relearning of the parser internals, but it does appear that functions are working correctly. A few caveats: for now, parameters are always constant, regardless of type, return values can't be specified, and some script tests have been written. Most importantly, a key feature is working: closures.
This commit is contained in:
@@ -334,8 +334,6 @@ int repl(const char* filepath, bool verbose) {
|
|||||||
|
|
||||||
printf("%s> ", prompt); //shows the terminal prompt and begin
|
printf("%s> ", prompt); //shows the terminal prompt and begin
|
||||||
|
|
||||||
unsigned int runCount = 0; //used for initial preserveScope
|
|
||||||
|
|
||||||
//read from the terminal
|
//read from the terminal
|
||||||
while(fgets(inputBuffer, INPUT_BUFFER_SIZE, stdin)) {
|
while(fgets(inputBuffer, INPUT_BUFFER_SIZE, stdin)) {
|
||||||
//work around fgets() adding a newline
|
//work around fgets() adding a newline
|
||||||
@@ -366,9 +364,8 @@ int repl(const char* filepath, bool verbose) {
|
|||||||
printf("%s> ", prompt); //shows the terminal prompt
|
printf("%s> ", prompt); //shows the terminal prompt
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
unsigned char* bytecode = Toy_compileToBytecode(ast);
|
||||||
Toy_bindVM(&vm, bytecode, runCount++ > 0);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -466,7 +463,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
//run the compiled code
|
//run the compiled code
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
//BUG: Not yet functional
|
//tentatively functional
|
||||||
|
|
||||||
//example of the fibonacci sequence
|
//fibonacci sequence
|
||||||
fn fib(n: int) {
|
fn fib(n) {
|
||||||
if (n < 2) return n;
|
if (n < 2) return n;
|
||||||
return fib(n-1) + fib(n-2);
|
return fib(n-1) + fib(n-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: type coercion syntax hasn't been decided on yet, but it will be needed
|
print fib(12);
|
||||||
for (var i = 1; i <= 10; i++) {
|
|
||||||
print i .. ":" .. fib(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Note to my future self: yes, the base case in 'fib()' is 'n < 2', stop second guessing yourself!
|
//Note to my future self: yes, the base case in 'fib()' is 'n < 2', stop second guessing yourself!
|
||||||
//Note to my past self: don't tell me what to do!
|
//Note to my past self: don't tell me what to do!
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//BUG: Not yet functional
|
|
||||||
|
|
||||||
//find the leap years
|
//find the leap years
|
||||||
fn isLeapYear(n: int) {
|
fn isLeapYear(n: int) {
|
||||||
if (n % 400 == 0) return true;
|
if (n % 400 == 0) return true;
|
||||||
if (n % 100 == 0) return false;
|
if (n % 100 == 0) return false;
|
||||||
return n % 4 == 0;
|
return n % 4 == 0;
|
||||||
}
|
}
|
||||||
|
print isLeapYear(1999);
|
||||||
|
print isLeapYear(2000);
|
||||||
|
print isLeapYear(2004);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
//BUG: functions aren't working yet
|
|
||||||
fn makeCounter() {
|
fn makeCounter() {
|
||||||
var counter: int = 0;
|
var counter: int = 0;
|
||||||
|
|
||||||
@@ -14,7 +13,9 @@ var tally = makeCounter();
|
|||||||
while (true) {
|
while (true) {
|
||||||
var result = tally();
|
var result = tally();
|
||||||
|
|
||||||
if (result >= 10_000_000) {
|
print result;
|
||||||
|
|
||||||
|
if (result >= 10) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,6 +81,8 @@ typedef enum Toy_AstFlag {
|
|||||||
TOY_AST_FLAG_POSTFIX_INCREMENT = 43,
|
TOY_AST_FLAG_POSTFIX_INCREMENT = 43,
|
||||||
TOY_AST_FLAG_POSTFIX_DECREMENT = 44,
|
TOY_AST_FLAG_POSTFIX_DECREMENT = 44,
|
||||||
|
|
||||||
|
TOY_AST_FLAG_INVOKATION = 45,
|
||||||
|
|
||||||
// TOY_AST_FLAG_TERNARY,
|
// TOY_AST_FLAG_TERNARY,
|
||||||
} Toy_AstFlag;
|
} Toy_AstFlag;
|
||||||
|
|
||||||
|
|||||||
@@ -188,14 +188,16 @@ static unsigned int emitParameters(Toy_Bytecode* mb, Toy_Ast* ast) {
|
|||||||
total += emitParameters(mb, ast->aggregate.right);
|
total += emitParameters(mb, ast->aggregate.right);
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
else if (ast->type != TOY_AST_VALUE) {
|
else if (ast->type != TOY_AST_VAR_DECLARE) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown AST type passed to 'emitParameters()'\n" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown AST type passed to 'emitParameters()'\n" TOY_CC_RESET);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//the address within the data section
|
//the address within the data section
|
||||||
unsigned int dataAddr = emitCStringToData(&(mb->data), &(mb->dataCapacity), &(mb->dataCount), TOY_VALUE_AS_STRING(ast->value.value)->leaf.data);
|
char buffer[128];
|
||||||
|
snprintf(buffer, 128, "%.*s", ast->varDeclare.name->info.length, ast->varDeclare.name->leaf.data);
|
||||||
|
unsigned int dataAddr = emitCStringToData(&(mb->data), &(mb->dataCapacity), &(mb->dataCount), buffer);
|
||||||
|
|
||||||
//check the param index for that entry i.e. don't reuse parameter names
|
//check the param index for that entry i.e. don't reuse parameter names
|
||||||
for (unsigned int i = 0; i < mb->paramCount; i++) {
|
for (unsigned int i = 0; i < mb->paramCount; i++) {
|
||||||
@@ -209,7 +211,7 @@ static unsigned int emitParameters(Toy_Bytecode* mb, Toy_Ast* ast) {
|
|||||||
|
|
||||||
//emit to the param index
|
//emit to the param index
|
||||||
EMIT_INT(&mb, param, dataAddr);
|
EMIT_INT(&mb, param, dataAddr);
|
||||||
EMIT_INT(&mb, param, ast->value.value.type);
|
EMIT_INT(&mb, param, ast->varDeclare.valueType); //'constant' is lost, but that's fine for params
|
||||||
|
|
||||||
//this returns the number of written parameters
|
//this returns the number of written parameters
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
#include "toy_function.h"
|
#include "toy_function.h"
|
||||||
|
|
||||||
Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode) {
|
Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode, Toy_Scope* parentScope) {
|
||||||
Toy_Function* fn = (Toy_Function*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Function));
|
Toy_Function* fn = (Toy_Function*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Function));
|
||||||
|
|
||||||
fn->type = TOY_FUNCTION_CUSTOM;
|
fn->type = TOY_FUNCTION_CUSTOM;
|
||||||
fn->bytecode.code = bytecode;
|
fn->bytecode.code = bytecode;
|
||||||
|
fn->bytecode.parentScope = parentScope;
|
||||||
|
Toy_private_incrementScopeRefCount(fn->bytecode.parentScope);
|
||||||
|
|
||||||
return fn;
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOY_API void Toy_freeFunction(Toy_Function* fn) {
|
||||||
|
if (fn->type == TOY_FUNCTION_CUSTOM) {
|
||||||
|
Toy_private_decrementScopeRefCount(fn->bytecode.parentScope);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
#include "toy_bucket.h"
|
#include "toy_bucket.h"
|
||||||
|
#include "toy_scope.h"
|
||||||
|
|
||||||
typedef enum Toy_FunctionType {
|
typedef enum Toy_FunctionType {
|
||||||
TOY_FUNCTION_CUSTOM,
|
TOY_FUNCTION_CUSTOM,
|
||||||
@@ -11,6 +12,7 @@ typedef enum Toy_FunctionType {
|
|||||||
typedef struct Toy_FunctionBytecode {
|
typedef struct Toy_FunctionBytecode {
|
||||||
Toy_FunctionType type;
|
Toy_FunctionType type;
|
||||||
unsigned char* code;
|
unsigned char* code;
|
||||||
|
Toy_Scope* parentScope;
|
||||||
} Toy_FunctionBytecode;
|
} Toy_FunctionBytecode;
|
||||||
|
|
||||||
typedef struct Toy_FunctionNative {
|
typedef struct Toy_FunctionNative {
|
||||||
@@ -24,4 +26,6 @@ typedef union Toy_Function_t {
|
|||||||
Toy_FunctionNative native;
|
Toy_FunctionNative native;
|
||||||
} Toy_Function;
|
} Toy_Function;
|
||||||
|
|
||||||
TOY_API Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode);
|
TOY_API Toy_Function* Toy_createFunctionFromBytecode(Toy_Bucket** bucketHandle, unsigned char* bytecode, Toy_Scope* parentScope);
|
||||||
|
|
||||||
|
TOY_API void Toy_freeFunction(Toy_Function* fn);
|
||||||
@@ -731,7 +731,7 @@ static Toy_AstFlag invoke(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
|
|||||||
//finally, emit the call as an Ast
|
//finally, emit the call as an Ast
|
||||||
Toy_private_emitAstFunctionInvokation(bucketHandle, rootHandle, args);
|
Toy_private_emitAstFunctionInvokation(bucketHandle, rootHandle, args);
|
||||||
|
|
||||||
return TOY_AST_FLAG_NONE;
|
return TOY_AST_FLAG_INVOKATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
//grammar rules
|
//grammar rules
|
||||||
@@ -964,25 +964,23 @@ static void makeFunctionDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p
|
|||||||
advance(parser);
|
advance(parser);
|
||||||
Toy_Token nameToken = parser->previous;
|
Toy_Token nameToken = parser->previous;
|
||||||
|
|
||||||
//TODO: fix this with param type info
|
//URGENT: fix this with param type info
|
||||||
//read the type specifier if present
|
//read the type specifier if present
|
||||||
// Toy_ValueType varType = TOY_VALUE_ANY;
|
Toy_ValueType varType = TOY_VALUE_ANY;
|
||||||
// bool constant = true; //parameters are immutable
|
bool constant = true; //parameters are immutable
|
||||||
|
|
||||||
if (match(parser, TOY_TOKEN_OPERATOR_COLON)) {
|
if (match(parser, TOY_TOKEN_OPERATOR_COLON)) {
|
||||||
// varType = readType(parser);
|
varType = readType(parser);
|
||||||
readType(parser);
|
|
||||||
|
|
||||||
if (match(parser, TOY_TOKEN_KEYWORD_CONST)) {
|
if (match(parser, TOY_TOKEN_KEYWORD_CONST)) {
|
||||||
// constant = true;
|
constant = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//emit the parameter as a name string
|
//emit the parameter as a var declaration
|
||||||
Toy_String* name = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length);
|
|
||||||
Toy_Value value = TOY_VALUE_FROM_STRING(name);
|
|
||||||
Toy_Ast* ast = NULL;
|
Toy_Ast* ast = NULL;
|
||||||
Toy_private_emitAstValue(bucketHandle, &ast, value); //TODO: params with type info
|
Toy_String* name = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length);
|
||||||
|
Toy_private_emitAstVariableDeclaration(bucketHandle, &ast, name, varType, constant, NULL);
|
||||||
|
|
||||||
//add to the params aggregate (is added backwards, because weird)
|
//add to the params aggregate (is added backwards, because weird)
|
||||||
Toy_private_emitAstAggregate(bucketHandle, ¶ms, TOY_AST_FLAG_COLLECTION, ast);
|
Toy_private_emitAstAggregate(bucketHandle, ¶ms, TOY_AST_FLAG_COLLECTION, ast);
|
||||||
|
|||||||
@@ -8,34 +8,6 @@
|
|||||||
#include "toy_print.h"
|
#include "toy_print.h"
|
||||||
|
|
||||||
//utils
|
//utils
|
||||||
static void incrementRefCount(Toy_Scope* scope) {
|
|
||||||
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
|
||||||
//check for issues
|
|
||||||
if (iter->next != NULL && iter->next->refCount == 0) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope's ancestor has a refcount of 0'\n" TOY_CC_RESET);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
iter->refCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void decrementRefCount(Toy_Scope* scope) {
|
|
||||||
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
|
||||||
iter->refCount--;
|
|
||||||
if (iter->refCount == 0 && iter->data != NULL) {
|
|
||||||
//free the scope entries when this scope is no longer needed
|
|
||||||
for (unsigned int i = 0; i < iter->capacity; i++) {
|
|
||||||
if (iter->data[i].psl > 0) {
|
|
||||||
Toy_freeString(&(iter->data[i].key));
|
|
||||||
Toy_freeValue(iter->data[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(iter->data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Toy_ScopeEntry* lookupScopeEntryPtr(Toy_Scope* scope, Toy_String* key, unsigned int hash, bool recursive) {
|
static Toy_ScopeEntry* lookupScopeEntryPtr(Toy_Scope* scope, Toy_String* key, unsigned int hash, bool recursive) {
|
||||||
//terminate
|
//terminate
|
||||||
if (scope == NULL || scope->data == NULL) {
|
if (scope == NULL || scope->data == NULL) {
|
||||||
@@ -61,7 +33,7 @@ static Toy_ScopeEntry* lookupScopeEntryPtr(Toy_Scope* scope, Toy_String* key, un
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void probeAndInsert(Toy_Scope* scope, Toy_String* key, Toy_Value value, Toy_ValueType type, bool constant) {
|
static void probeAndInsert(Toy_Scope* scope, Toy_String* key, Toy_Value value, Toy_ValueType type, bool constant) {
|
||||||
//make the entry
|
//make the entry
|
||||||
unsigned int probe = Toy_hashString(key) % scope->capacity;
|
unsigned int probe = Toy_hashString(key) % scope->capacity;
|
||||||
Toy_ScopeEntry entry = (Toy_ScopeEntry){ .key = *key, .value = value, .type = type, .constant = constant, .psl = 1 };
|
Toy_ScopeEntry entry = (Toy_ScopeEntry){ .key = *key, .value = value, .type = type, .constant = constant, .psl = 1 };
|
||||||
@@ -97,7 +69,7 @@ void probeAndInsert(Toy_Scope* scope, Toy_String* key, Toy_Value value, Toy_Valu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_ScopeEntry* adjustScopeEntries(Toy_Scope* scope, unsigned int newCapacity) {
|
static Toy_ScopeEntry* adjustScopeEntries(Toy_Scope* scope, unsigned int newCapacity) {
|
||||||
//allocate and zero a new Toy_ScopeEntry array in memory
|
//allocate and zero a new Toy_ScopeEntry array in memory
|
||||||
Toy_ScopeEntry* newEntries = malloc(newCapacity * sizeof(Toy_ScopeEntry));
|
Toy_ScopeEntry* newEntries = malloc(newCapacity * sizeof(Toy_ScopeEntry));
|
||||||
|
|
||||||
@@ -142,7 +114,7 @@ Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
|
|||||||
newScope->count = 0;
|
newScope->count = 0;
|
||||||
newScope->maxPsl = 0;
|
newScope->maxPsl = 0;
|
||||||
|
|
||||||
incrementRefCount(newScope);
|
Toy_private_incrementScopeRefCount(newScope);
|
||||||
|
|
||||||
return newScope;
|
return newScope;
|
||||||
}
|
}
|
||||||
@@ -152,7 +124,7 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
decrementRefCount(scope);
|
Toy_private_decrementScopeRefCount(scope);
|
||||||
return scope->next;
|
return scope->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +141,7 @@ void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy
|
|||||||
//type check
|
//type check
|
||||||
if (type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && type != value.type && value.type != TOY_VALUE_REFERENCE) {
|
if (type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && type != value.type && value.type != TOY_VALUE_REFERENCE) {
|
||||||
char buffer[key->info.length + 256];
|
char buffer[key->info.length + 256];
|
||||||
sprintf(buffer, "Incorrect value type in declaration of '%s' (expected %s, got %s)", key->leaf.data, Toy_private_getValueTypeAsCString(type), Toy_private_getValueTypeAsCString(value.type));
|
sprintf(buffer, "Incorrect value type in declaration of '%.*s' (expected %s, got %s)", key->info.length, key->leaf.data, Toy_private_getValueTypeAsCString(type), Toy_private_getValueTypeAsCString(value.type));
|
||||||
Toy_error(buffer);
|
Toy_error(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -190,7 +162,7 @@ void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
|
|||||||
//type check
|
//type check
|
||||||
if (entryPtr->type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && entryPtr->type != value.type && value.type != TOY_VALUE_REFERENCE) {
|
if (entryPtr->type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && entryPtr->type != value.type && value.type != TOY_VALUE_REFERENCE) {
|
||||||
char buffer[key->info.length + 256];
|
char buffer[key->info.length + 256];
|
||||||
sprintf(buffer, "Incorrect value type in assignment of '%s' (expected %s, got %s)", key->leaf.data, Toy_private_getValueTypeAsCString(entryPtr->type), Toy_private_getValueTypeAsCString(value.type));
|
sprintf(buffer, "Incorrect value type in assignment of '%.*s' (expected %s, got %s)", key->info.length, key->leaf.data, Toy_private_getValueTypeAsCString(entryPtr->type), Toy_private_getValueTypeAsCString(value.type));
|
||||||
Toy_error(buffer);
|
Toy_error(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -223,3 +195,31 @@ bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key) {
|
|||||||
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true);
|
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true);
|
||||||
return entryPtr != NULL;
|
return entryPtr != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Toy_private_incrementScopeRefCount(Toy_Scope* scope) {
|
||||||
|
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
||||||
|
//check for issues
|
||||||
|
if (iter->next != NULL && iter->next->refCount == 0) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope's ancestor has a refcount of 0'\n" TOY_CC_RESET);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
iter->refCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Toy_private_decrementScopeRefCount(Toy_Scope* scope) {
|
||||||
|
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
||||||
|
iter->refCount--;
|
||||||
|
if (iter->refCount == 0 && iter->data != NULL) {
|
||||||
|
//free the scope entries when this scope is no longer needed
|
||||||
|
for (unsigned int i = 0; i < iter->capacity; i++) {
|
||||||
|
if (iter->data[i].psl > 0) {
|
||||||
|
Toy_freeString(&(iter->data[i].key));
|
||||||
|
Toy_freeValue(iter->data[i].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(iter->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,10 @@ TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key);
|
|||||||
|
|
||||||
TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);
|
TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);
|
||||||
|
|
||||||
|
//manage refcounting
|
||||||
|
TOY_API void Toy_private_incrementScopeRefCount(Toy_Scope* scope);
|
||||||
|
TOY_API void Toy_private_decrementScopeRefCount(Toy_Scope* scope);
|
||||||
|
|
||||||
//some useful sizes, could be swapped out as needed
|
//some useful sizes, could be swapped out as needed
|
||||||
#ifndef TOY_SCOPE_INITIAL_CAPACITY
|
#ifndef TOY_SCOPE_INITIAL_CAPACITY
|
||||||
#define TOY_SCOPE_INITIAL_CAPACITY 8
|
#define TOY_SCOPE_INITIAL_CAPACITY 8
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ void Toy_freeValue(Toy_Value value) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case TOY_VALUE_FUNCTION:
|
case TOY_VALUE_FUNCTION:
|
||||||
//not sure this needs to be freed
|
Toy_freeFunction(value.as.function);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case TOY_VALUE_OPAQUE:
|
case TOY_VALUE_OPAQUE:
|
||||||
|
|||||||
@@ -151,14 +151,15 @@ static void processRead(Toy_VM* vm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case TOY_VALUE_FUNCTION: {
|
case TOY_VALUE_FUNCTION: {
|
||||||
// unsigned int paramCount = (unsigned int)READ_BYTE(vm); //unused
|
unsigned int paramCount = (unsigned int)READ_BYTE(vm); //unused
|
||||||
|
(void)paramCount;
|
||||||
|
|
||||||
fixAlignment(vm);
|
fixAlignment(vm);
|
||||||
|
|
||||||
unsigned int addr = (unsigned int)READ_INT(vm);
|
unsigned int addr = (unsigned int)READ_INT(vm);
|
||||||
|
|
||||||
//create and push the function value
|
//create and push the function value
|
||||||
Toy_Function* function = Toy_createFunctionFromBytecode(&vm->memoryBucket, vm->code + vm->subsAddr + addr);
|
Toy_Function* function = Toy_createFunctionFromBytecode(&vm->memoryBucket, vm->code + vm->subsAddr + addr, vm->scope);
|
||||||
value = TOY_VALUE_FROM_FUNCTION(function);
|
value = TOY_VALUE_FROM_FUNCTION(function);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -396,7 +397,7 @@ static void processInvoke(Toy_VM* vm) {
|
|||||||
//spin up a new sub-vm
|
//spin up a new sub-vm
|
||||||
Toy_VM subVM;
|
Toy_VM subVM;
|
||||||
Toy_inheritVM(&subVM, vm);
|
Toy_inheritVM(&subVM, vm);
|
||||||
Toy_bindVM(&subVM, fn->bytecode.code, false);
|
Toy_bindVM(&subVM, fn->bytecode.code, fn->bytecode.parentScope);
|
||||||
|
|
||||||
//check args count
|
//check args count
|
||||||
if (argCount * 8 != subVM.paramCount) {
|
if (argCount * 8 != subVM.paramCount) {
|
||||||
@@ -1104,7 +1105,7 @@ void Toy_inheritVM(Toy_VM* vm, Toy_VM* parent) {
|
|||||||
Toy_resetVM(vm, true);
|
Toy_resetVM(vm, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, bool preserveScope) {
|
void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, Toy_Scope* parentScope) {
|
||||||
vm->code = bytecode; //set code, so it can be read
|
vm->code = bytecode; //set code, so it can be read
|
||||||
|
|
||||||
(void)READ_UNSIGNED_INT(vm); //global header
|
(void)READ_UNSIGNED_INT(vm); //global header
|
||||||
@@ -1131,8 +1132,8 @@ void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, bool preserveScope) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//scopes
|
//scopes
|
||||||
if (preserveScope == false) {
|
if (vm->scope == NULL) {
|
||||||
vm->scope = Toy_pushScope(&vm->memoryBucket, NULL);
|
vm->scope = Toy_pushScope(&vm->memoryBucket, parentScope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ TOY_API void Toy_resetVM(Toy_VM* vm, bool preserveScope);
|
|||||||
TOY_API void Toy_initVM(Toy_VM* vm); //creates memory
|
TOY_API void Toy_initVM(Toy_VM* vm); //creates memory
|
||||||
TOY_API void Toy_inheritVM(Toy_VM* vm, Toy_VM* parent); //inherits scope bucket
|
TOY_API void Toy_inheritVM(Toy_VM* vm, Toy_VM* parent); //inherits scope bucket
|
||||||
|
|
||||||
void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, bool preserveScope);
|
void Toy_bindVM(Toy_VM* vm, unsigned char* bytecode, Toy_Scope* parentScope);
|
||||||
TOY_API unsigned int Toy_runVM(Toy_VM* vm);
|
TOY_API unsigned int Toy_runVM(Toy_VM* vm);
|
||||||
TOY_API void Toy_freeVM(Toy_VM* vm);
|
TOY_API void Toy_freeVM(Toy_VM* vm);
|
||||||
|
|
||||||
|
|||||||
22
tests/scripts/test_closures.toy
Normal file
22
tests/scripts/test_closures.toy
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//closures
|
||||||
|
fn makeCounter() {
|
||||||
|
var counter: int = 0;
|
||||||
|
|
||||||
|
fn increment() {
|
||||||
|
return ++counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tally = makeCounter();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var result = tally();
|
||||||
|
|
||||||
|
print result;
|
||||||
|
|
||||||
|
if (result >= 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
tests/scripts/test_fibonacci.toy
Normal file
20
tests/scripts/test_fibonacci.toy
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
fn fib(n) {
|
||||||
|
if (n < 2) return n;
|
||||||
|
return fib(n-1) + fib(n-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert fib(1) == 1;
|
||||||
|
assert fib(2) == 1;
|
||||||
|
assert fib(3) == 2;
|
||||||
|
assert fib(4) == 3;
|
||||||
|
assert fib(5) == 5;
|
||||||
|
assert fib(6) == 8;
|
||||||
|
assert fib(7) == 13;
|
||||||
|
assert fib(8) == 21;
|
||||||
|
assert fib(9) == 34;
|
||||||
|
assert fib(10) == 55;
|
||||||
|
assert fib(11) == 89;
|
||||||
|
assert fib(12) == 144;
|
||||||
|
|
||||||
|
print "Fibonacci passed";
|
||||||
9
tests/units/test_function.c
Normal file
9
tests/units/test_function.c
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include "toy_console_colors.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ int test_setup_and_teardown(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//check the bytecode was loaded correctly
|
//check the bytecode was loaded correctly
|
||||||
if (
|
if (
|
||||||
@@ -86,7 +86,7 @@ int test_simple_execution(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -132,7 +132,7 @@ int test_opcode_not_equal(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -188,7 +188,7 @@ int test_keyword_assert(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -232,7 +232,7 @@ int test_keyword_assert(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -277,7 +277,7 @@ int test_keyword_assert(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -326,7 +326,7 @@ int test_keyword_print(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -371,7 +371,7 @@ int test_keyword_print(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -416,7 +416,7 @@ int test_keyword_print(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -465,7 +465,7 @@ int test_keyword_ifThenElse(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -510,7 +510,7 @@ int test_keyword_ifThenElse(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -554,7 +554,7 @@ int test_keyword_ifThenElse(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -599,7 +599,7 @@ int test_keyword_ifThenElse(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -647,7 +647,7 @@ int test_scope(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -695,7 +695,7 @@ int test_scope(Toy_Bucket** bucketHandle) {
|
|||||||
//run the setup
|
//run the setup
|
||||||
Toy_VM vm;
|
Toy_VM vm;
|
||||||
Toy_initVM(&vm);
|
Toy_initVM(&vm);
|
||||||
Toy_bindVM(&vm, bytecode, false);
|
Toy_bindVM(&vm, bytecode, NULL);
|
||||||
|
|
||||||
//run
|
//run
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
@@ -737,7 +737,7 @@ int test_vm_reuse(Toy_Bucket** bucketHandle) {
|
|||||||
|
|
||||||
//run 1
|
//run 1
|
||||||
unsigned char* bytecode1 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
|
unsigned char* bytecode1 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
|
||||||
Toy_bindVM(&vm, bytecode1, false);
|
Toy_bindVM(&vm, bytecode1, NULL);
|
||||||
|
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
Toy_resetVM(&vm, true);
|
Toy_resetVM(&vm, true);
|
||||||
@@ -757,7 +757,7 @@ int test_vm_reuse(Toy_Bucket** bucketHandle) {
|
|||||||
|
|
||||||
//run 2
|
//run 2
|
||||||
unsigned char* bytecode2 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
|
unsigned char* bytecode2 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
|
||||||
Toy_bindVM(&vm, bytecode2, true); //preserve during repeated calls
|
Toy_bindVM(&vm, bytecode2, NULL); //preserve during repeated calls
|
||||||
|
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
Toy_resetVM(&vm, true);
|
Toy_resetVM(&vm, true);
|
||||||
@@ -777,7 +777,7 @@ int test_vm_reuse(Toy_Bucket** bucketHandle) {
|
|||||||
|
|
||||||
//run 3
|
//run 3
|
||||||
unsigned char* bytecode3 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
|
unsigned char* bytecode3 = makeCodeFromSource(bucketHandle, "print \"Hello world!\";");
|
||||||
Toy_bindVM(&vm, bytecode3, true); //preserve during repeated calls
|
Toy_bindVM(&vm, bytecode3, NULL); //preserve during repeated calls
|
||||||
|
|
||||||
Toy_runVM(&vm);
|
Toy_runVM(&vm);
|
||||||
Toy_resetVM(&vm, true);
|
Toy_resetVM(&vm, true);
|
||||||
|
|||||||
Reference in New Issue
Block a user