--- /dev/null
+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
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)
sbPool g_hashtable_pool = {0};
typedef struct hashtbl {
- usize size;
+ usize capacity;
usize used;
+ usize n_elems;
hHash handle;
flag allocated;
flag is_inline;
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;
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
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 {
}
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) {
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
get_ptrs_for_tbl(t, &keys, &values);
hV to_return = values[index];
keys[index].type = ITX_TOMBSTONE;
+ t->n_elems --;
return to_return;
}
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);
sbString_create_methods();
sbInteger_create_methods();
sbFloat_create_methods();
+ sbHash_create_methods();
}
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);
}
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 {
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);
return &g_integer_methods;
case IT_FLOAT:
return &g_float_methods;
+ case IT_HASH:
+ return &g_hash_methods;
default:
return NULL;
}
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);
--- /dev/null
+#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));
+}
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();
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));
}
--- /dev/null
+#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));
+}
#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() {
S_OP_SUB = SENTINEL("<op::sub>");
S_OP_MUL = SENTINEL("<op::mul>");
S_OP_DIV = SENTINEL("<op::div>");
- S_OP_FLDIV = SENTINEL("<op::intdiv>");
+ S_OP_INTDIV = SENTINEL("<op::intdiv>");
S_OP_MOD = SENTINEL("<op::mod>");
S_OP_EQ = SENTINEL("<op::eq>");
S_OP_LT = SENTINEL("<op::lt>");
S_OP_CALL = SENTINEL("<op::call>");
S_OP_SET = SENTINEL("<op::set>");
S_OP_INDEX = SENTINEL("<op::index>");
- S_OP_INDEX_SET = SENTINEL("<op::set_index>");
+ S_OP_SETINDEX = SENTINEL("<op::setindex>");
+ S_OP_RANGEINDEX = SENTINEL("<op::rangeindex>");
+ S_OP_SETRANGEINDEX = SENTINEL("<op::setrangeindex>");
S_OP_TO_STRING = SENTINEL("<string::convert>");
S_OP_TO_INT = SENTINEL("<integer::convert>");
S_OP_TO_FLOAT = SENTINEL("<float::convert>");
#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();
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 != '}') {
#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);
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);
}
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) {
}
peek_stack(vm, 0)->integer ++;
push_stack(vm, sbRef_deref(ref));
+
+ /* resolve method normally */
sbLib_resolve_method(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);
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);
#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) {
if (a->type == IT_INTEGER && b->type == IT_INTEGER) {
return sbV_int(a->integer % b->integer);
} else {
- PANIC("todo");
+ PANIC("todo %zd", a->type);
}
}
}
}
-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);
}
}
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);