Add seemingly functional but hacky gc

This commit is contained in:
2022-11-14 22:43:49 -05:00
parent fbc1055659
commit 706b4a586d
9 changed files with 120 additions and 18 deletions

View File

@@ -35,6 +35,7 @@ struct ucl_object *ucl_evaluate_builtin_form(struct ucl_state *state, struct ucl
i++;
}
result = ucl_progn(fun_state, fun_forms);
ucl_state_delete(fun_state);
} else {
assert(0);
}

View File

@@ -13,6 +13,15 @@
item_name != NULL && iter_name != NULL && (cond); \
iter_name = iter_name->cell.cdr, item_name = (iter_name == NULL) ? NULL : iter_name->cell.car)
// TODO: Refactor this struct's location
struct ucl_state {
// TODO: For garbage collection, we need references from the parent->child state
struct ucl_object *list;
struct ucl_state *parent;
};
extern struct ucl_arena *state_arena;
struct ucl_object *ucl_cell_create(struct ucl_object *car, struct ucl_object *cdr);
struct ucl_object *ucl_int_create(int integer);
struct ucl_object *ucl_symbol_create(const char* symbol);

View File

@@ -85,6 +85,7 @@ int main(int argc, const char **argv) {
printf("\n");
free(line);
ucl_gc();
}
} else {
struct ucl_object *sexp = ucl_parse(argv[1]);

View File

@@ -1,12 +1,26 @@
#include "uclisp.h"
#include "internal.h"
#include "arena.h"
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#ifndef UCL_STATE_ARENA_SIZE
#define UCL_STATE_ARENA_SIZE 16
#endif
#ifndef UCL_OBJECT_ARENA_SIZE
#define UCL_OBJECT_ARENA_SIZE (1 << 14)
#endif
static struct ucl_object *ucl_object_alloc();
static struct ucl_arena *object_arena = NULL;
struct ucl_object *ucl_cell_create(struct ucl_object *car, struct ucl_object *cdr)
{
struct ucl_object* obj = ucl_object_alloc();
@@ -67,7 +81,12 @@ struct ucl_object *ucl_special_create(ucl_lisp special)
static struct ucl_object* ucl_object_alloc() {
return malloc(sizeof(struct ucl_object));
if (object_arena == NULL) {
object_arena = ucl_arena_create(sizeof(struct ucl_object), UCL_OBJECT_ARENA_SIZE);
}
struct ucl_object *obj = ucl_arena_get(object_arena);
assert(obj != NULL);
return obj;
}
void ucl_object_delete(struct ucl_object *obj) {
@@ -77,9 +96,9 @@ void ucl_object_delete(struct ucl_object *obj) {
switch (obj->type) {
case UCL_TYPE_CELL:
ucl_object_delete(obj->cell.car);
//ucl_object_delete(obj->cell.car);
obj->cell.car = NULL;
ucl_object_delete(obj->cell.cdr);
//ucl_object_delete(obj->cell.cdr);
obj->cell.cdr = NULL;
break;
case UCL_TYPE_SYMBOL:
@@ -99,5 +118,50 @@ void ucl_object_delete(struct ucl_object *obj) {
case UCL_TYPE_COUNT:
break;
}
free(obj);
ucl_arena_put(object_arena, obj);
}
void ucl_object_mark(struct ucl_object *obj) {
if (obj == NULL || obj->reachable) {
return;
}
obj->reachable = 1;
if (obj->type == UCL_TYPE_CELL) {
ucl_object_mark(obj->cell.car);
ucl_object_mark(obj->cell.cdr);
}
}
void ucl_state_mark(struct ucl_arena * arena, void *obj) {
(void) arena;
struct ucl_state *state = (struct ucl_state *) obj;
ucl_object_mark(state->list);
}
void ucl_gc_unmark(struct ucl_arena * arena, void *obj) {
(void) arena;
struct ucl_object *object = (struct ucl_object *) obj;
object->reachable = 0;
}
void ucl_gc_sweep(struct ucl_arena * arena, void *obj) {
(void) arena;
struct ucl_object *object = (struct ucl_object *) obj;
if (object->reachable == 0) {
// TODO: Mapping across this is broken, since this is a recursive delete
ucl_object_delete(object);
}
}
void ucl_gc() {
ucl_arena_map(object_arena, ucl_gc_unmark);
ucl_arena_map(state_arena, ucl_state_mark);
ucl_arena_map(object_arena, ucl_gc_sweep);
}

View File

@@ -22,6 +22,7 @@ struct ucl_object *ucl_special_let(struct ucl_state *state, struct ucl_object *a
if (value->type == UCL_TYPE_ERROR) {
// TODO cleanup
ucl_state_delete(let_state);
return value;
}
ucl_state_put(let_state, sym->symbol, value);

View File

@@ -2,22 +2,24 @@
#include "internal.h"
#include "state.h"
#include "utility.h"
#include "arena.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
// Implements state as a giant alist
// TODO: Consider generalizing the alist concept
struct ucl_state {
struct ucl_object *list;
struct ucl_state *parent;
};
// TODO: Consider generalizing the alist concept
// TODO: Rename 'state' to 'scope'
#define NAME_POSITION 0
#define DATA_POSITION 1
#define STATE_ARENA_CAPACITY 32
struct ucl_arena *state_arena;
static struct ucl_object *ucl_state_get_cell(struct ucl_state *state, const char *name) {
FOREACH_LIST(state->list, iter, item) {
assert(item->type == UCL_TYPE_CELL);
@@ -57,7 +59,10 @@ void ucl_state_put(struct ucl_state *state, const char *name, struct ucl_object
}
struct ucl_state *ucl_state_create() {
struct ucl_state *state = malloc(sizeof(struct ucl_state));
if (state_arena == NULL) {
state_arena = ucl_arena_create(sizeof(struct ucl_state), STATE_ARENA_CAPACITY);
}
struct ucl_state *state = ucl_arena_get(state_arena);
state->list = ucl_nil_create();
state->parent = NULL;
return state;
@@ -77,6 +82,7 @@ struct ucl_state *ucl_state_get_root(struct ucl_state *state) {
}
void ucl_state_delete(struct ucl_state *state) {
// TODO: Cleanup
// ucl_object_delete(state->list);
assert(state_arena != NULL);
ucl_arena_put(state_arena, state);
// Garbage collection will handle the objects, they are shared
}

View File

@@ -21,7 +21,6 @@ struct ucl_state;
typedef struct ucl_object *(*ucl_lisp)(struct ucl_state* state, struct ucl_object *args);
struct ucl_object {
enum ucl_type type;
union {
struct ucl_cell cell;
const char *symbol;
@@ -31,6 +30,8 @@ struct ucl_object {
ucl_lisp builtin;
ucl_lisp special;
};
enum ucl_type type;
char reachable;
};
@@ -46,4 +47,7 @@ struct ucl_object *ucl_tokenize(const char *source);
struct ucl_object *ucl_parse(const char *sexp);
struct ucl_object *ucl_evaluate(struct ucl_state *state, struct ucl_object *sexp);
// TODO: State encapsulation is all wonky here)
void ucl_gc();
#endif

View File

@@ -77,6 +77,7 @@ struct ucl_object *ucl_list_nth(struct ucl_object *list, int n) {
int length = ucl_list_length(list)->integer;
UCL_COND_OR_RET_ERROR(length > n, "Position n >= list length in ucl_list_nth");
UCL_COND_OR_RET_ERROR(n >= 0, "Index to ucl_list_nth was less that zero");
struct ucl_object *node = list;
for (int i = 0; i < n; i++) {

View File

@@ -37,7 +37,8 @@ void tearDown(void) {
// TODO: Implement GC so we can clean these both up
//ucl_object_delete(input);
input = NULL;
ucl_object_delete(response);
ucl_state_delete(state);
ucl_gc();
response = NULL;
state = NULL;
}
@@ -150,15 +151,15 @@ void test_call_function(void) {
}
void test_setq(void) {
response = eval("(setq bar 2)");
response = eval("(setq bar 123)");
TEST_ASSERT_OBJ_INT(response);
TEST_ASSERT_EQUAL(response->symbol, 2);
TEST_ASSERT_EQUAL(response->symbol, 123);
response = eval("bar");
TEST_ASSERT_OBJ_INT(response);
TEST_ASSERT_EQUAL(response->symbol, 2);
TEST_ASSERT_EQUAL(response->symbol, 123);
}
void test_setq_from_function(void) {
@@ -238,6 +239,20 @@ void test_nth_oob(void) {
TEST_ASSERT_OBJ_ERROR(response);
}
void test_eval_defun_gc(void) {
response = eval("(defun foo (a b) (+ a b))");
TEST_ASSERT_OBJ_SYMBOL(response);
TEST_ASSERT_EQUAL_STRING(response->symbol, "foo");
ucl_gc();
response = eval("(foo 10 15)");
TEST_ASSERT_OBJ_INT(response);
TEST_ASSERT_EQUAL(response->integer, 25);
}
int main(void) {
UNITY_BEGIN();
@@ -266,7 +281,7 @@ int main(void) {
RUN_TEST(test_nth_0);
RUN_TEST(test_nth_1);
RUN_TEST(test_nth_oob);
RUN_TEST(test_eval_defun_gc);
return UNITY_END();
}