Add seemingly functional but hacky gc
This commit is contained in:
@@ -35,6 +35,7 @@ struct ucl_object *ucl_evaluate_builtin_form(struct ucl_state *state, struct ucl
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
result = ucl_progn(fun_state, fun_forms);
|
result = ucl_progn(fun_state, fun_forms);
|
||||||
|
ucl_state_delete(fun_state);
|
||||||
} else {
|
} else {
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,15 @@
|
|||||||
item_name != NULL && iter_name != NULL && (cond); \
|
item_name != NULL && iter_name != NULL && (cond); \
|
||||||
iter_name = iter_name->cell.cdr, item_name = (iter_name == NULL) ? NULL : iter_name->cell.car)
|
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_cell_create(struct ucl_object *car, struct ucl_object *cdr);
|
||||||
struct ucl_object *ucl_int_create(int integer);
|
struct ucl_object *ucl_int_create(int integer);
|
||||||
struct ucl_object *ucl_symbol_create(const char* symbol);
|
struct ucl_object *ucl_symbol_create(const char* symbol);
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ int main(int argc, const char **argv) {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
free(line);
|
free(line);
|
||||||
|
ucl_gc();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct ucl_object *sexp = ucl_parse(argv[1]);
|
struct ucl_object *sexp = ucl_parse(argv[1]);
|
||||||
|
|||||||
72
src/memory.c
72
src/memory.c
@@ -1,12 +1,26 @@
|
|||||||
#include "uclisp.h"
|
#include "uclisp.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
#include "arena.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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_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 *ucl_cell_create(struct ucl_object *car, struct ucl_object *cdr)
|
||||||
{
|
{
|
||||||
struct ucl_object* obj = ucl_object_alloc();
|
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() {
|
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) {
|
void ucl_object_delete(struct ucl_object *obj) {
|
||||||
@@ -77,9 +96,9 @@ void ucl_object_delete(struct ucl_object *obj) {
|
|||||||
|
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
case UCL_TYPE_CELL:
|
case UCL_TYPE_CELL:
|
||||||
ucl_object_delete(obj->cell.car);
|
//ucl_object_delete(obj->cell.car);
|
||||||
obj->cell.car = NULL;
|
obj->cell.car = NULL;
|
||||||
ucl_object_delete(obj->cell.cdr);
|
//ucl_object_delete(obj->cell.cdr);
|
||||||
obj->cell.cdr = NULL;
|
obj->cell.cdr = NULL;
|
||||||
break;
|
break;
|
||||||
case UCL_TYPE_SYMBOL:
|
case UCL_TYPE_SYMBOL:
|
||||||
@@ -99,5 +118,50 @@ void ucl_object_delete(struct ucl_object *obj) {
|
|||||||
case UCL_TYPE_COUNT:
|
case UCL_TYPE_COUNT:
|
||||||
break;
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ struct ucl_object *ucl_special_let(struct ucl_state *state, struct ucl_object *a
|
|||||||
|
|
||||||
if (value->type == UCL_TYPE_ERROR) {
|
if (value->type == UCL_TYPE_ERROR) {
|
||||||
// TODO cleanup
|
// TODO cleanup
|
||||||
|
ucl_state_delete(let_state);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
ucl_state_put(let_state, sym->symbol, value);
|
ucl_state_put(let_state, sym->symbol, value);
|
||||||
|
|||||||
22
src/state.c
22
src/state.c
@@ -2,22 +2,24 @@
|
|||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
#include "arena.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
// Implements state as a giant alist
|
// Implements state as a giant alist
|
||||||
// TODO: Consider generalizing the alist concept
|
|
||||||
|
|
||||||
struct ucl_state {
|
// TODO: Consider generalizing the alist concept
|
||||||
struct ucl_object *list;
|
// TODO: Rename 'state' to 'scope'
|
||||||
struct ucl_state *parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NAME_POSITION 0
|
#define NAME_POSITION 0
|
||||||
#define DATA_POSITION 1
|
#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) {
|
static struct ucl_object *ucl_state_get_cell(struct ucl_state *state, const char *name) {
|
||||||
FOREACH_LIST(state->list, iter, item) {
|
FOREACH_LIST(state->list, iter, item) {
|
||||||
assert(item->type == UCL_TYPE_CELL);
|
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 *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->list = ucl_nil_create();
|
||||||
state->parent = NULL;
|
state->parent = NULL;
|
||||||
return state;
|
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) {
|
void ucl_state_delete(struct ucl_state *state) {
|
||||||
// TODO: Cleanup
|
assert(state_arena != NULL);
|
||||||
// ucl_object_delete(state->list);
|
ucl_arena_put(state_arena, state);
|
||||||
|
// Garbage collection will handle the objects, they are shared
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ struct ucl_state;
|
|||||||
typedef struct ucl_object *(*ucl_lisp)(struct ucl_state* state, struct ucl_object *args);
|
typedef struct ucl_object *(*ucl_lisp)(struct ucl_state* state, struct ucl_object *args);
|
||||||
|
|
||||||
struct ucl_object {
|
struct ucl_object {
|
||||||
enum ucl_type type;
|
|
||||||
union {
|
union {
|
||||||
struct ucl_cell cell;
|
struct ucl_cell cell;
|
||||||
const char *symbol;
|
const char *symbol;
|
||||||
@@ -31,6 +30,8 @@ struct ucl_object {
|
|||||||
ucl_lisp builtin;
|
ucl_lisp builtin;
|
||||||
ucl_lisp special;
|
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_parse(const char *sexp);
|
||||||
struct ucl_object *ucl_evaluate(struct ucl_state *state, struct ucl_object *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
|
#endif
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ struct ucl_object *ucl_list_nth(struct ucl_object *list, int n) {
|
|||||||
|
|
||||||
int length = ucl_list_length(list)->integer;
|
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(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;
|
struct ucl_object *node = list;
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ void tearDown(void) {
|
|||||||
// TODO: Implement GC so we can clean these both up
|
// TODO: Implement GC so we can clean these both up
|
||||||
//ucl_object_delete(input);
|
//ucl_object_delete(input);
|
||||||
input = NULL;
|
input = NULL;
|
||||||
ucl_object_delete(response);
|
ucl_state_delete(state);
|
||||||
|
ucl_gc();
|
||||||
response = NULL;
|
response = NULL;
|
||||||
state = NULL;
|
state = NULL;
|
||||||
}
|
}
|
||||||
@@ -150,15 +151,15 @@ void test_call_function(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void test_setq(void) {
|
void test_setq(void) {
|
||||||
response = eval("(setq bar 2)");
|
response = eval("(setq bar 123)");
|
||||||
|
|
||||||
TEST_ASSERT_OBJ_INT(response);
|
TEST_ASSERT_OBJ_INT(response);
|
||||||
TEST_ASSERT_EQUAL(response->symbol, 2);
|
TEST_ASSERT_EQUAL(response->symbol, 123);
|
||||||
|
|
||||||
response = eval("bar");
|
response = eval("bar");
|
||||||
|
|
||||||
TEST_ASSERT_OBJ_INT(response);
|
TEST_ASSERT_OBJ_INT(response);
|
||||||
TEST_ASSERT_EQUAL(response->symbol, 2);
|
TEST_ASSERT_EQUAL(response->symbol, 123);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_setq_from_function(void) {
|
void test_setq_from_function(void) {
|
||||||
@@ -238,6 +239,20 @@ void test_nth_oob(void) {
|
|||||||
TEST_ASSERT_OBJ_ERROR(response);
|
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) {
|
int main(void) {
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
@@ -266,7 +281,7 @@ int main(void) {
|
|||||||
RUN_TEST(test_nth_0);
|
RUN_TEST(test_nth_0);
|
||||||
RUN_TEST(test_nth_1);
|
RUN_TEST(test_nth_1);
|
||||||
RUN_TEST(test_nth_oob);
|
RUN_TEST(test_nth_oob);
|
||||||
|
RUN_TEST(test_eval_defun_gc);
|
||||||
|
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user