+++ /dev/null
-#include "data/handle.h"
-
-#include "data/string.h"
-#include "data/hashtable.h"
-
-#define FLAG_NONINTRINSIC (1ULL << 63)
-
-hV sbV_nil() {
- return (hV) {0};
-}
-
-hV sbV_string(hString str) {
- return (hV) {
- .type = IT_STRING,
- .string = str,
- };
-}
-
-flag sbV_eq(hV *a, hV *b) {
- if (a->type != b->type) return FALSE;
- if (a->type == ITX_TOMBSTONE || b->type == ITX_TOMBSTONE) return FALSE;
- if (a->type == IT_NOTHING || b->type == IT_NOTHING) return FALSE;
-
- if (a->type == IT_STRING) {
- return sbString_eq(a->string, b->string);
- } else {
- return a == b;
- }
-}
-
-void sbV_retain(hV *a) {
- /* for some classes (e.g. nil, float), we don't need to retain them at all
- * because they are trivially copiable. some classes (integer, string) need
- * to sometimes be retained but sometimes not, so we delegate to them to
- * figure out if they need to or not. */
- if (a->type == IT_STRING) {
- sbString_clone(a->string);
- }
-}
-
-void sbV_release(hV *a) {
- if (a->type == IT_STRING) {
- sbString_release(a->string);
- }
-}
-
-/* --- */
-
-flag val_is_intrinsic(hV val) {
- return (val.type & FLAG_NONINTRINSIC) != 0;
-}
#include "data/hashtable.h"
#include "data/string.h"
-#include "data/handle.h"
+#include "data/value.h"
#define HASHES_PER_BLOCK 256
#define INLINE_TABLE_LENGTH 64
#include "common.h"
-#include "data/handle.h"
+#include "data/value.h"
/* Open-hashing hash table of object handle keys -> object handle values. */
--- /dev/null
+#include "integer.h"
+
+/* reserve high bit for sign bit.
+ * second-highest bit marks this as a handle to a bigint, which
+ * means normally integers have to be less than +/- 2^62. */
+#define FLAG_BIGINT (1LL << 62)
+
+#define NUM_PER_BLOCK 256
+
+sbBuffer g_bigint_blocks = {0};
+
+typedef struct bigint {
+ flag allocated;
+ hInteger handle;
+ flag sign_bit;
+ sbBuffer buf;
+} bigint;
+
+typedef struct intblk {
+ usize id;
+ usize used_count;
+ usize last_index;
+ bigint entries[NUM_PER_BLOCK];
+} intblk;
+
+flag is_bigint(hInteger n);
+static intblk *alloc_new_block();
+static intblk *find_free_block();
+static intblk *get_block(usize index);
+static bigint *find_free_entry(intblk *blk);
+static void set_bigint_size(bigint *i, usize new_size);
+static bigint *find_int_for_handle(hInteger handle);
+static bigint *new_bigint(usize initial_size);
+
+void sbInteger_sys_init() {
+ sbBuffer_initialize(&g_bigint_blocks, sizeof(intblk*));
+}
+
+hInteger sbInteger_new(u64 value) {
+ if (value >= SARABANDE_INT_MIN && value <= SARABANDE_INT_MAX) {
+ return value;
+ } else {
+ bigint *i = new_bigint(2);
+
+ if (value < 0) {
+ i->sign_bit = 1;
+ value *= -1;
+ }
+ i->buf.data[0] = (value & 0xFFFFFFFF);
+ i->buf.data[1] = ((value >> 32) & 0xFFFFFFFF);
+
+ return i->handle;
+ }
+}
+
+hInteger sbInteger_sum(hInteger a, hInteger b) {
+ if (!is_bigint(a) && !is_bigint(b)) {
+ /* if the sum is too big, this will create a bigint. we don't need to
+ * worry about signed overflow, because non-bigints are limited to
+ * 62 bits of magnitude, so the sum of two can't be bigger than 2^63 */
+ return sbInteger_new(a + b);
+ }
+ PANIC("I haven't implemented this yet!");
+}
+
+hInteger sbInteger_diff(hInteger a, hInteger b) {
+ if (!is_bigint(a) && !is_bigint(b)) {
+ /* if the sum is too big, this will create a bigint. we don't need to
+ * worry about signed overflow, because non-bigints are limited to
+ * 62 bits of magnitude, so the sum of two can't be bigger than 2^63 */
+ return sbInteger_new(a - b);
+ }
+ if (is_bigint(a)) {
+ /* i will use this later */
+ bigint *i = find_int_for_handle(a);
+ (void)i;
+ }
+ PANIC("I haven't implemented this yet!");
+}
+
+hInteger sbInteger_mul(hInteger a, hInteger b) {
+ if (!is_bigint(a) && !is_bigint(b)) {
+ if (b < SARABANDE_INT_MAX / a && b > SARABANDE_INT_MIN / a) {
+ return sbInteger_new(a * b);
+ }
+ }
+ PANIC("I haven't implemented this yet!");
+}
+
+hInteger sbInteger_floordiv(hInteger a, hInteger b) {
+ if (!is_bigint(a) && !is_bigint(b)) {
+ return sbInteger_new(a / b);
+ }
+ PANIC("I haven't implemented this yet!");
+}
+
+/* --- */
+
+flag is_bigint(hInteger n) {
+ return (n & FLAG_BIGINT);
+}
+
+static intblk *alloc_new_block() {
+ usize nblocks = g_bigint_blocks.size / sizeof(intblk*);
+ intblk *new_block = calloc(1, sizeof(intblk));
+ new_block->id = nblocks;
+ sbBuffer_append(&g_bigint_blocks, &new_block, sizeof(intblk*));
+ return new_block;
+}
+
+static intblk *find_free_block() {
+ usize nblocks = g_bigint_blocks.size / sizeof(intblk*);
+ intblk *free_block = NULL;
+ for (usize i = 0; i < nblocks; i++) {
+ if (((intblk**)g_bigint_blocks.data)[i]->used_count < NUM_PER_BLOCK) {
+ free_block = ((intblk**)g_bigint_blocks.data)[i];
+ break;
+ }
+ }
+
+ if (free_block == NULL) {
+ free_block = alloc_new_block();
+ }
+
+ return free_block;
+}
+
+static intblk *get_block(usize index) {
+ usize nblocks = g_bigint_blocks.size / sizeof(intblk*);
+ if (index > nblocks) PANIC("request for a bigint block (#%zu) that does not exist!", index);
+ return ((intblk**)g_bigint_blocks.data)[index];
+}
+
+static bigint *find_free_entry(intblk *blk) {
+ if (blk->used_count >= NUM_PER_BLOCK) PANIC("request for new entry in full intblk!");
+
+ while (blk->entries[blk->last_index].allocated) {
+ blk->last_index++;
+ blk->last_index %= NUM_PER_BLOCK;
+ }
+ blk->entries[blk->last_index].allocated = 1;
+ blk->used_count ++;
+
+ bigint *result = &blk->entries[blk->last_index];
+
+ result->handle = ((blk->id * NUM_PER_BLOCK + blk->last_index) | FLAG_BIGINT);
+
+ return result;
+}
+
+static void set_bigint_size(bigint *i, usize new_size) {
+ sbBuffer_set_size(&i->buf, new_size * sizeof(u32));
+}
+
+static bigint *find_int_for_handle(hInteger handle) {
+ hInteger h = handle & ~FLAG_BIGINT;
+ intblk *block = get_block(h / NUM_PER_BLOCK);
+ return &block->entries[h % NUM_PER_BLOCK];
+}
+
+static bigint *new_bigint(usize initial_size) {
+ bigint *i = find_free_entry(find_free_block());
+ set_bigint_size(i, initial_size);
+ return i;
+}
+
+
--- /dev/null
+#include "common.h"
+
+#include "data/value.h"
+
+#define SARABANDE_INT_MAX ((1LL << 62) - 1)
+#define SARABANDE_INT_MIN (-(1LL << 62) + 1)
+
+void sbInteger_sys_init();
+
+hInteger sbInteger_new(u64 value);
+
+hInteger sbInteger_sum(hInteger a, hInteger b);
+
+hInteger sbInteger_diff(hInteger a, hInteger b);
+
+hInteger sbInteger_mul(hInteger a, hInteger b);
+
+hInteger sbInteger_floordiv(hInteger a, hInteger b);
#define OBJSL(value) (sbString_new(value, sizeof(value) - 1))
-#include "data/handle.h"
+#include "data/value.h"
/* create hString:
*
#include "common.h"
-#include "handle.h"
+#include "data/value.h"
void sbSymbol_sys_init();
--- /dev/null
+#include "data/value.h"
+
+#include "data/string.h"
+#include "data/hashtable.h"
+
+#define FLAG_NONINTRINSIC (1ULL << 63)
+
+hV sbV_nil() {
+ return (hV) {0};
+}
+
+hV sbV_boolean(flag value) {
+ return (hV) {
+ .type = IT_BOOLEAN,
+ .boolean = (value == 0) ? 0 : 1,
+ };
+}
+
+hV sbV_string(hString str) {
+ return (hV) {
+ .type = IT_STRING,
+ .string = str,
+ };
+}
+
+hV sbV_symbol(hSymbol sym) {
+ return (hV) {
+ .type = IT_SYMBOL,
+ .symbol = sym,
+ };
+}
+
+hV sbV_float(double fl) {
+ return (hV) {
+ .type = IT_FLOAT,
+ .float_val = fl,
+ };
+}
+
+hV sbV_hash(hHash hash) {
+ return (hV) {
+ .type = IT_HASH,
+ .hash = hash,
+ };
+}
+
+hV sbV_int(hInteger i) {
+ return (hV) {
+ .type = IT_INTEGER,
+ .integer = i,
+ };
+}
+
+flag sbV_eq(hV *a, hV *b) {
+ if (a->type != b->type) return FALSE;
+ if (a->type == ITX_TOMBSTONE || b->type == ITX_TOMBSTONE) return FALSE;
+ if (a->type == IT_NOTHING || b->type == IT_NOTHING) return FALSE;
+
+ switch (a->type) {
+ case IT_NIL:
+ /* if a and b share a type, and it's nil, they must be equal */
+ return TRUE;
+ case IT_BOOLEAN:
+ /* for booleans, they are equal if both zero or neither zero */
+ return (a->boolean == 0 && b->boolean == 0) || (a->boolean != 0 && b->boolean != 0);
+ case IT_SYMBOL:
+ /* symbols are deduplicated so just compare pointers */
+ return a->symbol == b->symbol;
+ case IT_FLOAT:
+ /* floats are just comparable as 64 bit values by cursed float-logic */
+ return a->float_val == b->float_val;
+ case IT_STRING:
+ /* strings can be compared for equality character by character */
+ return sbString_eq(a->string, b->string);
+ case IT_HASH:
+ /* hashes should (but currently don't) check that they are the same by value.
+ * right now, we'll just see if they point to the same hash ("is"). */
+ return a->hash == b->hash;
+ default:
+ return a == b;
+ }
+}
+
+void sbV_retain(hV *a) {
+ /* for some classes (e.g. nil, float), we don't need to retain them at all
+ * because they are trivially copiable. some classes (integer, string) need
+ * to sometimes be retained but sometimes not, so we delegate to them to
+ * figure out if they need to or not. */
+ if (a->type == IT_STRING) {
+ sbString_clone(a->string);
+ }
+}
+
+void sbV_release(hV *a) {
+ if (a->type == IT_STRING) {
+ sbString_release(a->string);
+ }
+}
+
+/* --- */
+
+flag val_is_intrinsic(hV val) {
+ return (val.type & FLAG_NONINTRINSIC) != 0;
+}
typedef u64 hHash;
typedef u64 hString;
typedef u64 hSymbol;
+typedef i64 hInteger;
-typedef enum intrinsic_type {
+enum intrinsic_type {
IT_NOTHING, // sentinel for "no value here"
IT_NIL, // nil ("there is a value here, but it's nil")
IT_BOOLEAN, // true / false
IT_LIST, // list [1, 3, 5, 7]
IT_HASH, // hash {a: 1, b: 2}
IT_FUNCTION, // function => a, b { a + b }
- N_INTRINSIC_TYPES,
ITX_TOMBSTONE, // <hashtable_tombstone>
-} intrinsic_type;
+};
typedef struct hV {
u64 type;
hString string;
hSymbol symbol;
hHash hash;
+ hInteger integer;
+ u64 boolean;
+ double float_val;
};
} hV;
hV sbV_nil();
hV sbV_string(hString str);
+hV sbV_symbol(hSymbol sym);
+hV sbV_float(double fl);
+hV sbV_hash(hHash hash);
+hV sbV_int(hInteger i);
flag sbV_eq(hV *a, hV *b);
arena->current = arena->first;
}
-void sbArena_destroy(hArena arena) {
+void sbArena_deinitialize(hArena arena) {
struct block *blk = arena->first;
struct block *blk_next = arena->first->next;
do {
void sbArena_reset(hArena arena);
-void sbArena_destroy(hArena arena);
+void sbArena_deinitialize(hArena arena);
#include "common.h"
-#include "data/handle.h"
+#include "data/value.h"
typedef enum sbTokenType {
T_NULL = 0,