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:
2025-02-17 19:07:14 +11:00
parent 02dfc996b4
commit 639250f028
12 changed files with 215 additions and 26 deletions

View File

@@ -1,3 +1,5 @@
/*
fn name(param1: int, param2: float, param3: string, param4) { fn name(param1: int, param2: float, param3: string, param4) {
print param1; print param1;
print param2; print param2;
@@ -7,8 +9,6 @@ fn name(param1: int, param2: float, param3: string, param4) {
name(42, 3.14, "hello world", -1); name(42, 3.14, "hello world", -1);
//URGENT: return values are still needed
fn output(arg) { fn output(arg) {
print arg; print arg;
} }
@@ -21,3 +21,21 @@ output("woot!");
output([1, 23, 3]); output([1, 23, 3]);
output(["key":1]); output(["key":1]);
output(name); output(name);
*/
fn makeCounter() {
var counter: int = 0;
fn increment() {
return counter++;
}
return increment;
}
var tally = makeCounter();
print tally();
print tally();
print tally();

View File

@@ -163,6 +163,15 @@ void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle)
(*astHandle) = tmp; (*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) { void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

View File

@@ -24,6 +24,7 @@ typedef enum Toy_AstType {
TOY_AST_WHILE_THEN, TOY_AST_WHILE_THEN,
TOY_AST_BREAK, TOY_AST_BREAK,
TOY_AST_CONTINUE, TOY_AST_CONTINUE,
TOY_AST_RETURN,
TOY_AST_PRINT, TOY_AST_PRINT,
TOY_AST_VAR_DECLARE, TOY_AST_VAR_DECLARE,
@@ -171,6 +172,11 @@ typedef struct Toy_AstContinue {
Toy_AstType type; Toy_AstType type;
} Toy_AstContinue; } Toy_AstContinue;
typedef struct Toy_AstReturn {
Toy_AstType type;
Toy_Ast* child;
} Toy_AstReturn;
typedef struct Toy_AstPrint { typedef struct Toy_AstPrint {
Toy_AstType type; Toy_AstType type;
Toy_Ast* child; Toy_Ast* child;
@@ -235,6 +241,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
Toy_AstWhileThen whileThen; Toy_AstWhileThen whileThen;
Toy_AstBreak breakPoint; Toy_AstBreak breakPoint;
Toy_AstContinue continuePoint; Toy_AstContinue continuePoint;
Toy_AstReturn fnReturn;
Toy_AstPrint print; Toy_AstPrint print;
Toy_AstVarDeclare varDeclare; Toy_AstVarDeclare varDeclare;
Toy_AstVarAssign varAssign; 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_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_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
void Toy_private_emitAstContinue(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_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr); void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr);

View File

@@ -752,6 +752,21 @@ static unsigned int writeInstructionContinue(Toy_ModuleCompiler** mb, Toy_AstCon
return 0; 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) { static unsigned int writeInstructionPrint(Toy_ModuleCompiler** mb, Toy_AstPrint ast) {
//the thing to print //the thing to print
writeModuleCompilerCode(mb, ast.child); writeModuleCompilerCode(mb, ast.child);
@@ -1158,6 +1173,10 @@ static unsigned int writeModuleCompilerCode(Toy_ModuleCompiler** mb, Toy_Ast* as
result += writeInstructionContinue(mb, ast->continuePoint); result += writeInstructionContinue(mb, ast->continuePoint);
break; break;
case TOY_AST_RETURN:
result += writeInstructionReturn(mb, ast->fnReturn);
break;
case TOY_AST_PRINT: case TOY_AST_PRINT:
result += writeInstructionPrint(mb, ast->print); result += writeInstructionPrint(mb, ast->print);
break; break;

View File

@@ -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"); 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) { static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
makeExpr(bucketHandle, parser, rootHandle); makeExpr(bucketHandle, parser, rootHandle);
Toy_private_emitAstPrint(bucketHandle, rootHandle); Toy_private_emitAstPrint(bucketHandle, rootHandle);
@@ -1026,9 +1033,7 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
return; return;
} }
//for-pre-clause-post-then //TODO: for-pre-clause-post-then
//return
//import
//break //break
else if (match(parser, TOY_TOKEN_KEYWORD_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;
} }
//return
else if (match(parser, TOY_TOKEN_KEYWORD_RETURN)) {
makeReturnStmt(bucketHandle, parser, rootHandle);
return;
}
//TODO: import
//print //print
else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) { else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) {
makePrintStmt(bucketHandle, parser, rootHandle); makePrintStmt(bucketHandle, parser, rootHandle);

View File

@@ -10,6 +10,12 @@
//utils //utils
static void incrementRefCount(Toy_Scope* scope) { static void incrementRefCount(Toy_Scope* scope) {
for (Toy_Scope* iter = scope; iter; iter = iter->next) { 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++; iter->refCount++;
} }
} }

View File

@@ -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 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);
//TODO: delcare with a custom table (game engine entities)

View File

@@ -5,6 +5,7 @@
#include "toy_string.h" #include "toy_string.h"
#include "toy_array.h" #include "toy_array.h"
#include "toy_table.h" #include "toy_table.h"
#include "toy_function.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -136,7 +137,7 @@ Toy_Value Toy_copyValue(Toy_Value value) {
return TOY_VALUE_FROM_TABLE(result); return TOY_VALUE_FROM_TABLE(result);
} }
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION:
return value; // return value; //URGENT: concerning
case TOY_VALUE_OPAQUE: case TOY_VALUE_OPAQUE:
case TOY_VALUE_ANY: case TOY_VALUE_ANY:
@@ -150,6 +151,70 @@ Toy_Value Toy_copyValue(Toy_Value value) {
return TOY_VALUE_FROM_NULL(); 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) { void Toy_freeValue(Toy_Value value) {
switch(value.type) { switch(value.type) {
case TOY_VALUE_NULL: case TOY_VALUE_NULL:

View File

@@ -82,6 +82,7 @@ TOY_API Toy_Value Toy_unwrapValue(Toy_Value value);
TOY_API unsigned int Toy_hashValue(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_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 void Toy_freeValue(Toy_Value value);
TOY_API bool Toy_checkValueIsTruthy(Toy_Value value); TOY_API bool Toy_checkValueIsTruthy(Toy_Value value);

View File

@@ -360,6 +360,8 @@ static void processAccess(Toy_VM* vm) {
return; 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) //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)) { if (TOY_VALUE_IS_REFERENCE(*valuePtr) || TOY_VALUE_IS_ARRAY(*valuePtr) || TOY_VALUE_IS_TABLE(*valuePtr)) {
Toy_Value ref = TOY_REFERENCE_FROM_POINTER(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); Toy_declareScope(subVM.scope, name, argValue);
} }
//run and cleanup //run
Toy_runVM(&subVM); 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); Toy_freeVM(&subVM);
} }
break; 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) { static void processJump(Toy_VM* vm) {
Toy_OpParamJumpType type = READ_BYTE(vm); Toy_OpParamJumpType type = READ_BYTE(vm);
Toy_OpParamJumpConditional cond = READ_BYTE(vm); Toy_OpParamJumpConditional cond = READ_BYTE(vm);
@@ -926,7 +951,7 @@ static void processIndex(Toy_VM* vm) {
Toy_freeValue(length); Toy_freeValue(length);
} }
static void process(Toy_VM* vm) { static unsigned int process(Toy_VM* vm) {
while(true) { while(true) {
//prep by aligning to the 4-byte word //prep by aligning to the 4-byte word
fixAlignment(vm); fixAlignment(vm);
@@ -995,8 +1020,7 @@ static void process(Toy_VM* vm) {
//control instructions //control instructions
case TOY_OPCODE_RETURN: case TOY_OPCODE_RETURN:
//temp terminator return processReturn(vm); //the only return statement, which signals the number of values for extraction
return;
case TOY_OPCODE_JUMP: case TOY_OPCODE_JUMP:
processJump(vm); processJump(vm);
@@ -1074,6 +1098,8 @@ void Toy_initVM(Toy_VM* vm) {
vm->literalBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); vm->literalBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
vm->scopeBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); vm->scopeBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
vm->scopeBucketHandle = NULL; //not used
Toy_resetVM(vm, true); Toy_resetVM(vm, true);
} }
@@ -1081,10 +1107,10 @@ void Toy_inheritVM(Toy_VM* vm, Toy_VM* parent) {
//inherent persistent memory //inherent persistent memory
vm->scope = NULL; vm->scope = NULL;
vm->stack = Toy_allocateStack(); vm->stack = Toy_allocateStack();
vm->literalBucket = parent->literalBucket; vm->literalBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
vm->scopeBucket = parent->scopeBucket; 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); 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) { if (vm->codeAddr == 0) {
//ignore uninitialized VMs or empty modules //ignore uninitialized VMs or empty modules
return; return 0;
} }
//TODO: read params into scope
//prep the program counter for execution //prep the program counter for execution
vm->programCounter = vm->codeAddr; vm->programCounter = vm->codeAddr;
//begin //begin
process(vm); return process(vm);
//TODO: add return value extraction
} }
void Toy_freeVM(Toy_VM* vm) { void Toy_freeVM(Toy_VM* vm) {
@@ -1131,5 +1153,28 @@ void Toy_freeVM(Toy_VM* vm) {
//clear the persistent memory //clear the persistent memory
Toy_freeStack(vm->stack); Toy_freeStack(vm->stack);
Toy_freeBucket(&vm->literalBucket); Toy_freeBucket(&vm->literalBucket);
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_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;
} }

View File

@@ -40,17 +40,19 @@ typedef struct Toy_VM {
//easy access to memory //easy access to memory
Toy_Bucket* literalBucket; //stores the value literals (strings, functions, etc.) 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_VM;
TOY_API void Toy_resetVM(Toy_VM* vm, bool preserveScope); 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 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_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 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) //TODO: inject extra data (hook system for external libraries)

View File

@@ -46,6 +46,7 @@ int test_sizeof_ast(void) {
TEST_SIZEOF(Toy_AstWhileThen, 12 , 24); TEST_SIZEOF(Toy_AstWhileThen, 12 , 24);
TEST_SIZEOF(Toy_AstBreak, 4 , 4); TEST_SIZEOF(Toy_AstBreak, 4 , 4);
TEST_SIZEOF(Toy_AstContinue, 4 , 4); TEST_SIZEOF(Toy_AstContinue, 4 , 4);
TEST_SIZEOF(Toy_AstReturn, 8 , 16);
TEST_SIZEOF(Toy_AstPrint, 8 , 16); TEST_SIZEOF(Toy_AstPrint, 8 , 16);
TEST_SIZEOF(Toy_AstVarDeclare, 12 , 24); TEST_SIZEOF(Toy_AstVarDeclare, 12 , 24);
TEST_SIZEOF(Toy_AstVarAssign, 16 , 24); TEST_SIZEOF(Toy_AstVarAssign, 16 , 24);