diff --git a/scripts/funky.toy b/scripts/funky.toy index 40a006d..e8f74b5 100644 --- a/scripts/funky.toy +++ b/scripts/funky.toy @@ -1,3 +1,5 @@ +/* + fn name(param1: int, param2: float, param3: string, param4) { print param1; print param2; @@ -7,8 +9,6 @@ fn name(param1: int, param2: float, param3: string, param4) { name(42, 3.14, "hello world", -1); -//URGENT: return values are still needed - fn output(arg) { print arg; } @@ -20,4 +20,22 @@ output(3.1415); output("woot!"); output([1, 23, 3]); output(["key":1]); -output(name); \ No newline at end of file +output(name); + +*/ + +fn makeCounter() { + var counter: int = 0; + + fn increment() { + return counter++; + } + + return increment; +} + +var tally = makeCounter(); + +print tally(); +print tally(); +print tally(); \ No newline at end of file diff --git a/source/toy_ast.c b/source/toy_ast.c index ed6441a..31d45dd 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -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)); diff --git a/source/toy_ast.h b/source/toy_ast.h index c9f0384..3536b74 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -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); diff --git a/source/toy_module_compiler.c b/source/toy_module_compiler.c index 45de6e4..3088dc8 100644 --- a/source/toy_module_compiler.c +++ b/source/toy_module_compiler.c @@ -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; diff --git a/source/toy_parser.c b/source/toy_parser.c index 5a3133e..f6ba6b4 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -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); diff --git a/source/toy_scope.c b/source/toy_scope.c index a0a9ff2..746c654 100644 --- a/source/toy_scope.c +++ b/source/toy_scope.c @@ -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++; } } diff --git a/source/toy_scope.h b/source/toy_scope.h index ff88f7f..9766cf3 100644 --- a/source/toy_scope.h +++ b/source/toy_scope.h @@ -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) \ No newline at end of file diff --git a/source/toy_value.c b/source/toy_value.c index 0668614..1452185 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -5,6 +5,7 @@ #include "toy_string.h" #include "toy_array.h" #include "toy_table.h" +#include "toy_function.h" #include #include @@ -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: diff --git a/source/toy_value.h b/source/toy_value.h index a4b0851..a2bf5a7 100644 --- a/source/toy_value.h +++ b/source/toy_value.h @@ -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); diff --git a/source/toy_vm.c b/source/toy_vm.c index 6489fbd..9bc426e 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -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; } diff --git a/source/toy_vm.h b/source/toy_vm.h index 2e1d6fe..4d612a5 100644 --- a/source/toy_vm.h +++ b/source/toy_vm.h @@ -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) diff --git a/tests/cases/test_ast.c b/tests/cases/test_ast.c index 9611e21..3ba72cb 100644 --- a/tests/cases/test_ast.c +++ b/tests/cases/test_ast.c @@ -46,6 +46,7 @@ int test_sizeof_ast(void) { TEST_SIZEOF(Toy_AstWhileThen, 12 , 24); TEST_SIZEOF(Toy_AstBreak, 4 , 4); TEST_SIZEOF(Toy_AstContinue, 4 , 4); + TEST_SIZEOF(Toy_AstReturn, 8 , 16); TEST_SIZEOF(Toy_AstPrint, 8 , 16); TEST_SIZEOF(Toy_AstVarDeclare, 12 , 24); TEST_SIZEOF(Toy_AstVarAssign, 16 , 24);