From 1c028e3a2250a56eface56ebbb1835d77e4e2579 Mon Sep 17 00:00:00 2001 From: LLLL Colonq Date: Tue, 30 Jun 2026 14:30:19 -0400 Subject: Big refactor --- src/runtime/dump.c | 100 +++++++++++++++++++++++ src/runtime/eval.c | 107 +++++++++++++++++++++++++ src/runtime/gc.c | 100 +++++++++++++++++++++++ src/runtime/macroexpand.c | 111 ++++++++++++++++++++++++++ src/runtime/symtab.c | 118 +++++++++++++++++++++++++++ src/runtime/value.c | 76 ++++++++++++++++++ src/runtime/value/array.c | 57 +++++++++++++ src/runtime/value/bytes.c | 47 +++++++++++ src/runtime/value/cell.c | 47 +++++++++++ src/runtime/value/cons.c | 110 +++++++++++++++++++++++++ src/runtime/value/func.c | 177 +++++++++++++++++++++++++++++++++++++++++ src/runtime/value/nativedata.c | 36 +++++++++ src/runtime/value/small.c | 96 ++++++++++++++++++++++ 13 files changed, 1182 insertions(+) create mode 100644 src/runtime/dump.c create mode 100644 src/runtime/eval.c create mode 100644 src/runtime/gc.c create mode 100644 src/runtime/macroexpand.c create mode 100644 src/runtime/symtab.c create mode 100644 src/runtime/value.c create mode 100644 src/runtime/value/array.c create mode 100644 src/runtime/value/bytes.c create mode 100644 src/runtime/value/cell.c create mode 100644 src/runtime/value/cons.c create mode 100644 src/runtime/value/func.c create mode 100644 src/runtime/value/nativedata.c create mode 100644 src/runtime/value/small.c (limited to 'src/runtime') diff --git a/src/runtime/dump.c b/src/runtime/dump.c new file mode 100644 index 0000000..3d5ec9c --- /dev/null +++ b/src/runtime/dump.c @@ -0,0 +1,100 @@ +#include + +#define CHECK_BUF if (buf >= end) { return buf - start; } +#define CHECK_BUF_LABEL(label) if (buf >= end) { goto label; } +i64 pit_dump(pit_runtime *rt, char *buf, i64 len, pit_value v, bool readable) { + pit_value_heavy *h = NULL; + if (len <= 0) return 0; + switch (pit_value_sort(v)) { + case PIT_VALUE_SORT_DOUBLE: + #ifndef PIT_NO_DOUBLE + return pit_libc_string_snprintf(buf, (size_t) len, "%lf", pit_value_as_double(rt, v)); + #else + return pit_string_snprintf(buf, (size_t) len, ""); + #endif + case PIT_VALUE_SORT_INTEGER: + return pit_libc_string_snprintf(buf, (size_t) len, "%ld", pit_value_as_integer(rt, v)); + case PIT_VALUE_SORT_SYMBOL: { + pit_symtab_entry *ent = pit_symtab_lookup(rt, v); + if (ent + && pit_value_sort(ent->name) == PIT_VALUE_SORT_REF + && (h = pit_value_ref_deref(rt, pit_value_as_ref(rt, ent->name))) + ) { + i64 i = 0; + for (; i < h->in.bytes.len && i < len - 1; ++i) { + buf[i] = (char) h->in.bytes.data[i]; + } + return i; + } else { + return pit_libc_string_snprintf(buf, (size_t) len, "", pit_value_as_symbol(rt, v)); + } + } + case PIT_VALUE_SORT_REF: { + pit_ref r = pit_value_as_ref(rt, v); + char *end = buf + len; + char *start = buf; + h = pit_value_ref_deref(rt, r); + if (!h) pit_libc_string_snprintf(buf, (size_t) len, "", r); + else { + switch (h->hsort) { + case PIT_VALUE_HEAVY_SORT_CELL: { + CHECK_BUF; *(buf++) = '{'; + CHECK_BUF; buf += pit_dump(rt, buf, end - buf, pit_value_cons_car(rt, h->in.cell), readable); + CHECK_BUF; *(buf++) = '}'; + return buf - start; + } + case PIT_VALUE_HEAVY_SORT_CONS: { + pit_value cur = v; + CHECK_BUF_LABEL(list_end); + do { + if (pit_value_is_cons(rt, cur)) { + CHECK_BUF_LABEL(list_end); *(buf++) = ' '; + CHECK_BUF_LABEL(list_end); buf += pit_dump(rt, buf, end - buf, pit_value_cons_car(rt, cur), readable); + } else { + CHECK_BUF_LABEL(list_end); buf += pit_libc_string_snprintf(buf, (size_t) (end - buf), " . "); + CHECK_BUF_LABEL(list_end); buf += pit_dump(rt, buf, end - buf, cur, readable); + } + } while (!pit_value_eq((cur = pit_value_cons_cdr(rt, cur)), PIT_NIL)); + CHECK_BUF_LABEL(list_end); *(buf++) = ')'; + list_end: + *start = '('; + return buf - start; + } + case PIT_VALUE_HEAVY_SORT_ARRAY: { + i64 i = 0; + CHECK_BUF_LABEL(array_end); + if (h->in.array.len == 0) { + CHECK_BUF_LABEL(array_end); *(buf++) = '['; + } else for (; i < h->in.array.len; ++i) { + CHECK_BUF_LABEL(array_end); *(buf++) = ' '; + CHECK_BUF_LABEL(array_end); buf += pit_dump(rt, buf, end - buf, h->in.array.data[i], readable); + } + CHECK_BUF_LABEL(array_end); *(buf++) = ']'; + array_end: + *start = '['; + return buf - start; + } + case PIT_VALUE_HEAVY_SORT_BYTES: { + i64 i = 0; + if (readable) { CHECK_BUF; buf[i++] = '"'; } + i64 maxlen = len - i; + for (i64 j = 0; i < maxlen && j < h->in.bytes.len;) { + if (buf[i - 1] != '\\' && (h->in.bytes.data[j] == '\\' || h->in.bytes.data[j] == '"')) { + CHECK_BUF; buf[i++] = '\\'; + } + else { + CHECK_BUF; buf[i++] = (char) h->in.bytes.data[j++]; + } + } + if (readable && i < len - 1) buf[i++] = '"'; + return i; + } + default: + return pit_libc_string_snprintf(buf, (size_t) len, "", r); + } + } + break; + } + } + return 0; +} diff --git a/src/runtime/eval.c b/src/runtime/eval.c new file mode 100644 index 0000000..f5eba97 --- /dev/null +++ b/src/runtime/eval.c @@ -0,0 +1,107 @@ +#include + +pit_value pit_eval(pit_runtime *rt, pit_value top) { + i64 expr_stack_reset = rt->expr_stack->next; + i64 result_stack_reset = rt->result_stack->next; + i64 program_reset = rt->program->next; + // pit_vec_reset(pit_annotated_ref)(rt->backtrace); + if (pit_vec_push(pit_value)(rt->expr_stack, top) < 0) + pit_error(rt, "evaluation stack overflow"); + /* first, convert the expression tree into "polish notation" in program */ + while (rt->expr_stack->next > expr_stack_reset) { + pit_value cur = PIT_NIL; + if (rt->error != PIT_NIL) goto end; + if (pit_vec_pop(pit_value)(rt->expr_stack, &cur) < 0) + pit_error(rt, "evaluation stack underflow"); + if (pit_value_is_cons(rt, cur)) { /* compound expressions: function/macro application special forms */ + pit_value fsym = pit_value_cons_car(rt, cur); + bool is_symbol = pit_value_is_symbol(rt, fsym); + pit_annotated_ref *ann = pit_annotation_get(rt, pit_value_as_ref(rt, cur)); + if (is_symbol && pit_symtab_is_symbol_special_form(rt, fsym)) { /* special forms */ + pit_value f = pit_symtab_fget(rt, fsym); + pit_value args = pit_value_cons_cdr(rt, cur); + /* special forms are nativefuncs that directly manipulate the stacks + basically macros, but we don't need to evaluate the return value */ + pit_value_apply(rt, f, args); + } else if (is_symbol && pit_symtab_is_symbol_macro(rt, fsym)) { /* macros */ + pit_value f = pit_symtab_fget(rt, fsym); + pit_value args = pit_value_cons_cdr(rt, cur); + pit_value res = pit_value_apply(rt, f, args); + if (pit_vec_push(pit_value)(rt->expr_stack, res) < 0) + pit_error(rt, "evaluation stack overflow"); + } else { /* normal functions */ + pit_value args = pit_value_cons_cdr(rt, cur); + i64 argcount = 0; + while (args != PIT_NIL) { + if (pit_vec_push(pit_value)(rt->expr_stack, pit_value_cons_car(rt, args)) < 0) + pit_error(rt, "evaluation stack overflow"); + args = pit_value_cons_cdr(rt, args); + argcount += 1; + } + if (!is_symbol) { + if (pit_vec_push(pit_value)(rt->expr_stack, fsym) < 0) + pit_error(rt, "evaluation stack overflow"); + } + pit_runtime_eval_program_push_apply(rt, rt->program, argcount, ann); + if (is_symbol) { + pit_value f = pit_symtab_fget(rt, fsym); + pit_runtime_eval_program_push_literal(rt, rt->program, f); + } + } + } else if (pit_value_is_symbol(rt, cur)) { /* unquoted symbols: variable lookup */ + pit_symtab_entry *ent = pit_symtab_lookup(rt, cur); + if (ent->is_keyword) { + pit_runtime_eval_program_push_literal(rt, rt->program, cur); + } else { + pit_runtime_eval_program_push_literal(rt, rt->program, pit_symtab_get(rt, cur)); + } + } else { /* other expressions evaluate to themselves! */ + pit_runtime_eval_program_push_literal(rt, rt->program, cur); + } + } + /* then, execute the polish notation program from right to left + this has the nice consequence of putting the arguments in the right order */ + for (i64 idx = rt->program->next - 1; idx >= program_reset; --idx) { + pit_runtime_eval_ins *ent = pit_vec_get(pit_runtime_eval_ins)(rt->program, idx); + if (ent == NULL) pit_error(rt, "evaluation program invalid"); + if (rt->error != PIT_NIL) goto end; + switch (ent->sort) { + case PIT_RUNTIME_EVAL_INS_LITERAL: + if (pit_vec_push(pit_value)(rt->result_stack, ent->in.literal) < 0) + pit_error(rt, "evaluation result stack overflow"); + break; + case PIT_RUNTIME_EVAL_INS_APPLY: { + pit_value f = PIT_NIL; + pit_value args = PIT_NIL; + if (pit_vec_pop(pit_value)(rt->result_stack, &f) < 0) + pit_error(rt, "evaluation result stack underflow"); + for (i64 i = 0; i < ent->in.apply.arity; ++i) { + pit_value a = PIT_NIL; + if (pit_vec_pop(pit_value)(rt->result_stack, &a) < 0) + pit_error(rt, "evaluation result stack underflow"); + args = pit_value_cons(rt, a, args); + } + if (ent->in.apply.annotation != NULL) { + rt->source_line = ent->in.apply.annotation->annotation.line; + rt->source_column = ent->in.apply.annotation->annotation.column; + pit_vec_push(pit_annotated_ref)(rt->backtrace, *ent->in.apply.annotation); + } + if (pit_vec_push(pit_value)(rt->result_stack, pit_value_apply(rt, f, args)) < 0) + pit_error(rt, "evaluation result stack underflow"); + break; + } + default: + pit_error(rt, "unknown program entry"); + goto end; + } + } +end: { + pit_value ret = PIT_NIL; + if (pit_vec_pop(pit_value)(rt->result_stack, &ret) < 0) + pit_error(rt, "evaluation result stack underflow"); + rt->expr_stack->next = expr_stack_reset; + rt->result_stack->next = result_stack_reset; + rt->program->next = program_reset; + return ret; + } +} diff --git a/src/runtime/gc.c b/src/runtime/gc.c new file mode 100644 index 0000000..fcb5f89 --- /dev/null +++ b/src/runtime/gc.c @@ -0,0 +1,100 @@ +#include + +static i64 gc_copy(pit_runtime *rt, pit_value_heavy *h) { + if (h->hsort == PIT_VALUE_HEAVY_SORT_FORWARDING_POINTER) { + return h->in.forwarding_pointer; + } else { + i64 ret = rt->backbuffer->next; + pit_value_heavy *g = pit_arena_alloc(rt->backbuffer); + *g = *h; + h->hsort = PIT_VALUE_HEAVY_SORT_FORWARDING_POINTER; + h->in.forwarding_pointer = ret; + return ret; + } +} +static pit_value gc_copy_value(pit_runtime *rt, pit_value v) { + if (pit_value_sort(v) == PIT_VALUE_SORT_REF) { + pit_ref r = pit_value_as_ref(rt, v); + pit_value_heavy *h = pit_value_ref_deref(rt, r); + i64 new = gc_copy(rt, h); + pit_annotated_ref *ann = pit_annotation_get(rt, r); + if (ann != NULL) { + pit_annotated_ref newann = *ann; + newann.ref = new; + if (pit_vec_push(pit_annotated_ref)(rt->backtrace, newann) < 0) + pit_error(rt, "annotation overflow"); + } + return pit_value_ref_new(rt, new); + } else { + return v; + } +} +void pit_gc(pit_runtime *rt) { + rt->frozen_values = 0; + rt->frozen_symtab = 0; + pit_arena *fromspace = rt->heap; + pit_arena *tospace = rt->backbuffer; + pit_vec(pit_annotated_ref) *fromspace_ann = rt->annotations; + pit_vec(pit_annotated_ref) *tospace_ann = rt->backtrace; + pit_arena_reset(tospace); + pit_vec_reset(pit_annotated_ref)(tospace_ann); + /* populate tospace with immediately reachable values */ + for (i64 i = 0; i < rt->symtab->next; ++i) { + pit_symtab_entry *ent = pit_vec_get(pit_symtab_entry)(rt->symtab, i); + if (ent == NULL) continue; /* TODO warn on failure here? */ + ent->name = gc_copy_value(rt, ent->name); + ent->value = gc_copy_value(rt, ent->value); + ent->function = gc_copy_value(rt, ent->function); + } + for (i64 i = 0; i < rt->saved_bindings->next; ++i) { + pit_value *v = pit_vec_get(pit_value)(rt->saved_bindings, i); + if (v != NULL) *v = gc_copy_value(rt, *v); /* TODO warn on failure here? */ + } + for (i64 scan = 0; scan < tospace->next; ++scan) { + pit_value_heavy *h = pit_arena_get(tospace, scan); + switch (h->hsort) { + case PIT_VALUE_HEAVY_SORT_CELL: + h->in.cell = gc_copy_value(rt, h->in.cell); + break; + case PIT_VALUE_HEAVY_SORT_CONS: + h->in.cons.car = gc_copy_value(rt, h->in.cons.car); + h->in.cons.cdr = gc_copy_value(rt, h->in.cons.cdr); + break; + case PIT_VALUE_HEAVY_SORT_ARRAY: { + i64 byte_len = 0; pit_mul(&byte_len, sizeof(pit_value), h->in.array.len); + pit_value *data = pit_arena_alloc_back(tospace, byte_len); + for (i64 i = 0; i < h->in.array.len; ++i) { + data[i] = gc_copy_value(rt, h->in.array.data[i]); + } + h->in.array.data = data; + break; + } + case PIT_VALUE_HEAVY_SORT_BYTES: { + u8 *data = pit_arena_alloc_back(tospace, h->in.bytes.len); + for (i64 i = 0; i < h->in.bytes.len; ++i) { + data[i] = h->in.bytes.data[i]; + } + h->in.bytes.data = data; + break; + } + case PIT_VALUE_HEAVY_SORT_FUNC: + h->in.func.env = gc_copy_value(rt, h->in.func.env); + h->in.func.args = gc_copy_value(rt, h->in.func.args); + h->in.func.arg_rest_nm = gc_copy_value(rt, h->in.func.arg_rest_nm); + h->in.func.body = gc_copy_value(rt, h->in.func.body); + break; + case PIT_VALUE_HEAVY_SORT_NATIVEFUNC: break; + case PIT_VALUE_HEAVY_SORT_NATIVEDATA: + h->in.nativedata.tag = gc_copy_value(rt, h->in.nativedata.tag); + break; + case PIT_VALUE_HEAVY_SORT_FORWARDING_POINTER: + pit_error(rt, "garbage collection broken! encountered forwarding pointer in to-space"); + break; + } + } + rt->heap = tospace; + rt->backbuffer = fromspace; + rt->annotations = tospace_ann; + rt->backtrace = fromspace_ann; + pit_vec_reset(pit_annotated_ref)(rt->backtrace); +} diff --git a/src/runtime/macroexpand.c b/src/runtime/macroexpand.c new file mode 100644 index 0000000..13a4fdd --- /dev/null +++ b/src/runtime/macroexpand.c @@ -0,0 +1,111 @@ +#include + +pit_value pit_macroexpand(pit_runtime *rt, pit_value top) { + i64 expr_stack_reset = rt->expr_stack->next; + i64 result_stack_reset = rt->result_stack->next; + i64 program_reset = rt->program->next; + if (pit_vec_push(pit_value)(rt->expr_stack, top) < 0) + pit_error(rt, "macro expansion stack overflow"); + while (rt->expr_stack->next > expr_stack_reset) { + pit_value cur; + if (rt->error != PIT_NIL) goto end; + if (pit_vec_pop(pit_value)(rt->expr_stack, &cur) < 0) + pit_error(rt, "macro expansion stack underflow"); + 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_annotated_ref *ann = pit_annotation_get(rt, pit_value_as_ref(rt, cur)); + if (is_symbol && pit_symtab_is_symbol_macro(rt, fsym)) { + pit_value f = pit_symtab_fget(rt, fsym); + pit_value args = pit_value_cons_cdr(rt, cur); + pit_value res = pit_value_apply(rt, f, args); + if (pit_vec_push(pit_value)(rt->expr_stack, res) < 0) + pit_error(rt, "macro expansion stack overflow"); + } else if (is_symbol && pit_symtab_symbol_name_match_cstr(rt, fsym, "defer")) { + pit_value args = pit_value_cons_cdr(rt, cur); + pit_runtime_eval_program_push_literal(rt, rt->program, pit_value_cons_car(rt, args)); + } else if (is_symbol && pit_symtab_symbol_name_match_cstr(rt, fsym, "quote")) { + pit_runtime_eval_program_push_literal(rt, rt->program, cur); + } else if (is_symbol && pit_symtab_symbol_name_match_cstr(rt, fsym, "lambda")) { + pit_value args = pit_value_cons_cdr(rt, cur); + pit_value bindings = pit_value_cons_car(rt, args); + pit_value body = pit_value_cons_cdr(rt, args); + i64 argcount = 0; + if (pit_vec_push(pit_value)(rt->expr_stack, pit_value_list(rt, 2, pit_symtab_intern_cstr(rt, "defer"), bindings)) < 0) + pit_error(rt, "macro expansion stack overflow"); + while (body != PIT_NIL) { + pit_value a = pit_value_cons_car(rt, body); + if (pit_vec_push(pit_value)(rt->expr_stack, a) < 0) + pit_error(rt, "macro expansion stack overflow"); + body = pit_value_cons_cdr(rt, body); + argcount += 1; + } + pit_runtime_eval_program_push_apply(rt, rt->program, argcount + 1, ann); + pit_runtime_eval_program_push_literal(rt, rt->program, fsym); + } else { + pit_value args = pit_value_cons_cdr(rt, cur); + i64 argcount = 0; + while (args != PIT_NIL) { + pit_value a = pit_value_cons_car(rt, args); + if (pit_vec_push(pit_value)(rt->expr_stack, a) < 0) + pit_error(rt, "macro expansion stack overflow"); + args = pit_value_cons_cdr(rt, args); + argcount += 1; + } + if (!is_symbol) { + if (pit_vec_push(pit_value)(rt->expr_stack, fsym) < 0) + pit_error(rt, "macro expansion stack overflow"); + } + pit_runtime_eval_program_push_apply(rt, rt->program, argcount, ann); + if (is_symbol) { + pit_runtime_eval_program_push_literal(rt, rt->program, fsym); + } + } + } else { + pit_runtime_eval_program_push_literal(rt, rt->program, cur); + } + } + for (i64 idx = rt->program->next - 1; idx >= program_reset; --idx) { + pit_runtime_eval_ins *ent = pit_vec_get(pit_runtime_eval_ins)(rt->program, idx); + if (ent == NULL) pit_error(rt, "macro expansion program invalid"); + if (rt->error != PIT_NIL) goto end; + switch (ent->sort) { + case PIT_RUNTIME_EVAL_INS_LITERAL: + if (pit_vec_push(pit_value)(rt->result_stack, ent->in.literal) < 0) + pit_error(rt, "macro expansion stack overflow"); + break; + case PIT_RUNTIME_EVAL_INS_APPLY: { + pit_value f = PIT_NIL; + pit_value args = PIT_NIL; + pit_value app = PIT_NIL; + if (pit_vec_pop(pit_value)(rt->result_stack, &f) < 0) + pit_error(rt, "macro expansion stack underflow"); + for (i64 i = 0; i < ent->in.apply.arity; ++i) { + pit_value a; + if (pit_vec_pop(pit_value)(rt->result_stack, &a) < 0) + pit_error(rt, "macro expansion stack underflow"); + args = pit_value_cons(rt, a, args); + } + app = pit_value_cons(rt, f, args); + if (ent->in.apply.annotation != NULL) { + pit_annotation_set(rt, pit_value_as_ref(rt, app), ent->in.apply.annotation->annotation); + } + if (pit_vec_push(pit_value)(rt->result_stack, app) < 0) + pit_error(rt, "macro expansion stack overflow"); + break; + } + default: + pit_error(rt, "unknown program entry"); + goto end; + } + } +end: { + pit_value ret = PIT_NIL; + if (pit_vec_pop(pit_value)(rt->result_stack, &ret) < 0) + pit_error(rt, "macro expansion stack underflow"); + rt->expr_stack->next = expr_stack_reset; + rt->result_stack->next = result_stack_reset; + rt->program->next = program_reset; + return ret; + } +} diff --git a/src/runtime/symtab.c b/src/runtime/symtab.c new file mode 100644 index 0000000..d2bc51e --- /dev/null +++ b/src/runtime/symtab.c @@ -0,0 +1,118 @@ +#include + +pit_symtab_entry *pit_symtab_lookup(pit_runtime *rt, pit_value sym) { + pit_symbol s = pit_value_as_symbol(rt, sym); + return pit_vec_get(pit_symtab_entry)(rt->symtab, s); +} +pit_value pit_symtab_intern(pit_runtime *rt, u8 *nm, i64 len) { + if (rt->error != PIT_NIL) return PIT_NIL; + for (i64 sidx = 0; sidx < rt->symtab->next; ++sidx) { + pit_symtab_entry *sent = pit_vec_get(pit_symtab_entry)(rt->symtab, sidx); + if (sent == NULL) { pit_error(rt, "corrupted symbol table"); return PIT_NIL; } + if (pit_value_bytes_match(rt, sent->name, nm, len)) return pit_value_symbol_new(rt, sidx); + } + pit_symtab_entry ent; + ent.name = pit_value_bytes_new(rt, nm, len); + ent.value = PIT_NIL; + ent.function = PIT_NIL; + ent.is_macro = false; + ent.is_special_form = false; + ent.is_keyword = len >= 1 && nm[0] == ':'; + i64 idx = pit_vec_push(pit_symtab_entry)(rt->symtab, ent); + if (idx < 0) { pit_error(rt, "failed to allocate symtab entry"); return PIT_NIL; } + return pit_value_symbol_new(rt, idx); +} +pit_value pit_symtab_intern_cstr(pit_runtime *rt, char *nm) { + return pit_symtab_intern(rt, (u8 *) nm, (i64) pit_libc_string_strlen(nm)); +} +pit_value pit_symtab_symbol_name(pit_runtime *rt, pit_value sym) { + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return PIT_NIL; } + return ent->name; +} +bool pit_symtab_symbol_name_match(pit_runtime *rt, pit_value sym, u8 *buf, i64 len) { + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return PIT_NIL; } + return pit_value_bytes_match(rt, ent->name, buf, len); +} +bool pit_symtab_symbol_name_match_cstr(pit_runtime *rt, pit_value sym, char *s) { + return pit_symtab_symbol_name_match(rt, sym, (u8 *) s, (i64) pit_libc_string_strlen(s)); +} +pit_value pit_symtab_get_value_cell(pit_runtime *rt, pit_value sym) { + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return PIT_NIL; } + return ent->value; +} +pit_value pit_symtab_get_function_cell(pit_runtime *rt, pit_value sym) { + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return PIT_NIL; } + return ent->function; +} +pit_value pit_symtab_get(pit_runtime *rt, pit_value sym) { + return pit_value_cell_get(rt, pit_symtab_get_value_cell(rt, sym), sym); +} +void pit_symtab_set(pit_runtime *rt, pit_value sym, pit_value v) { + pit_symbol idx = pit_value_as_symbol(rt, sym); + if (idx < rt->frozen_symtab) { pit_error(rt, "attempted to modify frozen symbol"); return; } + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return; } + if (pit_value_sort(ent->value) != PIT_VALUE_SORT_REF) { + ent->value = pit_value_cell_new(rt, PIT_NIL); + } + pit_value_cell_set(rt, ent->value, v, sym); +} +pit_value pit_symtab_fget(pit_runtime *rt, pit_value sym) { + return pit_value_cell_get(rt, pit_symtab_get_function_cell(rt, sym), sym); +} +void pit_symtab_fset(pit_runtime *rt, pit_value sym, pit_value v) { + pit_symbol idx = pit_value_as_symbol(rt, sym); + if (idx < rt->frozen_symtab) { pit_error(rt, "attempted to modify frozen symbol"); return; } + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return; } + if (pit_value_sort(ent->function) != PIT_VALUE_SORT_REF) { + ent->function = pit_value_cell_new(rt, PIT_NIL); + } + pit_value_cell_set(rt, ent->function, v, sym); +} +bool pit_symtab_is_symbol_macro(pit_runtime *rt, pit_value sym) { + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return false; } + return ent->is_macro; +} +void pit_symtab_symbol_mark_macro(pit_runtime *rt, pit_value sym) { + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return; } + ent->is_macro = true; +} +void pit_symtab_mset(pit_runtime *rt, pit_value sym, pit_value v) { + pit_symtab_fset(rt, sym, v); + pit_symtab_symbol_mark_macro(rt, sym); +} +bool pit_symtab_is_symbol_special_form(pit_runtime *rt, pit_value sym) { + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return false; } + return ent->is_special_form; +} +void pit_symtab_symbol_mark_special_form(pit_runtime *rt, pit_value sym) { + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return; } + ent->is_special_form = true; +} +void pit_symtab_sfset(pit_runtime *rt, pit_value sym, pit_value v) { + pit_symtab_fset(rt, sym, v); + pit_symtab_symbol_mark_special_form(rt, sym); +} +void pit_symtab_bind(pit_runtime *rt, pit_value sym, pit_value cell) { + /* although we cannot set frozen symbols, we can still bind them temporarily - no need to check */ + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return; } + if (pit_vec_push(pit_value)(rt->saved_bindings, ent->value) < 0) pit_error(rt, "binding stack overflow"); + ent->value = cell; +} +pit_value pit_symtab_unbind(pit_runtime *rt, pit_value sym) { + pit_symtab_entry *ent = pit_symtab_lookup(rt, sym); + if (!ent) { pit_error(rt, "bad symbol"); return PIT_NIL; } + pit_value old = ent->value; + if (pit_vec_pop(pit_value)(rt->saved_bindings, &ent->value) < 0) pit_error(rt, "binding stack underflow"); + return old; +} diff --git a/src/runtime/value.c b/src/runtime/value.c new file mode 100644 index 0000000..1e9d543 --- /dev/null +++ b/src/runtime/value.c @@ -0,0 +1,76 @@ +#include + +pit_value pit_value_new(pit_runtime *rt, enum pit_value_sort s, u64 data) { + if (s == PIT_VALUE_SORT_DOUBLE) { + /* if (((data >> 52) & 0b011111111111) == 0b011111111111 && ((data >> 51) & 0b1) == 0) { */ + if (((data >> 52) & 0x7ff) == 0x7ff && ((data >> 51) & 1) == 0) { + pit_error(rt, "attempted to create a signalling NaN double"); + return PIT_NIL; + } + return data; + } + return + /* 0b1111111111110000000000000000000000000000000000000000000000000000 */ + 0xfff0000000000000 + /* | (((u64) (s & 0b11)) << 49) */ + | (((u64) (s & 3)) << 49) + /* | (data & 0b1111111111111111111111111111111111111111111111111); */ + | (data & 0x1ffffffffffff); +} + +bool pit_value_eq(pit_value a, pit_value b) { + return a == b; +} + +bool pit_value_equal(pit_runtime *rt, pit_value a, pit_value b) { + if (pit_value_sort(a) != pit_value_sort(b)) return false; + switch (pit_value_sort(a)) { + case PIT_VALUE_SORT_DOUBLE: + case PIT_VALUE_SORT_INTEGER: + case PIT_VALUE_SORT_SYMBOL: + return pit_value_data(a) == pit_value_data(b); + 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; } + pit_value_heavy *hb = pit_value_ref_deref(rt, pit_value_as_ref(rt, b)); + if (!hb) { pit_error(rt, "bad ref"); return false; } + if (ha->hsort != hb->hsort) return false; + switch (ha->hsort) { + case PIT_VALUE_HEAVY_SORT_CELL: + return pit_value_equal(rt, ha->in.cell, hb->in.cell); + case PIT_VALUE_HEAVY_SORT_CONS: + return pit_value_equal(rt, ha->in.cons.car, hb->in.cons.car) + && pit_value_equal(rt, ha->in.cons.cdr, hb->in.cons.cdr); + case PIT_VALUE_HEAVY_SORT_ARRAY: { + if (ha->in.array.len != hb->in.array.len) return false; + for (i64 i = 0; i < ha->in.array.len; ++i) { + if (!pit_value_equal(rt, ha->in.array.data[i], hb->in.array.data[i])) return false; + } + return true; + } + case PIT_VALUE_HEAVY_SORT_BYTES: { + if (ha->in.bytes.len != hb->in.bytes.len) return false; + for (i64 i = 0; i < ha->in.bytes.len; ++i) { + if (ha->in.bytes.data[i] != hb->in.bytes.data[i]) return false; + } + return true; + } + case PIT_VALUE_HEAVY_SORT_FUNC: + return + pit_value_equal(rt, ha->in.func.env, hb->in.func.env) + && pit_value_equal(rt, ha->in.func.args, hb->in.func.args) + && pit_value_equal(rt, ha->in.func.body, hb->in.func.body); + case PIT_VALUE_HEAVY_SORT_NATIVEFUNC: + return ha->in.nativefunc.f == hb->in.nativefunc.f + && ha->in.nativefunc.data == hb->in.nativefunc.data; + case PIT_VALUE_HEAVY_SORT_NATIVEDATA: + return + pit_value_eq(ha->in.nativedata.tag, hb->in.nativedata.tag) + && ha->in.nativedata.data == hb->in.nativedata.data; + case PIT_VALUE_HEAVY_SORT_FORWARDING_POINTER: + return ha->in.forwarding_pointer == hb->in.forwarding_pointer; + } + } + } + return false; +} 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 + +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 + +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 + +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 + +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 + +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 + +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 + +#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; +} -- cgit v1.2.3