more portable printing; callbacks from native code to script
authorcassowarii <2374677+cassowarii@users.noreply.github.com>
Sun, 5 Jul 2026 22:05:57 +0000 (15:05 -0700)
committercassowarii <2374677+cassowarii@users.noreply.github.com>
Sun, 5 Jul 2026 22:05:57 +0000 (15:05 -0700)
src/compile/emit.c
src/compile/print_ir.c
src/data/closure.c
src/data/hashtable.c
src/data/methods.c
src/data/string.c
src/data/symbol.c
src/vm/exec.c
src/vm/exec.h
src/vm/operations.c

index acab122..ffee6d2 100644 (file)
@@ -33,7 +33,7 @@ void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir) {
 /* --- */
 
 void emit_arg(sbVmCompiler *cm, i64 actual_number) {
-  debug("    arg %lld\n", actual_number);
+  debug("    arg %lld\n", (long long)actual_number);
   u8 buf[9];
   u64 number = actual_number;
   if (0 <= actual_number && actual_number < 253) {
index ab71f23..8643d0a 100644 (file)
@@ -57,7 +57,7 @@ static void print_expr(sbIrExpr *e) {
       if (e->value.type == IT_NIL) {
         debug("nil");
       } else if (e->value.type == IT_INTEGER) {
-        debug("%lld", e->value.integer);
+        debug("%lld", (long long)e->value.integer);
       } else {
         debug("some value");
       }
index e89942c..4207a30 100644 (file)
@@ -85,5 +85,5 @@ hV sbClosure_get_ref(hClosure which, usize index) {
 
 static sbClosure *find_closure_by_handle(hRef handle) {
   if (!(handle & FLAG_REAL)) PANIC("null closure");
-  return sbPool_get_entry(&g_closure_pool, handle);
+  return sbPool_get_entry(&g_closure_pool, handle & ~FLAG_REAL);
 }
index b18fa8b..3a9b1cf 100644 (file)
@@ -73,7 +73,7 @@ sbHashValue sbHash_hash_obj(hV *obj) {
     const char *strval = sbString_get_value(obj->string, scratch, &length);
     return sbHash_hash_bytes(strval, length);
   } else {
-    PANIC("don't know how to hash object of type %lld", obj->type);
+    PANIC("don't know how to hash object of type %lld", (long long)obj->type);
   }
 }
 
index dc8bd0a..d554db3 100644 (file)
@@ -6,6 +6,9 @@
 
 #define METHOD_IS(name) (!sbstrncmp(method_name, name, sizeof(name)))
 
+void list_each_cfunc(hVm vm, flag init);
+void list_map_cfunc(hVm vm, flag init);
+
 void sbList_method(hV list, hVm vm) {
   if (list.type != IT_LIST) {
     CHECK("can't call sbList_method on something that isn't a list");
@@ -24,26 +27,39 @@ void sbList_method(hV list, hVm vm) {
       if (num_params != 1) {
         PANIC("wrong number of arguments passed to list#each!");
       }
-      hV loop_func = sbVm_pop(vm);
-      sbVm_pop(vm); /* remove method name */
-      usize length;
-      hV *iter_values = sbList_get_value(list.list, &length);
-      for (usize i = 0; i < length; i++) {
-        /* TODO : This doesn't work! It almost works, but when we call the
-         * callback func, we need to, like, pause this function and run more
-         * VM instructions, then come back to this one. How do? :thinking emoji: */
-        printf("index %zu\n", i);
-        sbVm_push(vm, iter_values[i]);
-        sbVm_push(vm, HVINT(1));
-        printf("call loop-func\n");
-        sbVm_call_func(vm, loop_func);
-        /* remove result of loop_func */
-        printf("loop-func done\n");
-        sbVm_pop(vm);
-      }
+      sbVm_push(vm, list);
+      sbVm_call_c_func(vm, list_each_cfunc);
     }
   } else {
-    PANIC("method name to list is not symbol! (%lld)", method_name_val->type);
+    PANIC("method name to list is not symbol! (%ld)", method_name_val->type);
   }
 }
 
+void list_each_cfunc(hVm vm, flag init) {
+  if (init) {
+    /* store three state variables: the list we're iterating, the index into it, and the callback function */
+    sbVm_request_var_space(vm, 3);
+    hV iterating_list = sbVm_pop(vm);
+    hV loop_func = sbVm_pop(vm);
+    sbVm_pop(vm); /* remove method name */
+    hV index = HVINT(0);
+    vm->fp->locals[0] = iterating_list;
+    vm->fp->locals[1] = index;
+    vm->fp->locals[2] = loop_func;
+  } else {
+    /* loop func must have finished, so remove its result */
+    sbVm_pop(vm);
+  }
+
+  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(vm, HVINT(1));
+    sbVm_call_func(vm, vm->fp->locals[2]);
+  } else {
+    /* if index was past the end, callback function won't be called, and we'll exit. return nil */
+    sbVm_push(vm, HVNIL);
+  }
+}
index 38796df..c9da7d2 100644 (file)
@@ -325,13 +325,13 @@ static char *get_mutable_ptr_of_entry(strentry *e, usize length, hString *handle
 static void tinystr_into_buffer(char *buffer, hString handle, usize max_length) {
   /* we have max_length because we may need to cut off early in some corner cases,
    * like getting a mutable ptr of length 3 for a tinystr of length 6 */
-  if (!is_tinystr(handle)) PANIC("%llx is not a tinystr! can't parse!", handle);
+  if (!is_tinystr(handle)) PANIC("%llx is not a tinystr! can't parse!", (long long)handle);
 
   usize index = 0;
   usize tinylength = tinystr_length(handle);
   hString handle_to_eat = handle;
 
-  if (tinylength >= REQ_ALLOC_LENGTH) PANIC("%llx has improper tinylength %zu", handle, tinylength);
+  if (tinylength >= REQ_ALLOC_LENGTH) PANIC("%llx has improper tinylength %zu", (long long)handle, tinylength);
 
   /* string is stored backwards because it's easiest to just get the low byte */
   while (index < tinylength && index < max_length) {
index d4838d7..de7006c 100644 (file)
@@ -33,7 +33,7 @@ hSymbol sbSymbol_from_bytes(const char *text, usize length) {
     /* return already existing symbol */
     return result.symbol;
   } else {
-    PANIC("Only symbol values are allowed in the symbol table! (%lld)", result.type);
+    PANIC("Only symbol values are allowed in the symbol table! (%lld)", (long long)result.type);
   }
 }
 
index 42ff4f9..8be69e4 100644 (file)
@@ -6,6 +6,7 @@
 #include "data/closure.h"
 
 void call_block(hVm vm, usize block_id, hClosure closure);
+void return_from_block(hVm vm);
 void execute_instruction(hVm vm);
 void push_stack(hVm vm, const hV *value);
 hV pop_stack(hVm vm);
@@ -70,6 +71,34 @@ void sbVm_call_func(hVm vm, hV func) {
   call_block(vm, func.type, func.closure);
 }
 
+void sbVm_call_c_func(hVm vm, sbRuntimeCFunc func) {
+  sbVmStackFrame frame = {
+    .return_addr = vm->ip,
+    .last_fp = vm->fp,
+    .last_rp = vm->rp,
+    .is_c_func = TRUE,
+    .c_func = func,
+  };
+  *(sbVmStackFrame*)vm->rp = frame;
+  vm->fp = (sbVmStackFrame*)vm->rp;
+  vm->rp += sizeof(sbVmStackFrame);
+
+  func(vm, TRUE);
+
+  if (vm->fp->is_c_func) {
+    /* if c_func didn't call another callback, return from it */
+    return_from_block(vm);
+  }
+}
+
+void sbVm_request_var_space(hVm vm, usize amount) {
+  /* have to set new rstack space to 0 so we don't accidentally decrement
+   * the ref count of variables from a previous stack frame (or of just garbage)  */
+  memset(vm->rp, 0, amount * sizeof(hV));
+  vm->rp += amount * sizeof(hV);
+  vm->fp->num_locals += amount;
+}
+
 /* --- */
 
 void call_block(hVm vm, usize block_id, hClosure closure) {
@@ -79,8 +108,8 @@ void call_block(hVm vm, usize block_id, hClosure closure) {
     .return_addr = vm->ip,
     .last_fp = vm->fp,
     .last_rp = vm->rp,
-    .block = blk,
-    .closure = closure,
+    .block_func.block = blk,
+    .block_func.closure = closure,
   };
   *(sbVmStackFrame*)vm->rp = frame;
   vm->fp = (sbVmStackFrame*)vm->rp;
@@ -102,11 +131,21 @@ void return_from_block(hVm vm) {
     vm->fp = frame->last_fp;
     vm->rp = frame->last_rp;
     vm->ip = frame->return_addr;
+
+    if (vm->fp->is_c_func) {
+      /* if we returned into a c-function call frame, go back to the c-function */
+      vm->fp->c_func(vm, FALSE);
+
+      if (vm->fp->is_c_func) {
+        /* c-function didn't make a callback, so is done */
+        return_from_block(vm);
+      }
+    }
   }
 }
 
 void debug_print_hv(const hV *value) {
-  debug("%016llx %016llx\n", *(u64*)value, *((u64*)value+1));
+  debug("%016llx %016llx\n", *(long long*)value, *((long long*)value+1));
 }
 
 void store_local(hVm vm, usize local_index, const hV *value) {
@@ -141,7 +180,7 @@ hV *peek_stack(hVm vm, usize offset) {
 }
 
 sbOpcode get_opcode(hVm vm) {
-  if (vm->ip >= vm->fp->block->bytecode_end) PANIC("execution outside defined code area");
+  if (vm->ip >= vm->fp->block_func.block->bytecode_end) PANIC("execution outside defined code area");
   return *(vm->ip++);
 }
 
@@ -160,7 +199,7 @@ void next_byte(hVm vm, u64 *result) {
  * in big-endian format */
 i64 get_param(hVm vm) {
   u64 result = *((u8*)vm->ip++);
-  if (vm->debugmode) debug("%02llX ", result);
+  if (vm->debugmode) debug("%02llX ", (long long)result);
   i64 signed_result = result;
 
   if (result == BC_LONG_NUM) {
@@ -214,7 +253,7 @@ void execute_instruction(hVm vm) {
       break;
     case BC_LD_CONST:
       param = get_param(vm);
-      push_stack(vm, &vm->fp->block->constants[param]);
+      push_stack(vm, &vm->fp->block_func.block->constants[param]);
       break;
     case BC_LD_CTX:
       PANIC("todo");
@@ -235,12 +274,12 @@ void execute_instruction(hVm vm) {
     case BC_LD_UPVAL:
       /* Hey, was there upval in there? */
       param = get_param(vm);
-      v = sbClosure_get_var(vm->fp->closure, param);
+      v = sbClosure_get_var(vm->fp->block_func.closure, param);
       push_stack(vm, v);
       break;
     case BC_LD_UPREF:
       param = get_param(vm);
-      res = sbClosure_get_ref(vm->fp->closure, param);
+      res = sbClosure_get_ref(vm->fp->block_func.closure, param);
       push_stack(vm, &res);
       break;
     case BC_LD_BLK:
@@ -251,7 +290,7 @@ void execute_instruction(hVm vm) {
       param = get_param(vm);
       v = &vm->fp->locals[param];
       if (v->type != IT_REF) {
-        CHECK("indirect variables should be of type reference! (%lld)", v->type);
+        CHECK("indirect variables should be of type reference! (%lld)", (long long)v->type);
       }
       w = sbRef_deref(v->ref);
       push_stack(vm, w);
@@ -268,7 +307,7 @@ void execute_instruction(hVm vm) {
     case BC_ST_UPVAL:
       param = get_param(vm);
       vv = pop_stack(vm);
-      sbClosure_set_var(vm->fp->closure, param, &vv);
+      sbClosure_set_var(vm->fp->block_func.closure, param, &vv);
       break;
     case BC_ST_IND:
       param = get_param(vm);
@@ -280,7 +319,7 @@ void execute_instruction(hVm vm) {
       } else if (v->type == IT_REF) {
         sbRef_set_ref(v->ref, w);
       } else {
-        CHECK("indirect variables should be of type reference! (%lld)", v->type);
+        CHECK("indirect variables should be of type reference! (%lld)", (long long)v->type);
       }
       pop_stack(vm);
       break;
@@ -316,7 +355,7 @@ void execute_instruction(hVm vm) {
       } else if (v->type == IT_REF) {
         sbRef_set_ref(v->ref, w);
       } else {
-        CHECK("indirect variables should be of type reference! (%lld)", v->type);
+        CHECK("indirect variables should be of type reference! (%lld)", (long long)v->type);
       }
       pop_stack(vm);
       xx.integer --;
@@ -368,20 +407,20 @@ void execute_instruction(hVm vm) {
       break;
     case BC_JMP:
       param = get_param(vm);
-      vm->ip = &vm->fp->block->bytecode[param];
+      vm->ip = &vm->fp->block_func.block->bytecode[param];
       break;
     case BC_JT:
       param = get_param(vm);
       vv = pop_stack(vm);
       if (!sbV_c_falsy(&vv)) {
-        vm->ip = &vm->fp->block->bytecode[param];
+        vm->ip = &vm->fp->block_func.block->bytecode[param];
       }
       break;
     case BC_JF:
       param = get_param(vm);
       vv = pop_stack(vm);
       if (sbV_c_falsy(&vv)) {
-        vm->ip = &vm->fp->block->bytecode[param];
+        vm->ip = &vm->fp->block_func.block->bytecode[param];
       }
       break;
     case BC_RET:
@@ -481,12 +520,7 @@ void execute_instruction(hVm vm) {
     case BC_ALLOC_VARS:
       /* TODO check for overflow */
       param = get_param(vm);
-      /* have to set new rstack space to 0 so we don't accidentally decrement
-       * the ref count of variables from a previous stack frame (or of just garbage)  */
-      memset(vm->rp, 0, param * sizeof(hV));
-      /* we allocate one more variable at 0 for internal use */
-      vm->rp += (param + 1) * sizeof(hV);
-      vm->fp->num_locals += param;
+      sbVm_request_var_space(vm, param);
       break;
     case BC_CLOSURE:
       param = get_param(vm);
@@ -501,7 +535,7 @@ void execute_instruction(hVm vm) {
          * have previously been being manipulated using the IND instructions */
         w = peek_stack(vm, 0);
         if (w->type != IT_REF) {
-          CHECK("internal violation: closure should receive a set of reference variables (got %lld)", w->type);
+          CHECK("internal violation: closure should receive a set of reference variables (got %lld)", (long long)w->type);
         }
         sbClosure_set_ref(c, param, w);
         pop_stack(vm);
index ebcb8bf..e110e19 100644 (file)
@@ -36,6 +36,8 @@
 
 /* Also these stacks grow upwards in memory, from low to high addresses. */
 
+typedef void (*sbRuntimeCFunc)(hVm, flag init);
+
 typedef enum sbVmStatus {
   VM_STAT_UNKNOWN,
   VM_STAT_SUCCESS,
@@ -46,8 +48,14 @@ typedef struct sbVmStackFrame {
   const u8 *return_addr;
   struct sbVmStackFrame *last_fp;
   u8 *last_rp;
-  const sbVmBlock *block;
-  hClosure closure;
+  flag is_c_func;
+  union {
+    struct {
+      const sbVmBlock *block;
+      hClosure closure;
+    } block_func;
+    sbRuntimeCFunc c_func;
+  };
   usize num_locals;
   hV locals[];
 } sbVmStackFrame;
@@ -82,3 +90,7 @@ hV sbVm_pop(hVm vm);
 hV *sbVm_peek(hVm vm, usize where);
 
 void sbVm_call_func(hVm vm, hV func);
+
+void sbVm_call_c_func(hVm vm, sbRuntimeCFunc func);
+
+void sbVm_request_var_space(hVm vm, usize amount);
index 15e9f47..1b73140 100644 (file)
@@ -31,7 +31,7 @@ hV sbV_add(const hV *a, const hV *b) {
   if (a->type == IT_INTEGER && b->type == IT_INTEGER) {
     return sbV_int(sbInteger_sum(a->integer, b->integer));
   } else {
-    PANIC("todo (add type %llu and type %llu)", a->type, b->type);
+    PANIC("todo (add type %llu and type %llu)", (long long)a->type, (long long)b->type);
   }
 }
 
@@ -39,7 +39,7 @@ hV sbV_sub(const hV *a, const hV *b) {
   if (a->type == IT_INTEGER && b->type == IT_INTEGER) {
     return sbV_int(sbInteger_diff(a->integer, b->integer));
   } else {
-    PANIC("todo (subtract type %llu minus %llu)", a->type, b->type);
+    PANIC("todo (subtract type %llu minus %llu)", (long long)a->type, (long long)b->type);
   }
 }
 
@@ -120,7 +120,7 @@ hV sbV_index(hV *a, hV *b) {
   if (a->type == IT_LIST && b->type == IT_INTEGER) {
     return sbList_index(a->list, b->integer);
   } else {
-    PANIC("todo %lld %lld", a->type, b->type);
+    PANIC("todo %lld %lld", (long long)a->type, (long long)b->type);
   }
 }
 
@@ -128,7 +128,7 @@ hV sbV_scope_get(hV *obj, hV *key) {
   if (obj->type == IT_HASH) {
     return sbHash_find(obj->hash, key);
   } else {
-    PANIC("todo %lld", obj->type);
+    PANIC("todo %lld", (long long)obj->type);
   }
 }
 
@@ -136,6 +136,6 @@ void sbV_scope_set(hV *obj, hV *key, hV *value) {
   if (obj->type == IT_HASH) {
     sbHash_insert(obj->hash, key, value);
   } else {
-    PANIC("todo %lld", obj->type);
+    PANIC("todo %lld", (long long)obj->type);
   }
 }