From af30246e0ce2b4063f78f54b697a2a3c8629e978 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 26 Apr 2026 11:59:15 +1000 Subject: [PATCH] Implemented array.forEach(fn) --- repl/main.c | 13 ++++++++- repl/standard_library.c | 27 ++++++++++++++----- scripts/blockhead.toy | 23 ++++++---------- source/toy_attributes.c | 59 +++++++++++++++++++++++++++++++++++++++-- source/toy_vm.c | 14 +++++----- 5 files changed, 105 insertions(+), 31 deletions(-) diff --git a/repl/main.c b/repl/main.c index 3d11ab1..519b16e 100644 --- a/repl/main.c +++ b/repl/main.c @@ -8,6 +8,9 @@ #include "toy_compiler.h" #include "toy_vm.h" +//NOTE: for testing +#include "standard_library.h" + #include #include #include @@ -367,7 +370,14 @@ int repl(const char* filepath, bool verbose) { inspect_bytecode(bytecode); } - Toy_bindVM(&vm, bytecode, NULL); + //WARN: Hacky debugging + if (vm.scope == NULL) { + Toy_bindVM(&vm, bytecode, NULL); + initStandardLibrary(&vm); + } + else { + Toy_bindVM(&vm, bytecode, NULL); + } //run Toy_runVM(&vm); @@ -474,6 +484,7 @@ int main(int argc, const char* argv[]) { Toy_VM vm; Toy_initVM(&vm); Toy_bindVM(&vm, bytecode, NULL); + initStandardLibrary(&vm); //WARN: Hacky debugging Toy_runVM(&vm); diff --git a/repl/standard_library.c b/repl/standard_library.c index aaa74dd..c0ced3d 100644 --- a/repl/standard_library.c +++ b/repl/standard_library.c @@ -15,20 +15,35 @@ typedef struct CallbackPairs { } CallbackPairs; //example callbacks -void hello(Toy_VM* vm) { - (void)vm; - Toy_print("Hello world!"); -} - void debug(Toy_VM* vm) { (void)vm; Toy_print("This function returns the integer '42' to the calling scope."); Toy_pushStack(&vm->stack, TOY_VALUE_FROM_INTEGER(42)); } +void identity(Toy_VM* vm) { + //does nothing, but any arguements are left on the stack as results + (void)vm; +} + +void echo(Toy_VM* vm) { + //pops one argument, and prints it + Toy_Value value = Toy_popStack(&vm->stack); + Toy_String* string = Toy_stringifyValue(&vm->memoryBucket, value); + char* cstr = Toy_getStringRaw(string); + + Toy_print(cstr); + + free(cstr); + Toy_freeString(string); + Toy_freeValue(value); +} + CallbackPairs callbackPairs[] = { - {"hello", hello}, {"debug", debug}, + {"identity", identity}, + {"echo", echo}, + {NULL, NULL}, }; diff --git a/scripts/blockhead.toy b/scripts/blockhead.toy index 8e8e314..a0cecdb 100644 --- a/scripts/blockhead.toy +++ b/scripts/blockhead.toy @@ -1,18 +1,11 @@ +fn output(arg) { + print arg; +} + +var array = ["alpha", "bravo", "charlie"]; + -var table: Table = ["answer":42]; - -print table.hasKey("answer"); -print table.hasKey("question"); - -table.insert("question", "roads"); - -print table.hasKey("answer"); -print table.hasKey("question"); - -table.remove("answer"); - -print table.hasKey("answer"); -print table.hasKey("question"); - +array.forEach(echo); +array.forEach(output); \ No newline at end of file diff --git a/source/toy_attributes.c b/source/toy_attributes.c index fc99160..42c10ab 100644 --- a/source/toy_attributes.c +++ b/source/toy_attributes.c @@ -80,8 +80,63 @@ static void attr_arrayPopBack(Toy_VM* vm) { } static void attr_arrayForEach(Toy_VM* vm) { - (void)vm; - //URGENT: attr_arrayForEach + Toy_Value compound = Toy_popStack(&vm->stack); + Toy_Value callback = Toy_popStack(&vm->stack); + + if (TOY_VALUE_IS_FUNCTION(callback) != true) { + char buffer[256]; + snprintf(buffer, 256, "Expected function, found '%s'", Toy_private_getValueTypeAsCString(callback.type)); + Toy_error(buffer); + return; + } + + Toy_Array* array = TOY_VALUE_AS_ARRAY(compound); + Toy_Function* fn = TOY_VALUE_AS_FUNCTION(callback); + + //this emulates 'processInvoke' a bit, but not entirely + Toy_VM subVM; + Toy_inheritVM(vm, &subVM); + + switch(fn->type) { + case TOY_FUNCTION_CUSTOM: { + //push and run for each element + for (unsigned int iterator = 0; iterator < array->count; iterator++) { + //bind to the subVM (more expensive than I'd like) + Toy_bindVM(&subVM, fn->bytecode.code, fn->bytecode.parentScope); + + //get parameter name as a string + unsigned int paramAddr = ((unsigned int*)(subVM.code + subVM.paramAddr))[0]; + Toy_ValueType paramType = (Toy_ValueType)(((unsigned int*)(subVM.code + subVM.paramAddr))[1]); + const char* cstr = ((char*)(subVM.code + subVM.dataAddr)) + paramAddr; + Toy_String* name = Toy_toStringLength(&subVM.memoryBucket, cstr, strlen(cstr)); + + Toy_declareScope(subVM.scope, Toy_copyString(name), paramType, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]), true); + Toy_freeString(name); + + Toy_runVM(&subVM); //TODO: could use a 'map'-style method by storing the results + + Toy_resetVM(&subVM, false, true); + } + } + break; + + case TOY_FUNCTION_NATIVE: { + //this uses a subVM for the native function, which is a slight difference than 'processInoke' + for (unsigned int iterator = 0; iterator < array->count; iterator++) { + Toy_pushStack(&subVM.stack, Toy_copyValue(&subVM.memoryBucket, array->data[iterator])); + + fn->native.callback(&subVM); //NOTE: try not to leave anything on the stack afterwards + } + } + break; + + default: + Toy_error("Can't call an unknown function type in 'forEach'"); + break; + } + + //cleanup + Toy_freeVM(&subVM); } static void attr_arraySort(Toy_VM* vm) { diff --git a/source/toy_vm.c b/source/toy_vm.c index 204d71e..940a9d4 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -444,8 +444,8 @@ static void processAttribute(Toy_VM* vm) { return; } - //BUGFIX: check for callable native functions, so they can access the compound too - if (TOY_VALUE_IS_FUNCTION(result) && TOY_VALUE_AS_FUNCTION(result)->type == TOY_FUNCTION_NATIVE) { + //BUGFIX: check for callable functions, so they can access the compound too + if (TOY_VALUE_IS_FUNCTION(result)) { //WONTFIX: `print array.pushBack;' will leave the compound's reference on the stack Toy_pushStack(&vm->stack, compound); } @@ -1086,14 +1086,14 @@ void Toy_resetVM(Toy_VM* vm, bool preserveScope, bool preserveStack) { vm->programCounter = 0; - if (!preserveStack) { - Toy_resetStack(&vm->stack); //NOTE: has a realloc() - } - if (!preserveScope) { vm->scope = Toy_popScope(vm->scope); } + if (!preserveStack) { + Toy_resetStack(&vm->stack); //NOTE: has a realloc() + } + //NOTE: buckets are not altered during resets } @@ -1171,7 +1171,7 @@ void Toy_freeVM(Toy_VM* vm) { Toy_freeStack(vm->stack); if (vm->parentBucketHandle != NULL) { - *(vm->parentBucketHandle) = vm->memoryBucket; //update the outter VM, if there is one + *(vm->parentBucketHandle) = vm->memoryBucket; //update the outer VM, if there is one } else { Toy_freeBucket(&vm->memoryBucket);