map and filter methods for lists
authorcassowarii <cassowary@cassowary.me>
Tue, 7 Jul 2026 18:43:53 +0000 (11:43 -0700)
committercassowarii <cassowary@cassowary.me>
Tue, 7 Jul 2026 18:44:12 +0000 (11:44 -0700)
src/data/methods.c

index dbd57e4..653f309 100644 (file)
@@ -8,6 +8,7 @@
 
 void list_each_cfunc(hVm vm, flag init);
 void list_map_cfunc(hVm vm, flag init);
+void list_filter_cfunc(hVm vm, flag init);
 
 void sbList_method(hVm vm) {
   hV *list = sbVm_pop(vm);
@@ -23,30 +24,41 @@ void sbList_method(hVm vm) {
   hV *method_name_val = sbVm_peek(vm, num_params);
   if (method_name_val->type == IT_SYMBOL) {
     const char *method_name = sbSymbol_name(method_name_val->symbol);
+    /* TODO: Need a better way of resolving these */
     if (METHOD_IS("each")) {
       if (num_params != 1) {
         PANIC("wrong number of arguments passed to list#each!");
       }
       sbVm_push_immediate(vm, list);
       sbVm_call_c_func(vm, list_each_cfunc);
-    } else {
-      if (METHOD_IS("length")) {
-        if (num_params != 0) {
-          PANIC("list#length takes no arguments!");
-        }
-        sbVm_pop(vm); /* remove method name */
-        usize length;
-        sbList_get_value(list->list, &length);
-        sbVm_push_immediate(vm, &HVINT(length));
-      } else if (METHOD_IS("push")) {
-        if (num_params != 1) {
-          PANIC("list#push expects 1 argument!");
-        }
-        hV *to_append = sbVm_pop(vm);
-        sbVm_pop(vm); /* remove method name */
-        sbList_append(list->list, to_append);
-        sbVm_push_immediate(vm, &HVNIL);
+    } else if (METHOD_IS("map")) {
+      if (num_params != 1) {
+        PANIC("wrong number of arguments passed to list#map!");
+      }
+      sbVm_push_immediate(vm, list);
+      sbVm_call_c_func(vm, list_map_cfunc);
+    } else if (METHOD_IS("filter")) {
+      if (num_params != 1) {
+        PANIC("wrong number of arguments passed to list#filter!");
       }
+      sbVm_push_immediate(vm, list);
+      sbVm_call_c_func(vm, list_filter_cfunc);
+    } else if (METHOD_IS("length")) {
+      if (num_params != 0) {
+        PANIC("list#length takes no arguments!");
+      }
+      sbVm_pop(vm); /* remove method name */
+      usize length;
+      sbList_get_value(list->list, &length);
+      sbVm_push_immediate(vm, &HVINT(length));
+    } else if (METHOD_IS("push")) {
+      if (num_params != 1) {
+        PANIC("list#push expects 1 argument!");
+      }
+      hV *to_append = sbVm_pop(vm);
+      sbVm_pop(vm); /* remove method name */
+      sbList_append(list->list, to_append);
+      sbVm_push_immediate(vm, &HVNIL);
     }
   } else {
     PANIC("method name to list is not symbol! (%lld)", (long long)method_name_val->type);
@@ -81,3 +93,78 @@ void list_each_cfunc(hVm vm, flag init) {
     sbVm_push_immediate(vm, &HVNIL);
   }
 }
+
+void list_map_cfunc(hVm vm, flag init) {
+  if (init) {
+    /* state: list being mapped, index, callback, result */
+    sbVm_request_var_space(vm, 4);
+    hV *iterating_list = sbVm_pop(vm);
+    hV *map_func = sbVm_pop(vm);
+    sbVm_pop(vm); /* remove method name */
+    usize length;
+    sbList_get_value(iterating_list->list, &length);
+    hV index = HVINT(0);
+    hV result = sbV_empty_list(length);
+
+    vm->fp->locals[0] = *iterating_list;
+    vm->fp->locals[1] = index;
+    vm->fp->locals[2] = *map_func;
+    vm->fp->locals[3] = result;
+  } else {
+    /* get result of map function and append its result to result list */
+    hV *mapped = sbVm_pop(vm);
+    sbList_append(vm->fp->locals[3].list, mapped);
+  }
+
+  usize current_index = vm->fp->locals[1].integer++;
+  usize length;
+  hV *iter_values = sbList_get_value(vm->fp->locals[0].list, &length);
+  if (current_index < length) {
+    sbVm_push(vm, &iter_values[current_index]);
+    sbVm_push_immediate(vm, &HVINT(1));
+    sbVm_call_func(vm, &vm->fp->locals[2]);
+  } else {
+    /* return result list */
+    sbVm_push_immediate(vm, &vm->fp->locals[3]);
+  }
+}
+
+void list_filter_cfunc(hVm vm, flag init) {
+  if (init) {
+    /* state: list being filtered, index, callback, result */
+    sbVm_request_var_space(vm, 4);
+    hV *iterating_list = sbVm_pop(vm);
+    hV *filter_func = sbVm_pop(vm);
+    sbVm_pop(vm); /* remove method name */
+    usize length;
+    sbList_get_value(iterating_list->list, &length);
+    hV index = HVINT(0);
+    hV result = sbV_empty_list(length);
+
+    vm->fp->locals[0] = *iterating_list;
+    vm->fp->locals[1] = index;
+    vm->fp->locals[2] = *filter_func;
+    vm->fp->locals[3] = result;
+  } else {
+    /* get result of filter function and append element to result if true */
+    hV *mapped = sbVm_pop(vm);
+    hV *element = sbVm_pop(vm);
+    if (!sbV_c_falsy(mapped)) {
+      sbList_append(vm->fp->locals[3].list, element);
+    }
+  }
+
+  usize current_index = vm->fp->locals[1].integer++;
+  usize length;
+  hV *iter_values = sbList_get_value(vm->fp->locals[0].list, &length);
+  if (current_index < length) {
+    /* put current value on stack twice: once as parameter for function, once to save for ourselves */
+    sbVm_push(vm, &iter_values[current_index]);
+    sbVm_push(vm, &iter_values[current_index]);
+    sbVm_push_immediate(vm, &HVINT(1));
+    sbVm_call_func(vm, &vm->fp->locals[2]);
+  } else {
+    /* return result list */
+    sbVm_push_immediate(vm, &vm->fp->locals[3]);
+  }
+}