From 83f8259920e2e3b5f5cab6181823ef29fb2e469a Mon Sep 17 00:00:00 2001 From: cassowarii <2374677+cassowarii@users.noreply.github.com> Date: Wed, 8 Jul 2026 18:46:15 -0700 Subject: [PATCH] resolve methods with hash table --- sample/project_euler/pe04.sa | 18 +++ src/data/data.c | 10 +- src/data/integer.c | 12 -- src/data/list.c | 299 ----------------------------------------- src/data/string.c | 9 -- src/lib/integer.c | 32 +++++ src/lib/libtable.c | 144 ++++++++++++++++++++ src/lib/libtable.h | 36 +++++ src/lib/list.c | 312 +++++++++++++++++++++++++++++++++++++++++++ src/lib/string.c | 29 ++++ src/mem/debug.c | 4 +- src/mem/debug.h | 2 +- src/vm/exec.c | 3 +- src/vm/operations.c | 27 ---- 14 files changed, 584 insertions(+), 353 deletions(-) create mode 100644 sample/project_euler/pe04.sa create mode 100644 src/lib/integer.c create mode 100644 src/lib/libtable.c create mode 100644 src/lib/libtable.h create mode 100644 src/lib/list.c create mode 100644 src/lib/string.c diff --git a/sample/project_euler/pe04.sa b/sample/project_euler/pe04.sa new file mode 100644 index 0000000..9b8ef33 --- /dev/null +++ b/sample/project_euler/pe04.sa @@ -0,0 +1,18 @@ +def is_palindrome num { + num.to_string == num.to_string.split.reverse.join +} + +let max = 0 + +let a = 100 +while a < 1000 { + let b = 100 + while b < 1000 { + let product = a * b + max = product if is_palindrome product and product > max + b = b + 1 + } + a = a + 1 +} + +max diff --git a/src/data/data.c b/src/data/data.c index 49817ce..f274e2d 100644 --- a/src/data/data.c +++ b/src/data/data.c @@ -7,6 +7,8 @@ #include "data/reference.h" #include "data/closure.h" +#include "lib/libtable.h" + extern sbPool g_closure_pool; void data_sys_init() { @@ -16,13 +18,17 @@ void data_sys_init() { sbClosure_sys_init(); sbSymbol_sys_init(); sbList_sys_init(); + + sbLib_sys_init(); } void data_sys_deinit() { - sbList_sys_deinit(); + sbLib_sys_deinit(); + sbSymbol_sys_deinit(); - sbRef_sys_deinit(); + sbList_sys_deinit(); sbClosure_sys_deinit(); + sbRef_sys_deinit(); sbHash_sys_deinit(); sbString_sys_deinit(); } diff --git a/src/data/integer.c b/src/data/integer.c index 94524c0..7af907b 100644 --- a/src/data/integer.c +++ b/src/data/integer.c @@ -125,18 +125,6 @@ void sbInteger_method(hVm vm) { /* TODO: Need a better way of resolving these */ /* also TODO shouldn't be 'to_string' */ if (METHOD_IS("to_string")) { - sbVm_pop(vm); /* remove method name */ - char stackbuf[1024]; - char *buf = stackbuf; - usize length = snprintf(buf, 1024, "%lld", (long long)target->integer); - if (length + 1 >= sizeof(stackbuf)) { - buf = malloc(length + 1); - snprintf(buf, length, "%lld", (long long)target->integer); - } - sbVm_push_immediate(vm, &HVSTR(sbString_new(buf, length))); - if (buf != stackbuf) { - free(buf); - } } else { PANIC("unknown method name for integer"); } diff --git a/src/data/list.c b/src/data/list.c index e55875b..467e9ea 100644 --- a/src/data/list.c +++ b/src/data/list.c @@ -2,18 +2,10 @@ #include "gc/gcinfo.h" #include "vm/exec.h" -#include "data/symbol.h" -#include "data/string.h" #define LIST_PER_BLOCK 256 #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 list_filter_cfunc(hVm vm, flag init); -void list_any_cfunc(hVm vm, flag init); -void list_all_cfunc(hVm vm, flag init); - /* TODO etc etc */ sbPool g_list_pool; @@ -59,299 +51,8 @@ hV *sbList_index(hList list, usize index) { return item; } -void sbList_method(hVm vm) { - hV *list = sbVm_pop(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) { - /* TODO this may become not true */ - PANIC("method name for list must be symbol!"); - } - - const char *method_name = sbSymbol_name(method_name_val->symbol); - /* TODO: Need a better way of resolving these */ - 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("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("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("any?")) { - 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); - } else if (METHOD_IS("all?")) { - 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); - } else if (METHOD_IS("reverse")) { - if (num_params != 0) { - PANIC("list#reverse takes no arguments!"); - } - /* TODO maybe mutate in place if no other refs */ - sbVm_pop(vm); /* remove method name */ - usize length; - hV *elems = sbList_get_value(list->list, &length); - hList new_list = sbList_new(length); - for (usize i = length - 1; ; i--) { - sbList_append(new_list, &elems[i]); - if (i == 0) break; - } - sbList_get_value(new_list, &length); - sbVm_push_immediate(vm, &HVLIST(new_list)); - } else if (METHOD_IS("join")) { - if (num_params > 1) { - PANIC("list#join takes no arguments or 1 argument!"); - } - flag join_with = FALSE; - hString delimiter; - if (num_params == 1) { - hV *delimiter_v = sbVm_pop(vm); - if (delimiter_v->type != IT_STRING) { - PANIC("list#join parameter should be string!"); - } - join_with = TRUE; - delimiter = delimiter_v->string; - } - sbVm_pop(vm); /* remove method name */ - usize length; - hV *elems = sbList_get_value(list->list, &length); - hString joined = sbString_new("", 0); - for (usize i = 0; i < length; i++) { - if (elems[i].type != IT_STRING) { - PANIC("need string to join"); - } - joined = sbString_concat(joined, elems[i].string); - if (join_with && i < length - 1) { - joined = sbString_concat(joined, delimiter); - } - } - sbVm_push_immediate(vm, &HVSTR(joined)); - } else { - PANIC("unknown method name for list"); - } -} - /* --- */ sbList *get_list_by_handle(hList handle) { return sbPool_get_entry(&g_list_pool, handle); } - -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_immediate(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_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]); - } -} - -void list_any_cfunc(hVm vm, flag init) { - if (init) { - /* state: list being filtered, index, callback */ - sbVm_request_var_space(vm, 3); - hV *iterating_list = sbVm_pop(vm); - hV *pred_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] = *pred_func; - } else { - /* get result of predicate function */ - hV *mapped = sbVm_pop(vm); - if (!sbV_c_falsy(mapped)) { - /* one was true! return true */ - sbVm_push_immediate(vm, &HVBOOL(TRUE)); - return; - } - } - - /* haven't found any yet... */ - 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) { - /* try next element */ - sbVm_push(vm, &iter_values[current_index]); - sbVm_push_immediate(vm, &HVINT(1)); - sbVm_call_func(vm, &vm->fp->locals[2]); - } else { - /* no result found */ - sbVm_push_immediate(vm, &HVBOOL(FALSE)); - } -} - -void list_all_cfunc(hVm vm, flag init) { - if (init) { - /* state: list being filtered, index, callback */ - sbVm_request_var_space(vm, 3); - hV *iterating_list = sbVm_pop(vm); - hV *pred_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] = *pred_func; - } else { - /* get result of predicate function */ - hV *mapped = sbVm_pop(vm); - if (sbV_c_falsy(mapped)) { - /* one was false; return false */ - sbVm_push_immediate(vm, &HVBOOL(FALSE)); - return; - } - } - - /* all true so far */ - 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) { - /* try next element */ - sbVm_push(vm, &iter_values[current_index]); - sbVm_push_immediate(vm, &HVINT(1)); - sbVm_call_func(vm, &vm->fp->locals[2]); - } else { - /* all passed */ - sbVm_push_immediate(vm, &HVBOOL(TRUE)); - } -} diff --git a/src/data/string.c b/src/data/string.c index 13fc006..950af1d 100644 --- a/src/data/string.c +++ b/src/data/string.c @@ -255,15 +255,6 @@ void sbString_method(hVm vm) { const char *method_name = sbSymbol_name(method_name_val->symbol); /* TODO: Need a better way of resolving these */ if (METHOD_IS("split")) { - sbVm_pop(vm); /* remove method name */ - usize length; - char scratch[8]; - const char *buf = sbString_get_value(target->string, scratch, &length); - hList l = sbList_new(length); - for (usize i = 0; i < length; i++) { - sbList_append(l, &HVSTR(sbString_new(&buf[i], 1))); - } - sbVm_push_immediate(vm, &HVLIST(l)); } } diff --git a/src/lib/integer.c b/src/lib/integer.c new file mode 100644 index 0000000..76045b7 --- /dev/null +++ b/src/lib/integer.c @@ -0,0 +1,32 @@ +#include "lib/libtable.h" + +#include "data/list.h" +#include "data/string.h" +#include "vm/exec.h" + +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 list_any_cfunc(hVm vm, flag init); +void list_all_cfunc(hVm vm, flag init); + +sbLibTable g_integer_methods; + +static void to_string(hVm vm, hV *target, usize num_params) { + sbVm_pop(vm); /* remove method name */ + char stackbuf[1024]; + char *buf = stackbuf; + usize length = snprintf(buf, 1024, "%lld", (long long)target->integer); + if (length + 1 >= sizeof(stackbuf)) { + buf = malloc(length + 1); + snprintf(buf, length, "%lld", (long long)target->integer); + } + sbVm_push_immediate(vm, &HVSTR(sbString_new(buf, length))); + if (buf != stackbuf) { + free(buf); + } +} + +void sbInteger_create_methods(void) { + LIB_REGISTER(&g_integer_methods, "to_string", to_string); +} diff --git a/src/lib/libtable.c b/src/lib/libtable.c new file mode 100644 index 0000000..94ab7e1 --- /dev/null +++ b/src/lib/libtable.c @@ -0,0 +1,144 @@ +#include "lib/libtable.h" + +#include "data/hashtable.h" +#include "vm/exec.h" +#include "mem/debug.h" + +/* +typedef sbLibTable { + usize used; + usize capacity; + hSymbol *keys; + sbLibFunc *funcs; +} sbLibTable; +*/ + +extern void sbList_create_methods(); +extern void sbString_create_methods(); +extern void sbInteger_create_methods(); + +static void insert_func(hSymbol *keys, sbLibFunc *funcs, usize capacity, hSymbol key, sbLibFunc func); + +void sbLibTable_initialize(hLibTable t, usize capacity) { + *t = (sbLibTable) {0}; + if (capacity < 16) { + capacity = 16; + } + t->keys = calloc(capacity, sizeof(hSymbol)); + t->funcs = calloc(capacity, sizeof(sbLibFunc)); + t->capacity = capacity; +} + +void sbLibTable_deinitialize(hLibTable t) { + free(t->keys); + free(t->funcs); + *t = (sbLibTable) {0}; +} + +void sbLib_sys_init() { + sbLibTable_initialize(&g_list_methods, 16); + sbLibTable_initialize(&g_string_methods, 16); + sbLibTable_initialize(&g_integer_methods, 16); + + sbList_create_methods(); + sbString_create_methods(); + sbInteger_create_methods(); +} + +void sbLib_sys_deinit() { + sbLibTable_deinitialize(&g_list_methods); + sbLibTable_deinitialize(&g_string_methods); + sbLibTable_deinitialize(&g_integer_methods); +} + +void sbLib_resolve_method(hVm vm) { + hV *target = sbVm_pop(vm); + 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) { + /* TODO this may become not true */ + PANIC("method name must be symbol!"); + } + + 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; + default: + PANIC("Have not implemented this method table yet!"); + } + + sbLibFunc f = sbLibTable_find(table_to_use, method_name_val->symbol); + if (f) { + f(vm, target, num_params); + } else { + PANIC("invalid method name for type %lld: %s", (long long)target->type, sbSymbol_name(method_name_val->symbol)); + } +} + +sbLibFunc sbLibTable_find(hLibTable t, hSymbol key) { + sbHashValue hash = sbHash_hash_bytes((const char*)&key, sizeof(hSymbol)); + usize index = hash % t->capacity; + while (t->keys[index] && t->keys[index] != key) { + index ++; + index %= t->capacity; + } + + if (t->keys[index]) { + return t->funcs[index]; + } else { + return NULL; + } +} + +void sbLibTable_register(hLibTable t, const char *method_name, usize method_name_length, sbLibFunc behavior) { + if (t->used > t->capacity / 4 * 3) { + /* grow table and rehash */ + usize new_capacity = t->capacity * 2; + hSymbol *new_keys = calloc(new_capacity, sizeof(hSymbol)); + sbLibFunc *new_funcs = calloc(new_capacity, sizeof(sbLibFunc)); + + for (usize i = 0; i < t->capacity; i++) { + if (t->keys[i] != 0) { + insert_func(new_keys, new_funcs, new_capacity, t->keys[i], t->funcs[i]); + } + } + + free(t->keys); + free(t->funcs); + t->keys = new_keys; + t->funcs = new_funcs; + t->capacity = new_capacity; + } + + hSymbol sym = sbSymbol_from_bytes(method_name, method_name_length); + insert_func(t->keys, t->funcs, t->capacity, sym, behavior); + t->used ++; +} + +/* --- */ + +static void insert_func(hSymbol *keys, sbLibFunc *funcs, usize capacity, hSymbol key, sbLibFunc func) { + sbHashValue hash = sbHash_hash_bytes((const char*)&key, sizeof(hSymbol)); + usize index = hash % capacity; + + while (keys[index] != 0) { + index ++; + index %= capacity; + } + + keys[index] = key; + funcs[index] = func; +} diff --git a/src/lib/libtable.h b/src/lib/libtable.h new file mode 100644 index 0000000..3f11410 --- /dev/null +++ b/src/lib/libtable.h @@ -0,0 +1,36 @@ +#include "common.h" + +#include "data/symbol.h" + +#define LIB_REGISTER(t, n, b) (sbLibTable_register(t, n, sizeof(n) - 1, b)) + +typedef void (*sbLibFunc)(hVm, hV*, usize); + +typedef struct sbLibTable { + usize used; + usize capacity; + hSymbol *keys; + sbLibFunc *funcs; +} sbLibTable; + +extern sbLibTable g_list_methods; +extern sbLibTable g_string_methods; +extern sbLibTable g_integer_methods; + +typedef sbLibTable *hLibTable; + +void sbLibTable_initialize(hLibTable t, usize capacity); + +void sbLibTable_deinitialize(hLibTable t); + +sbLibFunc sbLibTable_find(hLibTable t, hSymbol key); + +void sbLibTable_register(hLibTable t, const char *method_name, usize method_name_length, sbLibFunc behavior); + +void sbLib_resolve_method(hVm vm); + +void sbLib_sys_init(); + +void sbLib_sys_deinit(); + +void sbList_method(hVm vm); diff --git a/src/lib/list.c b/src/lib/list.c new file mode 100644 index 0000000..ba9ba38 --- /dev/null +++ b/src/lib/list.c @@ -0,0 +1,312 @@ +#include "lib/libtable.h" + +#include "data/list.h" +#include "data/string.h" +#include "vm/exec.h" + +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 list_any_cfunc(hVm vm, flag init); +void 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!"); + } + sbVm_pop(vm); /* remove method name */ + 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); + sbVm_pop(vm); /* remove method name */ + 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 */ + sbVm_pop(vm); /* remove method name */ + usize length; + hV *elems = sbList_get_value(list->list, &length); + hList new_list = sbList_new(length); + for (usize i = length - 1; ; i--) { + sbList_append(new_list, &elems[i]); + if (i == 0) break; + } + sbList_get_value(new_list, &length); + sbVm_push_immediate(vm, &HVLIST(new_list)); +} + +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) { + hV *delimiter_v = sbVm_pop(vm); + if (delimiter_v->type != IT_STRING) { + PANIC("list#join parameter should be string!"); + } + join_with = TRUE; + delimiter = delimiter_v->string; + } + sbVm_pop(vm); /* remove method name */ + usize length; + hV *elems = sbList_get_value(list->list, &length); + hString joined = sbString_new("", 0); + for (usize i = 0; i < length; i++) { + if (elems[i].type != IT_STRING) { + PANIC("need string to join"); + } + joined = sbString_concat(joined, elems[i].string); + if (join_with && i < length - 1) { + joined = sbString_concat(joined, delimiter); + } + } + sbVm_push_immediate(vm, &HVSTR(joined)); +} + +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 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) { + LIB_REGISTER(&g_list_methods, "length", length); + LIB_REGISTER(&g_list_methods, "push", push); + LIB_REGISTER(&g_list_methods, "reverse", reverse); + LIB_REGISTER(&g_list_methods, "join", join); + LIB_REGISTER(&g_list_methods, "each", each); + LIB_REGISTER(&g_list_methods, "map", map); + LIB_REGISTER(&g_list_methods, "filter", filter); + LIB_REGISTER(&g_list_methods, "any?", any_p); + LIB_REGISTER(&g_list_methods, "all?", all_p); +} + +/* --- */ + +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_immediate(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_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]); + } +} + +void list_any_cfunc(hVm vm, flag init) { + if (init) { + /* state: list being filtered, index, callback */ + sbVm_request_var_space(vm, 3); + hV *iterating_list = sbVm_pop(vm); + hV *pred_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] = *pred_func; + } else { + /* get result of predicate function */ + hV *mapped = sbVm_pop(vm); + if (!sbV_c_falsy(mapped)) { + /* one was true! return true */ + sbVm_push_immediate(vm, &HVBOOL(TRUE)); + return; + } + } + + /* haven't found any yet... */ + 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) { + /* try next element */ + sbVm_push(vm, &iter_values[current_index]); + sbVm_push_immediate(vm, &HVINT(1)); + sbVm_call_func(vm, &vm->fp->locals[2]); + } else { + /* no result found */ + sbVm_push_immediate(vm, &HVBOOL(FALSE)); + } +} + +void list_all_cfunc(hVm vm, flag init) { + if (init) { + /* state: list being filtered, index, callback */ + sbVm_request_var_space(vm, 3); + hV *iterating_list = sbVm_pop(vm); + hV *pred_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] = *pred_func; + } else { + /* get result of predicate function */ + hV *mapped = sbVm_pop(vm); + if (sbV_c_falsy(mapped)) { + /* one was false; return false */ + sbVm_push_immediate(vm, &HVBOOL(FALSE)); + return; + } + } + + /* all true so far */ + 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) { + /* try next element */ + sbVm_push(vm, &iter_values[current_index]); + sbVm_push_immediate(vm, &HVINT(1)); + sbVm_call_func(vm, &vm->fp->locals[2]); + } else { + /* all passed */ + sbVm_push_immediate(vm, &HVBOOL(TRUE)); + } +} diff --git a/src/lib/string.c b/src/lib/string.c new file mode 100644 index 0000000..e90c98f --- /dev/null +++ b/src/lib/string.c @@ -0,0 +1,29 @@ +#include "lib/libtable.h" + +#include "data/list.h" +#include "data/string.h" +#include "vm/exec.h" + +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 list_any_cfunc(hVm vm, flag init); +void list_all_cfunc(hVm vm, flag init); + +sbLibTable g_string_methods; + +static void split(hVm vm, hV *target, usize num_params) { + sbVm_pop(vm); /* remove method name */ + usize length; + char scratch[8]; + const char *buf = sbString_get_value(target->string, scratch, &length); + hList l = sbList_new(length); + for (usize i = 0; i < length; i++) { + sbList_append(l, &HVSTR(sbString_new(&buf[i], 1))); + } + sbVm_push_immediate(vm, &HVLIST(l)); +} + +void sbString_create_methods(void) { + LIB_REGISTER(&g_string_methods, "split", split); +} diff --git a/src/mem/debug.c b/src/mem/debug.c index 15bec01..2f316a5 100644 --- a/src/mem/debug.c +++ b/src/mem/debug.c @@ -1,8 +1,8 @@ #include "mem/debug.h" -void print_memory_bytes(void *where, usize how_many) { +void print_memory_bytes(const char *where, usize how_many) { for (usize i = 0; i < how_many; i++) { - printf("%02X ", ((u8*)(char*)where)[i]); + printf("%02X ", ((u8*)where)[i]); } printf("\n"); } diff --git a/src/mem/debug.h b/src/mem/debug.h index d8e9627..a8fcaa4 100644 --- a/src/mem/debug.h +++ b/src/mem/debug.h @@ -1,3 +1,3 @@ #include "common.h" -void print_memory_bytes(void *where, usize how_many); +void print_memory_bytes(const char *where, usize how_many); diff --git a/src/vm/exec.c b/src/vm/exec.c index bf9acdf..34c49a4 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -4,6 +4,7 @@ #include "data/list.h" #include "data/reference.h" #include "data/closure.h" +#include "lib/libtable.h" void call_block(hVm vm, usize block_id, hClosure closure); void return_from_block(hVm vm); @@ -440,7 +441,7 @@ void execute_instruction(hVm vm) { call_block(vm, v->type, v->closure); break; case BC_SEND: - sbV_message_handler(vm); + sbLib_resolve_method(vm); break; case BC_NUMARG: param = get_param(vm); diff --git a/src/vm/operations.c b/src/vm/operations.c index 6d89274..9208eec 100644 --- a/src/vm/operations.c +++ b/src/vm/operations.c @@ -6,33 +6,6 @@ #include "data/hashtable.h" #include "data/string.h" -void sbV_message_handler(hVm vm) { - hV *target = sbVm_peek(vm, 0); - switch(target->type) { - case IT_LIST: - sbList_method(vm); - break; - case IT_INTEGER: - sbInteger_method(vm); - break; - case IT_STRING: - sbString_method(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) { return sbV_int(sbInteger_sum(a->integer, b->integer)); -- 1.8.3.1