From 66c5134eb58e8566f564d59776bd5431c6333828 Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Sun, 19 Oct 2025 23:04:04 +0100 Subject: Remove impl folder --- build.sh | 2 +- constructor.c | 64 ++++++++++ impl/constructor.c | 64 ---------- impl/stream.c | 340 ----------------------------------------------------- impl/sv.c | 27 ----- impl/symtable.c | 75 ------------ impl/sys.c | 77 ------------ impl/tag.c | 76 ------------ impl/test.c | 238 ------------------------------------- impl/vec.c | 94 --------------- main.c | 48 +------- stream.c | 340 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sv.c | 27 +++++ symtable.c | 75 ++++++++++++ sys.c | 77 ++++++++++++ tag.c | 76 ++++++++++++ test.c | 2 +- vec.c | 94 +++++++++++++++ 18 files changed, 759 insertions(+), 1037 deletions(-) create mode 100644 constructor.c delete mode 100644 impl/constructor.c delete mode 100644 impl/stream.c delete mode 100644 impl/sv.c delete mode 100644 impl/symtable.c delete mode 100644 impl/sys.c delete mode 100644 impl/tag.c delete mode 100644 impl/test.c delete mode 100644 impl/vec.c create mode 100644 stream.c create mode 100644 sv.c create mode 100644 symtable.c create mode 100644 sys.c create mode 100644 tag.c create mode 100644 vec.c diff --git a/build.sh b/build.sh index a3b91c6..c8e8cec 100644 --- a/build.sh +++ b/build.sh @@ -4,7 +4,7 @@ set -xe CFLAGS="-Wall -Wextra -std=c11 -ggdb -fsanitize=address -fsanitize=undefined -Wswitch -Wswitch-enum" LINK="-I." -LIB=$(find "./impl" -name "*.c" -not -name "main.c" -not -name "test.c") +LIB=$(find "./" -name "*.c" -not -name "main.c" -not -name "test.c") OUT="alisp.out" build() { diff --git a/constructor.c b/constructor.c new file mode 100644 index 0000000..e0fab52 --- /dev/null +++ b/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 . + + * Created: 2025-08-20 + * Description: Lisp constructors/destructors + */ + +#include + +#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/constructor.c b/impl/constructor.c deleted file mode 100644 index 4c8efa4..0000000 --- a/impl/constructor.c +++ /dev/null @@ -1,64 +0,0 @@ -/* 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 . - - * Created: 2025-08-20 - * Description: Lisp constructors/destructors - */ - -#include - -#include - -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 deleted file mode 100644 index 7b5e4e7..0000000 --- a/impl/stream.c +++ /dev/null @@ -1,340 +0,0 @@ -/* 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 . - - * Created: 2025-08-26 - * Description: Stream implementation - */ - -#include - -#include - -stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents) -{ - if (!stream) - return STREAM_ERR_INVALID_PTR; - name = name ? name : ""; - 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_pipe(stream_t *stream, char *name, FILE *pipe) -{ - if (!stream || !pipe) - return STREAM_ERR_INVALID_PTR; - name = name ? name : ""; - memset(stream, 0, sizeof(*stream)); - - stream->type = STREAM_TYPE_PIPE; - stream->name = name; - stream->pipe.file = pipe; - - vec_init(&stream->pipe.cache, STREAM_DEFAULT_CHUNK); - - 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 : ""; - 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_PIPE: - 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_PIPE: - 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_PIPE: - 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_PIPE: - case STREAM_TYPE_FILE: - return feof(stream->pipe.file) && - stream->position >= stream->pipe.cache.size; - default: - FAIL("Unreachable"); - return 0; - } -} - -bool stream_chunk(stream_t *stream) -{ - assert(stream); - u64 to_read = STREAM_DEFAULT_CHUNK; - switch (stream->type) - { - case STREAM_TYPE_STRING: - // vacuously true - return true; - case STREAM_TYPE_PIPE: - to_read = 1; - // fallthrough - case STREAM_TYPE_FILE: - { - if (feof(stream->pipe.file)) - // We can't read anymore. End of the line - return false; - vec_ensure_free(&stream->pipe.cache, to_read); - int read = fread(vec_data(&stream->pipe.cache) + stream->pipe.cache.size, 1, - to_read, stream->pipe.file); - - // If we read something it's a good thing - if (read > 0) - { - stream->pipe.cache.size += read; - return true; - } - else - return false; - } - 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 we've reached end of stream, and end of content, there's really nothing - // to check here. - if (stream_eoc(stream)) - return '\0'; - - switch (stream->type) - { - case STREAM_TYPE_STRING: - return stream->string.data[stream->position]; - case STREAM_TYPE_PIPE: - case STREAM_TYPE_FILE: - { - // Cached already? We are done. - if (stream->position < stream->pipe.cache.size) - { - const char *const str = vec_data(&stream->pipe.cache); - return str[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_eoc(stream)) - return false; - - switch (stream->type) - { - case STREAM_TYPE_STRING: - { - if (stream->position + offset >= stream->string.size) - return false; - - stream->position += offset; - return true; - } - case STREAM_TYPE_PIPE: - 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_eoc(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_eoc(stream)) - return SV(NULL, 0); - - // See if I can go forward enough to make this substring - u64 current_position = stream->position; - bool successful = stream_seek_forward(stream, size); - // Reset the position in either situation - 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_PIPE: - 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_PIPE: - case STREAM_TYPE_FILE: - { - if (index + size <= stream_size(stream)) - return SV(vec_data(&stream->pipe.cache) + index, size); - // (index + size > stream_size(stream)) => 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 deleted file mode 100644 index 97774c6..0000000 --- a/impl/sv.c +++ /dev/null @@ -1,27 +0,0 @@ -/* 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 . - - * Created: 2025-08-21 - * Description: String views - */ - -#include -#include - -#include - -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 deleted file mode 100644 index abef727..0000000 --- a/impl/symtable.c +++ /dev/null @@ -1,75 +0,0 @@ -/* 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 . - - * Created: 2025-08-19 - * Description: Symbol Table implementation - */ - -#include - -#include -#include - -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 deleted file mode 100644 index 4dcd664..0000000 --- a/impl/sys.c +++ /dev/null @@ -1,77 +0,0 @@ -/* 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 . - - * Created: 2025-08-20 - * Description: System management - */ - -#include - -#include -#include -#include - -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 deleted file mode 100644 index e6396cc..0000000 --- a/impl/tag.c +++ /dev/null @@ -1,76 +0,0 @@ -/* 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 . - - * Created: 2025-08-19 - * Description: Pointer tagging - */ - -#include -#include - -#include - -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 deleted file mode 100644 index e517286..0000000 --- a/impl/test.c +++ /dev/null @@ -1,238 +0,0 @@ -/* 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 . - - * Created: 2025-08-21 - * Description: Tests - */ - -#include -#include - -#include - -#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 deleted file mode 100644 index 12f577c..0000000 --- a/impl/vec.c +++ /dev/null @@ -1,94 +0,0 @@ -/* 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 . - - * Created: 2025-08-20 - * Description: Stable Vector implementation - */ - -#include -#include - -#include - -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); -} diff --git a/main.c b/main.c index 4db6f68..788df7a 100644 --- a/main.c +++ b/main.c @@ -16,54 +16,14 @@ #include #include -#include +#include "./alisp.h" int main(void) { stream_t stream = {0}; - char _data[] = "Hello, world!"; - sv_t data = SV(_data, ARRSIZE(_data) - 1); - char filename[] = "lorem.txt"; - - // stream_init_string(&stream, NULL, data); - - FILE *fp = fopen(filename, "rb"); - stream_init_file(&stream, filename, fp); - - // stream_init_file(&stream, "stdin", stdin); - - /// test 1 - do - { - printf("%s[%lu]: `%c`\n", stream.name, stream.position, - stream_next(&stream)); - } while (!stream_eoc(&stream)); - printf("%lu/%lu\n", stream.position, stream_size(&stream)); - - /// test 2 - stream.position = 0; - sv_t a = stream_substr(&stream, 100); - sv_t a_ = sv_copy(a); - stream_seek(&stream, 100); - sv_t b = stream_substr_abs(&stream, 0, 100); - sv_t b_ = sv_copy(b); - printf("a=b ? %s\na_=b_ ? %s\n", - memcmp(&a, &b, sizeof(a)) == 0 ? "yes" : "no", - a_.size == b_.size && strncmp(a_.data, b_.data, a_.size) == 0 ? "yes" - : "no"); - sv_t c = stream_substr(&stream, 100); - sv_t c_ = sv_copy(c); - stream_seek(&stream, 100); - sv_t d = stream_substr_abs(&stream, stream.position - 100, 100); - sv_t d_ = sv_copy(d); - printf("c=d ? %s\nc_=d_ ? %s\n", - memcmp(&c, &d, sizeof(a)) == 0 ? "yes" : "no", - c_.size == d_.size && strncmp(c_.data, d_.data, c_.size) == 0 ? "yes" - : "no"); - - printf("eoc?=%s\n", stream_eoc(&stream) ? "yes" : "no"); - + stream_init_pipe(&stream, "", stdin); + sv_t sv = stream_substr(&stream, 10); + printf("=> `" PR_SV "`\n", SV_FMT(sv)); stream_stop(&stream); - // fclose(fp); return 0; } diff --git a/stream.c b/stream.c new file mode 100644 index 0000000..e163066 --- /dev/null +++ b/stream.c @@ -0,0 +1,340 @@ +/* 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 . + + * Created: 2025-08-26 + * Description: Stream implementation + */ + +#include + +#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 : ""; + 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_pipe(stream_t *stream, char *name, FILE *pipe) +{ + if (!stream || !pipe) + return STREAM_ERR_INVALID_PTR; + name = name ? name : ""; + memset(stream, 0, sizeof(*stream)); + + stream->type = STREAM_TYPE_PIPE; + stream->name = name; + stream->pipe.file = pipe; + + vec_init(&stream->pipe.cache, STREAM_DEFAULT_CHUNK); + + 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 : ""; + 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_PIPE: + 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_PIPE: + 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_PIPE: + 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_PIPE: + case STREAM_TYPE_FILE: + return feof(stream->pipe.file) && + stream->position >= stream->pipe.cache.size; + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_chunk(stream_t *stream) +{ + assert(stream); + u64 to_read = STREAM_DEFAULT_CHUNK; + switch (stream->type) + { + case STREAM_TYPE_STRING: + // vacuously true + return true; + case STREAM_TYPE_PIPE: + to_read = 1; + // fallthrough + case STREAM_TYPE_FILE: + { + if (feof(stream->pipe.file)) + // We can't read anymore. End of the line + return false; + vec_ensure_free(&stream->pipe.cache, to_read); + int read = fread(vec_data(&stream->pipe.cache) + stream->pipe.cache.size, 1, + to_read, stream->pipe.file); + + // If we read something it's a good thing + if (read > 0) + { + stream->pipe.cache.size += read; + return true; + } + else + return false; + } + 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 we've reached end of stream, and end of content, there's really nothing + // to check here. + if (stream_eoc(stream)) + return '\0'; + + switch (stream->type) + { + case STREAM_TYPE_STRING: + return stream->string.data[stream->position]; + case STREAM_TYPE_PIPE: + case STREAM_TYPE_FILE: + { + // Cached already? We are done. + if (stream->position < stream->pipe.cache.size) + { + const char *const str = vec_data(&stream->pipe.cache); + return str[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_eoc(stream)) + return false; + + switch (stream->type) + { + case STREAM_TYPE_STRING: + { + if (stream->position + offset >= stream->string.size) + return false; + + stream->position += offset; + return true; + } + case STREAM_TYPE_PIPE: + 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_eoc(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_eoc(stream)) + return SV(NULL, 0); + + // See if I can go forward enough to make this substring + u64 current_position = stream->position; + bool successful = stream_seek_forward(stream, size); + // Reset the position in either situation + 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_PIPE: + 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_PIPE: + case STREAM_TYPE_FILE: + { + if (index + size <= stream_size(stream)) + return SV(vec_data(&stream->pipe.cache) + index, size); + // (index + size > stream_size(stream)) => 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/sv.c b/sv.c new file mode 100644 index 0000000..7debd30 --- /dev/null +++ b/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 . + + * Created: 2025-08-21 + * Description: String views + */ + +#include +#include + +#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/symtable.c b/symtable.c new file mode 100644 index 0000000..39139eb --- /dev/null +++ b/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 . + + * Created: 2025-08-19 + * Description: Symbol Table implementation + */ + +#include "./alisp.h" + +#include +#include + +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/sys.c b/sys.c new file mode 100644 index 0000000..d01b493 --- /dev/null +++ b/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 . + + * Created: 2025-08-20 + * Description: System management + */ + +#include "./alisp.h" + +#include +#include +#include + +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/tag.c b/tag.c new file mode 100644 index 0000000..8ca688a --- /dev/null +++ b/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 . + + * Created: 2025-08-19 + * Description: Pointer tagging + */ + +#include +#include + +#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/test.c b/test.c index e517286..c4c1838 100644 --- a/test.c +++ b/test.c @@ -16,7 +16,7 @@ #include #include -#include +#include "./alisp.h" #define TEST_PASSED() printf("[%s]: Pass\n", __func__) #define TEST(NAME, COND) \ diff --git a/vec.c b/vec.c new file mode 100644 index 0000000..66a9682 --- /dev/null +++ b/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 . + + * Created: 2025-08-20 + * Description: Stable Vector implementation + */ + +#include +#include + +#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); +} -- cgit v1.2.3-13-gbd6f