Improve memory efficiency via plain ints for length

This commit is contained in:
2022-11-29 20:57:50 -05:00
parent d965ca142d
commit 13704fae2c
16 changed files with 183 additions and 84 deletions

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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

View File

@@ -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,

View File

@@ -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);

View File

@@ -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;

View File

@@ -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");

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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");
}

View File

@@ -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)),

View File

@@ -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