diff --git a/src/evaluate.c b/src/evaluate.c index f611fc9..e3e057d 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -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); } diff --git a/src/internal.h b/src/internal.h index 42db698..35a28fe 100644 --- a/src/internal.h +++ b/src/internal.h @@ -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); diff --git a/src/main.c b/src/main.c index 5e8e670..426bfbc 100644 --- a/src/main.c +++ b/src/main.c @@ -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]); diff --git a/src/memory.c b/src/memory.c index 7adb695..14170bb 100644 --- a/src/memory.c +++ b/src/memory.c @@ -1,12 +1,26 @@ #include "uclisp.h" #include "internal.h" +#include "arena.h" +#include #include #include #include +#include +#include + +#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); } diff --git a/src/special.c b/src/special.c index f528096..63655d3 100644 --- a/src/special.c +++ b/src/special.c @@ -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); diff --git a/src/state.c b/src/state.c index 8b2bb55..287685c 100644 --- a/src/state.c +++ b/src/state.c @@ -2,22 +2,24 @@ #include "internal.h" #include "state.h" #include "utility.h" +#include "arena.h" #include #include #include // 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 } diff --git a/src/uclisp.h b/src/uclisp.h index 9a24014..e92fa53 100644 --- a/src/uclisp.h +++ b/src/uclisp.h @@ -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 diff --git a/src/utility.c b/src/utility.c index 75291fd..90b3249 100644 --- a/src/utility.c +++ b/src/utility.c @@ -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++) { diff --git a/test/test_e2e.c b/test/test_e2e.c index 76974ec..0ba8250 100644 --- a/test/test_e2e.c +++ b/test/test_e2e.c @@ -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(); }