diff options
Diffstat (limited to 'impl')
-rw-r--r-- | impl/constructor.c | 64 | ||||
-rw-r--r-- | impl/stream.c | 296 | ||||
-rw-r--r-- | impl/sv.c | 27 | ||||
-rw-r--r-- | impl/symtable.c | 75 | ||||
-rw-r--r-- | impl/sys.c | 77 | ||||
-rw-r--r-- | impl/tag.c | 76 | ||||
-rw-r--r-- | impl/test.c | 238 | ||||
-rw-r--r-- | impl/vec.c | 94 |
8 files changed, 947 insertions, 0 deletions
diff --git a/impl/constructor.c b/impl/constructor.c new file mode 100644 index 0000000..4c8efa4 --- /dev/null +++ b/impl/constructor.c @@ -0,0 +1,64 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Unlicense for details. + + * You may distribute and modify this code under the terms of the Unlicense, + * which you should have received a copy of along with this program. If not, + * please go to <https://unlicense.org/>. + + * Created: 2025-08-20 + * Description: Lisp constructors/destructors + */ + +#include <malloc.h> + +#include <alisp.h> + +lisp_t *make_int(i64 i) +{ + return tag_int(i); +} + +lisp_t *cons(sys_t *sys, lisp_t *car, lisp_t *cdr) +{ + cons_t *cons = calloc(1, sizeof(*cons)); + cons->car = car; + cons->cdr = cdr; + + lisp_t *lcons = tag_cons(cons); + sys_register(sys, lcons); + return lcons; +} + +lisp_t *make_vec(sys_t *sys, u64 capacity) +{ + vec_t *vec = calloc(1, sizeof(*vec)); + vec_init(vec, capacity); + lisp_t *ptr = tag_vec(vec); + sys_register(sys, ptr); + return ptr; +} + +lisp_t *intern(sys_t *sys, sv_t sv) +{ + char *str = sym_table_find(&sys->symtable, sv); + return tag_sym(str); +} + +lisp_t *car(lisp_t *lsp) +{ + if (!IS_TAG(lsp, CONS)) + return NIL; + else + return CAR(lsp); +} + +lisp_t *cdr(lisp_t *lsp) +{ + if (!IS_TAG(lsp, CONS)) + return NIL; + else + return CDR(lsp); +} diff --git a/impl/stream.c b/impl/stream.c new file mode 100644 index 0000000..3f109be --- /dev/null +++ b/impl/stream.c @@ -0,0 +1,296 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Unlicense for details. + + * You may distribute and modify this code under the terms of the Unlicense, + * which you should have received a copy of along with this program. If not, + * please go to <https://unlicense.org/>. + + * Created: 2025-08-26 + * Description: Stream implementation + */ + +#include <string.h> + +#include <alisp.h> + +stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents) +{ + if (!stream) + return STREAM_ERR_INVALID_PTR; + name = name ? name : "<stream>"; + memset(stream, 0, sizeof(*stream)); + + stream->type = STREAM_TYPE_STRING; + stream->name = name; + stream->string = contents; + + return STREAM_ERR_OK; +} + +stream_err_t stream_init_file(stream_t *stream, char *name, FILE *pipe) +{ + if (!stream || !pipe) + return STREAM_ERR_INVALID_PTR; + name = name ? name : "<stream>"; + memset(stream, 0, sizeof(*stream)); + + stream->type = STREAM_TYPE_FILE; + stream->name = name; + stream->pipe.file = pipe; + + vec_init(&stream->pipe.cache, STREAM_DEFAULT_CHUNK); + + return STREAM_ERR_OK; +} + +void stream_stop(stream_t *stream) +{ + if (!stream) + return; + switch (stream->type) + { + case STREAM_TYPE_STRING: + // Nothing to do, all dealt with outside of stream + break; + case STREAM_TYPE_FILE: + // Must cleanup vector + vec_free(&stream->pipe.cache); + break; + } + memset(stream, 0, sizeof(*stream)); +} + +u64 stream_size(stream_t *stream) +{ + assert(stream); + switch (stream->type) + { + case STREAM_TYPE_STRING: + return stream->string.size; + case STREAM_TYPE_FILE: + return stream->pipe.cache.size; + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_eos(stream_t *stream) +{ + assert(stream); + switch (stream->type) + { + case STREAM_TYPE_STRING: + return stream->position >= stream->string.size; + case STREAM_TYPE_FILE: + return feof(stream->pipe.file); + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_eoc(stream_t *stream) +{ + assert(stream); + switch (stream->type) + { + case STREAM_TYPE_STRING: + return stream->position >= stream->string.size; + case STREAM_TYPE_FILE: + return stream->position >= stream->pipe.cache.size; + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_chunk(stream_t *stream) +{ + assert(stream); + switch (stream->type) + { + case STREAM_TYPE_STRING: + // vacuously true + return true; + case STREAM_TYPE_FILE: + { + if (feof(stream->pipe.file)) + return false; + vec_ensure_free(&stream->pipe.cache, STREAM_DEFAULT_CHUNK); + int read = fread(vec_data(&stream->pipe.cache), 1, STREAM_DEFAULT_CHUNK, + stream->pipe.file); + stream->pipe.cache.size += read; + return true; + } + default: + FAIL("Unreachable"); + return 0; + } +} + +char stream_next(stream_t *stream) +{ + char c = stream_peek(stream); + if (c != '\0') + ++stream->position; + return c; +} + +char stream_peek(stream_t *stream) +{ + if (stream_eos(stream)) + return '\0'; + + switch (stream->type) + { + case STREAM_TYPE_STRING: + return stream->string.data[stream->position]; + case STREAM_TYPE_FILE: + { + // Cached already? We are done. + if (stream->position < stream->pipe.cache.size) + return ((char *)vec_data(&stream->pipe.cache))[stream->position]; + + // Try to read chunks in till we've reached it or we're at the end of the + // file. + for (bool read_chunk = stream_chunk(stream); + read_chunk && stream->position >= stream->pipe.cache.size; + read_chunk = stream_chunk(stream)) + continue; + + // Same principle as the stream_eos(stream) check. + if (stream->position >= stream->pipe.cache.size) + return '\0'; + return ((char *)vec_data(&stream->pipe.cache))[stream->position]; + } + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_seek(stream_t *stream, i64 offset) +{ + if (offset < 0) + return stream_seek_backward(stream, offset * -1); + else if (offset > 0) + return stream_seek_forward(stream, offset); + else + // vacuously successful + return true; +} + +bool stream_seek_forward(stream_t *stream, u64 offset) +{ + if (stream_eos(stream)) + return false; + switch (stream->type) + { + case STREAM_TYPE_STRING: + { + if (stream->position + offset < stream->string.size) + { + stream->position += offset; + return true; + } + return false; + } + case STREAM_TYPE_FILE: + { + // Similar principle as stream_peek really... + + // Cached already? We are done. + if (stream->position + offset < stream->pipe.cache.size) + { + stream->position += offset; + return true; + } + + // Try to read chunks in till we've reached it or we're at the end of the + // file. + for (bool read_chunk = stream_chunk(stream); + read_chunk && stream->position + offset >= stream->pipe.cache.size; + read_chunk = stream_chunk(stream)) + continue; + + // Same principle as the stream_eos(stream) check. + if (stream->position + offset >= stream->pipe.cache.size) + return false; + stream->position += offset; + return true; + } + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_seek_backward(stream_t *stream, u64 offset) +{ + assert(stream); + if (stream->position < offset) + return false; + stream->position -= offset; + return true; +} + +sv_t stream_substr(stream_t *stream, u64 size) +{ + if (stream_eos(stream)) + return SV(NULL, 0); + u64 current_position = stream->position; + bool successful = stream_seek_forward(stream, size); + // In case we did happen to move forward + stream->position = current_position; + if (!successful) + return SV(NULL, 0); + + char *ptr = NULL; + switch (stream->type) + { + case STREAM_TYPE_STRING: + ptr = stream->string.data; + break; + case STREAM_TYPE_FILE: + ptr = vec_data(&stream->pipe.cache); + break; + default: + FAIL("Unreachable"); + return SV(NULL, 0); + } + + return SV(ptr + stream->position, size); +} + +sv_t stream_substr_abs(stream_t *stream, u64 index, u64 size) +{ + + switch (stream->type) + { + case STREAM_TYPE_STRING: + if (index + size < stream_size(stream)) + return SV(stream->string.data + index, size); + return SV(NULL, 0); + case STREAM_TYPE_FILE: + { + if (index + size < stream_size(stream)) + return SV(vec_data(&stream->pipe.cache) + index, size); + // stream_size(stream) <= index + size => try reading chunks + for (bool read_chunk = stream_chunk(stream); + read_chunk && index + size >= stream->pipe.cache.size; + read_chunk = stream_chunk(stream)) + continue; + + if (index + size >= stream_size(stream)) + return SV(NULL, 0); + return SV(vec_data(&stream->pipe.cache) + index, size); + } + default: + assert("Unreachable"); + return SV(NULL, 0); + } +} diff --git a/impl/sv.c b/impl/sv.c new file mode 100644 index 0000000..97774c6 --- /dev/null +++ b/impl/sv.c @@ -0,0 +1,27 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Unlicense + * for details. + + * You may distribute and modify this code under the terms of the + * Unlicense, which you should have received a copy of along with this + * program. If not, please go to <https://unlicense.org/>. + + * Created: 2025-08-21 + * Description: String views + */ + +#include <malloc.h> +#include <string.h> + +#include <alisp.h> + +sv_t sv_copy(sv_t old) +{ + char *newstr = calloc(1, (old.size + 1) * sizeof(*newstr)); + memcpy(newstr, old.data, old.size); + newstr[old.size] = '\0'; + return SV(newstr, old.size); +} diff --git a/impl/symtable.c b/impl/symtable.c new file mode 100644 index 0000000..abef727 --- /dev/null +++ b/impl/symtable.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Unlicense for details. + + * You may distribute and modify this code under the terms of the Unlicense, + * which you should have received a copy of along with this program. If not, + * please go to <https://unlicense.org/>. + + * Created: 2025-08-19 + * Description: Symbol Table implementation + */ + +#include <alisp.h> + +#include <malloc.h> +#include <string.h> + +u64 djb2(sv_t string) +{ + u64 hash = 5381; + for (u64 i = 0; i < string.size; ++i) + hash = string.data[i] + (hash + (hash << 5)); + return hash; +} + +void sym_table_init(sym_table_t *table) +{ + table->capacity = MAX(table->capacity, SYM_TABLE_INIT_SIZE); + table->count = 0; + vec_init(&table->entries, table->capacity * sizeof(sv_t)); +} + +char *sym_table_find(sym_table_t *table, sv_t sv) +{ + // Initialise the table if it's not done already + if (table->entries.capacity == 0) + sym_table_init(table); + + // TODO: Deal with resizing this when table->count > table->size / 2 + u64 index = djb2(sv) & (table->capacity - 1); + + for (sv_t comp = VEC_GET(&table->entries, sv_t)[index]; comp.data; index += 1, + index = index & (table->capacity - 1), + comp = VEC_GET(&table->entries, sv_t)[index]) + // Is it present in the table? + if (sv.size == comp.size && strncmp(sv.data, comp.data, sv.size) == 0) + break; + + // we couldn't find it in our linear search (worst case scenario) + if (!VEC_GET(&table->entries, sv_t)[index].data) + { + sv_t newsv = sv_copy(sv); + VEC_GET(&table->entries, sv_t)[index] = newsv; + ++table->count; + } + + return VEC_GET(&table->entries, sv_t)[index].data; +} + +void sym_table_cleanup(sym_table_t *table) +{ + // kill the data + sv_t current = {0}; + for (u64 i = 0; i < table->capacity; ++i) + { + current = VEC_GET(&table->entries, sv_t)[i]; + if (current.data) + free(current.data); + } + // kill the container + vec_free(&table->entries); + memset(table, 0, sizeof(*table)); +} diff --git a/impl/sys.c b/impl/sys.c new file mode 100644 index 0000000..4dcd664 --- /dev/null +++ b/impl/sys.c @@ -0,0 +1,77 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Unlicense for details. + + * You may distribute and modify this code under the terms of the Unlicense, + * which you should have received a copy of along with this program. If not, + * please go to <https://unlicense.org/>. + + * Created: 2025-08-20 + * Description: System management + */ + +#include <alisp.h> + +#include <assert.h> +#include <malloc.h> +#include <string.h> + +void sys_init(sys_t *sys) +{ + memset(sys, 0, sizeof(*sys)); +} + +void sys_register(sys_t *sys, lisp_t *ptr) +{ + // Generate an unmanaged cons + cons_t *cons = calloc(1, sizeof(*cons)); + cons->car = ptr; + cons->cdr = sys->memory; + sys->memory = tag_cons(cons); +} + +void sys_cleanup(sys_t *sys) +{ + static_assert(NUM_TAGS == 5); + + sym_table_cleanup(&sys->symtable); + + if (!sys->memory) + return; + + // Iterate through each element of memory + for (lisp_t *cell = sys->memory, *next = cdr(cell); cell; + cell = next, next = cdr(next)) + { + // Only reason allocated exists is because we had to allocate memory on the + // heap for it. It's therefore enough to deal with only the allocated + // types. + lisp_t *allocated = car(cell); + switch (get_tag(allocated)) + { + case TAG_CONS: + // Delete the cons + free(as_cons(allocated)); + break; + case TAG_VEC: + { + vec_t *vec = as_vec(allocated); + vec_free(vec); + free(vec); + break; + } + case TAG_NIL: + case TAG_INT: + case TAG_SYM: + case NUM_TAGS: + // shouldn't be dealt with (either constant or dealt with elsewhere) + break; + } + + // Then free the current cell + free(as_cons(cell)); + } + memset(sys, 0, sizeof(*sys)); +} diff --git a/impl/tag.c b/impl/tag.c new file mode 100644 index 0000000..e6396cc --- /dev/null +++ b/impl/tag.c @@ -0,0 +1,76 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Unlicense for details. + + * You may distribute and modify this code under the terms of the Unlicense, + * which you should have received a copy of along with this program. If not, + * please go to <https://unlicense.org/>. + + * Created: 2025-08-19 + * Description: Pointer tagging + */ + +#include <assert.h> +#include <stdlib.h> + +#include <alisp.h> + +lisp_t *tag_int(i64 i) +{ + return TAG((u64)i, INT); +} + +lisp_t *tag_sym(char *str) +{ + return TAG((u64)str, SYM); +} + +lisp_t *tag_vec(vec_t *vec) +{ + return TAG((u64)vec, VEC); +} + +lisp_t *tag_cons(cons_t *cons) +{ + return TAG((u64)cons, CONS); +} + +tag_t get_tag(lisp_t *lisp) +{ + static_assert(NUM_TAGS == 5); + if (!lisp) + return TAG_NIL; + else if (IS_TAG(lisp, INT)) + return TAG_INT; + + return (u64)lisp & 0xFF; +} + +i64 as_int(lisp_t *obj) +{ + assert(IS_TAG(obj, INT)); + u64 p_obj = (u64)obj; + return UNTAG(p_obj, INT) | // Delete the tag + (NTH_BYTE(p_obj, 7) & 0x80) << 56 // duplicate the MSB (preserve sign) + ; +} + +char *as_sym(lisp_t *obj) +{ + assert(IS_TAG(obj, SYM)); + return (char *)UNTAG(obj, SYM); +} + +cons_t *as_cons(lisp_t *obj) +{ + assert(IS_TAG(obj, CONS)); + return (cons_t *)UNTAG(obj, CONS); +} + +vec_t *as_vec(lisp_t *obj) +{ + assert(IS_TAG(obj, VEC)); + return (vec_t *)UNTAG(obj, VEC); +} diff --git a/impl/test.c b/impl/test.c new file mode 100644 index 0000000..e517286 --- /dev/null +++ b/impl/test.c @@ -0,0 +1,238 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Unlicense + * for details. + + * You may distribute and modify this code under the terms of the + * Unlicense, which you should have received a copy of along with this + * program. If not, please go to <https://unlicense.org/>. + + * Created: 2025-08-21 + * Description: Tests + */ + +#include <stdio.h> +#include <string.h> + +#include <alisp.h> + +#define TEST_PASSED() printf("[%s]: Pass\n", __func__) +#define TEST(NAME, COND) \ + do \ + { \ + if (!(COND)) \ + { \ + printf("[%s]: `%s` failed!\n", __func__, (NAME)); \ + assert(0); \ + } \ + } while (0) + +// Sample data +const char *unique_words[] = { + "bibendum", "etiam", "gravida", "dui", "cursus", + "purus", "diam", "phasellus", "nam", "fermentum", + "leo", "enim", "ac", "semper", "non", + "mauris", "proin", "tellus", "vivamus", "lobortis", + "lacus", "neque", "in", "nullam", "felis", + "orci", "pede", "tempus", "nec", "at", + "tortor", "massa", "sed", "magna", "eget", + "tempor", "velit", "imperdiet", "praesent", "volutpat", + "tristique", "id", "commodo", "aliquet", "quis", + "pellentesque", "eleifend", "porta", "nunc", "euismod", + "aliquam", "a", "erat", "dignissim", "ut", + "vitae", "vel", "donec", +}; + +char *words[] = { + "aliquam", "erat", "volutpat", "nunc", "eleifend", + "leo", "vitae", "magna", "in", "id", + "erat", "non", "orci", "commodo", "lobortis", + "proin", "neque", "massa", "cursus", "ut", + "gravida", "ut", "lobortis", "eget", "lacus", + "sed", "diam", "praesent", "fermentum", "tempor", + "tellus", "nullam", "tempus", "mauris", "ac", + "felis", "vel", "velit", "tristique", "imperdiet", + "donec", "at", "pede", "etiam", "vel", + "neque", "nec", "dui", "dignissim", "bibendum", + "vivamus", "id", "enim", "phasellus", "neque", + "orci", "porta", "a", "aliquet", "quis", + "semper", "a", "massa", "phasellus", "purus", + "pellentesque", "tristique", "imperdiet", "tortor", "nam", + "euismod", "tellus", "id", "erat", +}; + +char words_text[] = + "aliquam erat volutpat nunc eleifend leo vitae magna in id erat non orci " + "commodo lobortis proin neque massa cursus ut gravida ut lobortis eget " + "lacus sed diam praesent fermentum tempor tellus nullam tempus mauris ac " + "felis vel velit tristique imperdiet donec at pede etiam vel neque nec dui " + "dignissim bibendum vivamus id enim phasellus neque orci porta a aliquet " + "quis semper a massa phasellus purus pellentesque tristique imperdiet " + "tortor nam euismod tellus id erat"; + +char text[] = + "Pellentesque dapibus suscipit ligula. Donec posuere augue in quam. " + "Etiam vel tortor sodales tellus ultricies commodo. Suspendisse potenti. " + "Aenean in sem ac leo mollis blandit. Donec neque quam, dignissim in, " + "mollis nec, sagittis eu, wisi. Phasellus lacus. Etiam laoreet quam sed " + "arcu. Phasellus at dui in ligula mollis ultricies. Integer placerat " + "tristique nisl. Praesent augue. Fusce commodo. Vestibulum convallis, " + "lorem a tempus semper, dui dui euismod elit, vitae placerat urna tortor " + "vitae lacus. Nullam libero mauris, consequat quis, varius et, dictum id, " + "arcu. Mauris mollis tincidunt felis. Aliquam feugiat tellus ut neque. " + "Nulla facilisis, risus a rhoncus fermentum, tellus tellus lacinia purus, " + "et dictum nunc justo sit amet elit."; + +void symtable_test(void) +{ + sym_table_t table = {0}; + sym_table_init(&table); + for (u64 i = 0; i < ARRSIZE(words); ++i) + sym_table_find(&table, SV(words[i], strlen(words[i]))); + + TEST("|table|=|set(words)|", table.count == ARRSIZE(unique_words)); + TEST("|table| < |words|", table.count < ARRSIZE(words)); + + TEST_PASSED(); + + sym_table_cleanup(&table); +} + +void int_test(void) +{ + i64 ints[] = { + 1, -1, (1 << 10) - 1, (-1) * ((1 << 10) - 1), INT_MIN, INT_MAX, + }; + + for (u64 i = 0; i < ARRSIZE(ints); ++i) + { + i64 in = ints[i]; + lisp_t *lisp = make_int(in); + i64 out = as_int(lisp); + + TEST("in == out", in == out); + } + + TEST_PASSED(); +} + +void sym_test(void) +{ + sys_t system = {0}; + sys_init(&system); + for (u64 i = 0; i < ARRSIZE(words); ++i) + { + char *in = words[i]; + lisp_t *lisp = intern(&system, SV(in, strlen(in))); + char *out = as_sym(lisp); + TEST("unique pointers when interning a symbol", in != out); + TEST("same size", strlen(in) == strlen(out)); + TEST("same string", strncmp(in, out, strlen(in)) == 0); + } + TEST_PASSED(); + sys_cleanup(&system); +} + +void vec_test1(void) +{ + sys_t system = {0}; + sys_init(&system); + + // Generating a vector word by word + lisp_t *lvec = make_vec(&system, 0); + for (u64 i = 0; i < ARRSIZE(words); ++i) + { + char *word = words[i]; + vec_append(as_vec(lvec), word, strlen(word)); + if (i != ARRSIZE(words) - 1) + vec_append(as_vec(lvec), " ", 1); + } + vec_append(as_vec(lvec), "\0", 1); + + vec_t *vec = as_vec(lvec); + + TEST("same size vector", vec->size == ARRSIZE(words_text)); + TEST("same as actually concatenated string", + strncmp(vec_data(vec), words_text, vec->size) == 0); + + TEST_PASSED(); + sys_cleanup(&system); +} + +void vec_test2(void) +{ + sys_t system = {0}; + sys_init(&system); + // Generating substrings + struct Test + { + u64 start, size; + } tests[] = { + {0, 16}, + {0, 32}, + {32, 64}, + {0, ARRSIZE(text)}, + }; + + for (u64 i = 0; i < ARRSIZE(tests); ++i) + { + struct Test test = tests[i]; + const sv_t substr = SV(text + test.start, test.size); + const u64 size = test.size / 2; + + lisp_t *lvec = make_vec(&system, size); + vec_append(as_vec(lvec), text + test.start, test.size); + TEST("Vector grew", as_vec(lvec)->size > size); + TEST("Vector's substring is equivalent to the actual substring", + strncmp(vec_data(as_vec(lvec)), substr.data, substr.size) == 0); + } + + printf("[vec_test2]: Pass\n"); + sys_cleanup(&system); +} + +void cons_test(void) +{ + sys_t system = {0}; + sys_init(&system); + + // Let's make a list of words using `cons` + lisp_t *lisp = NIL; + for (u64 i = 0; i < ARRSIZE(words); ++i) + { + char *word = words[i]; + lisp_t *lword = intern(&system, SV(word, strlen(word))); + lisp = cons(&system, lword, lisp); + } + + // Make sure we've essentially reversed the `words` array + u64 i = ARRSIZE(words); + for (lisp_t *iter = lisp; iter; iter = cdr(iter)) + { + lisp_t *item = car(iter); + TEST("we've reversed the array", + strncmp(words[i - 1], as_sym(item), strlen(words[i - 1])) == 0); + i -= 1; + } + + TEST_PASSED(); + + sys_cleanup(&system); +} + +typedef void (*test_fn)(void); + +const test_fn TESTS[] = { + int_test, sym_test, vec_test1, vec_test2, cons_test, +}; + +int main(void) +{ + for (u64 i = 0; i < ARRSIZE(TESTS); ++i) + { + TESTS[i](); + } + return 0; +} diff --git a/impl/vec.c b/impl/vec.c new file mode 100644 index 0000000..12f577c --- /dev/null +++ b/impl/vec.c @@ -0,0 +1,94 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Unlicense + * for details. + + * You may distribute and modify this code under the terms of the + * Unlicense, which you should have received a copy of along with this + * program. If not, please go to <https://unlicense.org/>. + + * Created: 2025-08-20 + * Description: Stable Vector implementation + */ + +#include <malloc.h> +#include <string.h> + +#include <alisp.h> + +void vec_init(vec_t *vec, u64 size) +{ + memset(vec, 0, sizeof(*vec)); + if (!vec) + return; + else if (size <= VEC_INLINE_CAPACITY) + { + vec->is_inlined = 1; + vec->capacity = VEC_INLINE_CAPACITY; + vec->ptr = NULL; + } + else + { + vec->is_inlined = 0; + vec->capacity = size; + vec->ptr = calloc(1, vec->capacity); + } +} + +void vec_free(vec_t *vec) +{ + if (!vec) + return; + if (!vec->is_inlined && vec->ptr) + free(vec->ptr); + memset(vec, 0, sizeof(*vec)); +} + +void *vec_data(vec_t *vec) +{ + return vec->is_inlined ? vec->inlined : vec->ptr; +} + +void vec_ensure_free(vec_t *vec, u64 size) +{ + if (!vec) + return; + if (vec->capacity - vec->size < size) + { + vec->capacity = MAX(vec->capacity * VEC_MULT, vec->size + size); + if (vec->is_inlined) + { + // If we're inlined, we need to allocate on the heap now. So let's copy + // vec->inlined over to vec->ptr, then turn off inlining. + + // We need to do a two-way swap since vec->ptr and vec->inlined are taking + // up the same space. + u8 buffer[VEC_INLINE_CAPACITY]; + memcpy(buffer, vec->inlined, vec->size); + vec->ptr = calloc(1, vec->capacity); + memcpy(vec->ptr, buffer, vec->size); + vec->is_inlined = 0; + } + else + vec->ptr = realloc(vec->ptr, vec->capacity); + } +} + +void vec_append(vec_t *vec, const void *const ptr, u64 size) +{ + if (!vec) + return; + vec_ensure_free(vec, size); + memcpy(vec_data(vec) + vec->size, ptr, size); + vec->size += size; +} + +void vec_clone(vec_t *dest, vec_t *src) +{ + if (!src || !dest) + return; + vec_init(dest, src->capacity); + memcpy(vec_data(dest), vec_data(src), src->size); +} |