summaryrefslogtreecommitdiff
path: root/src/runtime/value
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/value')
-rw-r--r--src/runtime/value/array.c57
-rw-r--r--src/runtime/value/bytes.c47
-rw-r--r--src/runtime/value/cell.c47
-rw-r--r--src/runtime/value/cons.c110
-rw-r--r--src/runtime/value/func.c177
-rw-r--r--src/runtime/value/nativedata.c36
-rw-r--r--src/runtime/value/small.c96
7 files changed, 570 insertions, 0 deletions
diff --git a/src/runtime/value/array.c b/src/runtime/value/array.c
new file mode 100644
index 0000000..1af84fd
--- /dev/null
+++ b/src/runtime/value/array.c
@@ -0,0 +1,57 @@
+#include <lcq/pit/runtime/value/array.h>
+
+bool pit_value_is_array(pit_runtime *rt, pit_value a) {
+ return pit_value_is_ref_heavy_sort(rt, a, PIT_VALUE_HEAVY_SORT_ARRAY);
+}
+
+pit_value pit_value_array_new(pit_runtime *rt, i64 len) {
+ if (len < 0) { pit_error(rt, "failed to create array of negative size"); return PIT_NIL; }
+ i64 byte_len = 0; pit_mul(&byte_len, sizeof(pit_value), len);
+ pit_value *dest = pit_arena_alloc_array(rt->heap, byte_len);
+ if (!dest) { pit_error(rt, "failed to allocate array"); return PIT_NIL; }
+ for (i64 i = 0; i < len; ++i) dest[i] = PIT_NIL;
+ pit_value ret = pit_value_ref_heavy_new(rt);
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ret));
+ if (!h) { pit_error(rt, "failed to create new heavy value for array"); return PIT_NIL; }
+ h->hsort = PIT_VALUE_HEAVY_SORT_ARRAY;
+ h->in.array.data = dest;
+ h->in.array.len = len;
+ return ret;
+}
+pit_value pit_value_array_from_buf(pit_runtime *rt, pit_value *xs, i64 len) {
+ pit_value ret = pit_value_array_new(rt, len);
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ret));
+ if (!h) { pit_error(rt, "failed to deref heavy value for array"); return PIT_NIL; }
+ pit_libc_string_memcpy((u8 *) h->in.array.data, (u8 *) xs, (size_t) len * (size_t) sizeof(pit_value));
+ return ret;
+}
+i64 pit_value_array_len(pit_runtime *rt, pit_value arr) {
+ if (pit_value_sort(arr) != PIT_VALUE_SORT_REF) { pit_error(rt, "not a ref"); return -1; }
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, arr));
+ if (!h) { pit_error(rt, "bad ref"); return -1; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_ARRAY) { pit_error(rt, "not an array"); return -1; }
+ return h->in.array.len;
+}
+pit_value pit_value_array_get(pit_runtime *rt, pit_value arr, i64 idx) {
+ if (pit_value_sort(arr) != PIT_VALUE_SORT_REF) { pit_error(rt, "not a ref"); return PIT_NIL; }
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, arr));
+ if (!h) { pit_error(rt, "bad ref"); return PIT_NIL; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_ARRAY) { pit_error(rt, "not an array"); return PIT_NIL; }
+ if (idx < 0 || idx >= h->in.array.len) {
+ pit_error(rt, "array index out of bounds: %d", idx);
+ return PIT_NIL;
+ }
+ return h->in.array.data[idx];
+}
+pit_value pit_value_array_set(pit_runtime *rt, pit_value arr, i64 idx, pit_value v) {
+ if (pit_value_sort(arr) != PIT_VALUE_SORT_REF) { pit_error(rt, "not a ref"); return PIT_NIL; }
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, arr));
+ if (!h) { pit_error(rt, "bad ref"); return PIT_NIL; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_ARRAY) { pit_error(rt, "not an array"); return PIT_NIL; }
+ if (idx < 0 || idx >= h->in.array.len) {
+ pit_error(rt, "array index out of bounds: %d", idx);
+ return PIT_NIL;
+ }
+ h->in.array.data[idx] = v;
+ return v;
+}
diff --git a/src/runtime/value/bytes.c b/src/runtime/value/bytes.c
new file mode 100644
index 0000000..6a6edb6
--- /dev/null
+++ b/src/runtime/value/bytes.c
@@ -0,0 +1,47 @@
+#include <lcq/pit/runtime/value/bytes.h>
+
+bool pit_value_is_bytes(pit_runtime *rt, pit_value a) {
+ return pit_value_is_ref_heavy_sort(rt, a, PIT_VALUE_HEAVY_SORT_BYTES);
+}
+pit_value pit_value_bytes_new(pit_runtime *rt, u8 *buf, i64 len) {
+ u8 *dest = pit_arena_alloc_back(rt->heap, len);
+ if (!dest) { pit_error(rt, "failed to allocate bytes"); return PIT_NIL; }
+ pit_libc_string_memcpy(dest, buf, (size_t) len);
+ pit_value ret = pit_value_ref_heavy_new(rt);
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ret));
+ if (!h) { pit_error(rt, "failed to create new heavy value for bytes"); return PIT_NIL; }
+ h->hsort = PIT_VALUE_HEAVY_SORT_BYTES;
+ h->in.bytes.data = dest;
+ h->in.bytes.len = len;
+ return ret;
+}
+pit_value pit_value_bytes_new_cstr(pit_runtime *rt, char *s) {
+ return pit_value_bytes_new(rt, (u8 *) s, (i64) pit_libc_string_strlen(s));
+}
+/* return true if v is a reference to bytes that are the same as those in buf */
+bool pit_value_bytes_match(pit_runtime *rt, pit_value v, u8 *buf, i64 len) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_REF) return false;
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, v));
+ if (!h) { pit_error(rt, "bad ref"); return false; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_BYTES) return false;
+ if (h->in.bytes.len != len) return false;
+ for (i64 i = 0; i < len; ++i)
+ if (h->in.bytes.data[i] != buf[i]) {
+ return false;
+ }
+ return true;
+}
+i64 pit_value_bytes_copy(pit_runtime *rt, pit_value v, u8 *buf, i64 maxlen) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_REF) { pit_error(rt, "not a ref"); return -1; }
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, v));
+ if (!h) { pit_error(rt, "bad ref"); return -1; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_BYTES) {
+ pit_error(rt, "invalid use of value as bytes");
+ return -1;
+ }
+ i64 len = maxlen < h->in.bytes.len ? maxlen : h->in.bytes.len;
+ for (i64 i = 0; i < len; ++i) {
+ buf[i] = h->in.bytes.data[i];
+ }
+ return len;
+}
diff --git a/src/runtime/value/cell.c b/src/runtime/value/cell.c
new file mode 100644
index 0000000..34b9226
--- /dev/null
+++ b/src/runtime/value/cell.c
@@ -0,0 +1,47 @@
+#include <lcq/pit/runtime/value/cell.h>
+
+bool pit_value_is_cell(pit_runtime *rt, pit_value a) {
+ return pit_value_is_ref_heavy_sort(rt, a, PIT_VALUE_HEAVY_SORT_CELL);
+}
+pit_value pit_value_cell_new(pit_runtime *rt, pit_value v) {
+ pit_value ret = pit_value_ref_heavy_new(rt);
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ret));
+ if (!h) { pit_error(rt, "failed to create new heavy value for cell"); return PIT_NIL; }
+ h->hsort = PIT_VALUE_HEAVY_SORT_CELL;
+ h->in.cell = v;
+ return ret;
+}
+pit_value pit_value_cell_get(pit_runtime *rt, pit_value cell, pit_value sym) {
+ if (pit_value_sort(cell) != PIT_VALUE_SORT_REF) {
+ char buf[256];
+ i64 end = pit_dump(rt, buf, sizeof(buf) - 1, sym, false);
+ buf[end] = 0;
+ pit_error(rt, "attempted to get unbound variable/function: %s", buf);
+ return PIT_NIL;
+ }
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, cell));
+ if (!h) { pit_error(rt, "bad ref"); return PIT_NIL; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_CELL) {
+ pit_error(rt, "cell value ref does not point to cell");
+ return PIT_NIL;
+ }
+ return h->in.cell;
+}
+void pit_value_cell_set(pit_runtime *rt, pit_value cell, pit_value v, pit_value sym) {
+ if (pit_value_sort(cell) != PIT_VALUE_SORT_REF) {
+ char buf[256];
+ i64 end = pit_dump(rt, buf, sizeof(buf) - 1, sym, false);
+ buf[end] = 0;
+ pit_error(rt, "attempted to set unbound variable/function: %s", buf);
+ return;
+ }
+ pit_ref idx = pit_value_as_ref(rt, cell);
+ if (idx < rt->frozen_values) { pit_error(rt, "attempt to modify frozen cell"); return; }
+ pit_value_heavy *h = pit_value_ref_deref(rt, idx);
+ if (!h) { pit_error(rt, "bad ref"); return; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_CELL) {
+ pit_error(rt, "cell value ref does not point to cell");
+ return;
+ }
+ h->in.cell = v;
+}
diff --git a/src/runtime/value/cons.c b/src/runtime/value/cons.c
new file mode 100644
index 0000000..0f35631
--- /dev/null
+++ b/src/runtime/value/cons.c
@@ -0,0 +1,110 @@
+#include <lcq/pit/runtime/value/cons.h>
+
+bool pit_value_is_cons(pit_runtime *rt, pit_value a) {
+ return pit_value_is_ref_heavy_sort(rt, a, PIT_VALUE_HEAVY_SORT_CONS);
+}
+pit_value pit_value_cons(pit_runtime *rt, pit_value car, pit_value cdr) {
+ pit_value ret = pit_value_ref_heavy_new(rt);
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ret));
+ if (!h) { pit_error(rt, "failed to create new heavy value for cons"); return PIT_NIL; }
+ h->hsort = PIT_VALUE_HEAVY_SORT_CONS;
+ h->in.cons.car = car;
+ h->in.cons.cdr = cdr;
+ return ret;
+}
+pit_value pit_value_cons_car(pit_runtime *rt, pit_value v) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_REF) return PIT_NIL;
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, v));
+ if (!h) { pit_error(rt, "bad ref"); return PIT_NIL; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_CONS) return PIT_NIL;
+ return h->in.cons.car;
+}
+pit_value pit_value_cons_cdr(pit_runtime *rt, pit_value v) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_REF) return PIT_NIL;
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, v));
+ if (!h) { pit_error(rt, "bad ref"); return PIT_NIL; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_CONS) return PIT_NIL;
+ return h->in.cons.cdr;
+}
+void pit_value_cons_setcar(pit_runtime *rt, pit_value v, pit_value x) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_REF) { pit_error(rt, "not a ref"); return; }
+ pit_ref idx = pit_value_as_ref(rt, v);
+ if (idx < rt->frozen_values) { pit_error(rt, "attempted to modify frozen cons"); return; }
+ pit_value_heavy *h = pit_value_ref_deref(rt, idx);
+ if (!h) { pit_error(rt, "bad ref"); return; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_CONS) { pit_error(rt, "not a cons"); return; }
+ h->in.cons.car = x;
+}
+void pit_value_cons_setcdr(pit_runtime *rt, pit_value v, pit_value x) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_REF) { pit_error(rt, "not a ref"); return; }
+ pit_ref idx = pit_value_as_ref(rt, v);
+ if (idx < rt->frozen_values) { pit_error(rt, "attempted to modify frozen cons"); return; }
+ pit_value_heavy *h = pit_value_ref_deref(rt, idx);
+ if (!h) { pit_error(rt, "bad ref"); return; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_CONS) { pit_error(rt, "not a cons"); return; }
+ h->in.cons.cdr = x;
+}
+
+pit_value pit_value_list(pit_runtime *rt, i64 num, ...) {
+ pit_value temp[64] = {0};
+ pit_value ret = PIT_NIL;
+ if (num > 64) { pit_error(rt, "failed to create list of size %d\n", num); return PIT_NIL; }
+ va_list elems;
+ va_start(elems, num);
+ for (i64 i = 0; i < num; ++i) {
+ temp[i] = va_arg(elems, pit_value);
+ }
+ va_end(elems);
+ for (i64 i = 0; i < num; ++i) {
+ ret = pit_value_cons(rt, temp[num - i - 1], ret);
+ }
+ return ret;
+}
+i64 pit_value_list_len(pit_runtime *rt, pit_value xs) {
+ i64 ret = 0;
+ while (xs != PIT_NIL) {
+ ret += 1;
+ xs = pit_value_cons_cdr(rt, xs);
+ }
+ return ret;
+}
+pit_value pit_value_list_append(pit_runtime *rt, pit_value xs, pit_value ys) {
+ pit_value ret = ys;
+ xs = pit_value_list_reverse(rt, xs);
+ while (xs != PIT_NIL) {
+ ret = pit_value_cons(rt, pit_value_cons_car(rt, xs), ret);
+ xs = pit_value_cons_cdr(rt, xs);
+ }
+ return ret;
+}
+pit_value pit_value_list_reverse(pit_runtime *rt, pit_value xs) {
+ pit_value ret = PIT_NIL;
+ while (xs != PIT_NIL) {
+ ret = pit_value_cons(rt, pit_value_cons_car(rt, xs), ret);
+ xs = pit_value_cons_cdr(rt, xs);
+ }
+ return ret;
+}
+pit_value pit_value_list_contains_eq(pit_runtime *rt, pit_value needle, pit_value haystack) {
+ while (haystack != PIT_NIL) {
+ if (pit_value_eq(needle, pit_value_cons_car(rt, haystack))) return PIT_T;
+ haystack = pit_value_cons_cdr(rt, haystack);
+ }
+ return PIT_NIL;
+}
+pit_value pit_value_list_contains_equal(pit_runtime *rt, pit_value needle, pit_value haystack) {
+ while (haystack != PIT_NIL) {
+ if (pit_value_equal(rt, needle, pit_value_cons_car(rt, haystack))) return PIT_T;
+ haystack = pit_value_cons_cdr(rt, haystack);
+ }
+ return PIT_NIL;
+}
+pit_value pit_value_list_plist_get(pit_runtime *rt, pit_value k, pit_value vs) {
+ while (vs != PIT_NIL) {
+ if (pit_value_eq(k, pit_value_cons_car(rt, vs))) {
+ return pit_value_cons_car(rt, pit_value_cons_cdr(rt, vs));
+ }
+ vs = pit_value_cons_cdr(rt, vs);
+ }
+ return PIT_NIL;
+}
diff --git a/src/runtime/value/func.c b/src/runtime/value/func.c
new file mode 100644
index 0000000..af039eb
--- /dev/null
+++ b/src/runtime/value/func.c
@@ -0,0 +1,177 @@
+#include <lcq/pit/runtime/value/func.h>
+
+static pit_value free_vars(pit_runtime *rt, pit_value initial_bound, pit_value body) {
+ i64 expr_stack_reset = rt->expr_stack->next;
+ pit_value ret = PIT_NIL;
+ if (pit_vec_push(pit_value)(rt->expr_stack, pit_value_cons(rt, initial_bound, body)) < 0) {
+ pit_error(rt, "free variable search stack overflow");
+ return PIT_NIL;
+ }
+ while (rt->expr_stack->next > expr_stack_reset) {
+ pit_value boundscur, bound, cur;
+ if (pit_vec_pop(pit_value)(rt->expr_stack, &boundscur) < 0) {
+ pit_error(rt, "free variable search stack underflow");
+ return PIT_NIL;
+ }
+ bound = pit_value_cons_car(rt, boundscur);
+ cur = pit_value_cons_cdr(rt, boundscur);
+ if (pit_value_is_cons(rt, cur)) {
+ pit_value fsym = pit_value_cons_car(rt, cur);
+ bool is_symbol = pit_value_is_symbol(rt, fsym);
+ pit_value fargs = pit_value_cons_cdr(rt, cur);
+ if (is_symbol && pit_symtab_symbol_name_match_cstr(rt, fsym, "lambda")) {
+ pit_value new_bound = pit_value_list_append(rt, pit_value_cons_car(rt, fargs), bound);
+ fargs = pit_value_cons_cdr(rt, fargs);
+ while (fargs != PIT_NIL) {
+ if (pit_vec_push(pit_value)(rt->expr_stack, pit_value_cons(rt, new_bound, pit_value_cons_car(rt, fargs))) < 0) {
+ pit_error(rt, "free variable search stack overflow");
+ return PIT_NIL;
+ }
+ fargs = pit_value_cons_cdr(rt, fargs);
+ }
+ } else if (is_symbol && pit_symtab_symbol_name_match_cstr(rt, fsym, "quote")) {
+ /* don't look inside quote!
+ if we add other special forms, make sure to consider them here if necessary! */
+ } else {
+ while (fargs != PIT_NIL) {
+ if (pit_vec_push(pit_value)(rt->expr_stack, pit_value_cons(rt, bound, pit_value_cons_car(rt, fargs))) < 0) {
+ pit_error(rt, "free variable search stack overflow");
+ return PIT_NIL;
+ }
+ fargs = pit_value_cons_cdr(rt, fargs);
+ }
+ if (!is_symbol) {
+ if (pit_vec_push(pit_value)(rt->expr_stack, pit_value_cons(rt, bound, fsym)) < 0) {
+ pit_error(rt, "free variable search stack overflow");
+ return PIT_NIL;
+ }
+ }
+ }
+ } else if (pit_value_is_symbol(rt, cur)) {
+ if (pit_value_list_contains_eq(rt, cur, bound) == PIT_NIL) {
+ ret = pit_value_cons(rt, cur, ret);
+ }
+ }
+ }
+ rt->expr_stack->next = expr_stack_reset;
+ return ret;
+}
+
+bool pit_value_is_func(pit_runtime *rt, pit_value a) {
+ return pit_value_is_ref_heavy_sort(rt, a, PIT_VALUE_HEAVY_SORT_FUNC);
+}
+bool pit_value_is_nativefunc(pit_runtime *rt, pit_value a) {
+ return pit_value_is_ref_heavy_sort(rt, a, PIT_VALUE_HEAVY_SORT_NATIVEFUNC);
+}
+pit_value pit_value_func_lambda(pit_runtime *rt, pit_value args, pit_value body) {
+ pit_value ret = pit_value_ref_heavy_new(rt);
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ret));
+ if (!h) { pit_error(rt, "failed to create new heavy value for lambda"); return PIT_NIL; }
+ pit_value expanded = pit_macroexpand(rt, pit_value_cons(rt, pit_symtab_intern_cstr(rt, "progn"), body));
+ pit_value freevars = free_vars(rt, args, expanded);
+ pit_value env = PIT_NIL;
+ while (freevars != PIT_NIL) {
+ pit_value sym = pit_value_cons_car(rt, freevars);
+ pit_value cell = pit_symtab_get_value_cell(rt, sym);
+ env = pit_value_cons(rt, pit_value_cons(rt, sym, cell), env);
+ freevars = pit_value_cons_cdr(rt, freevars);
+ }
+ h->hsort = PIT_VALUE_HEAVY_SORT_FUNC;
+ pit_value arg_cells = PIT_NIL;
+ pit_value arg_rest_nm = PIT_NIL;
+ pit_value separator = pit_symtab_intern_cstr(rt, "&");
+ while (args != PIT_NIL) {
+ pit_value nm = pit_value_cons_car(rt, args);
+ if (pit_value_eq(nm, separator)) {
+ pit_value next_nm = pit_value_cons_car(rt, pit_value_cons_cdr(rt, args));
+ if (next_nm == PIT_NIL) { pit_error(rt, "invalid & in lambda list"); return PIT_NIL; }
+ arg_rest_nm = next_nm;
+ arg_cells = pit_value_cons(rt, next_nm, arg_cells);
+ break;
+ } else {
+ arg_cells = pit_value_cons(rt, nm, arg_cells);
+ args = pit_value_cons_cdr(rt, args);
+ }
+ }
+ arg_cells = pit_value_list_reverse(rt, arg_cells);
+ h->in.func.args = arg_cells;
+ h->in.func.arg_rest_nm = arg_rest_nm;
+ h->in.func.env = env;
+ h->in.func.body = expanded;
+ return ret;
+}
+pit_value pit_value_nativefunc_new_with_data(pit_runtime *rt, pit_nativefunc f, void *data) {
+ pit_value ret = pit_value_ref_heavy_new(rt);
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ret));
+ if (!h) { pit_error(rt, "failed to create new heavy value for nativefunc"); return PIT_NIL; }
+ h->hsort = PIT_VALUE_HEAVY_SORT_NATIVEFUNC;
+ h->in.nativefunc.f = f;
+ h->in.nativefunc.data = data;
+ return ret;
+}
+pit_value pit_value_nativefunc_new(pit_runtime *rt, pit_nativefunc f) {
+ return pit_value_nativefunc_new_with_data(rt, f, NULL);
+}
+pit_value pit_value_apply(pit_runtime *rt, pit_value f, pit_value args) {
+ char buf[256] = {0};
+ if (pit_value_is_symbol(rt, f)) {
+ f = pit_symtab_fget(rt, f);
+ }
+ /* if f is not a symbol, assume it is a func or nativefunc
+ most commonly, this happens when you funcall a variable
+ with a function in the value cell, e.g. passing a lambda to a function */
+ switch (pit_value_sort(f)) {
+ case PIT_VALUE_SORT_REF: {
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, f));
+ if (!h) { pit_error(rt, "bad ref"); return PIT_NIL; }
+ if (h->hsort == PIT_VALUE_HEAVY_SORT_FUNC) {
+ /* calling a Lisp function is simple! */
+ pit_value bound = PIT_NIL;
+ pit_value env = h->in.func.env;
+ while (env != PIT_NIL) { /* first, bind all entries in the closure */
+ pit_value b = pit_value_cons_car(rt, env);
+ pit_value nm = pit_value_cons_car(rt, b);
+ pit_symtab_bind(rt, nm, pit_value_cons_cdr(rt, b));
+ bound = pit_value_cons(rt, nm, bound);
+ env = pit_value_cons_cdr(rt, env);
+ }
+ pit_value anames = h->in.func.args;
+ while (anames != PIT_NIL) { /* bind all argument names to their values */
+ pit_value nm = pit_value_cons_car(rt, anames);
+ pit_value cell = pit_value_cell_new(rt, PIT_NIL);
+ if (h->in.func.arg_rest_nm != PIT_NIL && pit_value_eq(nm, h->in.func.arg_rest_nm)) {
+ pit_value_cell_set(rt, cell, args, nm);
+ pit_symtab_bind(rt, nm, cell);
+ break;
+ } else {
+ pit_value_cell_set(rt, cell, pit_value_cons_car(rt, args), nm);
+ pit_symtab_bind(rt, nm, cell);
+ }
+ bound = pit_value_cons(rt, nm, bound);
+ args = pit_value_cons_cdr(rt, args);
+ anames = pit_value_cons_cdr(rt, anames);
+ }
+ pit_value ret = pit_eval(rt, h->in.func.body); /* evaluate the body */
+ while (bound != PIT_NIL) { /* unbind everything we bound earlier, in reverse */
+ pit_symtab_unbind(rt, pit_value_cons_car(rt, bound));
+ bound = pit_value_cons_cdr(rt, bound);
+ }
+ return ret;
+ } else if (h->hsort == PIT_VALUE_HEAVY_SORT_NATIVEFUNC) {
+ /* calling native functions is even simpler */
+ return h->in.nativefunc.f(rt, args, h->in.nativefunc.data);
+ } else {
+ i64 end = pit_dump(rt, buf, sizeof(buf) - 1, f, true);
+ buf[end] = 0;
+ pit_error(rt, "attempted to apply non-function ref: %s", buf);
+ return PIT_NIL;
+ }
+ }
+ default: {
+ i64 end = pit_dump(rt, buf, sizeof(buf) - 1, f, true);
+ buf[end] = 0;
+ pit_error(rt, "attempted to apply non-function value: %s", buf);
+ return PIT_NIL;
+ }
+ }
+}
diff --git a/src/runtime/value/nativedata.c b/src/runtime/value/nativedata.c
new file mode 100644
index 0000000..5c6fe2b
--- /dev/null
+++ b/src/runtime/value/nativedata.c
@@ -0,0 +1,36 @@
+#include <lcq/pit/runtime/value/nativedata.h>
+
+bool pit_value_is_nativedata(pit_runtime *rt, pit_value a) {
+ return pit_value_is_ref_heavy_sort(rt, a, PIT_VALUE_HEAVY_SORT_NATIVEDATA);
+}
+pit_value pit_value_nativedata_new(pit_runtime *rt, pit_value tag, void *d) {
+ pit_value ret = pit_value_ref_heavy_new(rt);
+ pit_value_heavy *h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ret));
+ if (!h) { pit_error(rt, "failed to create new heavy value for nativedata"); return PIT_NIL; }
+ h->hsort = PIT_VALUE_HEAVY_SORT_NATIVEDATA;
+ h->in.nativedata.tag = tag;
+ h->in.nativedata.data = d;
+ return ret;
+}
+void *pit_value_nativedata_get(pit_runtime *rt, pit_value tag, pit_value v) {
+ pit_value_heavy *h = NULL;
+ if (pit_value_sort(v) != PIT_VALUE_SORT_REF) {
+ pit_error(rt, "value was not a reference");
+ return NULL;
+ }
+ h = pit_value_ref_deref(rt, pit_value_as_ref(rt, v));
+ if (!h) { pit_error(rt, "bad ref"); return NULL; }
+ if (h->hsort != PIT_VALUE_HEAVY_SORT_NATIVEDATA) {
+ pit_error(rt, "invalid use of value as nativedata");
+ return NULL;
+ }
+ if (!pit_value_eq(h->in.nativedata.tag, tag)) {
+ pit_error(rt, "native value does not match tag");
+ return NULL;
+ }
+ if (!h->in.nativedata.data) {
+ pit_error(rt, "nativedata was already freed");
+ return NULL;
+ }
+ return h->in.nativedata.data;
+}
diff --git a/src/runtime/value/small.c b/src/runtime/value/small.c
new file mode 100644
index 0000000..f07e405
--- /dev/null
+++ b/src/runtime/value/small.c
@@ -0,0 +1,96 @@
+#include <lcq/pit/runtime/value/small.h>
+
+#ifndef PIT_NO_DOUBLE
+double pit_value_as_double(pit_runtime *rt, pit_value v) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_DOUBLE) {
+ pit_error(rt, "invalid use of value as double");
+ return 0.0;
+ }
+ union { double dval; u64 ival; } x;
+ x.ival = v;
+ return x.dval;
+}
+bool pit_value_is_double(pit_runtime *rt, pit_value a) {
+ (void) rt;
+ return pit_value_sort(a) == PIT_VALUE_SORT_DOUBLE;
+}
+pit_value pit_value_double_new(pit_runtime *rt, double d) {
+ union { double dval; u64 ival; } x;
+ x.dval = d;
+ return pit_value_new(rt, PIT_VALUE_SORT_DOUBLE, x.ival);
+}
+#endif
+
+i64 pit_value_as_integer(pit_runtime *rt, pit_value v) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_INTEGER) {
+ pit_error(rt, "invalid use of value as integer");
+ return -1;
+ }
+ u64 lo = pit_value_data(v);
+ return ((i64) (lo << 15)) >> 15; /* sign-extend low 49 bits */
+
+}
+bool pit_value_is_integer(pit_runtime *rt, pit_value a) {
+ (void) rt;
+ return pit_value_sort(a) == PIT_VALUE_SORT_INTEGER;
+}
+pit_value pit_value_integer_new(pit_runtime *rt, i64 i) {
+ return pit_value_new(rt, PIT_VALUE_SORT_INTEGER, 0x1ffffffffffff & (u64) i);
+}
+pit_value pit_value_bool_new(pit_runtime *rt, bool i) {
+ (void) rt;
+ return i ? PIT_T : PIT_NIL;
+}
+
+pit_symbol pit_value_as_symbol(pit_runtime *rt, pit_value v) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_SYMBOL) {
+ pit_error(rt, "invalid use of value as symbol");
+ return -1;
+ }
+ return (pit_symbol) (pit_value_data(v) & 0xffffffff);
+}
+bool pit_value_is_symbol(pit_runtime *rt, pit_value a) {
+ (void) rt;
+ return pit_value_sort(a) == PIT_VALUE_SORT_SYMBOL;
+}
+pit_value pit_value_symbol_new(pit_runtime *rt, pit_symbol s) {
+ return pit_value_new(rt, PIT_VALUE_SORT_SYMBOL, (u64) s);
+}
+
+pit_ref pit_value_as_ref(pit_runtime *rt, pit_value v) {
+ if (pit_value_sort(v) != PIT_VALUE_SORT_REF) {
+ pit_error(rt, "invalid use of value as ref");
+ return -1;
+ }
+ return (pit_ref) (pit_value_data(v) & 0xffffffff);
+}
+bool pit_value_is_ref(pit_runtime *rt, pit_value a) {
+ (void) rt;
+ return pit_value_sort(a) == PIT_VALUE_SORT_REF;
+}
+pit_value pit_value_ref_new(pit_runtime *rt, pit_ref r) {
+ return pit_value_new(rt, PIT_VALUE_SORT_REF, (u64) r);
+}
+pit_value pit_value_ref_heavy_new(pit_runtime *rt) {
+ pit_arena_index idx = pit_arena_alloc_index(rt->heap);
+ if (idx < 0) {
+ pit_error(rt, "failed to allocate space for heavy value");
+ return PIT_NIL;
+ }
+ return pit_value_ref_new(rt, idx);
+}
+pit_value_heavy *pit_value_ref_deref(pit_runtime *rt, pit_ref p) {
+ return pit_arena_get(rt->heap, p);
+}
+bool pit_value_is_ref_heavy_sort(pit_runtime *rt, pit_value a, enum pit_value_heavy_sort e) {
+ switch (pit_value_sort(a)) {
+ case PIT_VALUE_SORT_REF: {
+ pit_value_heavy *ha = pit_value_ref_deref(rt, pit_value_as_ref(rt, a));
+ if (!ha) { pit_error(rt, "bad ref"); return false; }
+ return ha->hsort == e;
+ }
+ default:
+ break;
+ }
+ return false;
+}