From a093fb0b9c954e51b176ca91b622151ffb5986a0 Mon Sep 17 00:00:00 2001 From: Max Regan Date: Wed, 16 Nov 2022 22:55:06 -0500 Subject: [PATCH] Add reduce and filter --- src/builtins.c | 28 +++++++++++++++++++++++++++- src/builtins.h | 2 ++ src/lisp.h | 19 +++++++++++++++++++ src/main.c | 6 ++---- test/test_e2e.c | 14 ++++++++++++++ 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index c662e37..6663002 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -196,6 +196,33 @@ LISP_FUNC_2(ucl_builtin_mapcar, scope, fun, elems) { return result; } +LISP_FUNC_2(ucl_builtin_filter, scope, predicate, elems) { + // TODO: Support arbitrary number of 'elems' lists + struct ucl_object *result = ucl_nil_create(); + struct ucl_object *result_tail = result; + FOREACH_LIST(elems, iter, elem) { + struct ucl_object *form = ucl_tuple_create(predicate, elem); + struct ucl_object *value = ucl_evaluate(scope, form); + UCL_RET_IF_ERROR(value); + if (ucl_truthy_bool(value)) { + result_tail = ucl_list_append(result_tail, elem); + } + } + return result; +} + +LISP_FUNC_3(ucl_builtin_reduce, scope, fun, elems, initial_value) { + // TODO: Support arbitrary number of 'elems' lists + struct ucl_object *result = initial_value; + FOREACH_LIST(elems, iter, elem) { + struct ucl_object *form = ucl_tuple_create(fun, elem); + ucl_list_append(form, result); + result = ucl_evaluate(scope, form); + UCL_RET_IF_ERROR(result); + } + return result; +} + LISP_FUNC_2(ucl_builtin_equal, scope, arg0, arg1) { return ucl_equal(arg0, arg1); } @@ -212,7 +239,6 @@ LISP_FUNC_2(ucl_builtin_ge, scope, arg0, arg1) { return ucl_predicate(arg0->integer > arg1->integer); } - LISP_FUNC_2(ucl_builtin_lt, scope, arg0, arg1) { UCL_COND_OR_RET_ERROR(arg0->type == UCL_TYPE_INT, "First argument to < must be an integer"); UCL_COND_OR_RET_ERROR(arg0->type == UCL_TYPE_INT, "Second argument to < must be an integer"); diff --git a/src/builtins.h b/src/builtins.h index 05778b3..98dab81 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -32,6 +32,8 @@ struct ucl_object *ucl_builtin_cdr(struct ucl_scope *scope, struct ucl_object *a struct ucl_object *ucl_builtin_nth(struct ucl_scope *scope, struct ucl_object *args); struct ucl_object *ucl_builtin_list(struct ucl_scope *scope, struct ucl_object *args); struct ucl_object *ucl_builtin_mapcar(struct ucl_scope *scope, struct ucl_object *args); +struct ucl_object *ucl_builtin_filter(struct ucl_scope *scope, struct ucl_object *args); +struct ucl_object *ucl_builtin_reduce(struct ucl_scope *scope, struct ucl_object *args); struct ucl_object *ucl_builtin_append(struct ucl_scope *scope, struct ucl_object *args); struct ucl_object *ucl_builtin_print(struct ucl_scope *scope, struct ucl_object *args); diff --git a/src/lisp.h b/src/lisp.h index 3ea30ed..f2a279a 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -46,4 +46,23 @@ } \ static struct ucl_object *func_name##_impl(struct ucl_scope *scope, struct ucl_object *arg0_name, struct ucl_object *arg1_name) +// TODO: Unroll the args more efficiently, this is O(n^2) +#define LISP_FUNC_3(func_name, scope_name, arg0_name, arg1_name, arg2_name) \ + static struct ucl_object *func_name##_impl(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_scope *scope, struct ucl_object *args) { \ + struct ucl_object *len_obj = ucl_list_length(args); \ + if (len_obj->type != UCL_TYPE_INT) { \ + return NULL; \ + } \ + if (len_obj->integer != 3) { \ + return NULL; \ + } \ + 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(scope_name, arg0, arg1, arg2); \ + } \ + static struct ucl_object *func_name##_impl(struct ucl_scope *scope, struct ucl_object *arg0_name, struct ucl_object *arg1_name, struct ucl_object *arg2_name) + + #endif diff --git a/src/main.c b/src/main.c index eefbdcc..9fe7121 100644 --- a/src/main.c +++ b/src/main.c @@ -60,6 +60,8 @@ int main(int argc, const char **argv) { ucl_scope_put(scope, "cdr", ucl_builtin_create(ucl_builtin_cdr)); ucl_scope_put(scope, "nth", ucl_builtin_create(ucl_builtin_nth)); ucl_scope_put(scope, "mapcar", ucl_builtin_create(ucl_builtin_mapcar)); + ucl_scope_put(scope, "filter", ucl_builtin_create(ucl_builtin_filter)); + ucl_scope_put(scope, "reduce", ucl_builtin_create(ucl_builtin_reduce)); ucl_scope_put(scope, "equal", ucl_builtin_create(ucl_builtin_equal)); ucl_scope_put(scope, "append", ucl_builtin_create(ucl_builtin_append)); @@ -67,10 +69,6 @@ int main(int argc, const char **argv) { ucl_scope_put(scope, "dolist", ucl_special_create(ucl_special_dolist)); ucl_scope_put(scope, "while", ucl_special_create(ucl_special_while)); - // TODO: - // - reduce - // - filter - if (argc < 2) { while (1) { char *line = readline("> "); diff --git a/test/test_e2e.c b/test/test_e2e.c index 51b660f..c3dc701 100644 --- a/test/test_e2e.c +++ b/test/test_e2e.c @@ -29,8 +29,11 @@ void setUp(void) { ucl_scope_put(scope, "cdr", ucl_builtin_create(ucl_builtin_cdr)); ucl_scope_put(scope, "nth", ucl_builtin_create(ucl_builtin_nth)); ucl_scope_put(scope, "mapcar", ucl_builtin_create(ucl_builtin_mapcar)); + ucl_scope_put(scope, "filter", ucl_builtin_create(ucl_builtin_filter)); + ucl_scope_put(scope, "reduce", ucl_builtin_create(ucl_builtin_reduce)); ucl_scope_put(scope, "lambda", ucl_special_create(ucl_special_lambda)); ucl_scope_put(scope, "quote", ucl_special_create(ucl_special_quote)); + ucl_scope_put(scope, "%", ucl_builtin_create(ucl_builtin_mod)); } void tearDown(void) { @@ -226,6 +229,16 @@ void test_eval_defun_gc(void) { TEST_ASSERT_OBJ_INT_V(response, 25); } +void test_complex(void) { + response = eval("(defun sum (elems) (reduce (quote +) elems 0))"); + + TEST_ASSERT_OBJ_SYMBOL_V(response, "sum"); + + response = eval("(sum (filter (quote (lambda (x) (% x 2))) (list 1 2 3 4 5)))"); + + TEST_ASSERT_OBJ_INT_V(response, 9); +} + int main(void) { UNITY_BEGIN(); @@ -254,6 +267,7 @@ int main(void) { RUN_TEST(test_nth_1); RUN_TEST(test_nth_oob); RUN_TEST(test_eval_defun_gc); + RUN_TEST(test_complex); return UNITY_END(); }