From d81d8c5156b52a145f02293b5b5e5b84b24e70e8 Mon Sep 17 00:00:00 2001 From: Max Regan Date: Thu, 27 Oct 2022 16:51:21 -0400 Subject: [PATCH] Add lots of utilties, initial builtins --- SConstruct | 4 +- run_tests.sh.in | 6 +- src/builtins.c | 142 ++++++++++++++++++++++++++ src/builtins.h | 60 +++++++++++ src/evaluate.c | 29 ++++++ src/memory.c | 2 - src/nihilispm.h | 2 +- src/nihilispm_internal.h | 10 +- src/nihilispm_utility.h | 30 ++++++ src/utility.c | 113 ++++++++++++++++++++ test/test_utility.c | 215 +++++++++++++++++++++++++++++++++++++++ 11 files changed, 603 insertions(+), 10 deletions(-) create mode 100644 src/builtins.c create mode 100644 src/builtins.h create mode 100644 src/evaluate.c create mode 100644 src/nihilispm_utility.h create mode 100644 src/utility.c create mode 100644 test/test_utility.c diff --git a/SConstruct b/SConstruct index 1b20c83..8b8e6f4 100644 --- a/SConstruct +++ b/SConstruct @@ -8,10 +8,10 @@ build_dir = "build/default/" src_dir = "src/" program_sources = ["src/main.c"] -lib_srcs = ["src/parse.c", "src/memory.c"] +lib_srcs = ["src/parse.c", "src/memory.c", "src/builtins.c", "src/evaluate.c", "src/utility.c"] lib_includes = ["src/"] -test_srcs = ["test/test_parse.c"] +test_srcs = ["test/test_parse.c", "test/test_utility.c"] test_lib_srcs = ["third-party/unity/src/unity.c"] test_lib_includes = ["third-party/unity/src/"] diff --git a/run_tests.sh.in b/run_tests.sh.in index 6ae5cfb..d9a1be9 100755 --- a/run_tests.sh.in +++ b/run_tests.sh.in @@ -1,7 +1,11 @@ #!/bin/bash -TESTS=@tests@ +TESTS="@tests@" for test in $TESTS; do echo =========================== echo Test: $test $test + ret=$? + if [ $? -ne 0 ] ; then + exit $ret + fi done diff --git a/src/builtins.c b/src/builtins.c new file mode 100644 index 0000000..f794ec7 --- /dev/null +++ b/src/builtins.c @@ -0,0 +1,142 @@ +#include "nihilispm.h" +#include "nihilispm_internal.h" +#include "nihilispm_utility.h" +#include "builtins.h" + +#include +#include +#include +#include + +LISP_FUNC_0(nl_builtin_hello_world) { + return nl_string_create(strdup("Hello, world!")); +} + +LISP_FUNC_1(nl_builtin_type, arg) { + switch (arg->type) { + case NL_TYPE_CELL: + return nl_symbol_create(strdup("list")); + case NL_TYPE_SYMBOL: + return nl_symbol_create(strdup("symbol")); + case NL_TYPE_INT: + return nl_symbol_create(strdup("int")); + case NL_TYPE_STRING: + return nl_symbol_create(strdup("string")); + case NL_TYPE_COUNT: + assert(0); + return NULL; + } +} + +LISP_FUNC_1(nl_builtin_symbol_p, arg) { + return nl_predicate(arg->type == NL_TYPE_SYMBOL); +} + +LISP_FUNC_1(nl_builtin_string_p, arg) { + return nl_predicate(arg->type == NL_TYPE_STRING); +} + +LISP_FUNC_1(nl_builtin_int_p, arg) { + return nl_predicate(arg->type == NL_TYPE_INT); +} + +LISP_FUNC_1(nl_builtin_list_p, arg) { + return nl_predicate(arg->type == NL_TYPE_CELL); +} + +LISP_FUNC_1(nl_builtin_car, arg) { + return nl_car(arg); +} + +LISP_FUNC_1(nl_builtin_cdr, arg) { + return nl_cdr(arg); +} + +LISP_FUNC_2(nl_builtin_add, arg0, arg1) { + // TODO: Return an error type + if (arg0->type != arg1->type) { + return NULL; + } + + if (arg0->type != NL_TYPE_INT) { + return NULL; + } + + return nl_int_create(arg0->integer + arg1->integer); +} + +LISP_FUNC_2(nl_builtin_sub, arg0, arg1) { + // TODO: Return an error type + if (arg0->type != arg1->type) { + return NULL; + } + + if (arg0->type != NL_TYPE_INT) { + return NULL; + } + + return nl_int_create(arg0->integer - arg1->integer); +} + +LISP_FUNC_2(nl_builtin_mul, arg0, arg1) { + // TODO: Return an error type + if (arg0->type != arg1->type) { + return NULL; + } + + if (arg0->type != NL_TYPE_INT) { + return NULL; + } + + return nl_int_create(arg0->integer * arg1->integer); +} + +LISP_FUNC_2(nl_builtin_div, arg0, arg1) { + // TODO: Return an error type + if (arg0->type != arg1->type) { + return NULL; + } + + if (arg0->type != NL_TYPE_INT) { + return NULL; + } + + return nl_int_create(arg0->integer / arg1->integer); +} + +LISP_FUNC_2(nl_builtin_mod, arg0, arg1) { + // TODO: Return an error type + if (arg0->type != arg1->type) { + return NULL; + } + + if (arg0->type != NL_TYPE_INT) { + return NULL; + } + + return nl_int_create(arg0->integer % arg1->integer); +} + +LISP_FUNC_2(nl_builtin_concat, arg0, arg1) { + // TODO: Return an error type + if (arg0->type != arg1->type) { + return NULL; + } + + if (arg0->type != NL_TYPE_STRING) { + return NULL; + } + + int len = strlen(arg0->string) + strlen(arg1->string); + char *outstr = malloc(strlen(arg0->string) + strlen(arg1->string)); + outstr[0] = '\0'; + strcat(outstr, arg0->string); + strcat(outstr, arg1->string); + + return nl_string_create(outstr); +} + +LISP_FUNC_0(nl_builtin_now_millis_mono) { + // TODO: Implement and move to a 'platform' file + return NULL; +} diff --git a/src/builtins.h b/src/builtins.h new file mode 100644 index 0000000..c996def --- /dev/null +++ b/src/builtins.h @@ -0,0 +1,60 @@ +#include "nihilispm_utility.h" + +#define LISP_FUNC_0(func_name) \ + static struct nl_object *func_name##_impl(); \ + struct nl_object *func_name(struct nl_object *args) { \ + if (args->cell.car != NULL) { \ + return NULL; \ + } \ + return func_name##_impl(); \ + } \ + static struct nl_object *func_name##_impl() + +#define LISP_FUNC_1(func_name, arg0_name) \ + static struct nl_object *func_name##_impl(struct nl_object *arg0_name); \ + struct nl_object *func_name(struct nl_object *args) { \ + struct nl_object *len_obj = nl_list_length(args); \ + if (len_obj->type != NL_TYPE_INT) { \ + return NULL; \ + } \ + if (len_obj->integer != 1) { \ + return NULL; \ + } \ + struct nl_object *arg0 = nl_car(args); \ + return func_name##_impl(arg0); \ + } \ + static struct nl_object *func_name##_impl(struct nl_object *arg0_name) + +// TODO: Unroll the args more efficiently, this is O(n^2) +#define LISP_FUNC_2(func_name, arg0_name, arg1_name) \ + static struct nl_object *func_name##_impl(struct nl_object *arg0_name, struct nl_object *arg1_name); \ + struct nl_object *func_name(struct nl_object *args) { \ + struct nl_object *len_obj = nl_list_length(args); \ + if (len_obj->type != NL_TYPE_INT) { \ + return NULL; \ + } \ + if (len_obj->integer != 2) { \ + return NULL; \ + } \ + struct nl_object *arg0 = nl_list_nth(args, 0); \ + struct nl_object *arg1 = nl_list_nth(args, 1); \ + return func_name##_impl(arg0, arg1); \ + } \ + static struct nl_object *func_name##_impl(struct nl_object *arg0_name, struct nl_object *arg1_name) + + +struct nl_object *nl_builtin_hello_world(struct nl_object *args); +struct nl_object *nl_builtin_type(struct nl_object *args); +struct nl_object *nl_builtin_symbol_p(struct nl_object *args); +struct nl_object *nl_builtin_string_p(struct nl_object *args); +struct nl_object *nl_builtin_int_p(struct nl_object *args); +struct nl_object *nl_builtin_list_p(struct nl_object *args); + +struct nl_object *nl_builtin_add(struct nl_object *args); +struct nl_object *nl_builtin_sub(struct nl_object *args); +struct nl_object *nl_builtin_mul(struct nl_object *args); +struct nl_object *nl_builtin_div(struct nl_object *args); +struct nl_object *nl_builtin_mod(struct nl_object *args); +struct nl_object *nl_builtin_concat(struct nl_object *args); + +struct nl_object *nl_builtin_now_millis_mono(struct nl_object *args); diff --git a/src/evaluate.c b/src/evaluate.c new file mode 100644 index 0000000..2a055a2 --- /dev/null +++ b/src/evaluate.c @@ -0,0 +1,29 @@ +#include +#include + +#include "nihilispm.h" + +struct nl_object *nl_evaluate_list(struct nl_object *list) { + return NULL; +} + +struct nl_object *nl_evaluate_symbol(struct nl_object *symbol) { + return NULL; +} + +struct nl_object *nl_evaluate(struct nl_object *obj) { + assert(obj != NULL); + + switch (obj->type) { + case NL_TYPE_CELL: + return nl_evaluate_list(obj); + case NL_TYPE_SYMBOL: + return NULL; //nl_evalute_symbol(obj); + case NL_TYPE_INT: + case NL_TYPE_STRING: + return obj; + case NL_TYPE_COUNT: + assert(0); + return NULL; + } +} diff --git a/src/memory.c b/src/memory.c index fc49bb4..2b5874e 100644 --- a/src/memory.c +++ b/src/memory.c @@ -6,7 +6,6 @@ static struct nl_object *nl_object_alloc(); -static void nl_cell_delete(struct nl_cell *cell); struct nl_object *nl_cell_create(struct nl_object *car, struct nl_object *cdr) { @@ -70,4 +69,3 @@ void nl_object_delete(struct nl_object *obj) { } free(obj); } - diff --git a/src/nihilispm.h b/src/nihilispm.h index 927c4b1..562bc15 100644 --- a/src/nihilispm.h +++ b/src/nihilispm.h @@ -35,6 +35,6 @@ struct nl_state; // TODO struct nl_object *nl_tokenize(const char *source); struct nl_object *nl_parse(const char *sexp); -struct nl_cell *nl_evaluate(const struct nl_cell *sexp); +struct nl_object *nl_evaluate(struct nl_object *sexp); #endif diff --git a/src/nihilispm_internal.h b/src/nihilispm_internal.h index a3c340f..ac53fb7 100644 --- a/src/nihilispm_internal.h +++ b/src/nihilispm_internal.h @@ -1,11 +1,13 @@ #include "nihilispm.h" #define ARRAY_SIZE(x) sizeof(x) / sizeof(x[0]) -#define FOREACH_LIST(list, iter, item) \ - for (struct nl_object *iter = list, *item = iter->cell.car; \ - iter != NULL; \ - iter = token->cell.cdr, item = iter->cell.car) +#define FOREACH_LIST(list, iter_name, item_name) \ + FOREACH_LIST_COND(list, iter_name, item_name, true) +#define FOREACH_LIST_COND(list, iter_name, item_name, cond) \ + for (struct nl_object *iter_name = (list), *item_name = iter_name->cell.car; \ + iter_name != NULL && (cond); \ + iter_name = token->cell.cdr, item_name = iter_name->cell.car) struct nl_object *nl_cell_create(struct nl_object *car, struct nl_object *cdr); diff --git a/src/nihilispm_utility.h b/src/nihilispm_utility.h new file mode 100644 index 0000000..9f006e5 --- /dev/null +++ b/src/nihilispm_utility.h @@ -0,0 +1,30 @@ +#include + +#include "nihilispm.h" + +struct nl_object *nl_nil_create(); +struct nl_object *nl_t_create(); +struct nl_object *nl_predicate(bool value); +struct nl_object *nl_truthy(struct nl_object *arg); + +struct nl_object* nl_car(struct nl_object *list); +struct nl_object* nl_cdr(struct nl_object *list); + +struct nl_object* nl_list_length(struct nl_object *list); +struct nl_object* nl_list_nth(struct nl_object *list, int n); +struct nl_object* nl_list_append(struct nl_object *list, struct nl_object *obj); +struct nl_object* nl_tuple_create(struct nl_object *obj0, struct nl_object *obj1); + + +/* #define NL_RET_IF_ERROR(obj) \ */ +/* do { \ */ +/* if ((obj)->type == NL_TYPE_ERROR) { \ */ +/* return obj; \ */ +/* } while(0) */ + +#define NL_COND_OR_RET_ERROR(cond, msg) \ + do { \ + if (!(cond)) { \ + return nl_error_create(strdup(msg)); \ + } \ + } while(0) diff --git a/src/utility.c b/src/utility.c new file mode 100644 index 0000000..285f32e --- /dev/null +++ b/src/utility.c @@ -0,0 +1,113 @@ +#include "nihilispm.h" +#include "nihilispm_internal.h" +#include "nihilispm_utility.h" + +#include +#include +#include + +struct nl_object *nl_car(struct nl_object *list) { + if (list == NULL || list->type != NL_TYPE_CELL) { + return NULL; // TODO: Return an error type + } + + struct nl_object *car = list->cell.car; + if (car == NULL) { + return nl_nil_create(); + } + + return car; +} + +struct nl_object *nl_cdr(struct nl_object *list) { + if (list == NULL || list->type != NL_TYPE_CELL) { + return NULL; // TODO: Return an error type + } + + struct nl_object *cdr = list->cell.cdr; + if (cdr == NULL) { + return nl_nil_create(); + } + + return cdr; +} + +struct nl_object *nl_nil_create() { + return nl_cell_create(NULL, NULL); +} + +struct nl_object *nl_t_create() { + return nl_symbol_create(strdup("t")); +} + +struct nl_object *nl_predicate(bool value) { + if (value) { + return nl_t_create(); + } else { + return nl_nil_create(); + } +} + +struct nl_object *nl_list_length(struct nl_object *list) { + if (list == NULL || list->type != NL_TYPE_CELL) { + return NULL; // TODO: Return an error type + } + + struct nl_object *node = list; + if (list->cell.car == NULL) { + return nl_int_create(0); + } + + int length = 1; + while (node->cell.cdr != NULL) { + node = node->cell.cdr; + length++; + } + + return nl_int_create(length); +} + +struct nl_object *nl_list_nth(struct nl_object *list, int n) { + if (list == NULL || list->type != NL_TYPE_CELL) { + return NULL; // TODO: Return an error type + } + + int length = nl_list_length(list)->integer; + if (length <= n) { + return NULL; // TODO: Return an error type + } + + struct nl_object *node = list; + for (int i = 0; i < n; i++) { + node = node->cell.cdr; + } + + return node->cell.car; +} + +struct nl_object *nl_truthy(struct nl_object *obj) { + // TODO: Implement me + return NULL; +} + +struct nl_object *nl_tuple_create(struct nl_object *obj0, struct nl_object *obj1) { + struct nl_object *tuple = nl_cell_create(obj0, NULL); + nl_list_append(tuple, obj1); + return tuple; +} + +struct nl_object *nl_list_append(struct nl_object *list, struct nl_object *obj) { + struct nl_object *iter = list; + + if (list->cell.car == NULL) { + list->cell.car = obj; + return list; + } + + while (iter->cell.cdr != NULL) { + iter = iter->cell.cdr; + } + iter->cell.cdr = nl_cell_create(obj, NULL); + + return list; +} diff --git a/test/test_utility.c b/test/test_utility.c new file mode 100644 index 0000000..7a35401 --- /dev/null +++ b/test/test_utility.c @@ -0,0 +1,215 @@ +#include +#include +#include + +#include "nihilispm.h" +#include "nihilispm_internal.h" +#include "nihilispm_utility.h" + +static struct nl_object *input; +static struct nl_object *response; + +#define TEST_ASSERT_OBJ_SYMBOL(obj) \ + TEST_ASSERT_EQUAL(obj->type, NL_TYPE_SYMBOL) + +#define TEST_ASSERT_OBJ_STRING(obj) \ + TEST_ASSERT_EQUAL(obj->type, NL_TYPE_STRING) + +#define TEST_ASSERT_OBJ_INT(obj) \ + TEST_ASSERT_EQUAL(obj->type, NL_TYPE_INT) + +#define TEST_ASSERT_OBJ_LIST(obj) \ + TEST_ASSERT_EQUAL(obj->type, NL_TYPE_CELL) + +#define TEST_ASSERT_LIST_LEN(list, len) \ + do { \ + TEST_ASSERT_OBJ_LIST(list); \ + TEST_ASSERT_EQUAL(nl_list_length(list)->integer, len); \ + } while(0) + +#define TEST_ASSERT_NIL(obj) \ + TEST_ASSERT_LIST_LEN(obj, 0) + +#define TEST_ASSERT_T(obj) \ + do { \ + TEST_ASSERT_OBJ_SYMBOL(obj); \ + TEST_ASSERT_EQUAL_STRING(obj->symbol, "t"); \ + } while(0) + + +void setUp(void) { + input = NULL; + response = NULL; +} + +void tearDown(void) { + // TODO: Implement GC so we can clean these both up + //nl_object_delete(input); + input = NULL; + nl_object_delete(response); + response = NULL; +} + +static void test_nil_create(void) { + response = nl_nil_create(); + + TEST_ASSERT_NIL(response); +} + +static void test_t_create(void) { + response = nl_t_create(); + + TEST_ASSERT_T(response); +} + +static void test_predicate_true(void) { + response = nl_predicate(true); + + TEST_ASSERT_T(response); +} + +static void test_predicate_false(void) { + response = nl_predicate(false); + + TEST_ASSERT_NIL(response); +} + +static void test_car_nil(void) { + response = nl_car(nl_nil_create()); + + TEST_ASSERT_NIL(response); +} + +static void test_cdr_nil(void) { + response = nl_cdr(nl_nil_create()); + + TEST_ASSERT_NIL(response); +} + +static void test_car_t(void) { + response = nl_car(nl_t_create()); + + TEST_ASSERT_NULL(response); +} + +static void test_cdr_t(void) { + response = nl_car(nl_t_create()); + + TEST_ASSERT_NULL(response); +} + +static void test_car_list(void) { + input = nl_tuple_create( + nl_string_create(strdup("foo")), + nl_t_create()); + response = nl_car(input); + + TEST_ASSERT_OBJ_STRING(response); +} + +static void test_cdr_list(void) { + input = nl_tuple_create( + nl_t_create(), + nl_string_create(strdup("foo"))); + response = nl_cdr(input); + + TEST_ASSERT_OBJ_STRING(nl_car(response)); +} + +static void test_list_length_nil() { + response = nl_list_length(nl_nil_create()); + + TEST_ASSERT_OBJ_INT(response); + TEST_ASSERT_EQUAL(response->integer, 0); +} + +static void test_list_length_1() { + response = nl_list_length( + nl_cell_create( + nl_t_create(), NULL)); + + TEST_ASSERT_OBJ_INT(response); + TEST_ASSERT_EQUAL(response->integer, 1); +} + +static void test_list_length_2() { + response = nl_list_length( + nl_tuple_create( + nl_t_create(), + nl_t_create())); + + TEST_ASSERT_OBJ_INT(response); + TEST_ASSERT_EQUAL(response->integer, 2); +} + + +static void test_list_append_nil() { + input = nl_nil_create(); + nl_list_append(input, nl_t_create()); + + TEST_ASSERT_EQUAL(nl_list_length(input)->integer, 1); +} + +static void test_list_append_list() { + input = nl_tuple_create(nl_t_create(), nl_t_create()); + nl_list_append(input, nl_t_create()); + + TEST_ASSERT_EQUAL(nl_list_length(input)->integer, 3); +} + +static void test_list_nth_nil_0() { + response = nl_list_nth(nl_nil_create(), 0); + + TEST_ASSERT_NULL(response); +} + +static void test_list_nth_nil_1() { + response = nl_list_nth(nl_nil_create(), 1); + + TEST_ASSERT_NULL(response); +} + +static void test_list_nth_list_0() { + response = nl_list_nth( + nl_tuple_create( + nl_t_create(), + nl_string_create(strdup("foo"))), + 0); + + TEST_ASSERT_T(response); +} + +static void test_list_nth_list_1() { + response = nl_list_nth( + nl_tuple_create( + nl_t_create(), + nl_string_create(strdup("foo"))), + 1); + + TEST_ASSERT_OBJ_STRING(response); +} + + +int main(void) { + UNITY_BEGIN(); + RUN_TEST(test_nil_create); + RUN_TEST(test_t_create); + RUN_TEST(test_predicate_true); + RUN_TEST(test_predicate_false); + RUN_TEST(test_car_nil); + RUN_TEST(test_cdr_nil); + RUN_TEST(test_car_t); + RUN_TEST(test_cdr_t); + RUN_TEST(test_car_list); + RUN_TEST(test_cdr_list); + RUN_TEST(test_list_length_nil); + RUN_TEST(test_list_length_1); + RUN_TEST(test_list_length_2); + RUN_TEST(test_list_append_nil); + RUN_TEST(test_list_append_list); + RUN_TEST(test_list_nth_nil_0); + RUN_TEST(test_list_nth_nil_1); + RUN_TEST(test_list_nth_list_0); + RUN_TEST(test_list_nth_list_1); + return UNITY_END(); +}