From: cassowarii Date: Mon, 13 Jul 2026 01:34:57 +0000 (-0700) Subject: method dispatch cleanup + 'properties' (builtin methods w/o ()) X-Git-Url: https://git.cassowary.me/gitweb.cgi?a=commitdiff_plain;h=f2ea120386417c7e09886af7a14a9cbc84127bce;p=sarabande.git method dispatch cleanup + 'properties' (builtin methods w/o ()) --- diff --git a/sample/project_euler/pe03.sa b/sample/project_euler/pe03.sa index 9336183..5be690a 100644 --- a/sample/project_euler/pe03.sa +++ b/sample/project_euler/pe03.sa @@ -10,7 +10,7 @@ def factors num { } def prime? num { - factors(num).length() == 0 + factors(num).length == 0 } factors 600851475143 diff --git a/src/lib/lib.c b/src/lib/lib.c index 2f293d2..11dcc28 100644 --- a/src/lib/lib.c +++ b/src/lib/lib.c @@ -7,7 +7,8 @@ #include "lib/method.h" #include "lib/module.h" -sbCFuncStatus please_call_again(hVm vm, flag init); +static sbLibTable *find_method_table(hV *target); +static sbCFuncStatus please_call_again(hVm vm, flag init); void sbLib_sys_init() { sbLib_create_sentinels(); @@ -35,30 +36,19 @@ void sbLib_resolve_method(hVm vm) { if (argc->type != IT_INTEGER) { CHECK("argc of send should be integer!"); } + if (argc->integer == 0) { + PANIC("Method selection is required!"); + } + /* subtract 1 because the method name is itself a param */ + usize num_params = argc->integer - 1; + hV *method_name_val = sbVm_pop(vm); if (method_name_val->type != IT_SYMBOL) { - /* TODO this may become not true */ PANIC("method name must be symbol!"); } hSymbol method_sym = method_name_val->symbol; - /* subtract 1 because the method name is itself a param */ - usize num_params = argc->integer - 1; - hLibTable table_to_use = NULL; - switch(target->type) { - case IT_LIST: - table_to_use = &g_list_methods; - break; - case IT_STRING: - table_to_use = &g_string_methods; - break; - case IT_INTEGER: - table_to_use = &g_integer_methods; - break; - case IT_FLOAT: - table_to_use = &g_float_methods; - break; - } + sbLibTable *table_to_use = find_method_table(target); if (table_to_use == NULL) { /* did not find built in method table */ @@ -78,9 +68,72 @@ void sbLib_resolve_method(hVm vm) { } } else { /* built in type that has a method table defined: find method */ - sbLibMethod f = sbLibTable_find_method(table_to_use, method_name_val->symbol); + sbLibMethod *f = sbLibTable_find_method(table_to_use, method_name_val->symbol); + if (f) { + if (f->is_property) { + /* uhhh... this is theoretically possible but weird. it's like, + * saying "list.length(string::convert)". fortunately we can just + * sort of treat it as a normal method call again */ + f->behavior(vm, target, 0); + if (num_params > 0) { + sbVm_push(vm, &HVINT(num_params)); + sbLib_resolve_method(vm); + } + } else { + if (num_params >= f->min_args && (num_params <= f->max_args || f->max_args == -1)) { + f->behavior(vm, target, num_params); + } else { + PANIC("Wrong number of arguments passed to method '%s'! (expected %d~%d)", sbSymbol_name(method_name_val->symbol), f->min_args, f->max_args); + } + } + } else { + PANIC("invalid method name for type %lld: %s", (long long)target->type, sbSymbol_name(method_name_val->symbol)); + } + } +} + +/* function that resolves constructs like 'a.b' or 'a(:b)' for intrinsic types. + * (non-intrinsic types are always functions, so they can be resolved just by + * calling them)*/ +void sbLib_resolve_property(hVm vm) { + printf("Resolving property\n"); + sbVm_print_stack(vm); + hV *target = sbVm_pop(vm); + hV *argc = sbVm_pop(vm); + if (argc->type != IT_INTEGER) { + CHECK("argc of send should be integer!"); + } + if (argc->integer != 1) { + PANIC("Illegal number of arguments!"); + } + + hV *method_name_val = sbVm_pop(vm); + if (method_name_val->type != IT_SYMBOL) { + PANIC("method name must be symbol!"); + } + + sbLibTable *table_to_use = find_method_table(target); + + if (table_to_use == NULL) { + /* did not find built in method table */ + if (target->type <= 0) { + /* for built in type, just fail, it's not done yet */ + PANIC("Have not implemented this method table yet! (%lld)", (long long)target->type); + } else { + CHECK("This should never be reached!"); + } + } else { + /* built in type that has a method table defined: find method */ + sbLibMethod *f = sbLibTable_find_method(table_to_use, method_name_val->symbol); if (f) { - f(vm, target, num_params); + if (f->is_property) { + /* we found a property! great, just compute it and be done */ + f->behavior(vm, target, 0); + } else { + /* it is a method, but it is being called without parameters. + * annoyingly, we need to construct a bound version of this method. */ + PANIC("I haven't done this yet!"); + } } else { PANIC("invalid method name for type %lld: %s", (long long)target->type, sbSymbol_name(method_name_val->symbol)); } @@ -102,10 +155,26 @@ void sbLib_resolve_global(hVm vm) { /* --- */ +/* function that looks up something in a built-in method table */ +static sbLibTable *find_method_table(hV *target) { + switch(target->type) { + case IT_LIST: + return &g_list_methods; + case IT_STRING: + return &g_string_methods; + case IT_INTEGER: + return &g_integer_methods; + case IT_FLOAT: + return &g_float_methods; + default: + return NULL; + } +} + /* function that resolves methods from BC_SEND for non intrinsic types (aka functions) */ /* we have something like a.b(c,d,e), we need to call twice to get result of a(:b)(c,d,e) */ /* resolve_method should have set us up so we can just call in twice */ -sbCFuncStatus please_call_again(hVm vm, flag init) { +static sbCFuncStatus please_call_again(hVm vm, flag init) { hV *target = sbVm_pop(vm); if (target->type <= 0) { PANIC("i have not implemented partial methods for builtin types yet! i'll get to it"); @@ -125,3 +194,4 @@ sbCFuncStatus please_call_again(hVm vm, flag init) { return CFUNC_TAILCALL; } } + diff --git a/src/lib/lib.h b/src/lib/lib.h index 97697a6..bc10dfc 100644 --- a/src/lib/lib.h +++ b/src/lib/lib.h @@ -1,10 +1,20 @@ #ifndef __SARABANDE_LIB_H__ #define __SARABANDE_LIB_H__ -typedef void (*sbLibMethod)(hVm, hV*, usize); +#define METHOD(f, min, max) (sbLibMethod) { .behavior = f, .is_property = FALSE, .min_args = min, .max_args = max } +#define PROPERTY(f) (sbLibMethod) { .behavior = f, .is_property = TRUE } + +typedef struct sbLibMethod { + void (*behavior)(hVm, hV*, usize); + flag is_property : 1; + i16 min_args : 15; + i16 max_args; +} sbLibMethod; void sbLib_resolve_method(hVm vm); +void sbLib_resolve_property(hVm vm); + void sbLib_resolve_global(hVm vm); void sbLib_sys_init(); diff --git a/src/lib/method/float.c b/src/lib/method/float.c index a7304cb..146cd38 100644 --- a/src/lib/method/float.c +++ b/src/lib/method/float.c @@ -24,5 +24,5 @@ static void to_string(hVm vm, hV *target, usize num_params) { void sbFloat_create_methods(void) { sbLibTable_initialize(&g_float_methods, 16, TRUE); - REGISTER_METHOD_SYM(&g_float_methods, S_OP_TO_STRING, to_string); + REGISTER_METHOD_SYM(&g_float_methods, S_OP_TO_STRING, &PROPERTY(to_string)); } diff --git a/src/lib/method/integer.c b/src/lib/method/integer.c index 6b94af7..d00640c 100644 --- a/src/lib/method/integer.c +++ b/src/lib/method/integer.c @@ -25,5 +25,5 @@ static void to_string(hVm vm, hV *target, usize num_params) { void sbInteger_create_methods(void) { sbLibTable_initialize(&g_integer_methods, 16, TRUE); - REGISTER_METHOD_SYM(&g_integer_methods, S_OP_TO_STRING, to_string); + REGISTER_METHOD_SYM(&g_integer_methods, S_OP_TO_STRING, &PROPERTY(to_string)); } diff --git a/src/lib/method/list.c b/src/lib/method/list.c index c55feca..9c2dcf7 100644 --- a/src/lib/method/list.c +++ b/src/lib/method/list.c @@ -16,27 +16,18 @@ sbCFuncStatus list_all_cfunc(hVm vm, flag init); sbLibTable g_list_methods; static void length(hVm vm, hV *list, usize num_params) { - if (num_params != 0) { - PANIC("list#length takes no arguments!"); - } usize length; sbList_get_value(list->list, &length); sbVm_push_immediate(vm, &HVINT(length)); } static void push(hVm vm, hV *list, usize num_params) { - if (num_params != 1) { - PANIC("list#push expects 1 argument!"); - } hV *to_append = sbVm_pop(vm); sbList_append(list->list, to_append); sbVm_push_immediate(vm, &HVNIL); } static void reverse(hVm vm, hV *list, usize num_params) { - if (num_params != 0) { - PANIC("list#reverse takes no arguments!"); - } /* TODO maybe mutate in place if no other refs */ usize length; hV *elems = sbList_get_value(list->list, &length); @@ -50,9 +41,6 @@ static void reverse(hVm vm, hV *list, usize num_params) { } static void join(hVm vm, hV *list, usize num_params) { - if (num_params > 1) { - PANIC("list#join takes no arguments or 1 argument!"); - } flag join_with = FALSE; hString delimiter; if (num_params == 1) { @@ -79,65 +67,47 @@ static void join(hVm vm, hV *list, usize num_params) { } static void each(hVm vm, hV *list, usize num_params) { - 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); } static void map(hVm vm, hV *list, usize num_params) { - 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); } static void filter(hVm vm, hV *list, usize num_params) { - 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); } static void reduce(hVm vm, hV *list, usize num_params) { - if (num_params != 2) { - PANIC("wrong number of arguments passed to list#reduce"); - } sbVm_push_immediate(vm, list); sbVm_call_c_func(vm, list_reduce_cfunc); } static void any_p(hVm vm, hV *list, usize num_params) { - if (num_params != 1) { - PANIC("wrong number of arguments passed to list#any?"); - } sbVm_push_immediate(vm, list); sbVm_call_c_func(vm, list_any_cfunc); } static void all_p(hVm vm, hV *list, usize num_params) { - if (num_params != 1) { - PANIC("wrong number of arguments passed to list#any?"); - } sbVm_push_immediate(vm, list); sbVm_call_c_func(vm, list_all_cfunc); } void sbList_create_methods(void) { sbLibTable_initialize(&g_list_methods, 16, TRUE); - REGISTER_METHOD(&g_list_methods, "length", length); - REGISTER_METHOD(&g_list_methods, "push", push); - REGISTER_METHOD(&g_list_methods, "reverse", reverse); - REGISTER_METHOD(&g_list_methods, "join", join); - REGISTER_METHOD(&g_list_methods, "each", each); - REGISTER_METHOD(&g_list_methods, "map", map); - REGISTER_METHOD(&g_list_methods, "filter", filter); - REGISTER_METHOD(&g_list_methods, "reduce", reduce); - REGISTER_METHOD(&g_list_methods, "any?", any_p); - REGISTER_METHOD(&g_list_methods, "all?", all_p); + REGISTER_METHOD(&g_list_methods, "length", &PROPERTY(length)); + REGISTER_METHOD(&g_list_methods, "push", &METHOD(push, 1, 1)); + REGISTER_METHOD(&g_list_methods, "reverse", &METHOD(reverse, 0, 0)); + REGISTER_METHOD(&g_list_methods, "join", &METHOD(join, 0, 1)); + REGISTER_METHOD(&g_list_methods, "each", &METHOD(each, 1, 1)); + REGISTER_METHOD(&g_list_methods, "map", &METHOD(map, 1, 1)); + REGISTER_METHOD(&g_list_methods, "filter", &METHOD(filter, 1, 1)); + REGISTER_METHOD(&g_list_methods, "reduce", &METHOD(reduce, 2, 2)); + REGISTER_METHOD(&g_list_methods, "any?", &METHOD(any_p, 1, 1)); + REGISTER_METHOD(&g_list_methods, "all?", &METHOD(all_p, 1, 1)); } /* --- */ diff --git a/src/lib/method/string.c b/src/lib/method/string.c index 614156d..2ae655d 100644 --- a/src/lib/method/string.c +++ b/src/lib/method/string.c @@ -36,6 +36,6 @@ static void to_string(hVm vm, hV *target, usize num_params) { void sbString_create_methods(void) { sbLibTable_initialize(&g_string_methods, 16, TRUE); - REGISTER_METHOD(&g_string_methods, "split", split); - REGISTER_METHOD_SYM(&g_string_methods, S_OP_TO_STRING, to_string); + REGISTER_METHOD(&g_string_methods, "split", &METHOD(split, 0, 1)); + REGISTER_METHOD_SYM(&g_string_methods, S_OP_TO_STRING, &PROPERTY(to_string)); } diff --git a/src/lib/table.c b/src/lib/table.c index f266116..bf68559 100644 --- a/src/lib/table.c +++ b/src/lib/table.c @@ -17,7 +17,7 @@ typedef sbLibTable { } sbLibTable; */ -static void insert_method(hSymbol *keys, sbLibMethod *methods, usize capacity, hSymbol key, sbLibMethod method); +static void insert_method(hSymbol *keys, sbLibMethod *methods, usize capacity, hSymbol key, sbLibMethod *method); static void insert_value(hSymbol *keys, hV *values, usize capacity, hSymbol key, hV *value); static void check_grow_table(hLibTable t); @@ -46,7 +46,7 @@ void sbLibTable_deinitialize(hLibTable t) { *t = (sbLibTable) {0}; } -sbLibMethod sbLibTable_find_method(hLibTable t, hSymbol key) { +sbLibMethod *sbLibTable_find_method(hLibTable t, hSymbol key) { if (!t->method_table) CHECK("cannot find method in non-method table"); sbHashValue hash = sbHash_hash_bytes((const char*)&key, sizeof(hSymbol)); @@ -57,7 +57,7 @@ sbLibMethod sbLibTable_find_method(hLibTable t, hSymbol key) { } if (t->keys[index]) { - return t->methods[index]; + return &t->methods[index]; } else { return NULL; } @@ -80,7 +80,7 @@ hV *sbLibTable_find_value(hLibTable t, hSymbol key) { } } -void sbLibTable_register_method_sym(hLibTable t, hSymbol key, sbLibMethod method) { +void sbLibTable_register_method_sym(hLibTable t, hSymbol key, sbLibMethod *method) { if (!t->method_table) CHECK("cannot put method in non-method table"); check_grow_table(t); @@ -89,7 +89,7 @@ void sbLibTable_register_method_sym(hLibTable t, hSymbol key, sbLibMethod method t->used ++; } -void sbLibTable_register_method(hLibTable t, const char *method_name, usize method_name_length, sbLibMethod method) { +void sbLibTable_register_method(hLibTable t, const char *method_name, usize method_name_length, sbLibMethod *method) { hSymbol sym = sbSymbol_from_bytes(method_name, method_name_length); sbLibTable_register_method_sym(t, sym, method); } @@ -110,7 +110,7 @@ void sbLibTable_register_value(hLibTable t, const char *value_name, usize value_ /* --- */ -static void insert_method(hSymbol *keys, sbLibMethod *methods, usize capacity, hSymbol key, sbLibMethod method) { +static void insert_method(hSymbol *keys, sbLibMethod *methods, usize capacity, hSymbol key, sbLibMethod *method) { sbHashValue hash = sbHash_hash_bytes((const char*)&key, sizeof(hSymbol)); usize index = hash % capacity; @@ -120,7 +120,7 @@ static void insert_method(hSymbol *keys, sbLibMethod *methods, usize capacity, h } keys[index] = key; - methods[index] = method; + methods[index] = *method; } static void insert_value(hSymbol *keys, hV *values, usize capacity, hSymbol key, hV *value) { @@ -147,7 +147,7 @@ static void check_grow_table(hLibTable t) { for (usize i = 0; i < t->capacity; i++) { if (t->keys[i] != 0) { - insert_method(new_keys, new_methods, new_capacity, t->keys[i], t->methods[i]); + insert_method(new_keys, new_methods, new_capacity, t->keys[i], &t->methods[i]); } } diff --git a/src/lib/table.h b/src/lib/table.h index 46cb685..a8c9bfd 100644 --- a/src/lib/table.h +++ b/src/lib/table.h @@ -23,13 +23,13 @@ void sbLibTable_initialize(hLibTable t, usize capacity, flag method_table); void sbLibTable_deinitialize(hLibTable t); -sbLibMethod sbLibTable_find_method(hLibTable t, hSymbol key); +sbLibMethod *sbLibTable_find_method(hLibTable t, hSymbol key); hV *sbLibTable_find_value(hLibTable t, hSymbol key); -void sbLibTable_register_method(hLibTable t, const char *method_name, usize method_name_length, sbLibMethod method); +void sbLibTable_register_method(hLibTable t, const char *method_name, usize method_name_length, sbLibMethod *method); -void sbLibTable_register_method_sym(hLibTable t, hSymbol key, sbLibMethod method); +void sbLibTable_register_method_sym(hLibTable t, hSymbol key, sbLibMethod *method); void sbLibTable_register_value(hLibTable t, const char *value_name, usize value_name_length, hV *value); diff --git a/src/vm/exec.c b/src/vm/exec.c index 9b8ac03..f4dd780 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -486,9 +486,9 @@ void execute_instruction(hVm vm) { if (v->type == IT_BUILTIN) { call_builtin(vm, v); } else if (v->type <= 0) { - /* We need to figure out exception support or some such. - * User error should not panic. */ - PANIC("attempt to call a non-function value"); + /* intrinsic type: resolve a property on it */ + push_stack(vm, v); + sbLib_resolve_property(vm); } else { call_block(vm, v->type, v->closure); }