Implemented array.forEach(fn)

This commit is contained in:
2026-04-26 11:59:15 +10:00
parent efc9fe1406
commit af30246e0c
5 changed files with 105 additions and 31 deletions
+57 -2
View File
@@ -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) {
+7 -7
View File
@@ -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);