Improve memory efficiency via plain ints for length
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
#ifndef _UCLISP_H_
|
||||
#define _UCLISP_H_
|
||||
|
||||
struct ucl_scope;
|
||||
struct ucl_object;
|
||||
struct ucl;
|
||||
|
||||
@@ -11,6 +10,9 @@ void ucl_delete(struct ucl* state);
|
||||
struct ucl_object *ucl_tokenize(struct ucl* state, const char *source);
|
||||
struct ucl_object *ucl_parse(struct ucl* state, const char *sexp);
|
||||
struct ucl_object *ucl_evaluate(struct ucl* state, struct ucl_object *sexp);
|
||||
struct ucl_object *ucl_evaluate_progn(struct ucl* state, struct ucl_object *sexps);
|
||||
void ucl_print_obj(struct ucl_object *obj);
|
||||
|
||||
|
||||
void ucl_gc(struct ucl* ucl);
|
||||
|
||||
|
||||
19
src/arena.c
19
src/arena.c
@@ -4,12 +4,8 @@
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
struct ucl_arena {
|
||||
size_t object_size;
|
||||
size_t capacity;
|
||||
void *objects;
|
||||
int *used_map;
|
||||
};
|
||||
#include "arena.h"
|
||||
#include "types.h"
|
||||
|
||||
#define DIV_ROUND_UP(numerator, denominator) \
|
||||
(((numerator) + (denominator) - 1) / (denominator))
|
||||
@@ -24,6 +20,8 @@ struct ucl_arena *ucl_arena_create(size_t object_size, size_t capacity) {
|
||||
arena->capacity = capacity;
|
||||
arena->objects = malloc(capacity * object_size);
|
||||
arena->used_map = malloc(used_map_size);
|
||||
arena->stats.used = 0;
|
||||
arena->stats.freed = 0;
|
||||
|
||||
memset(arena->used_map, 0, used_map_size);
|
||||
|
||||
@@ -49,7 +47,6 @@ void ucl_arena_map(struct ucl_arena *arena, void (*map_function)(struct ucl_aren
|
||||
}
|
||||
}
|
||||
|
||||
int total_arena_gets = 0;
|
||||
|
||||
void *ucl_arena_get(struct ucl_arena *arena) {
|
||||
int used_map_ints = DIV_ROUND_UP(arena->capacity, INT_BITS);
|
||||
@@ -67,7 +64,7 @@ void *ucl_arena_get(struct ucl_arena *arena) {
|
||||
return NULL;
|
||||
}
|
||||
arena->used_map[i] |= 1 << bit_index;
|
||||
total_arena_gets++;
|
||||
arena->stats.used += 1;
|
||||
return (char *) arena->objects + (index * arena->object_size);
|
||||
}
|
||||
|
||||
@@ -77,7 +74,6 @@ void *ucl_arena_get(struct ucl_arena *arena) {
|
||||
int total_arena_puts = 0;
|
||||
|
||||
void ucl_arena_put(struct ucl_arena *arena, void *object) {
|
||||
|
||||
if (object == NULL) {
|
||||
return;
|
||||
}
|
||||
@@ -91,10 +87,9 @@ void ucl_arena_put(struct ucl_arena *arena, void *object) {
|
||||
assert(index < arena->capacity);
|
||||
|
||||
assert(arena->used_map[int_index] & (1 << bit_index));
|
||||
//if (arena->used_map[int_index] & (1 << bit_index)) {
|
||||
total_arena_puts++;
|
||||
//}
|
||||
|
||||
|
||||
arena->used_map[int_index] &= ~(1 << bit_index);
|
||||
arena->stats.used -= 1;
|
||||
arena->stats.freed += 1;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ LISP_FUNC_2(ucl_builtin_nth, state, scope, n, list) {
|
||||
UCL_COND_OR_RET_ERROR(n->type == UCL_TYPE_INT, "First argument to nth must be an integer");
|
||||
UCL_COND_OR_RET_ERROR(list->type == UCL_TYPE_CELL, "Second argument to nth must be a list");
|
||||
|
||||
return ucl_list_nth(state, list, n->integer);
|
||||
return ucl_list_nth(list, n->integer);
|
||||
}
|
||||
|
||||
LISP_FUNC_2(ucl_builtin_add, state, scope, arg0, arg1) {
|
||||
@@ -272,6 +272,30 @@ LISP_FUNC_2(ucl_builtin_append, state, scope, list, elem) {
|
||||
return list;
|
||||
}
|
||||
|
||||
LISP_FUNC_0(ucl_builtin_gc_obj_freed, state, scope) {
|
||||
return ucl_int_create(state, state->obj_arena->stats.freed);
|
||||
}
|
||||
|
||||
LISP_FUNC_0(ucl_builtin_gc_obj_used, state, scope) {
|
||||
return ucl_int_create(state, state->obj_arena->stats.used);
|
||||
}
|
||||
|
||||
LISP_FUNC_0(ucl_builtin_gc_obj_remaining, state, scope) {
|
||||
return ucl_int_create(state, state->obj_arena->capacity - state->obj_arena->stats.used);
|
||||
}
|
||||
|
||||
LISP_FUNC_0(ucl_builtin_gc_scope_freed, state, scope) {
|
||||
return ucl_int_create(state, state->scope_arena->stats.freed);
|
||||
}
|
||||
|
||||
LISP_FUNC_0(ucl_builtin_gc_scope_used, state, scope) {
|
||||
return ucl_int_create(state, state->scope_arena->stats.used);
|
||||
}
|
||||
|
||||
LISP_FUNC_0(ucl_builtin_gc_scope_remaining, state, scope) {
|
||||
return ucl_int_create(state, state->scope_arena->capacity - state->scope_arena->stats.used);
|
||||
}
|
||||
|
||||
void ucl_add_builtin(struct ucl* ucl, const char *name, ucl_lisp fun) {
|
||||
ucl_scope_put(ucl, ucl->global_scope, name, ucl_builtin_create(ucl, fun));
|
||||
}
|
||||
@@ -316,4 +340,11 @@ void ucl_add_builtins(struct ucl* state) {
|
||||
ucl_add_builtin(state, "reduce", ucl_builtin_reduce);
|
||||
ucl_add_builtin(state, "equal", ucl_builtin_equal);
|
||||
ucl_add_builtin(state, "append", ucl_builtin_append);
|
||||
|
||||
ucl_add_builtin(state, "gc-obj-used", ucl_builtin_gc_obj_used);
|
||||
ucl_add_builtin(state, "gc-obj-freed", ucl_builtin_gc_obj_freed);
|
||||
ucl_add_builtin(state, "gc-obj-remaining", ucl_builtin_gc_obj_remaining);
|
||||
ucl_add_builtin(state, "gc-scope-used", ucl_builtin_gc_scope_used);
|
||||
ucl_add_builtin(state, "gc-scope-freed", ucl_builtin_gc_scope_freed);
|
||||
ucl_add_builtin(state, "gc-scope-remaining", ucl_builtin_gc_scope_remaining);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ struct ucl_object *ucl_evaluate_builtin_form(struct ucl *state, struct ucl_scope
|
||||
struct ucl_object *fun_forms = ucl_cdr(state, fun);
|
||||
int i = 0;
|
||||
FOREACH_LIST(fun_arg_syms, iter, sym) {
|
||||
ucl_scope_put(state, fun_scope, sym->symbol, ucl_list_nth(state, evaluated_list, i + 1));
|
||||
ucl_scope_put(state, fun_scope, sym->symbol, ucl_list_nth(evaluated_list, i + 1));
|
||||
i++;
|
||||
}
|
||||
result = ucl_progn(state, fun_scope, fun_forms);
|
||||
@@ -98,3 +98,7 @@ struct ucl_object *ucl_evaluate_in_scope(struct ucl *state, struct ucl_scope *sc
|
||||
struct ucl_object *ucl_evaluate(struct ucl *state, struct ucl_object *obj) {
|
||||
return ucl_evaluate_in_scope(state, state->global_scope, obj);
|
||||
}
|
||||
|
||||
struct ucl_object *ucl_evaluate_progn(struct ucl *state, struct ucl_object *forms) {
|
||||
return ucl_progn(state, state->global_scope, forms);
|
||||
}
|
||||
|
||||
31
src/lisp.h
31
src/lisp.h
@@ -21,11 +21,8 @@ void ucl_add_special(struct ucl *state, const char *name, ucl_lisp fun);
|
||||
#define LISP_FUNC_1(func_name, ucl_name, scope_name, arg0_name) \
|
||||
static struct ucl_object *func_name##_impl(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *arg0_name); \
|
||||
struct ucl_object *func_name(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *args) { \
|
||||
struct ucl_object *len_obj = ucl_list_length(ucl, args); \
|
||||
if (len_obj->type != UCL_TYPE_INT) { \
|
||||
return NULL; \
|
||||
} \
|
||||
if (len_obj->integer != 1) { \
|
||||
int len = ucl_list_length_int(args); \
|
||||
if (len != 1) { \
|
||||
return NULL; \
|
||||
} \
|
||||
struct ucl_object *arg0 = ucl_car(ucl, args); \
|
||||
@@ -37,15 +34,12 @@ void ucl_add_special(struct ucl *state, const char *name, ucl_lisp fun);
|
||||
#define LISP_FUNC_2(func_name, ucl_name, scope_name, arg0_name, arg1_name) \
|
||||
static struct ucl_object *func_name##_impl(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *arg0_name, struct ucl_object *arg1_name); \
|
||||
struct ucl_object *func_name(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *args) { \
|
||||
struct ucl_object *len_obj = ucl_list_length(ucl, args); \
|
||||
if (len_obj->type != UCL_TYPE_INT) { \
|
||||
int len = ucl_list_length_int(args); \
|
||||
if (len != 2) { \
|
||||
return NULL; \
|
||||
} \
|
||||
if (len_obj->integer != 2) { \
|
||||
return NULL; \
|
||||
} \
|
||||
struct ucl_object *arg0 = ucl_list_nth(ucl, args, 0); \
|
||||
struct ucl_object *arg1 = ucl_list_nth(ucl, args, 1); \
|
||||
struct ucl_object *arg0 = ucl_list_nth(args, 0); \
|
||||
struct ucl_object *arg1 = ucl_list_nth(args, 1); \
|
||||
return func_name##_impl(ucl, scope, arg0, arg1); \
|
||||
} \
|
||||
static struct ucl_object *func_name##_impl(UNUSED struct ucl *ucl_name, UNUSED struct ucl_scope *scope, struct ucl_object *arg0_name, struct ucl_object *arg1_name)
|
||||
@@ -54,16 +48,13 @@ void ucl_add_special(struct ucl *state, const char *name, ucl_lisp fun);
|
||||
#define LISP_FUNC_3(func_name, ucl_name, scope_name, arg0_name, arg1_name, arg2_name) \
|
||||
static struct ucl_object *func_name##_impl(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *arg0_name, struct ucl_object *arg1_name, struct ucl_object *arg2_name); \
|
||||
struct ucl_object *func_name(struct ucl* ucl, struct ucl_scope *scope, struct ucl_object *args) { \
|
||||
struct ucl_object *len_obj = ucl_list_length(ucl, args); \
|
||||
if (len_obj->type != UCL_TYPE_INT) { \
|
||||
int len = ucl_list_length_int(args); \
|
||||
if (len != 3) { \
|
||||
return NULL; \
|
||||
} \
|
||||
if (len_obj->integer != 3) { \
|
||||
return NULL; \
|
||||
} \
|
||||
struct ucl_object *arg0 = ucl_list_nth(ucl, args, 0); \
|
||||
struct ucl_object *arg1 = ucl_list_nth(ucl, args, 1); \
|
||||
struct ucl_object *arg2 = ucl_list_nth(ucl, args, 2); \
|
||||
struct ucl_object *arg0 = ucl_list_nth(args, 0); \
|
||||
struct ucl_object *arg1 = ucl_list_nth(args, 1); \
|
||||
struct ucl_object *arg2 = ucl_list_nth(args, 2); \
|
||||
return func_name##_impl(ucl, scope, arg0, arg1, arg2); \
|
||||
} \
|
||||
static struct ucl_object *func_name##_impl(UNUSED struct ucl* ucl_name, UNUSED struct ucl_scope *scope_name, struct ucl_object *arg0_name, struct ucl_object *arg1_name, struct ucl_object *arg2_name)
|
||||
|
||||
10
src/memory.c
10
src/memory.c
@@ -79,6 +79,14 @@ struct ucl_object *ucl_special_create(struct ucl* ucl, ucl_lisp special)
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ucl_object_pin(struct ucl_object *obj) {
|
||||
obj->flags |= UCL_OBJ_FLAG_PINNED;
|
||||
}
|
||||
|
||||
void ucl_object_unpinned(struct ucl_object *obj) {
|
||||
obj->flags &= ~UCL_OBJ_FLAG_PINNED;
|
||||
}
|
||||
|
||||
|
||||
static struct ucl_object* ucl_object_alloc(struct ucl* ucl) {
|
||||
struct ucl_object *obj = ucl_arena_get(ucl->obj_arena);
|
||||
@@ -175,7 +183,7 @@ void ucl_gc(struct ucl* ucl) {
|
||||
struct ucl *ucl_create() {
|
||||
struct ucl *ucl = malloc(sizeof(*ucl));
|
||||
|
||||
ucl->obj_arena = ucl_arena_create(sizeof(struct ucl_object), 1 << 16);
|
||||
ucl->obj_arena = ucl_arena_create(sizeof(struct ucl_object), 4096);
|
||||
ucl->scope_arena = ucl_arena_create(sizeof(struct ucl_scope), 64);
|
||||
ucl->global_scope = ucl_scope_create(ucl);
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ struct ucl_object *ucl_error_create(struct ucl* ucl, const char* error);
|
||||
struct ucl_object *ucl_builtin_create(struct ucl* ucl, ucl_lisp builtin);
|
||||
struct ucl_object *ucl_special_create(struct ucl* ucl, ucl_lisp special);
|
||||
|
||||
void ucl_object_pin(struct ucl_object *obj);
|
||||
void ucl_object_unpin(struct ucl_object *obj);
|
||||
|
||||
void ucl_object_delete(struct ucl* ucl, struct ucl_object *obj);
|
||||
|
||||
#endif
|
||||
|
||||
10
src/scope.c
10
src/scope.c
@@ -16,10 +16,10 @@
|
||||
|
||||
#define SCOPE_ARENA_CAPACITY 32
|
||||
|
||||
static struct ucl_object *ucl_scope_get_cell(struct ucl *state, struct ucl_scope *scope, const char *name) {
|
||||
static struct ucl_object *ucl_scope_get_cell(struct ucl_scope *scope, const char *name) {
|
||||
FOREACH_LIST(scope->list, iter, item) {
|
||||
assert(item->type == UCL_TYPE_CELL);
|
||||
const char *item_name = ucl_list_nth(state, item, NAME_POSITION)->string;
|
||||
const char *item_name = ucl_list_nth(item, NAME_POSITION)->string;
|
||||
if (!strcmp(name, item_name)) {
|
||||
return item;
|
||||
}
|
||||
@@ -29,7 +29,7 @@ static struct ucl_object *ucl_scope_get_cell(struct ucl *state, struct ucl_scope
|
||||
}
|
||||
|
||||
struct ucl_object *ucl_scope_get(struct ucl *state, struct ucl_scope *scope, const char *name) {
|
||||
struct ucl_object *cell = ucl_scope_get_cell(state, scope, name);
|
||||
struct ucl_object *cell = ucl_scope_get_cell(scope, name);
|
||||
if (cell == NULL) {
|
||||
if (scope->parent == NULL) {
|
||||
// TODO: Include the symbol name
|
||||
@@ -38,11 +38,11 @@ struct ucl_object *ucl_scope_get(struct ucl *state, struct ucl_scope *scope, con
|
||||
return ucl_scope_get(state, scope->parent, name);
|
||||
}
|
||||
}
|
||||
return ucl_list_nth(state, cell, DATA_POSITION);
|
||||
return ucl_list_nth(cell, DATA_POSITION);
|
||||
}
|
||||
|
||||
void ucl_scope_put(struct ucl *state, struct ucl_scope *scope, const char *name, struct ucl_object *obj) {
|
||||
struct ucl_object *cell = ucl_scope_get_cell(state, scope, name);
|
||||
struct ucl_object *cell = ucl_scope_get_cell(scope, name);
|
||||
if (cell == NULL) {
|
||||
ucl_list_append(state,
|
||||
scope->list,
|
||||
|
||||
@@ -37,7 +37,7 @@ struct ucl_object *ucl_special_let(struct ucl *state, struct ucl_scope *scope, s
|
||||
struct ucl_object *ucl_special_if(struct ucl *state, struct ucl_scope *scope, struct ucl_object *args) {
|
||||
// TODO: Check arguments
|
||||
struct ucl_object *cond = ucl_car(state, args);
|
||||
struct ucl_object *true_form = ucl_list_nth(state, args, 1);
|
||||
struct ucl_object *true_form = ucl_list_nth(args, 1);
|
||||
struct ucl_object *false_forms = ucl_cdr(state, ucl_cdr(state, args));
|
||||
|
||||
struct ucl_object *cond_result = ucl_evaluate_in_scope(state, scope, cond);
|
||||
@@ -57,7 +57,7 @@ struct ucl_object *ucl_special_defun(struct ucl *state, struct ucl_scope *scope,
|
||||
return ucl_error_create(state, "First argument to defun must be a symbol");
|
||||
}
|
||||
|
||||
struct ucl_object *fun_args = ucl_list_nth(state, args, 1);
|
||||
struct ucl_object *fun_args = ucl_list_nth(args, 1);
|
||||
if (fun_args->type != UCL_TYPE_CELL) {
|
||||
// TODO: Check that the list contains only symbols
|
||||
return ucl_error_create(state, "Second argument to defun must be a list of symbols");
|
||||
@@ -75,7 +75,7 @@ struct ucl_object *ucl_special_defun(struct ucl *state, struct ucl_scope *scope,
|
||||
|
||||
struct ucl_object *ucl_special_lambda(struct ucl *state, UNUSED struct ucl_scope *scope, struct ucl_object *args) {
|
||||
// TODO: Check arguments
|
||||
struct ucl_object *fun_args = ucl_list_nth(state, args, 0);
|
||||
struct ucl_object *fun_args = ucl_list_nth(args, 0);
|
||||
if (fun_args->type != UCL_TYPE_CELL) {
|
||||
// TODO: Check that the list contains only symbols
|
||||
return ucl_error_create(state, "First argument to lambda must be a list of symbols");
|
||||
@@ -90,19 +90,27 @@ struct ucl_object *ucl_special_dotimes(struct ucl *state, struct ucl_scope *scop
|
||||
struct ucl_object *body = ucl_cdr(state, args);
|
||||
|
||||
struct ucl_object *var = ucl_car(state, assignment);
|
||||
struct ucl_object *times = ucl_evaluate_in_scope(state, scope, ucl_list_nth(state, assignment, 1));
|
||||
struct ucl_object *times = ucl_evaluate_in_scope(state, scope, ucl_list_nth(assignment, 1));
|
||||
|
||||
UCL_COND_OR_RET_ERROR(var->type == UCL_TYPE_SYMBOL, "'var' argument to dotimes must be an symbol");
|
||||
UCL_COND_OR_RET_ERROR(times->type == UCL_TYPE_INT, "'times' argument to dotimes must be an int");
|
||||
|
||||
struct ucl_scope *let_scope = ucl_scope_create_child(state, scope);
|
||||
|
||||
struct ucl_object *iter = ucl_int_create(state, 0);
|
||||
ucl_scope_put(state, let_scope, var->symbol, var);
|
||||
|
||||
for (int i = 0; i < times->integer; i++) {
|
||||
ucl_scope_put(state, let_scope, var->symbol, ucl_int_create(state, i));
|
||||
iter->integer = i;
|
||||
struct ucl_object *iterval = ucl_progn(state, let_scope, body);
|
||||
UCL_RET_IF_ERROR(iterval);
|
||||
// TODO: don't make this an error, just rebind?
|
||||
/* UCL_COND_OR_RET_ERROR(ucl_scope_get(state, let_scope, var->symbol)->type == UCL_TYPE_INT, */
|
||||
/* "dotimes iterator was re-bound to a non-integer type"); */
|
||||
}
|
||||
|
||||
// TODO: cleanup scopes on error
|
||||
ucl_scope_delete(state, let_scope);
|
||||
return ucl_nil_create(state);
|
||||
}
|
||||
|
||||
@@ -112,7 +120,7 @@ struct ucl_object *ucl_special_dolist(struct ucl *state, struct ucl_scope *scope
|
||||
struct ucl_object *body = ucl_cdr(state, args);
|
||||
|
||||
struct ucl_object *var = ucl_car(state, assignment);
|
||||
struct ucl_object *list = ucl_evaluate_in_scope(state, scope, ucl_list_nth(state, assignment, 1));
|
||||
struct ucl_object *list = ucl_evaluate_in_scope(state, scope, ucl_list_nth(assignment, 1));
|
||||
|
||||
UCL_COND_OR_RET_ERROR(var->type == UCL_TYPE_SYMBOL, "'var' argument to dolist must be an symbol");
|
||||
UCL_COND_OR_RET_ERROR(list->type == UCL_TYPE_CELL, "'list' argument to dolist must be a list");
|
||||
@@ -128,6 +136,8 @@ struct ucl_object *ucl_special_dolist(struct ucl *state, struct ucl_scope *scope
|
||||
UCL_RET_IF_ERROR(iterval);
|
||||
}
|
||||
|
||||
// TODO: cleanup scopes on error
|
||||
ucl_scope_delete(state, let_scope);
|
||||
return ucl_nil_create(state);
|
||||
}
|
||||
|
||||
@@ -155,7 +165,7 @@ struct ucl_object *ucl_special_setq(struct ucl *state, struct ucl_scope *scope,
|
||||
return ucl_error_create(state, "First argument to setq must be a symbol");
|
||||
}
|
||||
|
||||
struct ucl_object *value = ucl_evaluate_in_scope(state, scope, ucl_list_nth(state, args, 1));
|
||||
struct ucl_object *value = ucl_evaluate_in_scope(state, scope, ucl_list_nth(args, 1));
|
||||
UCL_RET_IF_ERROR(value);
|
||||
|
||||
ucl_scope_put(state, scope, sym->symbol, value);
|
||||
|
||||
14
src/types.h
14
src/types.h
@@ -1,6 +1,8 @@
|
||||
#ifndef _UCLISP_TYPES_H_
|
||||
#define _UCLISP_TYPES_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct ucl;
|
||||
struct ucl_scope;
|
||||
struct ucl_cell;
|
||||
@@ -48,6 +50,18 @@ struct ucl_scope {
|
||||
struct ucl_scope *parent;
|
||||
};
|
||||
|
||||
struct ucl_arena {
|
||||
size_t object_size;
|
||||
size_t capacity;
|
||||
void *objects;
|
||||
int *used_map;
|
||||
|
||||
struct {
|
||||
size_t used;
|
||||
size_t freed;
|
||||
} stats;
|
||||
};
|
||||
|
||||
struct ucl {
|
||||
struct ucl_scope *global_scope;
|
||||
|
||||
|
||||
@@ -52,31 +52,41 @@ struct ucl_object *ucl_predicate(struct ucl* state, bool value) {
|
||||
}
|
||||
}
|
||||
|
||||
struct ucl_object *ucl_list_length(struct ucl *state, struct ucl_object *list) {
|
||||
UCL_COND_OR_RET_ERROR(
|
||||
list != NULL && list->type == UCL_TYPE_CELL,
|
||||
"Invalid type of argument 0 to 'ucl_list_length'");
|
||||
|
||||
struct ucl_object *node = list;
|
||||
if (list->cell.car == NULL) {
|
||||
return ucl_int_create(state, 0);
|
||||
int ucl_list_length_int(struct ucl_object *list) {
|
||||
if (list == NULL || list->type != UCL_TYPE_CELL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (list->cell.car == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ucl_object *node = list;
|
||||
int length = 1;
|
||||
while (node->cell.cdr != NULL) {
|
||||
node = node->cell.cdr;
|
||||
length++;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
struct ucl_object *ucl_list_length(struct ucl *state, struct ucl_object *list) {
|
||||
int length = ucl_list_length_int(list);
|
||||
|
||||
UCL_COND_OR_RET_ERROR(
|
||||
length >= 0,
|
||||
"Invalid type of argument 0 to 'ucl_list_length'");
|
||||
|
||||
return ucl_int_create(state, length);
|
||||
}
|
||||
|
||||
struct ucl_object *ucl_list_nth(struct ucl *state, struct ucl_object *list, int n) {
|
||||
struct ucl_object *ucl_list_nth(struct ucl_object *list, int n) {
|
||||
UCL_COND_OR_RET_ERROR(
|
||||
list != NULL && list->type == UCL_TYPE_CELL,
|
||||
"Invalid type of argument 0 to 'ucl_list_nth'");
|
||||
|
||||
int length = ucl_list_length(state, list)->integer;
|
||||
int length = ucl_list_length_int(list);
|
||||
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");
|
||||
|
||||
|
||||
@@ -14,15 +14,14 @@ struct ucl_object *ucl_equal(struct ucl* state, struct ucl_object *arg0, struct
|
||||
struct ucl_object* ucl_car(struct ucl* state, struct ucl_object *list);
|
||||
struct ucl_object* ucl_cdr(struct ucl* state, struct ucl_object *list);
|
||||
|
||||
int ucl_list_length_int(struct ucl_object *list);
|
||||
struct ucl_object* ucl_list_length(struct ucl* state, struct ucl_object *list);
|
||||
struct ucl_object* ucl_list_nth(struct ucl* state, struct ucl_object *list, int n);
|
||||
struct ucl_object* ucl_list_nth(struct ucl_object *list, int n);
|
||||
struct ucl_object* ucl_list_append(struct ucl* state, struct ucl_object *list, struct ucl_object *obj);
|
||||
struct ucl_object* ucl_tuple_create(struct ucl* state, struct ucl_object *obj0, struct ucl_object *obj1);
|
||||
|
||||
struct ucl_object *ucl_progn(struct ucl* state, struct ucl_scope *scope, struct ucl_object *forms);
|
||||
|
||||
void ucl_print_obj(struct ucl_object *obj);
|
||||
|
||||
struct ucl_object* ucl_evaluate_in_scope(struct ucl *state, struct ucl_scope *scope, struct ucl_object* sexp);
|
||||
|
||||
void ucl_add_builtins(struct ucl *state);
|
||||
|
||||
@@ -225,6 +225,36 @@ void test_complex(void) {
|
||||
TEST_ASSERT_OBJ_INT_V(response, 9);
|
||||
}
|
||||
|
||||
void test_memory_perf_low(void) {
|
||||
response = eval("(defun fib (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))");
|
||||
|
||||
TEST_ASSERT_OBJ_SYMBOL_V(response, "fib");
|
||||
|
||||
response = eval("(fib 4)");
|
||||
|
||||
TEST_ASSERT_OBJ_INT_V(response, 3);
|
||||
}
|
||||
|
||||
void test_memory_perf_medium(void) {
|
||||
response = eval("(defun fib (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))");
|
||||
|
||||
TEST_ASSERT_OBJ_SYMBOL_V(response, "fib");
|
||||
|
||||
response = eval("(fib 10)");
|
||||
|
||||
TEST_ASSERT_OBJ_INT_V(response, 55);
|
||||
}
|
||||
|
||||
void test_memory_perf_high(void) {
|
||||
response = eval("(defun fib (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))");
|
||||
|
||||
TEST_ASSERT_OBJ_SYMBOL_V(response, "fib");
|
||||
|
||||
response = eval("(fib 8)");
|
||||
|
||||
TEST_ASSERT_OBJ_INT_V(response, 21);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
UNITY_BEGIN();
|
||||
|
||||
@@ -254,6 +284,9 @@ int main(void) {
|
||||
RUN_TEST(test_nth_oob);
|
||||
RUN_TEST(test_eval_defun_gc);
|
||||
RUN_TEST(test_complex);
|
||||
RUN_TEST(test_memory_perf_low);
|
||||
RUN_TEST(test_memory_perf_medium);
|
||||
RUN_TEST(test_memory_perf_high);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
@@ -50,11 +50,11 @@ static void test_put_modify_get(void) {
|
||||
ucl_list_append(state, obj, ucl_string_create(state, "quux"));
|
||||
response = ucl_scope_get(state, scope, "foo");
|
||||
|
||||
TEST_ASSERT_OBJ_STRING(ucl_list_nth(state, response, 2));
|
||||
TEST_ASSERT_EQUAL_STRING(ucl_list_nth(state, response, 2)->string, "quux");
|
||||
TEST_ASSERT_OBJ_STRING(ucl_list_nth(response, 2));
|
||||
TEST_ASSERT_EQUAL_STRING(ucl_list_nth(response, 2)->string, "quux");
|
||||
|
||||
TEST_ASSERT_OBJ_STRING(ucl_list_nth(state, obj, 2));
|
||||
TEST_ASSERT_EQUAL_STRING(ucl_list_nth(state, obj, 2)->string, "quux");
|
||||
TEST_ASSERT_OBJ_STRING(ucl_list_nth(obj, 2));
|
||||
TEST_ASSERT_EQUAL_STRING(ucl_list_nth(obj, 2)->string, "quux");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -134,20 +134,19 @@ static void test_list_append_list() {
|
||||
}
|
||||
|
||||
static void test_list_nth_nil_0() {
|
||||
response = ucl_list_nth(state, ucl_nil_create(state), 0);
|
||||
response = ucl_list_nth(ucl_nil_create(state), 0);
|
||||
|
||||
TEST_ASSERT_OBJ_ERROR(response);
|
||||
}
|
||||
|
||||
static void test_list_nth_nil_1() {
|
||||
response = ucl_list_nth(state, ucl_nil_create(state), 1);
|
||||
response = ucl_list_nth(ucl_nil_create(state), 1);
|
||||
|
||||
TEST_ASSERT_OBJ_ERROR(response);
|
||||
}
|
||||
|
||||
static void test_list_nth_list_0() {
|
||||
response = ucl_list_nth(
|
||||
state,
|
||||
ucl_tuple_create(state,
|
||||
ucl_t_create(state),
|
||||
ucl_string_create(state, "foo")),
|
||||
@@ -158,7 +157,6 @@ static void test_list_nth_list_0() {
|
||||
|
||||
static void test_list_nth_list_1() {
|
||||
response = ucl_list_nth(
|
||||
state,
|
||||
ucl_tuple_create(state,
|
||||
ucl_t_create(state),
|
||||
ucl_string_create(state, "foo")),
|
||||
@@ -168,20 +166,19 @@ static void test_list_nth_list_1() {
|
||||
}
|
||||
|
||||
static void test_list_nth_t_0() {
|
||||
response = ucl_list_nth(state, ucl_t_create(state), 1);
|
||||
response = ucl_list_nth(ucl_t_create(state), 1);
|
||||
|
||||
TEST_ASSERT_OBJ_ERROR(response);
|
||||
}
|
||||
|
||||
static void test_list_nth_bounds_0() {
|
||||
response = ucl_list_nth(state, ucl_nil_create(state), 0);
|
||||
response = ucl_list_nth(ucl_nil_create(state), 0);
|
||||
|
||||
TEST_ASSERT_OBJ_ERROR(response);
|
||||
}
|
||||
|
||||
static void test_list_nth_bounds_1() {
|
||||
response = ucl_list_nth(
|
||||
state,
|
||||
ucl_cell_create(state,
|
||||
ucl_nil_create(state),
|
||||
NULL),
|
||||
@@ -192,7 +189,6 @@ static void test_list_nth_bounds_1() {
|
||||
|
||||
static void test_list_nth_bounds_2() {
|
||||
response = ucl_list_nth(
|
||||
state,
|
||||
ucl_tuple_create(state,
|
||||
ucl_nil_create(state),
|
||||
ucl_nil_create(state)),
|
||||
|
||||
@@ -40,21 +40,24 @@
|
||||
} while (0)
|
||||
|
||||
#define TEST_ASSERT_OBJ_LIST(obj) \
|
||||
TEST_ASSERT_EQUAL_MESSAGE(UCL_TYPE_CELL, obj->type, "Expected cell type")
|
||||
TEST_ASSERT_EQUAL_MESSAGE(UCL_TYPE_CELL, (obj)->type, "Expected cell type")
|
||||
|
||||
#define TEST_ASSERT_LIST_LEN(list, len) \
|
||||
do { \
|
||||
TEST_ASSERT_OBJ_LIST(list); \
|
||||
TEST_ASSERT_EQUAL(len, ucl_list_length(state, list)->integer); \
|
||||
TEST_ASSERT_EQUAL_MESSAGE(len, ucl_list_length(state, list)->integer, "Unexpected list length"); \
|
||||
} while(0)
|
||||
|
||||
#define TEST_ASSERT_NIL(obj) \
|
||||
TEST_ASSERT_LIST_LEN(obj, 0)
|
||||
do { \
|
||||
TEST_ASSERT_OBJ_LIST(obj); \
|
||||
TEST_ASSERT_LIST_LEN(obj, 0); \
|
||||
} while(0)
|
||||
|
||||
#define TEST_ASSERT_T(obj) \
|
||||
do { \
|
||||
TEST_ASSERT_OBJ_SYMBOL(obj); \
|
||||
TEST_ASSERT_EQUAL_STRING(obj->symbol, "t"); \
|
||||
TEST_ASSERT_EQUAL_STRING((obj)->symbol, "t"); \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user