From e25935f4a4f18588048d55f0b071f10ef3c6a6a8 Mon Sep 17 00:00:00 2001 From: Marc Kirchner Date: Mon, 20 Jun 2022 23:26:29 +0200 Subject: [PATCH 1/4] Consistent vector/array naming: call it a vector --- include/array.h | 34 ----------- include/value.h | 8 ++- include/vector.h | 34 +++++++++++ src/value.c | 3 +- src/{array.c => vector.c} | 44 +++++++------- test/Makefile | 10 ++-- test/test_array.c | 120 -------------------------------------- test/test_vector.c | 120 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 188 insertions(+), 185 deletions(-) delete mode 100644 include/array.h create mode 100644 include/vector.h rename src/{array.c => vector.c} (62%) delete mode 100644 test/test_array.c create mode 100644 test/test_vector.c diff --git a/include/array.h b/include/array.h deleted file mode 100644 index d22d4d3..0000000 --- a/include/array.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef __ARRAY_H__ -#define __ARRAY_H__ - -#include - -/* - * Contiguous, indexable chunk of memory. - */ -typedef struct array { - char *p; - size_t size; - size_t capacity; - size_t bytes; -} Array; - - -Array *array_new(const size_t item_size); -Array *array_new_with_capacity(const size_t isize, const size_t capacity); -void array_delete(Array *a); - -#define array_size(a) (a->size) -#define array_capacity(a) (a->capacity) - -void *array_at(Array *a, size_t i); -#define array_typed_at(a,i,t) ((t*) array_at(a, i)) -void array_push_back(Array *a, const void *value, size_t n); -void array_push_front(Array *a, const void *value, size_t n); -void *array_pop_back(Array *a); -#define array_typed_pop_back(a,t) ((t*) array_pop_back(a)) -void *array_pop_front(Array *a); -#define array_typed_pop_front(a,t) ((t*) array_pop_front(a)) -void array_shrink(Array *a); - -#endif /* !__ARRAY_H__ */ diff --git a/include/value.h b/include/value.h index 829f0e0..f200374 100644 --- a/include/value.h +++ b/include/value.h @@ -1,11 +1,11 @@ #ifndef VALUE_H #define VALUE_H -#include "array.h" #include "env.h" #include "gc.h" #include "list.h" #include "map.h" +#include "vector.h" #define BOOL(v) (v->value.bool_) #define BUILTIN_FN(v) (v->value.builtin_fn) @@ -16,6 +16,7 @@ #define LIST(v) (v->value.list) #define STRING(v) (v->value.str) #define SYMBOL(v) (v->value.str) +#define VECTOR(v) (v->value.vector) typedef enum { VALUE_BOOL, @@ -28,7 +29,8 @@ typedef enum { VALUE_MACRO_FN, VALUE_NIL, VALUE_STRING, - VALUE_SYMBOL + VALUE_SYMBOL, + VALUE_VECTOR } ValueType; extern const char *value_type_names[]; @@ -46,7 +48,7 @@ typedef struct Value { int int_; double float_; char *str; - Array *vector; + Vector *vector; const List *list; Map *map; struct Value *(*builtin_fn)(const struct Value *); diff --git a/include/vector.h b/include/vector.h new file mode 100644 index 0000000..d11e6cb --- /dev/null +++ b/include/vector.h @@ -0,0 +1,34 @@ +#ifndef __VECTOR_H__ +#define __VECTOR_H__ + +#include + +/* + * Contiguous, indexable chunk of memory. + */ +typedef struct vector { + char *p; + size_t size; + size_t capacity; + size_t bytes; +} Vector; + + +Vector *vector_new(const size_t item_size); +Vector *vector_new_with_capacity(const size_t isize, const size_t capacity); +void vector_delete(Vector *a); + +#define vector_size(a) (a->size) +#define vector_capacity(a) (a->capacity) + +void *vector_at(Vector *a, size_t i); +#define vector_typed_at(a,i,t) ((t*) vector_at(a, i)) +void vector_push_back(Vector *a, const void *value, size_t n); +void vector_push_front(Vector *a, const void *value, size_t n); +void *vector_pop_back(Vector *a); +#define vector_typed_pop_back(a,t) ((t*) vector_pop_back(a)) +void *vector_pop_front(Vector *a); +#define vector_typed_pop_front(a,t) ((t*) vector_pop_front(a)) +void vector_shrink(Vector *a); + +#endif /* !__VECTOR_H__ */ diff --git a/src/value.c b/src/value.c index e70abc5..4eceb91 100644 --- a/src/value.c +++ b/src/value.c @@ -16,7 +16,8 @@ const char *value_type_names[] = { "VALUE_MACRO_FN", "VALUE_NIL", "VALUE_STRING", - "VALUE_SYMBOL" + "VALUE_SYMBOL", + "VALUE_VECTOR" }; diff --git a/src/array.c b/src/vector.c similarity index 62% rename from src/array.c rename to src/vector.c index 27847e3..1cc4d58 100644 --- a/src/array.c +++ b/src/vector.c @@ -1,26 +1,26 @@ -#include "array.h" +#include "vector.h" #include #include -Array *array_new(const size_t item_size) +Vector *vector_new(const size_t item_size) { - // default to 2 elements for empty arrays - return array_new_with_capacity(item_size, 2); + // default to 2 elements for empty vectors + return vector_new_with_capacity(item_size, 2); } -Array *array_new_with_capacity(const size_t item_size, const size_t capacity) +Vector *vector_new_with_capacity(const size_t item_size, const size_t capacity) { - Array *array = malloc(sizeof(Array)); - array->p = calloc(capacity, item_size); - array->bytes = item_size; - array->capacity = capacity; - array->size = 0; - return array; + Vector *vector = malloc(sizeof(Vector)); + vector->p = calloc(capacity, item_size); + vector->bytes = item_size; + vector->capacity = capacity; + vector->size = 0; + return vector; } -void array_delete(Array *a) +void vector_delete(Vector *a) { free(a->p); free(a); @@ -40,7 +40,7 @@ static uint64_t next_power_of_2(uint64_t v) return v; } -static void array_resize(Array *a, size_t requested_capacity) +static void vector_resize(Vector *a, size_t requested_capacity) { size_t new_capacity = next_power_of_2(requested_capacity); if (a->size > new_capacity) a->size = new_capacity; @@ -48,25 +48,25 @@ static void array_resize(Array *a, size_t requested_capacity) a->capacity = new_capacity; } -void *array_at(Array *a, size_t i) +void *vector_at(Vector *a, size_t i) { return (void *) (a->p + i * a->bytes); } -void array_push_back(Array *a, const void *value, size_t n) +void vector_push_back(Vector *a, const void *value, size_t n) { size_t total = n + a->size; - array_resize(a, total); + vector_resize(a, total); char *pos = a->p + (a->size * a->bytes); memcpy(pos, value, n * a->bytes); a->size += n; } -void array_push_front(Array *a, const void *value, size_t n) +void vector_push_front(Vector *a, const void *value, size_t n) { size_t total = n + a->size; // allocate sufficient memory - array_resize(a, total); + vector_resize(a, total); // shift contents away from the front size_t width = n * a->bytes; char *pos = a->p + width; @@ -77,14 +77,14 @@ void array_push_front(Array *a, const void *value, size_t n) a->size += n; } -void *array_pop_back(Array *a) +void *vector_pop_back(Vector *a) { if (a->size == 0) return NULL; a->size--; return a->p + a->size * a->bytes; } -void *array_pop_front(Array *a) +void *vector_pop_front(Vector *a) { if (a->size == 0) return NULL; @@ -97,7 +97,7 @@ void *array_pop_front(Array *a) return (void *) (a->p + a->size * a->bytes); } -void array_shrink(Array *a) +void vector_shrink(Vector *a) { - array_resize(a, a->size); + vector_resize(a, a->size); } diff --git a/test/Makefile b/test/Makefile index 6379234..4f8033a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -8,7 +8,7 @@ BUILD_DIR=../build # targets are roughly in topological order TARGETS=test_list \ test_ast \ - test_array \ + test_vector \ test_djb2 \ test_parser \ test_primes \ @@ -54,12 +54,12 @@ test_list: test_setup gc $(BUILD_DIR)/test/test_list.o -o $(BUILD_DIR)/test/test_list # -# test_array +# test_vector # -test_array: test_setup - $(CC) $(CFLAGS) -MMD -c test_array.c -o $(BUILD_DIR)/test/test_array.o +test_vector: test_setup + $(CC) $(CFLAGS) -MMD -c test_vector.c -o $(BUILD_DIR)/test/test_vector.o $(CC) $(LDFLAGS) $(LDLIBS) \ - $(BUILD_DIR)/test/test_array.o -o $(BUILD_DIR)/test/test_array + $(BUILD_DIR)/test/test_vector.o -o $(BUILD_DIR)/test/test_vector # # test_ast diff --git a/test/test_array.c b/test/test_array.c deleted file mode 100644 index 50aa918..0000000 --- a/test/test_array.c +++ /dev/null @@ -1,120 +0,0 @@ -#include -#include -#include "minunit.h" - -#include "../src/array.c" - -static char *test_array() -{ - // creation - Array *a = array_new(sizeof(int)); - mu_assert(a != NULL, "New array should not be NULL"); - - // get/set - int i = 42; - array_push_back(a, &i, 1); - mu_assert(array_size(a) == 1, "Size should be 1"); - mu_assert(array_capacity(a) == 1, "Capacity should be 1"); - i++; - array_push_back(a, &i, 1); - i++; - array_push_back(a, &i, 1); - i++; - array_push_back(a, &i, 1); - mu_assert(array_size(a) == 4, "Size should be 4"); - mu_assert(array_capacity(a) == 4, "Capacity should be 4"); - mu_assert(*array_typed_at(a, 0, int) == 42, "42 != 42"); - mu_assert(*array_typed_at(a, 1, int) == 43, "43 != 43 1"); - mu_assert(*array_typed_at(a, 2, int) == 44, "44 != 44"); - mu_assert(*array_typed_at(a, 3, int) == 45, "45 != 45"); - - int i2[4] = {46, 47, 48, 49}; - array_push_back(a, i2, 4); - mu_assert(array_size(a) == 8, "Size should be 8"); - mu_assert(array_capacity(a) == 8, "Capacity should be 8"); - mu_assert(*array_typed_at(a, 0, int) == 42, "42 != 42"); - mu_assert(*array_typed_at(a, 1, int) == 43, "43 != 43"); - mu_assert(*array_typed_at(a, 2, int) == 44, "44 != 44"); - mu_assert(*array_typed_at(a, 3, int) == 45, "45 != 45"); - mu_assert(*array_typed_at(a, 4, int) == 46, "46 != 46"); - mu_assert(*array_typed_at(a, 5, int) == 47, "47 != 47"); - mu_assert(*array_typed_at(a, 6, int) == 48, "48 != 48"); - mu_assert(*array_typed_at(a, 7, int) == 49, "49 != 49"); - - int j = 41; - array_push_front(a, &j, 1); - mu_assert(*array_typed_at(a, 0, int) == 41, "push fail 41"); - j--; - array_push_front(a, &j, 1); - mu_assert(*array_typed_at(a, 0, int) == 40, "push fail 40"); - mu_assert(*array_typed_at(a, 1, int) == 41, "assert fail 41"); - j--; - array_push_front(a, &j, 1); - mu_assert(*array_typed_at(a, 0, int) == 39, "push fail 39"); - mu_assert(*array_typed_at(a, 1, int) == 40, "assert fail 40"); - mu_assert(*array_typed_at(a, 2, int) == 41, "assert fail 41"); - j--; - array_push_front(a, &j, 1); - mu_assert(*array_typed_at(a, 0, int) == 38, "push fail 38"); - mu_assert(*array_typed_at(a, 1, int) == 39, "assert fail 39"); - mu_assert(*array_typed_at(a, 2, int) == 40, "assert fail 40"); - mu_assert(*array_typed_at(a, 3, int) == 41, "assert fail 41"); - - mu_assert(array_size(a) == 12, "Size should be 12"); - mu_assert(array_capacity(a) == 16, "Capacity should be 16"); - mu_assert(*array_typed_at(a, 0, int) == 38, "38 != 38"); - mu_assert(*array_typed_at(a, 1, int) == 39, "39 != 39"); - mu_assert(*array_typed_at(a, 2, int) == 40, "40 != 40"); - mu_assert(*array_typed_at(a, 3, int) == 41, "41 != 41"); - mu_assert(*array_typed_at(a, 4, int) == 42, "42 != 42"); - mu_assert(*array_typed_at(a, 5, int) == 43, "43 != 43"); - mu_assert(*array_typed_at(a, 6, int) == 44, "44 != 44"); - mu_assert(*array_typed_at(a, 7, int) == 45, "45 != 45"); - mu_assert(*array_typed_at(a, 8, int) == 46, "46 != 46"); - mu_assert(*array_typed_at(a, 9, int) == 47, "47 != 47"); - mu_assert(*array_typed_at(a, 10, int) == 48, "48 != 48"); - mu_assert(*array_typed_at(a, 11, int) == 49, "49 != 49"); - - mu_assert(*array_typed_pop_back(a, int) == 49, "49 pop fail"); - mu_assert(array_size(a) == 11, "Wrong size after popping 49"); - for (int i = 10; i >= 0; --i) { - array_typed_pop_back(a, int); - } - mu_assert(array_size(a) == 0, "Wrong size after emptying array"); - mu_assert(array_pop_back(a) == NULL, "Pop from empty array must return NULL"); - - int i3[4] = {0, 1, 2, 3}; - array_push_back(a, i3, 4); - mu_assert(array_size(a) == 4, "Wrong size after refill"); - mu_assert(*array_typed_pop_front(a, int) == 0, "Pop front 0 fail"); - mu_assert(array_size(a) == 3, "Pop front 0 size fail"); - mu_assert(*array_typed_pop_front(a, int) == 1, "Pop front 1 fail"); - mu_assert(*array_typed_pop_front(a, int) == 2, "Pop front 2 fail"); - mu_assert(*array_typed_pop_front(a, int) == 3, "Pop front 3 fail"); - mu_assert(array_typed_pop_front(a, int) == NULL, "Pop on empty array must return NULL"); - - // clean up - array_delete(a); - return 0; -} - -int tests_run = 0; - -static char *test_suite() -{ - mu_run_test(test_array); - return 0; -} - -int main() -{ - printf("---=[ Array tests\n"); - char *result = test_suite(); - if (result != 0) { - printf("%s\n", result); - } else { - printf("ALL TESTS PASSED\n"); - } - printf("Tests run: %d\n", tests_run); - return result != 0; -} diff --git a/test/test_vector.c b/test/test_vector.c new file mode 100644 index 0000000..02272e6 --- /dev/null +++ b/test/test_vector.c @@ -0,0 +1,120 @@ +#include +#include +#include "minunit.h" + +#include "../src/vector.c" + +static char *test_vector() +{ + // creation + Vector *a = vector_new(sizeof(int)); + mu_assert(a != NULL, "New vector should not be NULL"); + + // get/set + int i = 42; + vector_push_back(a, &i, 1); + mu_assert(vector_size(a) == 1, "Size should be 1"); + mu_assert(vector_capacity(a) == 1, "Capacity should be 1"); + i++; + vector_push_back(a, &i, 1); + i++; + vector_push_back(a, &i, 1); + i++; + vector_push_back(a, &i, 1); + mu_assert(vector_size(a) == 4, "Size should be 4"); + mu_assert(vector_capacity(a) == 4, "Capacity should be 4"); + mu_assert(*vector_typed_at(a, 0, int) == 42, "42 != 42"); + mu_assert(*vector_typed_at(a, 1, int) == 43, "43 != 43 1"); + mu_assert(*vector_typed_at(a, 2, int) == 44, "44 != 44"); + mu_assert(*vector_typed_at(a, 3, int) == 45, "45 != 45"); + + int i2[4] = {46, 47, 48, 49}; + vector_push_back(a, i2, 4); + mu_assert(vector_size(a) == 8, "Size should be 8"); + mu_assert(vector_capacity(a) == 8, "Capacity should be 8"); + mu_assert(*vector_typed_at(a, 0, int) == 42, "42 != 42"); + mu_assert(*vector_typed_at(a, 1, int) == 43, "43 != 43"); + mu_assert(*vector_typed_at(a, 2, int) == 44, "44 != 44"); + mu_assert(*vector_typed_at(a, 3, int) == 45, "45 != 45"); + mu_assert(*vector_typed_at(a, 4, int) == 46, "46 != 46"); + mu_assert(*vector_typed_at(a, 5, int) == 47, "47 != 47"); + mu_assert(*vector_typed_at(a, 6, int) == 48, "48 != 48"); + mu_assert(*vector_typed_at(a, 7, int) == 49, "49 != 49"); + + int j = 41; + vector_push_front(a, &j, 1); + mu_assert(*vector_typed_at(a, 0, int) == 41, "push fail 41"); + j--; + vector_push_front(a, &j, 1); + mu_assert(*vector_typed_at(a, 0, int) == 40, "push fail 40"); + mu_assert(*vector_typed_at(a, 1, int) == 41, "assert fail 41"); + j--; + vector_push_front(a, &j, 1); + mu_assert(*vector_typed_at(a, 0, int) == 39, "push fail 39"); + mu_assert(*vector_typed_at(a, 1, int) == 40, "assert fail 40"); + mu_assert(*vector_typed_at(a, 2, int) == 41, "assert fail 41"); + j--; + vector_push_front(a, &j, 1); + mu_assert(*vector_typed_at(a, 0, int) == 38, "push fail 38"); + mu_assert(*vector_typed_at(a, 1, int) == 39, "assert fail 39"); + mu_assert(*vector_typed_at(a, 2, int) == 40, "assert fail 40"); + mu_assert(*vector_typed_at(a, 3, int) == 41, "assert fail 41"); + + mu_assert(vector_size(a) == 12, "Size should be 12"); + mu_assert(vector_capacity(a) == 16, "Capacity should be 16"); + mu_assert(*vector_typed_at(a, 0, int) == 38, "38 != 38"); + mu_assert(*vector_typed_at(a, 1, int) == 39, "39 != 39"); + mu_assert(*vector_typed_at(a, 2, int) == 40, "40 != 40"); + mu_assert(*vector_typed_at(a, 3, int) == 41, "41 != 41"); + mu_assert(*vector_typed_at(a, 4, int) == 42, "42 != 42"); + mu_assert(*vector_typed_at(a, 5, int) == 43, "43 != 43"); + mu_assert(*vector_typed_at(a, 6, int) == 44, "44 != 44"); + mu_assert(*vector_typed_at(a, 7, int) == 45, "45 != 45"); + mu_assert(*vector_typed_at(a, 8, int) == 46, "46 != 46"); + mu_assert(*vector_typed_at(a, 9, int) == 47, "47 != 47"); + mu_assert(*vector_typed_at(a, 10, int) == 48, "48 != 48"); + mu_assert(*vector_typed_at(a, 11, int) == 49, "49 != 49"); + + mu_assert(*vector_typed_pop_back(a, int) == 49, "49 pop fail"); + mu_assert(vector_size(a) == 11, "Wrong size after popping 49"); + for (int i = 10; i >= 0; --i) { + vector_typed_pop_back(a, int); + } + mu_assert(vector_size(a) == 0, "Wrong size after emptying vector"); + mu_assert(vector_pop_back(a) == NULL, "Pop from empty vector must return NULL"); + + int i3[4] = {0, 1, 2, 3}; + vector_push_back(a, i3, 4); + mu_assert(vector_size(a) == 4, "Wrong size after refill"); + mu_assert(*vector_typed_pop_front(a, int) == 0, "Pop front 0 fail"); + mu_assert(vector_size(a) == 3, "Pop front 0 size fail"); + mu_assert(*vector_typed_pop_front(a, int) == 1, "Pop front 1 fail"); + mu_assert(*vector_typed_pop_front(a, int) == 2, "Pop front 2 fail"); + mu_assert(*vector_typed_pop_front(a, int) == 3, "Pop front 3 fail"); + mu_assert(vector_typed_pop_front(a, int) == NULL, "Pop on empty vector must return NULL"); + + // clean up + vector_delete(a); + return 0; +} + +int tests_run = 0; + +static char *test_suite() +{ + mu_run_test(test_vector); + return 0; +} + +int main() +{ + printf("---=[ Vector tests\n"); + char *result = test_suite(); + if (result != 0) { + printf("%s\n", result); + } else { + printf("ALL TESTS PASSED\n"); + } + printf("Tests run: %d\n", tests_run); + return result != 0; +} From 9bf89793217ad64e2c0a161fab5f8f3ab6f39fd0 Mon Sep 17 00:00:00 2001 From: Marc Kirchner Date: Tue, 21 Jun 2022 09:08:12 +0200 Subject: [PATCH 2/4] Expose vector type --- include/core.h | 2 ++ include/value.h | 5 +++++ include/vector.h | 25 +++++++++++++------------ src/core.c | 25 +++++++++++++++++++++++++ src/main.c | 2 ++ src/value.c | 39 +++++++++++++++++++++++++++++++++++++++ src/vector.c | 20 +++++++++++++++----- test/Makefile | 9 +++++++-- test/test_vector.c | 12 ++++++++++++ 9 files changed, 120 insertions(+), 19 deletions(-) diff --git a/include/core.h b/include/core.h index 3456576..1a7ad4b 100644 --- a/include/core.h +++ b/include/core.h @@ -25,11 +25,13 @@ Value *core_gt(const Value *args); Value *core_is_empty(const Value *args); Value *core_is_false(const Value *args); Value *core_is_list(const Value *args); +Value *core_is_vector(const Value *args); Value *core_is_nil(const Value *args); Value *core_is_symbol(const Value *args); Value *core_is_true(const Value *args); Value *core_leq(const Value *args); Value *core_list(const Value *args); +Value *core_vector(const Value *args); Value *core_lt(const Value *args); Value *core_map(const Value *args); Value *core_mul(const Value *args); diff --git a/include/value.h b/include/value.h index f200374..f08ec24 100644 --- a/include/value.h +++ b/include/value.h @@ -69,6 +69,7 @@ extern Value *VALUE_CONST_NIL; bool is_symbol(const Value *value); bool is_macro(const Value *value); bool is_list(const Value *value); +bool is_vector(const Value *value); bool is_exception(const Value *value); Value *value_new_nil(); Value *value_new_bool(const bool bool_); @@ -83,6 +84,10 @@ Value *value_new_string(const char *str); Value *value_new_symbol(const char *str); Value *value_new_list(const List *l); Value *value_make_list(Value *v); +Value *value_new_list(const List *l); +Value *value_make_list(Value *v); +Value *value_new_vector(const List *l); +Value *value_make_vector(Value *v); Value *value_head(const Value *v); Value *value_tail(const Value *v); void value_delete(Value *v); diff --git a/include/vector.h b/include/vector.h index d11e6cb..f491d7b 100644 --- a/include/vector.h +++ b/include/vector.h @@ -16,19 +16,20 @@ typedef struct vector { Vector *vector_new(const size_t item_size); Vector *vector_new_with_capacity(const size_t isize, const size_t capacity); -void vector_delete(Vector *a); +Vector *vector_dup(const Vector *vec); +void vector_delete(Vector *vec); -#define vector_size(a) (a->size) -#define vector_capacity(a) (a->capacity) +#define vector_size(v) (v->size) +#define vector_capacity(v) (v->capacity) -void *vector_at(Vector *a, size_t i); -#define vector_typed_at(a,i,t) ((t*) vector_at(a, i)) -void vector_push_back(Vector *a, const void *value, size_t n); -void vector_push_front(Vector *a, const void *value, size_t n); -void *vector_pop_back(Vector *a); -#define vector_typed_pop_back(a,t) ((t*) vector_pop_back(a)) -void *vector_pop_front(Vector *a); -#define vector_typed_pop_front(a,t) ((t*) vector_pop_front(a)) -void vector_shrink(Vector *a); +void *vector_at(Vector *v, size_t i); +#define vector_typed_at(v,i,t) ((t*) vector_at(v, i)) +void vector_push_back(Vector *v, const void *value, size_t n); +void vector_push_front(Vector *v, const void *value, size_t n); +void *vector_pop_back(Vector *v); +#define vector_typed_pop_back(v,t) ((t*) vector_pop_back(v)) +void *vector_pop_front(Vector *v); +#define vector_typed_pop_front(v,t) ((t*) vector_pop_front(v)) +void vector_shrink(Vector *v); #endif /* !__VECTOR_H__ */ diff --git a/src/core.c b/src/core.c index eb59777..702d492 100644 --- a/src/core.c +++ b/src/core.c @@ -102,6 +102,20 @@ Value *core_is_list(const Value *args) return arg0->type == VALUE_LIST ? VALUE_CONST_TRUE : VALUE_CONST_FALSE; } +Value *core_vector(const Value *args) +{ + CHECK_ARGLIST(args); + return value_new_vector(LIST(args)); +} + +Value *core_is_vector(const Value *args) +{ + CHECK_ARGLIST(args); + REQUIRE_LIST_CARDINALITY(args, 1ul, "vector? requires exactly one parameter"); + Value *arg0 = ARG(args, 0); + return arg0->type == VALUE_VECTOR ? VALUE_CONST_TRUE : VALUE_CONST_FALSE; +} + Value *core_is_empty(const Value *args) { CHECK_ARGLIST(args); @@ -502,6 +516,17 @@ static char *core_str_inner(char *str, const Value *v) } str = str_append(str, strlen(str), ")", 1); break; + case VALUE_VECTOR: + str = str_append(str, strlen(str), "[", 1); + size_t vec_size = vector_size(VECTOR(v)); + for (size_t i = 0; i < vec_size; ++i) { + str = core_str_inner(str, vector_typed_at(VECTOR(v), i, Value)); + if (i < vec_size - 1) { + str = str_append(str, strlen(str), " ", 1); + } + } + str = str_append(str, strlen(str), "]", 1); + break; case VALUE_FN: case VALUE_MACRO_FN: str = str_append(str, strlen(str), "(lambda ", 8); diff --git a/src/main.c b/src/main.c index 4923a2d..b128082 100644 --- a/src/main.c +++ b/src/main.c @@ -69,6 +69,8 @@ Environment *global_env() env_set(env, "list", value_new_builtin_fn(core_list)); env_set(env, "list?", value_new_builtin_fn(core_is_list)); + env_set(env, "vector", value_new_builtin_fn(core_vector)); + env_set(env, "vector?", value_new_builtin_fn(core_is_vector)); env_set(env, "empty?", value_new_builtin_fn(core_is_empty)); env_set(env, "count", value_new_builtin_fn(core_count)); env_set(env, "nth", value_new_builtin_fn(core_nth)); diff --git a/src/value.c b/src/value.c index 4eceb91..a0ae398 100644 --- a/src/value.c +++ b/src/value.c @@ -54,6 +54,11 @@ bool is_list(const Value *value) return value->type == VALUE_LIST; } +bool is_vector(const Value *value) +{ + return value->type == VALUE_VECTOR; +} + static Value *value_new(ValueType type) { Value *v = (Value *) gc_malloc(&gc, sizeof(Value)); @@ -166,6 +171,33 @@ Value *value_make_list(Value *v) return r; } +Value *value_new_vector(const List *l) +{ + Value *v = value_new(VALUE_VECTOR); + VECTOR(v) = vector_new(sizeof(Value)); + if (l) { + for (const ListItem *i = l->head; i != NULL; i = i->next) { + vector_push_back(VECTOR(v), i->val, 1); + } + } + return v; +} + +Value *value_make_vector(Value *v) +{ + Value *r = value_new_vector(NULL); + if (!v) return r; + if (v->type == VALUE_VECTOR) { + VECTOR(r) = vector_dup(VECTOR(v)); + } else if (v->type == VALUE_LIST) { + for (const ListItem *i = LIST(v)->head; + i != NULL; i = i->next) { + vector_push_back(VECTOR(r), i->val, 1); + } + } + return r; +} + void value_print(const Value *v) { if (!v) return; @@ -198,6 +230,13 @@ void value_print(const Value *v) } fprintf(stderr, ")"); break; + case VALUE_VECTOR: + fprintf(stderr, "[ "); + for (size_t i = 0; i < vector_size(VECTOR(v)); ++i) { + value_print(vector_typed_at(VECTOR(v), i, Value)); + } + fprintf(stderr, "]"); + break; case VALUE_FN: fprintf(stderr, "lambda: "); value_print(FN(v)->args); diff --git a/src/vector.c b/src/vector.c index 1cc4d58..b3fa673 100644 --- a/src/vector.c +++ b/src/vector.c @@ -6,8 +6,7 @@ Vector *vector_new(const size_t item_size) { - // default to 2 elements for empty vectors - return vector_new_with_capacity(item_size, 2); + return vector_new_with_capacity(item_size, 1); } Vector *vector_new_with_capacity(const size_t item_size, const size_t capacity) @@ -20,10 +19,21 @@ Vector *vector_new_with_capacity(const size_t item_size, const size_t capacity) return vector; } -void vector_delete(Vector *a) +Vector *vector_dup(const Vector *vec) { - free(a->p); - free(a); + Vector *copy = malloc(sizeof(Vector)); + copy->bytes = vec->bytes; + copy->size = vec->size; + copy->capacity = vec->capacity; + copy->p = calloc(copy->capacity, copy->bytes); + memcpy(copy->p, vec->p, copy->bytes * copy->size); + return copy; +} + +void vector_delete(Vector *vec) +{ + free(vec->p); + free(vec); } static uint64_t next_power_of_2(uint64_t v) diff --git a/test/Makefile b/test/Makefile index 4f8033a..ac96f75 100644 --- a/test/Makefile +++ b/test/Makefile @@ -48,9 +48,10 @@ gc: ../lib/gc/src/gc.c ../lib/gc/src/log.c test_list: test_setup gc $(CC) $(CFLAGS) -MMD -c test_list.c -o $(BUILD_DIR)/test/test_list.o $(CC) $(LDFLAGS) $(LDLIBS) \ - $(BUILD_DIR)/lib/gc/src/gc.o \ + $(BUILD_DIR)/lib/gc/src/gc.o \ $(BUILD_DIR)/lib/gc/src/log.o \ - $(BUILD_DIR)/src/value.o \ + $(BUILD_DIR)/src/value.o \ + $(BUILD_DIR)/src/vector.o \ $(BUILD_DIR)/test/test_list.o -o $(BUILD_DIR)/test/test_list # @@ -89,6 +90,7 @@ test_env: test_setup gc $(BUILD_DIR)/src/list.o \ $(BUILD_DIR)/src/map.o \ $(BUILD_DIR)/src/primes.o \ + $(BUILD_DIR)/src/vector.o \ $(BUILD_DIR)/src/value.o \ $(BUILD_DIR)/test/test_env.o -o $(BUILD_DIR)/test/test_env @@ -102,6 +104,7 @@ test_ir: test_setup gc $(BUILD_DIR)/lib/gc/src/gc.o \ $(BUILD_DIR)/src/ast.o \ $(BUILD_DIR)/src/list.o \ + $(BUILD_DIR)/src/vector.o \ $(BUILD_DIR)/src/value.o \ $(BUILD_DIR)/test/test_ir.o -o $(BUILD_DIR)/test/test_ir @@ -124,6 +127,7 @@ test_map: test_setup gc $(BUILD_DIR)/lib/gc/src/gc.o \ $(BUILD_DIR)/src/djb2.o \ $(BUILD_DIR)/src/list.o \ + $(BUILD_DIR)/src/vector.o \ $(BUILD_DIR)/src/value.o \ $(BUILD_DIR)/src/primes.o \ $(BUILD_DIR)/test/test_map.o -o $(BUILD_DIR)/test/test_map @@ -139,6 +143,7 @@ test_parser: test_setup gc $(BUILD_DIR)/lib/gc/src/gc.o \ $(BUILD_DIR)/src/lexer.o \ $(BUILD_DIR)/src/list.o \ + $(BUILD_DIR)/src/vector.o \ $(BUILD_DIR)/src/value.o \ $(BUILD_DIR)/test/test_parser.o -o $(BUILD_DIR)/test/test_parser diff --git a/test/test_vector.c b/test/test_vector.c index 02272e6..24102aa 100644 --- a/test/test_vector.c +++ b/test/test_vector.c @@ -93,6 +93,18 @@ static char *test_vector() mu_assert(*vector_typed_pop_front(a, int) == 3, "Pop front 3 fail"); mu_assert(vector_typed_pop_front(a, int) == NULL, "Pop on empty vector must return NULL"); + int i4[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + vector_push_back(a, i4, 11); + Vector *cpy = vector_dup(a); + mu_assert(vector_size(a) == vector_size(cpy), "Size of copy must equal size of original"); + for (size_t k = 0; k < 11; ++k) { + vector_pop_front(a); + } + for (int k = 0; k < 11; ++k) { + mu_assert(*vector_typed_pop_front(cpy, int) == k, "Copy must be invaraint to modification of original"); + } + + // clean up vector_delete(a); return 0; From 52765017f28e50cd962751e6a063a1232d0c988e Mon Sep 17 00:00:00 2001 From: Marc Kirchner Date: Wed, 22 Jun 2022 09:02:49 +0200 Subject: [PATCH 3/4] Add nth and first for vectors --- include/value.h | 24 ++++++++++++------------ src/core.c | 39 +++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/include/value.h b/include/value.h index f08ec24..96ec230 100644 --- a/include/value.h +++ b/include/value.h @@ -19,18 +19,18 @@ #define VECTOR(v) (v->value.vector) typedef enum { - VALUE_BOOL, - VALUE_BUILTIN_FN, - VALUE_EXCEPTION, - VALUE_FLOAT, - VALUE_FN, - VALUE_INT, - VALUE_LIST, - VALUE_MACRO_FN, - VALUE_NIL, - VALUE_STRING, - VALUE_SYMBOL, - VALUE_VECTOR + VALUE_BOOL = 1 << 0, + VALUE_BUILTIN_FN = 1 << 1, + VALUE_EXCEPTION = 1 << 2, + VALUE_FLOAT = 1 << 3, + VALUE_FN = 1 << 4, + VALUE_INT = 1 << 5, + VALUE_LIST = 1 << 6, + VALUE_MACRO_FN = 1 << 7, + VALUE_NIL = 1 << 8, + VALUE_STRING = 1 << 9, + VALUE_SYMBOL = 1 << 10, + VALUE_VECTOR = 1 << 11 } ValueType; extern const char *value_type_names[]; diff --git a/src/core.c b/src/core.c index 702d492..89aa87d 100644 --- a/src/core.c +++ b/src/core.c @@ -21,7 +21,7 @@ } while (0) #define REQUIRE_VALUE_TYPE(value, t, msg) do {\ - if (value->type != t) {\ + if (!(value->type & (t))) {\ LOG_CRITICAL("%s: expected %s, got %s", msg, value_type_names[t], value_type_names[value->type]);\ exc_set(value_make_exception("%s: expected %s, got %s", msg, value_type_names[t], value_type_names[value->type]));\ return NULL;\ @@ -297,6 +297,9 @@ static Value *cmp_lt(const Value *a, const Value *b) case VALUE_LIST: exc_set(value_make_exception("Cannot order lists")); return NULL; + case VALUE_VECTOR: + exc_set(value_make_exception("Cannot order lists")); + return NULL; } } else if (a->type == VALUE_INT && b->type == VALUE_FLOAT) { return ((float) INT(a)) < FLOAT(b) ? VALUE_CONST_TRUE : VALUE_CONST_FALSE; @@ -600,12 +603,13 @@ Value *core_prn(const Value *args) Value *core_count(const Value *args) { CHECK_ARGLIST(args); - Value *list = ARG(args, 0); - if (is_nil(list)) { - return value_new_int(0); + const Value *seq = ARG(args, 0); + size_t cnt = 0; + if (!is_nil(seq)) { + REQUIRE_VALUE_TYPE(seq, VALUE_LIST | VALUE_VECTOR, "count requires a sequence argument"); + cnt = is_list(seq) ? list_size(LIST(seq)) : vector_size(VECTOR(seq)); } - REQUIRE_VALUE_TYPE(list, VALUE_LIST, "count requires a list argument"); - return value_new_int(NARGS(list)); + return value_new_int(cnt); } Value *core_slurp(const Value *args) @@ -822,14 +826,19 @@ Value *core_nth(const Value *args) CHECK_ARGLIST(args); REQUIRE_LIST_CARDINALITY(args, 2ul, "NTH takes exactly two arguments"); Value *coll = ARG(args, 0); - REQUIRE_VALUE_TYPE(coll, VALUE_LIST, "First argument to nth must be a collection"); + REQUIRE_VALUE_TYPE(coll, VALUE_LIST | VALUE_VECTOR, "First argument to nth must be a list or a vector"); Value *pos = ARG(args, 1); REQUIRE_VALUE_TYPE(pos, VALUE_INT, "Second argument to nth must be an integer"); if (INT(pos) < 0 || (unsigned) INT(pos) >= NARGS(coll)) { exc_set(value_make_exception("Index error")); return NULL; } - return ARG(coll, (unsigned) INT(pos)); + if (is_list(coll)) { + return list_nth(LIST(coll), INT(pos)); + } else { + return vector_typed_at(VECTOR(coll), INT(pos), Value); + } + return NULL; } Value *core_first(const Value *args) @@ -837,12 +846,18 @@ Value *core_first(const Value *args) // (first coll) CHECK_ARGLIST(args); REQUIRE_LIST_CARDINALITY(args, 1ul, "FIRST takes exactly one argument"); - Value *coll = ARG(args, 0); - if (is_nil(coll) || NARGS(coll) == 0) { + const Value *coll = ARG(args, 0); + if (is_nil(coll)) { return VALUE_CONST_NIL; } - REQUIRE_VALUE_TYPE(coll, VALUE_LIST, "Argument to FIRST must be a collection or NIL"); - return ARG(coll, 0); + REQUIRE_VALUE_TYPE(coll, VALUE_LIST | VALUE_VECTOR, "Argument to FIRST must be a collection or NIL"); + if (is_list(coll)) { + if (list_size(LIST(coll)) == 0) return VALUE_CONST_NIL; + return ARG(coll, 0); + } else { + if (vector_size(VECTOR(coll)) == 0) return VALUE_CONST_NIL; + return vector_typed_at(VECTOR(coll), 0, Value); + } } Value *core_rest(const Value *args) From 235c0bd10c1909a34d6cffccd33f017141b767fb Mon Sep 17 00:00:00 2001 From: Marc Kirchner Date: Wed, 22 Jun 2022 09:12:22 +0200 Subject: [PATCH 4/4] Add empty? for vectors --- src/core.c | 7 ++++--- test/lang/core.stt | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/core.c b/src/core.c index 89aa87d..b742c81 100644 --- a/src/core.c +++ b/src/core.c @@ -120,9 +120,10 @@ Value *core_is_empty(const Value *args) { CHECK_ARGLIST(args); REQUIRE_LIST_CARDINALITY(args, 1ul, "empty? requires exactly one parameter"); - Value *arg0 = ARG(args, 0); - REQUIRE_VALUE_TYPE(arg0, VALUE_LIST, "empty? requires a list type"); - return NARGS(arg0) == 0 ? VALUE_CONST_TRUE : VALUE_CONST_FALSE; + const Value *seq = ARG(args, 0); + REQUIRE_VALUE_TYPE(seq, VALUE_LIST | VALUE_VECTOR, "empty? requires a list type"); + size_t cnt = is_list(seq) ? list_size(LIST(seq)) : vector_size(VECTOR(seq)); + return cnt ? VALUE_CONST_FALSE : VALUE_CONST_TRUE; } static float acc_add(float acc, float x) diff --git a/test/lang/core.stt b/test/lang/core.stt index 413ff65..688fc39 100644 --- a/test/lang/core.stt +++ b/test/lang/core.stt @@ -41,6 +41,19 @@ (check (= (count nil) 0)) (check (= (count (list)) 0))))) +(define test-vector + (lambda () + (do + (check (= (vector? (vector)) true)) + (check (= (vector? (vector 1 2 3)) true)) + (check (= (count (vector 1 2 3)) 3)) + (check (= (nth (vector 1 2 3) 2) 3)) + (check (= (first (vector 1 2 3)) 1)) + (check (= (list? (first (vector '(1 2) 3))) true)) + (check (= (count (vector)) 0)) + (check (= (empty? (vector)) true)) + (check (= (empty? (vector 1)) false))))) + (define fib (lambda (n) (if (= n 0) @@ -84,6 +97,7 @@ (test-arithmetic) (test-env) (test-list) +(test-vector) (test-conditionals) (test-apply) (test-map)