much more value things, symbol table, etc.
authorcassowarii <cassowary@cassowary.me>
Fri, 26 Jun 2026 05:07:18 +0000 (22:07 -0700)
committercassowarii <cassowary@cassowary.me>
Fri, 26 Jun 2026 05:07:18 +0000 (22:07 -0700)
12 files changed:
src/data/handle.c [deleted file]
src/data/hashtable.c
src/data/hashtable.h
src/data/integer.c [new file with mode: 0644]
src/data/integer.h [new file with mode: 0644]
src/data/string.h
src/data/symbol.h
src/data/value.c [new file with mode: 0644]
src/data/value.h [moved from src/data/handle.h with 80% similarity]
src/mem/arena.c
src/mem/arena.h
src/parse/token.h

diff --git a/src/data/handle.c b/src/data/handle.c
deleted file mode 100644 (file)
index 93dd728..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#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;
-}
index beb1cc3..23a8505 100644 (file)
@@ -1,7 +1,7 @@
 #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
index 56748cd..e523f95 100644 (file)
@@ -1,6 +1,6 @@
 #include "common.h"
 
-#include "data/handle.h"
+#include "data/value.h"
 
 /* Open-hashing hash table of object handle keys -> object handle values. */
 
diff --git a/src/data/integer.c b/src/data/integer.c
new file mode 100644 (file)
index 0000000..37d91b7
--- /dev/null
@@ -0,0 +1,167 @@
+#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;
+}
+
+
diff --git a/src/data/integer.h b/src/data/integer.h
new file mode 100644 (file)
index 0000000..a0a6d15
--- /dev/null
@@ -0,0 +1,18 @@
+#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);
index 5f916c9..2406c2c 100644 (file)
@@ -2,7 +2,7 @@
 
 #define OBJSL(value) (sbString_new(value, sizeof(value) - 1))
 
-#include "data/handle.h"
+#include "data/value.h"
 
 /* create hString:
  *
index d400e07..091b66e 100644 (file)
@@ -1,6 +1,6 @@
 #include "common.h"
 
-#include "handle.h"
+#include "data/value.h"
 
 void sbSymbol_sys_init();
 
diff --git a/src/data/value.c b/src/data/value.c
new file mode 100644 (file)
index 0000000..1475de6
--- /dev/null
@@ -0,0 +1,104 @@
+#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;
+}
similarity index 80%
rename from src/data/handle.h
rename to src/data/value.h
index 8886c41..746637f 100644 (file)
@@ -6,8 +6,9 @@
 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
@@ -20,9 +21,8 @@ typedef enum intrinsic_type {
   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;
@@ -30,11 +30,18 @@ typedef struct hV {
     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);
 
index d62e236..fec89a3 100644 (file)
@@ -69,7 +69,7 @@ void sbArena_reset(hArena arena) {
     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 {
index 270b15a..e597de7 100644 (file)
@@ -21,4 +21,4 @@ void *sbArena_alloc(hArena arena, usize size);
 
 void sbArena_reset(hArena arena);
 
-void sbArena_destroy(hArena arena);
+void sbArena_deinitialize(hArena arena);
index 4f3b94d..24e2c42 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "common.h"
 
-#include "data/handle.h"
+#include "data/value.h"
 
 typedef enum sbTokenType {
     T_NULL                  = 0,