mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
WIP return keyword, read more
Functions are having issues with being copied around, especially between buckets, leading to the scopes getting looped. The program gets stuck in 'incrementRefCount()'. It's past my time limit, so I'll keep working on it tomorrow with a fresh mind. All function stuff is still untested. See #163
This commit is contained in:
@@ -163,6 +163,15 @@ void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle)
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
tmp->type = TOY_AST_RETURN;
|
||||
tmp->fnReturn.child = (*astHandle);
|
||||
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef enum Toy_AstType {
|
||||
TOY_AST_WHILE_THEN,
|
||||
TOY_AST_BREAK,
|
||||
TOY_AST_CONTINUE,
|
||||
TOY_AST_RETURN,
|
||||
TOY_AST_PRINT,
|
||||
|
||||
TOY_AST_VAR_DECLARE,
|
||||
@@ -171,6 +172,11 @@ typedef struct Toy_AstContinue {
|
||||
Toy_AstType type;
|
||||
} Toy_AstContinue;
|
||||
|
||||
typedef struct Toy_AstReturn {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* child;
|
||||
} Toy_AstReturn;
|
||||
|
||||
typedef struct Toy_AstPrint {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* child;
|
||||
@@ -235,6 +241,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
|
||||
Toy_AstWhileThen whileThen;
|
||||
Toy_AstBreak breakPoint;
|
||||
Toy_AstContinue continuePoint;
|
||||
Toy_AstReturn fnReturn;
|
||||
Toy_AstPrint print;
|
||||
Toy_AstVarDeclare varDeclare;
|
||||
Toy_AstVarAssign varAssign;
|
||||
@@ -263,6 +270,7 @@ void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandl
|
||||
void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch);
|
||||
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
|
||||
void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
|
||||
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
|
||||
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr);
|
||||
|
||||
@@ -752,6 +752,21 @@ static unsigned int writeInstructionContinue(Toy_ModuleCompiler** mb, Toy_AstCon
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionReturn(Toy_ModuleCompiler** mb, Toy_AstReturn ast) {
|
||||
//the things to return
|
||||
unsigned int retCount = writeModuleCompilerCode(mb, ast.child);
|
||||
|
||||
//output the print opcode
|
||||
EMIT_BYTE(mb, code,TOY_OPCODE_RETURN);
|
||||
|
||||
//4-byte alignment
|
||||
EMIT_BYTE(mb, code,(unsigned char)retCount);
|
||||
EMIT_BYTE(mb, code,0);
|
||||
EMIT_BYTE(mb, code,0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int writeInstructionPrint(Toy_ModuleCompiler** mb, Toy_AstPrint ast) {
|
||||
//the thing to print
|
||||
writeModuleCompilerCode(mb, ast.child);
|
||||
@@ -1158,6 +1173,10 @@ static unsigned int writeModuleCompilerCode(Toy_ModuleCompiler** mb, Toy_Ast* as
|
||||
result += writeInstructionContinue(mb, ast->continuePoint);
|
||||
break;
|
||||
|
||||
case TOY_AST_RETURN:
|
||||
result += writeInstructionReturn(mb, ast->fnReturn);
|
||||
break;
|
||||
|
||||
case TOY_AST_PRINT:
|
||||
result += writeInstructionPrint(mb, ast->print);
|
||||
break;
|
||||
|
||||
@@ -881,6 +881,13 @@ static void makeContinueStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_
|
||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of continue statement");
|
||||
}
|
||||
|
||||
static void makeReturnStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //expect an aggregate
|
||||
Toy_private_emitAstReturn(bucketHandle, rootHandle);
|
||||
|
||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of return statement");
|
||||
}
|
||||
|
||||
static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
makeExpr(bucketHandle, parser, rootHandle);
|
||||
Toy_private_emitAstPrint(bucketHandle, rootHandle);
|
||||
@@ -1026,9 +1033,7 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
||||
return;
|
||||
}
|
||||
|
||||
//for-pre-clause-post-then
|
||||
//return
|
||||
//import
|
||||
//TODO: for-pre-clause-post-then
|
||||
|
||||
//break
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_BREAK)) {
|
||||
@@ -1042,6 +1047,14 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
||||
return;
|
||||
}
|
||||
|
||||
//return
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_RETURN)) {
|
||||
makeReturnStmt(bucketHandle, parser, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: import
|
||||
|
||||
//print
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) {
|
||||
makePrintStmt(bucketHandle, parser, rootHandle);
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
//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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,3 +27,5 @@ TOY_API void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value)
|
||||
TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key);
|
||||
|
||||
TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);
|
||||
|
||||
//TODO: delcare with a custom table (game engine entities)
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "toy_string.h"
|
||||
#include "toy_array.h"
|
||||
#include "toy_table.h"
|
||||
#include "toy_function.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -136,7 +137,7 @@ Toy_Value Toy_copyValue(Toy_Value value) {
|
||||
return TOY_VALUE_FROM_TABLE(result);
|
||||
}
|
||||
case TOY_VALUE_FUNCTION:
|
||||
return value;
|
||||
// return value; //URGENT: concerning
|
||||
|
||||
case TOY_VALUE_OPAQUE:
|
||||
case TOY_VALUE_ANY:
|
||||
@@ -150,6 +151,70 @@ Toy_Value Toy_copyValue(Toy_Value value) {
|
||||
return TOY_VALUE_FROM_NULL();
|
||||
}
|
||||
|
||||
Toy_Value Toy_deepCopyValue(struct Toy_Bucket** bucketHandle, Toy_Value value) {
|
||||
//this should be the same as Toy_copyValue(), but it forces a deep copy for the strings
|
||||
MAYBE_UNWRAP(value);
|
||||
|
||||
switch(value.type) {
|
||||
case TOY_VALUE_NULL:
|
||||
case TOY_VALUE_BOOLEAN:
|
||||
case TOY_VALUE_INTEGER:
|
||||
case TOY_VALUE_FLOAT:
|
||||
return value;
|
||||
|
||||
case TOY_VALUE_STRING: {
|
||||
return TOY_VALUE_FROM_STRING(Toy_deepCopyString(bucketHandle, value.as.string));
|
||||
}
|
||||
|
||||
case TOY_VALUE_ARRAY: {
|
||||
//arrays probably won't get copied much
|
||||
Toy_Array* ptr = value.as.array;
|
||||
Toy_Array* result = Toy_resizeArray(NULL, ptr->capacity);
|
||||
|
||||
for (unsigned int i = 0; i < ptr->count; i++) {
|
||||
result->data[i] = Toy_deepCopyValue(bucketHandle, ptr->data[i]);
|
||||
}
|
||||
|
||||
result->capacity = ptr->capacity;
|
||||
result->count = ptr->count;
|
||||
|
||||
return TOY_VALUE_FROM_ARRAY(result);
|
||||
}
|
||||
|
||||
case TOY_VALUE_TABLE: {
|
||||
//tables probably won't get copied much
|
||||
Toy_Table* ptr = value.as.table;
|
||||
Toy_Table* result = Toy_private_adjustTableCapacity(NULL, ptr->capacity);
|
||||
|
||||
for (unsigned int i = 0; i < ptr->capacity; i++) {
|
||||
if (TOY_VALUE_IS_NULL(ptr->data[i].key) != true) {
|
||||
result->data[i].key = Toy_deepCopyValue(bucketHandle, ptr->data[i].key);
|
||||
result->data[i].value = Toy_deepCopyValue(bucketHandle, ptr->data[i].value);
|
||||
}
|
||||
}
|
||||
|
||||
result->capacity = ptr->capacity;
|
||||
result->count = ptr->count;
|
||||
|
||||
return TOY_VALUE_FROM_TABLE(result);
|
||||
}
|
||||
case TOY_VALUE_FUNCTION: {
|
||||
Toy_Function* fn = Toy_createModuleFunction(bucketHandle, TOY_VALUE_AS_FUNCTION(value)->module.module); //URGENT: concerning
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
}
|
||||
|
||||
case TOY_VALUE_OPAQUE:
|
||||
case TOY_VALUE_ANY:
|
||||
case TOY_VALUE_REFERENCE:
|
||||
case TOY_VALUE_UNKNOWN:
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't deep-copy an unknown value type, exiting\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//dummy return
|
||||
return TOY_VALUE_FROM_NULL();
|
||||
}
|
||||
|
||||
void Toy_freeValue(Toy_Value value) {
|
||||
switch(value.type) {
|
||||
case TOY_VALUE_NULL:
|
||||
|
||||
@@ -82,6 +82,7 @@ TOY_API Toy_Value Toy_unwrapValue(Toy_Value value);
|
||||
TOY_API unsigned int Toy_hashValue(Toy_Value value);
|
||||
|
||||
TOY_API Toy_Value Toy_copyValue(Toy_Value value);
|
||||
TOY_API Toy_Value Toy_deepCopyValue(struct Toy_Bucket** bucketHandle, Toy_Value value); //don't use refcounting
|
||||
TOY_API void Toy_freeValue(Toy_Value value);
|
||||
|
||||
TOY_API bool Toy_checkValueIsTruthy(Toy_Value value);
|
||||
|
||||
@@ -360,6 +360,8 @@ static void processAccess(Toy_VM* vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
//URGENT: should I loop functions into the reference system?
|
||||
|
||||
//in the event of a certain subset of types, create references instead (these should only exist on the stack)
|
||||
if (TOY_VALUE_IS_REFERENCE(*valuePtr) || TOY_VALUE_IS_ARRAY(*valuePtr) || TOY_VALUE_IS_TABLE(*valuePtr)) {
|
||||
Toy_Value ref = TOY_REFERENCE_FROM_POINTER(valuePtr);
|
||||
@@ -433,8 +435,23 @@ static void processInvoke(Toy_VM* vm) {
|
||||
Toy_declareScope(subVM.scope, name, argValue);
|
||||
}
|
||||
|
||||
//run and cleanup
|
||||
Toy_runVM(&subVM);
|
||||
//run
|
||||
unsigned int resultCount = Toy_runVM(&subVM);
|
||||
|
||||
//extract and store any results
|
||||
if (resultCount > 0) {
|
||||
Toy_Array* results = Toy_extractResultsFromVM(&vm->literalBucket, &subVM, resultCount);
|
||||
|
||||
for (unsigned int i = 0; i < results->count; i++) {
|
||||
//NOTE: since the results array is being immediately freed, just push each element without a call to copy
|
||||
Toy_pushStack(&vm->stack, results->data[i]);
|
||||
}
|
||||
|
||||
//a bit naughty
|
||||
free(results);
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeVM(&subVM);
|
||||
}
|
||||
break;
|
||||
@@ -628,6 +645,14 @@ static void processLogical(Toy_VM* vm, Toy_OpcodeType opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int processReturn(Toy_VM* vm) {
|
||||
//the values to be returned are waiting on the stack
|
||||
unsigned int resultCount = (unsigned int)READ_BYTE(vm);
|
||||
fixAlignment(vm);
|
||||
|
||||
return resultCount;
|
||||
}
|
||||
|
||||
static void processJump(Toy_VM* vm) {
|
||||
Toy_OpParamJumpType type = READ_BYTE(vm);
|
||||
Toy_OpParamJumpConditional cond = READ_BYTE(vm);
|
||||
@@ -926,7 +951,7 @@ static void processIndex(Toy_VM* vm) {
|
||||
Toy_freeValue(length);
|
||||
}
|
||||
|
||||
static void process(Toy_VM* vm) {
|
||||
static unsigned int process(Toy_VM* vm) {
|
||||
while(true) {
|
||||
//prep by aligning to the 4-byte word
|
||||
fixAlignment(vm);
|
||||
@@ -995,8 +1020,7 @@ static void process(Toy_VM* vm) {
|
||||
|
||||
//control instructions
|
||||
case TOY_OPCODE_RETURN:
|
||||
//temp terminator
|
||||
return;
|
||||
return processReturn(vm); //the only return statement, which signals the number of values for extraction
|
||||
|
||||
case TOY_OPCODE_JUMP:
|
||||
processJump(vm);
|
||||
@@ -1074,6 +1098,8 @@ void Toy_initVM(Toy_VM* vm) {
|
||||
vm->literalBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
vm->scopeBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
|
||||
vm->scopeBucketHandle = NULL; //not used
|
||||
|
||||
Toy_resetVM(vm, true);
|
||||
}
|
||||
|
||||
@@ -1081,10 +1107,10 @@ void Toy_inheritVM(Toy_VM* vm, Toy_VM* parent) {
|
||||
//inherent persistent memory
|
||||
vm->scope = NULL;
|
||||
vm->stack = Toy_allocateStack();
|
||||
vm->literalBucket = parent->literalBucket;
|
||||
vm->literalBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
vm->scopeBucket = parent->scopeBucket;
|
||||
|
||||
//TODO: parent bucket pointers are updated after function calls
|
||||
vm->scopeBucketHandle = &parent->scopeBucket; //track this to update it later
|
||||
|
||||
Toy_resetVM(vm, true);
|
||||
}
|
||||
@@ -1108,21 +1134,17 @@ void Toy_bindVM(Toy_VM* vm, Toy_Module* module, bool preserveScope) {
|
||||
}
|
||||
}
|
||||
|
||||
void Toy_runVM(Toy_VM* vm) {
|
||||
unsigned int Toy_runVM(Toy_VM* vm) {
|
||||
if (vm->codeAddr == 0) {
|
||||
//ignore uninitialized VMs or empty modules
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: read params into scope
|
||||
|
||||
//prep the program counter for execution
|
||||
vm->programCounter = vm->codeAddr;
|
||||
|
||||
//begin
|
||||
process(vm);
|
||||
|
||||
//TODO: add return value extraction
|
||||
return process(vm);
|
||||
}
|
||||
|
||||
void Toy_freeVM(Toy_VM* vm) {
|
||||
@@ -1131,5 +1153,28 @@ void Toy_freeVM(Toy_VM* vm) {
|
||||
//clear the persistent memory
|
||||
Toy_freeStack(vm->stack);
|
||||
Toy_freeBucket(&vm->literalBucket);
|
||||
Toy_freeBucket(&vm->scopeBucket);
|
||||
|
||||
if (vm->scopeBucketHandle != NULL) {
|
||||
*(vm->scopeBucketHandle) = vm->scopeBucket; //re-adjust the parent's scopeBucket pointer, in case it was expanded
|
||||
}
|
||||
else {
|
||||
Toy_freeBucket(&vm->scopeBucket);
|
||||
}
|
||||
}
|
||||
|
||||
Toy_Array* Toy_extractResultsFromVM(Toy_Bucket** bucketHandle, Toy_VM* subVM, unsigned int resultCount) {
|
||||
if (subVM->stack->count < resultCount) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Too many results requested from VM, exiting\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
Toy_Array* results = Toy_resizeArray(NULL, resultCount);
|
||||
|
||||
const unsigned int offset = subVM->stack->count - resultCount; //first element to extract
|
||||
|
||||
for (/* EMPTY */; results->count < resultCount; results->count++) {
|
||||
results->data[results->count] = Toy_deepCopyValue(bucketHandle, subVM->stack->data[offset + results->count]);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -40,17 +40,19 @@ typedef struct Toy_VM {
|
||||
|
||||
//easy access to memory
|
||||
Toy_Bucket* literalBucket; //stores the value literals (strings, functions, etc.)
|
||||
Toy_Bucket* scopeBucket; //stores the scope instances TODO: is this separation needed?
|
||||
Toy_Bucket* scopeBucket; //stores the scope instances
|
||||
Toy_Bucket** scopeBucketHandle; //for reusing the scope bucket to save on alloc/free
|
||||
} Toy_VM;
|
||||
|
||||
TOY_API void Toy_resetVM(Toy_VM* vm, bool preserveScope);
|
||||
|
||||
TOY_API void Toy_initVM(Toy_VM* vm); //creates memory
|
||||
TOY_API void Toy_inheritVM(Toy_VM* vm, Toy_VM* parent); //inherits memory
|
||||
TOY_API void Toy_inheritVM(Toy_VM* vm, Toy_VM* parent); //inherits scope bucket
|
||||
|
||||
TOY_API void Toy_bindVM(Toy_VM* vm, Toy_Module* module, bool preserveScope);
|
||||
TOY_API void Toy_runVM(Toy_VM* vm);
|
||||
|
||||
TOY_API unsigned int Toy_runVM(Toy_VM* vm);
|
||||
TOY_API void Toy_freeVM(Toy_VM* vm);
|
||||
|
||||
TOY_API Toy_Array* Toy_extractResultsFromVM(Toy_Bucket** bucketHandle, Toy_VM* subVM, unsigned int resultCount);
|
||||
|
||||
//TODO: inject extra data (hook system for external libraries)
|
||||
|
||||
Reference in New Issue
Block a user