From: cassowarii <2374677+cassowarii@users.noreply.github.com> Date: Tue, 14 Jul 2026 06:21:18 +0000 (-0700) Subject: experimenting with writing a nontrivial amount of code X-Git-Url: https://git.cassowary.me/gitweb.cgi?a=commitdiff_plain;h=92c9779ff02e9d6a28380cee6d9f46f6bcb22a16;p=sarabande.git experimenting with writing a nontrivial amount of code --- diff --git a/sample/generator.sbd b/sample/generator.sbd new file mode 100644 index 0000000..0687f95 --- /dev/null +++ b/sample/generator.sbd @@ -0,0 +1,128 @@ +let generator = (=> { + let instance_methods = { + filter: => f { + => pred { + generator => { + let result = nil + # boolean operators don't short circuit currently, oops + let done = false + repeat { + result = f() + if result == nil { + done = true + } else if pred result { + done = true + } + } while not done + result + } + } + }, + + map: => f { + => transform { + generator => { + let result = f() + if result != nil { + transform(result) + } else { + nil + } + } + } + }, + + continue: => f { + => make_next { + let last = nil + let ended = false + generator => { + if not ended { + let this_one = f() + if this_one != nil { + last = this_one + } else { + ended = true + } + } + + if ended { + last = make_next(last) + } + last + } + } + }, + + upuntil: => f { + => cond { + generator => { + let result = f() + if result == nil or cond result { + nil + } else { + result + } + } + } + }, + + reduce: => f { + => init, combine { + let value = init + repeat { + let result = f() + value = combine(result, value) unless result == nil + } while result != nil + value + } + }, + + each: => f { + => callback { + repeat { + let result = f() + callback(result) unless result == nil + } while result != nil + } + }, + } + + let class_methods = { + of: => ...list { + let index = 0 + generator => { + if index >= list.length { + nil + } else { + let result = list[index] + index = index + 1 + result + } + } + } + } + + => f { + if f == op::index { + class_methods(op::index) + } else { + => method { + if method == :next { + f + } else { + instance_methods[method](f) + } + } + } + } +})() + +let sum_odd_squares_under_1_000_000 = generator::of(1) + .continue(=> a { a + 1 }) + .map(=> a { a * a }) + .filter(=> a { not a %% 2 }) + .upuntil(=> a { a >= 1_000_000 }) + .reduce(0, => a, b { a + b }) + +println sum_odd_squares_under_1_000_000 diff --git a/sample/project_euler/pe08.sa b/sample/project_euler/pe08.sa index 0485fe6..855cb15 100644 --- a/sample/project_euler/pe08.sa +++ b/sample/project_euler/pe08.sa @@ -4,3 +4,5 @@ let span = 13 println list::iota(0, digits.length - span + 1).map(=> index { digits[index..index + span].split().map(integer::from).reduce(1, => a, b { a * b }) }).reduce(0, math::max) + +println list::iota(0, digits.length - span + 1).map(| digits[_.._ + span].split().map(integer::from).reduce(1, {*})).reduce(0, math::max) diff --git a/src/data/hashtable.c b/src/data/hashtable.c index 93978e5..5166904 100644 --- a/src/data/hashtable.c +++ b/src/data/hashtable.c @@ -9,8 +9,9 @@ sbPool g_hashtable_pool = {0}; typedef struct hashtbl { - usize size; + usize capacity; usize used; + usize n_elems; hHash handle; flag allocated; flag is_inline; @@ -115,11 +116,16 @@ void sbHash_delete(hHash h, hV *key, hV *value) { delete_key(t, key); } +usize sbHash_get_size(hHash h) { + hashtbl *t = find_tbl_for_handle(h); + return t->n_elems; +} + /* --- */ static void set_hashtbl_size(hashtbl *t, usize new_size, flag rehash_all) { if (new_size < INLINE_TABLE_LENGTH) new_size = INLINE_TABLE_LENGTH; - if (new_size < t->size) PANIC("cannot shrink hashtable allocation"); + if (new_size < t->capacity) PANIC("cannot shrink hashtable allocation"); if (new_size == INLINE_TABLE_LENGTH) { t->is_inline = 1; @@ -129,7 +135,7 @@ static void set_hashtbl_size(hashtbl *t, usize new_size, flag rehash_all) { if (rehash_all) { hV *current_keys, *current_values; get_ptrs_for_tbl(t, ¤t_keys, ¤t_values); - for (usize i = 0; i < t->size; i++) { + for (usize i = 0; i < t->capacity; i++) { if (current_keys[i].type == IT_NOTHING || current_keys[i].type == ITX_TOMBSTONE) continue; set_key_in_array(new_keys, new_values, new_size, ¤t_keys[i], ¤t_values[i]); /* adding a new k/v pair will retain the values, so we need to release them @@ -148,11 +154,11 @@ static void set_hashtbl_size(hashtbl *t, usize new_size, flag rehash_all) { t->external.values = new_values; t->is_inline = 0; } - t->size = new_size; + t->capacity = new_size; } static hV *get_key_ptr_for_tbl(hashtbl *t, usize *length_out) { - if (length_out) *length_out = t->size; + if (length_out) *length_out = t->capacity; if (t->is_inline) { return t->internal.keys; } else { @@ -186,7 +192,7 @@ static usize find_key_index_in_array(hV *keys, usize length, hV *key) { } static usize find_index_by_key(hashtbl *t, hV *key) { - return find_key_index_in_array(get_key_ptr_for_tbl(t, NULL), t->size, key); + return find_key_index_in_array(get_key_ptr_for_tbl(t, NULL), t->capacity, key); } static usize set_key_in_array(hV *keys, hV *values, usize length, hV *key, hV *value) { @@ -226,8 +232,9 @@ static usize set_key(hashtbl *t, hV *key, hV *value) { keys[index] = *key; values[index] = *value; t->used ++; - if (t->used >= t->size * 3 / 4) { - set_hashtbl_size(t, t->size * 2, TRUE); + t->n_elems ++; + if (t->used >= t->capacity * 3 / 4) { + set_hashtbl_size(t, t->capacity * 3 / 2, TRUE); } } else { /* replacing something that already exists. we can keep @@ -247,6 +254,7 @@ static hV delete_key(hashtbl *t, hV *key) { get_ptrs_for_tbl(t, &keys, &values); hV to_return = values[index]; keys[index].type = ITX_TOMBSTONE; + t->n_elems --; return to_return; } diff --git a/src/data/hashtable.h b/src/data/hashtable.h index b2b4f09..78b35eb 100644 --- a/src/data/hashtable.h +++ b/src/data/hashtable.h @@ -23,3 +23,5 @@ void sbHash_insert(hHash h, hV *key, hV *value); hV *sbHash_find_or_insert(hHash h, hV *key, hV *value); void sbHash_delete(hHash h, hV *key, hV *value); + +usize sbHash_get_size(hHash h); diff --git a/src/lib/lib.c b/src/lib/lib.c index c0dea9d..20fddd1 100644 --- a/src/lib/lib.c +++ b/src/lib/lib.c @@ -21,6 +21,7 @@ void sbLib_sys_init() { sbString_create_methods(); sbInteger_create_methods(); sbFloat_create_methods(); + sbHash_create_methods(); } void sbLib_sys_deinit() { @@ -28,6 +29,7 @@ void sbLib_sys_deinit() { sbLibTable_deinitialize(&g_string_methods); sbLibTable_deinitialize(&g_integer_methods); sbLibTable_deinitialize(&g_float_methods); + sbLibTable_deinitialize(&g_hash_methods); sbLibTable_deinitialize(&g_global_module); } @@ -85,7 +87,9 @@ void sbLib_resolve_method(hVm vm) { 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); + sbVm_print_stack(vm); + PANIC("Wrong number of arguments passed to method '%s'! (expected %d~%d, got %lld)", + sbSymbol_name(method_name_val->symbol), f->min_args, f->max_args, (long long)num_params); } } } else { @@ -109,7 +113,7 @@ void sbLib_resolve_property(hVm vm) { hV *method_name_val = sbVm_pop(vm); if (method_name_val->type != IT_SYMBOL) { - PANIC("method name must be symbol!"); + PANIC("method name must be symbol! (is %zd, target %zd)", method_name_val->type, target->type); } sbLibTable *table_to_use = find_method_table(target); @@ -167,6 +171,8 @@ static sbLibTable *find_method_table(hV *target) { return &g_integer_methods; case IT_FLOAT: return &g_float_methods; + case IT_HASH: + return &g_hash_methods; default: return NULL; } diff --git a/src/lib/method.h b/src/lib/method.h index 36ed223..3b1ebcd 100644 --- a/src/lib/method.h +++ b/src/lib/method.h @@ -2,8 +2,10 @@ extern sbLibTable g_list_methods; extern sbLibTable g_string_methods; extern sbLibTable g_integer_methods; extern sbLibTable g_float_methods; +extern sbLibTable g_hash_methods; void sbList_create_methods(void); void sbString_create_methods(void); void sbInteger_create_methods(void); void sbFloat_create_methods(void); +void sbHash_create_methods(void); diff --git a/src/lib/method/hash.c b/src/lib/method/hash.c new file mode 100644 index 0000000..2e20ddc --- /dev/null +++ b/src/lib/method/hash.c @@ -0,0 +1,36 @@ +#include "common.h" + +#include "lib/table.h" +#include "lib/sentinel.h" +#include "data/integer.h" +#include "data/hashtable.h" +#include "vm/exec.h" + +sbLibTable g_hash_methods; + +static void to_hash(hVm vm, hV *target, usize num_params) { + /* to_hash for a hash just returns itself */ + sbVm_push_immediate(vm, target); +} + +static void length(hVm vm, hV *target, usize num_params) { + usize length = sbHash_get_size(target->hash); + sbVm_push_immediate(vm, &HVINT(length)); +} + +static void get_index(hVm vm, hV *target, usize num_params) { + hV *key = sbVm_pop(vm); + hV *result = sbHash_find(target->hash, key); + if (result == IT_NOTHING) { + sbVm_push_immediate(vm, &HVNIL); + } else { + sbVm_push(vm, result); + } +} + +void sbHash_create_methods(void) { + sbLibTable_initialize(&g_hash_methods, 16, TRUE); + REGISTER_METHOD(&g_hash_methods, "length", &PROPERTY(length)); + REGISTER_METHOD_SYM(&g_hash_methods, S_OP_INDEX, &METHOD(get_index, 1, 1)); + REGISTER_METHOD_SYM(&g_hash_methods, S_OP_TO_HASH, &PROPERTY(to_hash)); +} diff --git a/src/lib/module.h b/src/lib/module.h index b0797dc..e477e22 100644 --- a/src/lib/module.h +++ b/src/lib/module.h @@ -1,10 +1,12 @@ extern sbLibTable g_global_module; +extern sbLibTable g_op_module; extern sbLibTable g_math_module; extern sbLibTable g_list_module; extern sbLibTable g_string_module; extern sbLibTable g_integer_module; void sbLib_loadmodule_global(); +void sbLib_loadmodule_op(); void sbLib_loadmodule_math(); void sbLib_loadmodule_list(); void sbLib_loadmodule_string(); diff --git a/src/lib/module/global.c b/src/lib/module/global.c index f47a87b..2524594 100644 --- a/src/lib/module/global.c +++ b/src/lib/module/global.c @@ -37,6 +37,9 @@ void sbLib_loadmodule_global() { sbLib_loadmodule_list(); REGISTER_VALUE(&g_global_module, "list", &HVMODULE(&g_list_module)); + sbLib_loadmodule_op(); + REGISTER_VALUE(&g_global_module, "op", &HVMODULE(&g_op_module)); + sbLib_loadmodule_math(); REGISTER_VALUE(&g_global_module, "math", &HVMODULE(&g_math_module)); } diff --git a/src/lib/module/op.c b/src/lib/module/op.c new file mode 100644 index 0000000..7401492 --- /dev/null +++ b/src/lib/module/op.c @@ -0,0 +1,28 @@ +#include "common.h" + +#include "lib/table.h" +#include "lib/sentinel.h" +#include "data/list.h" +#include "data/symbol.h" +#include "vm/exec.h" + +sbLibTable g_op_module; + +void sbLib_loadmodule_op() { + sbLibTable_initialize(&g_op_module, 16, FALSE); + REGISTER_VALUE(&g_op_module, "add", &HVSYM(S_OP_ADD)); + REGISTER_VALUE(&g_op_module, "sub", &HVSYM(S_OP_SUB)); + REGISTER_VALUE(&g_op_module, "mul", &HVSYM(S_OP_MUL)); + REGISTER_VALUE(&g_op_module, "div", &HVSYM(S_OP_DIV)); + REGISTER_VALUE(&g_op_module, "intdiv", &HVSYM(S_OP_INTDIV)); + REGISTER_VALUE(&g_op_module, "mod", &HVSYM(S_OP_MOD)); + REGISTER_VALUE(&g_op_module, "eq", &HVSYM(S_OP_EQ)); + REGISTER_VALUE(&g_op_module, "lt", &HVSYM(S_OP_LT)); + REGISTER_VALUE(&g_op_module, "le", &HVSYM(S_OP_LE)); + REGISTER_VALUE(&g_op_module, "call", &HVSYM(S_OP_CALL)); + REGISTER_VALUE(&g_op_module, "set", &HVSYM(S_OP_SET)); + REGISTER_VALUE(&g_op_module, "index", &HVSYM(S_OP_INDEX)); + REGISTER_VALUE(&g_op_module, "setindex", &HVSYM(S_OP_SETINDEX)); + REGISTER_VALUE(&g_op_module, "rangeindex", &HVSYM(S_OP_RANGEINDEX)); + REGISTER_VALUE(&g_op_module, "setrangeindex", &HVSYM(S_OP_SETRANGEINDEX)); +} diff --git a/src/lib/sentinel.c b/src/lib/sentinel.c index dab8ba5..1a1261e 100644 --- a/src/lib/sentinel.c +++ b/src/lib/sentinel.c @@ -2,9 +2,9 @@ #include "data/symbol.h" -hSymbol S_OP_ADD, S_OP_SUB, S_OP_MUL, S_OP_DIV, S_OP_FLDIV, S_OP_MOD; +hSymbol S_OP_ADD, S_OP_SUB, S_OP_MUL, S_OP_DIV, S_OP_INTDIV, S_OP_MOD; hSymbol S_OP_EQ, S_OP_LT, S_OP_LE; -hSymbol S_OP_CALL, S_OP_SET, S_OP_INDEX, S_OP_INDEX_SET; +hSymbol S_OP_CALL, S_OP_SET, S_OP_INDEX, S_OP_SETINDEX, S_OP_RANGEINDEX, S_OP_SETRANGEINDEX; hSymbol S_OP_TO_STRING, S_OP_TO_INT, S_OP_TO_FLOAT, S_OP_TO_LIST, S_OP_TO_HASH; void sbLib_create_sentinels() { @@ -12,7 +12,7 @@ void sbLib_create_sentinels() { S_OP_SUB = SENTINEL(""); S_OP_MUL = SENTINEL(""); S_OP_DIV = SENTINEL(""); - S_OP_FLDIV = SENTINEL(""); + S_OP_INTDIV = SENTINEL(""); S_OP_MOD = SENTINEL(""); S_OP_EQ = SENTINEL(""); S_OP_LT = SENTINEL(""); @@ -20,7 +20,9 @@ void sbLib_create_sentinels() { S_OP_CALL = SENTINEL(""); S_OP_SET = SENTINEL(""); S_OP_INDEX = SENTINEL(""); - S_OP_INDEX_SET = SENTINEL(""); + S_OP_SETINDEX = SENTINEL(""); + S_OP_RANGEINDEX = SENTINEL(""); + S_OP_SETRANGEINDEX = SENTINEL(""); S_OP_TO_STRING = SENTINEL(""); S_OP_TO_INT = SENTINEL(""); S_OP_TO_FLOAT = SENTINEL(""); diff --git a/src/lib/sentinel.h b/src/lib/sentinel.h index fca6174..4a394d8 100644 --- a/src/lib/sentinel.h +++ b/src/lib/sentinel.h @@ -1,8 +1,8 @@ #include "common.h" -extern hSymbol S_OP_ADD, S_OP_SUB, S_OP_MUL, S_OP_DIV, S_OP_FLDIV, S_OP_MOD; +extern hSymbol S_OP_ADD, S_OP_SUB, S_OP_MUL, S_OP_DIV, S_OP_INTDIV, S_OP_MOD; extern hSymbol S_OP_EQ, S_OP_LT, S_OP_LE; -extern hSymbol S_OP_CALL, S_OP_SET, S_OP_INDEX, S_OP_INDEX_SET; +extern hSymbol S_OP_CALL, S_OP_SET, S_OP_INDEX, S_OP_SETINDEX, S_OP_RANGEINDEX, S_OP_SETRANGEINDEX; extern hSymbol S_OP_TO_STRING, S_OP_TO_INT, S_OP_TO_FLOAT, S_OP_TO_LIST, S_OP_TO_HASH; void sbLib_create_sentinels(); diff --git a/src/parse/lexer.c b/src/parse/lexer.c index 7ec339a..be57d9e 100644 --- a/src/parse/lexer.c +++ b/src/parse/lexer.c @@ -390,8 +390,20 @@ static void compute_next_token(hLexer lx) { unstack_all_invisible_parentheses(lx); } + flag at_line_start; + switch (lx->last_token_seen.type) { + case ';': + case '{': + case '}': + case T_rELSE: + at_line_start = TRUE; + break; + default: + at_line_start = FALSE; + } + if (begins_brace_terminated_state(token.type) - || (begins_brace_terminated_state_at_line_start(token.type) && lx->last_token_seen.type == ';')) { + || (begins_brace_terminated_state_at_line_start(token.type) && at_line_start)) { /* don't start brace-terminated state if we are directly after a '}' (otherwise it gets * confused by things like repeat..until */ if (lx->last_token_seen.type != '}') { diff --git a/src/vm/exec.c b/src/vm/exec.c index 24be18b..2d1f0a2 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -5,6 +5,7 @@ #include "data/reference.h" #include "data/closure.h" #include "lib/lib.h" +#include "lib/sentinel.h" void call_block(hVm vm, usize block_id, hClosure closure); void call_builtin(hVm vm, hV *to_call); @@ -72,6 +73,10 @@ hV *sbVm_pop(hVm vm) { return pop_stack(vm); } +hV *sbVm_npop(hVm vm, usize how_many) { + return npop_stack(vm, how_many); +} + hV *sbVm_peek(hVm vm, usize where) { return peek_stack(vm, where); } @@ -97,7 +102,7 @@ void sbVm_call_func(hVm vm, hV *func) { void sbVm_transfer_to_func(hVm vm, hV *func) { /* tail call */ return_from_block(vm); - call_block(vm, func->type, func->closure); + sbVm_call_func(vm, func); } void sbVm_call_c_func(hVm vm, sbRuntimeCFunc func) { @@ -189,6 +194,8 @@ void call_bound_method(hVm vm, hV *to_call) { } peek_stack(vm, 0)->integer ++; push_stack(vm, sbRef_deref(ref)); + + /* resolve method normally */ sbLib_resolve_method(vm); } @@ -645,11 +652,7 @@ void execute_instruction(hVm vm) { sbV_decr(v); break; case BC_OP_INDEX: - v = peek_stack(vm, 1); - w = peek_stack(vm, 0); - x = sbV_index(v, w); - npop_stack(vm, 2); - push_stack(vm, x); + sbV_index(vm); break; case BC_OP_RANGEINDEX: v = peek_stack(vm, 2); diff --git a/src/vm/exec.h b/src/vm/exec.h index 6041238..2e4067d 100644 --- a/src/vm/exec.h +++ b/src/vm/exec.h @@ -97,6 +97,8 @@ void sbVm_push_immediate(hVm vm, hV *value); hV *sbVm_pop(hVm vm); +hV *sbVm_npop(hVm vm, usize how_many); + hV *sbVm_peek(hVm vm, usize where); void sbVm_swap(hVm vm); diff --git a/src/vm/operations.c b/src/vm/operations.c index bd406ae..0904670 100644 --- a/src/vm/operations.c +++ b/src/vm/operations.c @@ -6,6 +6,7 @@ #include "data/hashtable.h" #include "data/string.h" #include "lib/table.h" +#include "lib/sentinel.h" hV sbV_add(const hV *a, const hV *b) { if (a->type == IT_INTEGER && b->type == IT_INTEGER) { @@ -43,7 +44,7 @@ hV sbV_mod(const hV *a, const hV *b) { if (a->type == IT_INTEGER && b->type == IT_INTEGER) { return sbV_int(a->integer % b->integer); } else { - PANIC("todo"); + PANIC("todo %zd", a->type); } } @@ -126,19 +127,24 @@ hV sbV_append(hV *a, hV *b) { } } -hV *sbV_index(hV *a, hV *b) { +void sbV_index(hVm vm) { + hV *a = sbVm_peek(vm, 1); + hV *b = sbVm_peek(vm, 0); if (a->type == IT_LIST && b->type == IT_INTEGER) { - return sbList_index(a->list, b->integer); - } else if (a->type == IT_HASH) { - return sbHash_find(a->hash, b); - } else if (a->type == IT_MODULE) { - if (b->type == IT_SYMBOL) { - return sbLibTable_find_value(a->module, b->symbol); - } else { - PANIC("module cannot be indexed by non-symbol"); - } + sbVm_npop(vm, 2); + hV *result = sbList_index(a->list, b->integer); + sbVm_push(vm, result); + } else if (a->type == IT_MODULE && b->type == IT_SYMBOL) { + sbVm_npop(vm, 2); + hV *result = sbLibTable_find_value(a->module, b->symbol); + sbVm_push(vm, result); } else { - PANIC("todo %lld %lld", (long long)a->type, (long long)b->type); + sbVm_swap(vm); /* b a */ + sbVm_push_immediate(vm, &HVSYM(S_OP_INDEX)); /* b a op::index */ + sbVm_swap(vm); /* b op::index a */ + sbVm_push_immediate(vm, &HVINT(2)); /* b op::index a 2 */ + sbVm_swap(vm); /* b op::index 2 a */ + sbLib_resolve_method(vm); } } diff --git a/src/vm/operations.h b/src/vm/operations.h index 5683aa4..56c69b2 100644 --- a/src/vm/operations.h +++ b/src/vm/operations.h @@ -24,7 +24,7 @@ hV sbV_le(const hV *a, const hV *b); hV sbV_append(hV *a, hV *b); -hV *sbV_index(hV *a, hV *b); +void sbV_index(hVm vm); hV sbV_rangeindex(hV *a, hV *b, hV *c);