initial stab at method calls; *almost* works, but not quite
authorcassowarii <cassowary@cassowary.me>
Fri, 3 Jul 2026 23:22:16 +0000 (16:22 -0700)
committercassowarii <cassowary@cassowary.me>
Fri, 3 Jul 2026 23:22:16 +0000 (16:22 -0700)
13 files changed:
src/compile/emit.c
src/compile/ir.c
src/compile/ir.h
src/compile/print_ir.c
src/data/methods.c [new file with mode: 0644]
src/data/methods.h [new file with mode: 0644]
src/data/value.h
src/parse/parser.c
src/vm/exec.c
src/vm/exec.h
src/vm/operations.c
src/vm/operations.h
src/vm/vm.h

index 2c91afc..acab122 100644 (file)
@@ -323,6 +323,11 @@ void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) {
       compile_expr(cm, expr->call.func);
       EMIT(BC_CALL);
       break;
+    case IR_E_SEND:
+      compile_list(cm, expr->send.message);
+      compile_expr(cm, expr->send.target);
+      EMIT(BC_SEND);
+      break;
     case IR_E_VAR:
       if (expr->var->is_upvalue) {
         EMIT(BC_LD_UPVAL);
index 70ba765..6bd6545 100644 (file)
@@ -340,6 +340,14 @@ static sbIrExpr *expr_call(hIrChunk ck, sbIrExpr *to_call, sbIrExpr *param) {
   });
 }
 
+static sbIrExpr *expr_send(hIrChunk ck, sbIrExpr *target, sbIrExpr *message) {
+  return new_expr(ck, &(sbIrExpr) {
+    .type = IR_E_SEND,
+    .send.target = target,
+    .send.message = message,
+  });
+}
+
 static sbIrExpr *expr_list(hIrChunk ck, sbIrExpr *value) {
   return new_expr(ck, &(sbIrExpr) {
     .type = IR_E_LIST,
@@ -929,6 +937,10 @@ static sbIrExpr *compile_ast_expr(hIrChunk ck, sbAst node, flag list_context) {
     sbIrExpr *called = compile_ast_expr(ck, node->seq.left, FALSE);
     sbIrExpr *params = compile_ast_list(ck, node->seq.right);
     return expr_call(ck, called, params);
+  } else if (node->type == AST_NODE_METHODCALL) {
+    sbIrExpr *target = compile_ast_expr(ck, node->seq.left, FALSE);
+    sbIrExpr *message = compile_ast_list(ck, node->seq.right);
+    return expr_send(ck, target, message);
   } else if (node->type == AST_VAL_FUNC) {
     sbIrChunk *func = compile_ast_function(ck->program, node->seq.left, node->seq.right);
     return expr_func(ck, func);
index 3ddb128..a1044fc 100644 (file)
@@ -46,6 +46,7 @@ typedef enum sbIrExprType {
   IR_E_VAR,
   IR_E_FUNC,
   IR_E_CALL,
+  IR_E_SEND,
   IR_E_LIST,
   IR_E_HASH,
 } sbIrExprType;
@@ -100,6 +101,10 @@ typedef struct sbIrExpr {
       struct sbIrExpr *this;
       struct sbIrExpr *next;
     } list;
+    struct {
+      struct sbIrExpr *target;
+      struct sbIrExpr *message;
+    } send;
   };
 } sbIrExpr;
 
index 374eda4..ab71f23 100644 (file)
@@ -74,13 +74,16 @@ static void print_expr(sbIrExpr *e) {
     case IR_E_CALL:
       debug("CALL: ");
       print_expr(e->call.func);
-      debug(" with params ");
-      sbIrExpr *param = e->call.param;
-      while (param) {
-        print_expr(param->list.this);
-        debug(",");
-        param = param->list.next;
-      }
+      debug(" with params (");
+      print_expr(e->call.param);
+      debug(")");
+      break;
+    case IR_E_SEND:
+      debug("SEND: ");
+      print_expr(e->send.target);
+      debug(" <~( ");
+      print_expr(e->send.message);
+      debug(" )");
       break;
     case IR_E_LIST:
       print_expr(e->list.this);
diff --git a/src/data/methods.c b/src/data/methods.c
new file mode 100644 (file)
index 0000000..dc8bd0a
--- /dev/null
@@ -0,0 +1,49 @@
+#include "data/methods.h"
+
+#include "vm/exec.h"
+#include "data/symbol.h"
+#include "data/list.h"
+
+#define METHOD_IS(name) (!sbstrncmp(method_name, name, sizeof(name)))
+
+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");
+  }
+  hV argc = sbVm_pop(vm);
+  if (argc.type != IT_INTEGER) {
+    CHECK("argc of send should be integer!");
+  }
+  /* subtract 1 because the method name is itself a param */
+  usize num_params = argc.integer - 1;
+  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);
+    printf("method name: %s\n", method_name);
+    if (METHOD_IS("each")) {
+      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);
+      }
+    }
+  } else {
+    PANIC("method name to list is not symbol! (%lld)", method_name_val->type);
+  }
+}
+
diff --git a/src/data/methods.h b/src/data/methods.h
new file mode 100644 (file)
index 0000000..c93dc5a
--- /dev/null
@@ -0,0 +1,3 @@
+#include "common.h"
+
+void sbList_method(hV list, hVm vm);
index 61c1956..46bfc03 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "common.h"
 
+#define FLAG_SQUIGGLY (1ULL << 62)
+
 #define HVINT(n) ((hV) { .type = IT_INTEGER, .integer = n })
 #define HVSTR(s) ((hV) { .type = IT_STRING, .string = s })
 #define HVSYM(s) ((hV) { .type = IT_SYMBOL, .symbol = s })
@@ -12,6 +14,7 @@
 #define HVNIL ((hV) { .type = IT_NIL })
 #define HVNOTHING ((hV) {0})
 #define HVFUNC(i, c) ((hV) { .type = i, .closure = c })
+#define HVFUNC2(i, c) ((hV) { .type = i | FLAG_SQUIGGLY, .closure = c })
 
 typedef u64 hHash;
 typedef u64 hString;
index b34fe1d..09076ad 100644 (file)
@@ -605,15 +605,17 @@ static sbAst parse_expr(hParser pr, u8 min_precedence) {
         method_name = parse_name_as_sym(pr);
       }
       if (method_name == NO_NODE) return syntax_error(pr);
-      if (!expect(pr, T_LPAREN)) return syntax_error(pr);
-      sbAst params = parse_comma_exprs(pr, NULL);
-      if (!expect(pr, T_RPAREN)) return syntax_error(pr);
+      sbAst params = NO_NODE;
+      if (expect(pr, T_LPAREN)) {
+        params = parse_comma_exprs(pr, NULL);
+        if (!expect(pr, T_RPAREN)) return syntax_error(pr);
+      }
       ast_type = AST_NODE_METHODCALL;
       if (op.type == T_ARROW) {
         /* (whatever)->x is rewritten as (*whatever).x */
         lhs = unop_node(pr, AST_OP_DEREF, lhs);
       }
-      rhs = seq_node(pr, AST_NODE_NEXT, method_name, params);
+      rhs = seq_node(pr, AST_NODE_MULTIVAL, method_name, params);
     } else if (op.type == T_BACKSQUIGARROW) {
       /* a <~ b, c, d can have multiple comma things on the right side */
       if (!expect(pr, T_LPAREN)) return syntax_error(pr);
index 1c4934a..42ff4f9 100644 (file)
@@ -7,6 +7,10 @@
 
 void call_block(hVm vm, usize block_id, hClosure closure);
 void execute_instruction(hVm vm);
+void push_stack(hVm vm, const hV *value);
+hV pop_stack(hVm vm);
+hV *npop_stack(hVm vm, usize count);
+hV *peek_stack(hVm vm, usize offset);
 
 void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize, flag debugmode) {
   *vm = (sbVm) {0};
@@ -50,6 +54,22 @@ sbVmStatus sbVm_execute(hVm vm, sbVmProgram *pm) {
   return VM_STAT_SUCCESS;
 }
 
+void sbVm_push(hVm vm, hV value) {
+  push_stack(vm, &value);
+}
+
+hV sbVm_pop(hVm vm) {
+  return pop_stack(vm);
+}
+
+hV *sbVm_peek(hVm vm, usize where) {
+  return peek_stack(vm, where);
+}
+
+void sbVm_call_func(hVm vm, hV func) {
+  call_block(vm, func.type, func.closure);
+}
+
 /* --- */
 
 void call_block(hVm vm, usize block_id, hClosure closure) {
@@ -327,6 +347,9 @@ void execute_instruction(hVm vm) {
       }
       call_block(vm, vv.type, vv.closure);
       break;
+    case BC_SEND:
+      sbV_message_handler(vm);
+      break;
     case BC_NUMARG:
       param = get_param(vm);
       vv = pop_stack(vm);
@@ -364,8 +387,6 @@ void execute_instruction(hVm vm) {
     case BC_RET:
       return_from_block(vm);
       break;
-    case BC_SEND:
-      PANIC("todo");
     case BC_OP_EQ:
       v = peek_stack(vm, 1);
       w = peek_stack(vm, 0);
index 0b8b219..ebcb8bf 100644 (file)
@@ -69,10 +69,16 @@ typedef struct sbVm {
   flag debugmode;
 } sbVm;
 
-typedef sbVm *hVm;
-
 void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize, flag debugmode);
 
 void sbVm_deinitialize(hVm vm);
 
 sbVmStatus sbVm_execute(hVm vm, sbVmProgram *pm);
+
+void sbVm_push(hVm vm, hV value);
+
+hV sbVm_pop(hVm vm);
+
+hV *sbVm_peek(hVm vm, usize where);
+
+void sbVm_call_func(hVm vm, hV func);
index 3504db4..15e9f47 100644 (file)
@@ -1,8 +1,31 @@
 #include "vm/operations.h"
 
+#include "vm/exec.h"
 #include "data/integer.h"
 #include "data/list.h"
 #include "data/hashtable.h"
+#include "data/methods.h"
+
+void sbV_message_handler(hVm vm) {
+  hV target = sbVm_pop(vm);
+  switch(target.type) {
+    case IT_LIST:
+      sbList_method(target, vm);
+      break;
+    default:
+      if (target.type < 0) {
+        PANIC("haven't implemented method calling for this intrinsic type!");
+      }
+      if (target.type & FLAG_SQUIGGLY) {
+        /* squiggle function */
+        sbVm_call_func(vm, target);
+      } else {
+        /* normal function */
+        PANIC("haven't implemented method calling for functions yet!");
+        //sbFunction_method(target, vm);
+      }
+  }
+}
 
 hV sbV_add(const hV *a, const hV *b) {
   if (a->type == IT_INTEGER && b->type == IT_INTEGER) {
index 672fa8b..f14c8b8 100644 (file)
@@ -1,5 +1,7 @@
 #include "common.h"
 
+void sbV_message_handler(hVm vm);
+
 hV sbV_add(const hV *a, const hV *b);
 
 hV sbV_sub(const hV *a, const hV *b);
index 24e3b76..a952ec6 100644 (file)
@@ -2,5 +2,9 @@
 #define __SARABANDE_VM_H__
 
 #include "vm/bytecode.h"
+#include "data/value.h"
+
+struct sbVm;
+typedef struct sbVm *hVm;
 
 #endif