Compare commits

...

14 Commits

Author SHA1 Message Date
Aryadev Chavali
d88523d39f allocator: rework alloc_delete
Added a switch case at start of alloc_delete to only run it on
container (read: heap allocated) types.

If adding to free vector, iterate through free vector first to ensure
we've not added it already.  Reset the object before adding it, so
reuse is trivial.
2026-03-05 22:24:10 +00:00
Aryadev Chavali
13f3de726b sys: sys_delete -> calls alloc_delete 2026-03-05 22:21:00 +00:00
Aryadev Chavali
8231cf4e14 allocator: "padding" field for alloc_metadata_t, static_assert on sizeof 2026-03-05 22:20:10 +00:00
Aryadev Chavali
55ed8c5939 lisp: lisp_reset, vec: vec_reset
Reset method to "clear the memory" of a lisp object.  Only operates on
heap allocated objects.
2026-03-05 22:11:23 +00:00
Aryadev Chavali
edce319957 main: fix issue with unused variable on release mode builds 2026-03-05 20:33:41 +00:00
Aryadev Chavali
775d9f51bf reader: add position restoration for read_vec and read_list
Same as read_str really, using a label to push control flow.
2026-03-05 20:27:13 +00:00
Aryadev Chavali
c65ec319f5 lisp: lisp_print: implement support for strings 2026-03-05 20:27:00 +00:00
Aryadev Chavali
fd9cc93c45 lisp: lisp_print: fix issue with extra space when printing vectors 2026-03-05 20:26:40 +00:00
Aryadev Chavali
e594b6ce70 reader: read_str restores stream position for no closing speechmarks
Say you have the following Lisp code: `"hello world` (no closing
speechmark).  This read_str implementation will now place
stream->position at the first speechmark rather than at the
EOF (what happened previously) which is a bit nicer.
2026-03-05 20:25:03 +00:00
Aryadev Chavali
fee6614670 reader: implement reader for strings 2026-03-05 20:13:52 +00:00
Aryadev Chavali
1998954b56 reader: slight adjustments based on change INT -> SMI 2026-03-05 20:13:18 +00:00
Aryadev Chavali
e629b9919e Makefile: added -Wswitch-enum to warning flags 2026-03-05 19:59:59 +00:00
Aryadev Chavali
37d1764c6e reader: use make_list in read_quote 2026-03-05 19:59:46 +00:00
Aryadev Chavali
cb8d1b1139 sys: make_list constructor
Takes a fixed array of lisp_t pointers, and makes a cons list out of
them.
2026-03-05 19:59:23 +00:00
12 changed files with 167 additions and 24 deletions

View File

@@ -5,7 +5,7 @@ OUT=$(DIST)/alisp.out
TEST=$(DIST)/test.out
LDFLAGS=
GFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c23 -I./include/
GFLAGS=-Wall -Wextra -Wswitch-enum -Wpedantic -Werror -std=c23 -I./include/
DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined
RFLAGS=-O3

View File

@@ -15,10 +15,14 @@
typedef struct
{
u64 references;
u64 padding : 56;
tag_t tag : 8;
u64 references;
} alloc_metadata_t;
static_assert(sizeof(alloc_metadata_t) == 16,
"16 byte metadata required for alignment purposes");
typedef struct
{
alloc_metadata_t metadata;

View File

@@ -49,6 +49,11 @@ typedef enum Tag
#define INT_MIN (-(INT_MAX + 1))
tag_t tag_get(const lisp_t *);
u64 tag_sizeof(tag_t);
u64 lisp_sizeof(lisp_t *);
lisp_t *lisp_reset(lisp_t *);
void lisp_print(FILE *, lisp_t *);
lisp_t *tag_smi(const i64);
lisp_t *tag_sym(const char *);
lisp_t *tag_cons(const cons_t *);
@@ -65,10 +70,6 @@ str_t *as_str(lisp_t *);
#define CAR(L) (as_cons(L)->car)
#define CDR(L) (as_cons(L)->cdr)
void lisp_print(FILE *, lisp_t *);
u64 tag_sizeof(tag_t);
u64 lisp_sizeof(lisp_t *);
#endif
/* Copyright (C) 2026 Aryadev Chavali

View File

@@ -17,6 +17,7 @@ typedef enum
READ_ERR_EOF,
READ_ERR_EXPECTED_CLOSED_BRACE,
READ_ERR_EXPECTED_CLOSED_SQUARE_BRACKET,
READ_ERR_EXPECTED_CLOSING_SPEECHMARKS,
READ_ERR_UNEXPECTED_CLOSED_BRACE,
READ_ERR_UNEXPECTED_CLOSED_SQUARE_BRACKET,
READ_ERR_UNKNOWN_CHAR,

View File

@@ -21,6 +21,7 @@ typedef struct
void sys_init(sys_t *);
lisp_t *sys_alloc(sys_t *, tag_t type);
void sys_delete(sys_t *, lisp_t *);
void sys_free(sys_t *);
// Debugging function: provides total memory usage from system.
@@ -30,6 +31,7 @@ u64 sys_cost(sys_t *);
lisp_t *make_int(i64);
lisp_t *intern(sys_t *, sv_t);
lisp_t *cons(sys_t *, lisp_t *, lisp_t *);
lisp_t *make_list(sys_t *, lisp_t **, u64);
lisp_t *make_vec(sys_t *, u64);
lisp_t *make_str(sys_t *, u64);

View File

@@ -36,6 +36,7 @@ static_assert(sizeof(vec_t) == 64, "vec_t has to be 64 bytes as part of SBO");
void vec_init(vec_t *, u64);
void vec_free(vec_t *);
void vec_reset(vec_t *);
u8 *vec_data(vec_t *);
// Append, possibly reallocating memory

View File

@@ -158,8 +158,35 @@ end:
void alloc_delete(alloc_t *alloc, lisp_t *lisp)
{
switch (tag_get(lisp))
{
case TAG_CONS:
case TAG_VEC:
case TAG_STR:
break;
case TAG_NIL: // These can't be deleted (not allocated)
case TAG_SMI:
case TAG_SYM:
default:
FAIL("Unreachable");
return;
}
alloc_node_t *node = lisp_to_node(lisp);
assert(node && node->metadata.references == 0);
// If already present in the free vector, stop.
FOR_VEC(i, &alloc->free_vec, alloc_node_t *)
{
alloc_node_t *other = VEC_GET(&alloc->pages, i, alloc_node_t *);
if (other == node)
{
return;
}
}
// Otherwise, add to the free vector.
lisp_reset(lisp);
vec_append(&alloc->free_vec, &node, sizeof(node));
}
@@ -179,7 +206,7 @@ void alloc_free(alloc_t *alloc)
FOR_VEC(i, &alloc->pages, page_t *)
{
page_t *page = VEC_GET(&alloc->pages, i, page_t *);
// Iterate through every alloc_node in this page
// Iterate through every alloc_node in this page (dynamic walk)
for (u64 j = 0; j < VEC_SIZE(&page->data, u8);)
{
alloc_node_t *node = (alloc_node_t *)(vec_data(&page->data) + j);

View File

@@ -174,7 +174,7 @@ void lisp_print(FILE *fp, lisp_t *lisp)
{
lisp_t *item = VEC_GET(vec, i, lisp_t *);
lisp_print(fp, item);
if (i < VEC_SIZE(vec, lisp_t *))
if (i < VEC_SIZE(vec, lisp_t *) - 1)
{
fprintf(fp, " ");
}
@@ -188,8 +188,23 @@ void lisp_print(FILE *fp, lisp_t *lisp)
break;
}
case TAG_STR:
TODO("Implement lisp_print for strings");
{
#if VERBOSE_LOGS == 2
fprintf(fp, "STR[");
#else
fprintf(fp, "\"");
#endif
sv_t sv = string_sv(as_str(lisp));
fprintf(fp, PR_SV, SV_FMT(sv));
#if VERBOSE_LOGS == 2
fprintf(fp, "]");
#else
fprintf(fp, "\"");
#endif
break;
}
default:
FAIL("Unreachable");
break;
@@ -223,6 +238,40 @@ u64 lisp_sizeof(lisp_t *lisp)
return tag_sizeof(tag_get(lisp));
}
lisp_t *lisp_reset(lisp_t *lisp)
{
switch (tag_get(lisp))
{
case TAG_NIL:
case TAG_SMI:
case TAG_SYM:
// Nothing to "reset" here.
return lisp;
case TAG_CONS:
{
// Make `car` and `cons` NIL
CAR(lisp) = NIL;
CDR(lisp) = NIL;
return lisp;
}
case TAG_VEC:
{
vec_reset(as_vec(lisp));
return lisp;
}
case TAG_STR:
{
vec_reset(&as_str(lisp)->data);
return lisp;
}
default:
{
FAIL("Unreachable");
return lisp;
}
}
}
/* Copyright (C) 2025, 2026 Aryadev Chavali
* This program is distributed in the hope that it will be useful, but WITHOUT

View File

@@ -47,8 +47,8 @@ int main(int argc, char *argv[])
{
FOR_VEC(i, &ast, lisp_t *)
{
lisp_t *expr = VEC_GET(&ast, i, lisp_t *);
#if VERBOSE_LOGS
lisp_t *expr = VEC_GET(&ast, i, lisp_t *);
printf("\t[%lu]: ", i);
lisp_print(stdout, expr);
printf("\n");

View File

@@ -20,11 +20,12 @@ const char *read_err_to_cstr(read_err_t err)
return "EOF";
case READ_ERR_UNKNOWN_CHAR:
return "UNKNOWN_CHAR";
break;
case READ_ERR_EXPECTED_CLOSED_BRACE:
return "EXPECTED_CLOSED_BRACE";
case READ_ERR_EXPECTED_CLOSED_SQUARE_BRACKET:
return "EXPECTED_CLOSED_SQUARE_BRACKET";
case READ_ERR_EXPECTED_CLOSING_SPEECHMARKS:
return "EXPECTED_CLOSING_SPEECHMARKS";
case READ_ERR_UNEXPECTED_CLOSED_BRACE:
return "UNEXPECTED_CLOSED_BRACE";
case READ_ERR_UNEXPECTED_CLOSED_SQUARE_BRACKET:
@@ -37,7 +38,7 @@ const char *read_err_to_cstr(read_err_t err)
// Accepted characters for symbols.
static const char *SYMBOL_CHARS =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!$%&*+,-./"
":<=>?@\\^_`{|}~0123456789";
":<=>?@\\^_{|}~0123456789";
// Little predicate using SYMBOL_CHARS
bool is_sym(char c)
@@ -76,9 +77,9 @@ read_err_t read_int(sys_t *sys, stream_t *stream, lisp_t **ret)
return read_sym(sys, stream, ret);
}
if (digits_sv.size > 19)
if (digits_sv.size >= 18)
{
TODO("alisp doesn't support big integers (bigger than 63 bits) yet");
TODO("alisp doesn't support big integers (bigger than 56 bits) yet");
}
i64 n = 0;
@@ -92,7 +93,7 @@ read_err_t read_int(sys_t *sys, stream_t *stream, lisp_t **ret)
// => i > (INT_MAX - digit) / 10
if (n > (INT_MAX - digit) / 10)
{
TODO("alisp doesn't support big integers (bigger than 63 bits) yet");
TODO("alisp doesn't support big integers (bigger than 56 bits) yet");
}
n *= 10;
@@ -111,9 +112,7 @@ read_err_t read_negative(sys_t *sys, stream_t *stream, lisp_t **ret)
read_err_t err = read_int(sys, stream, ret);
if (err)
return err;
i64 n = as_smi(*ret);
n *= -1;
*ret = make_int(n);
*ret = make_int(as_smi(*ret) * -1);
return READ_ERR_OK;
}
else if (is_sym(c) || isspace(c))
@@ -122,11 +121,14 @@ read_err_t read_negative(sys_t *sys, stream_t *stream, lisp_t **ret)
return read_sym(sys, stream, ret);
}
else
{
return READ_ERR_UNKNOWN_CHAR;
}
}
read_err_t read_list(sys_t *sys, stream_t *stream, lisp_t **ret)
{
u64 old_pos = stream->position;
// skip past the open parentheses '('
(void)stream_next(stream);
@@ -138,7 +140,7 @@ read_err_t read_list(sys_t *sys, stream_t *stream, lisp_t **ret)
read_err_t err = read(sys, stream, &item);
if (err == READ_ERR_EOF)
{
return READ_ERR_EXPECTED_CLOSED_BRACE;
goto no_close_brace;
}
else if (err)
{
@@ -157,16 +159,23 @@ read_err_t read_list(sys_t *sys, stream_t *stream, lisp_t **ret)
}
if (stream_peek(stream) != ')')
return READ_ERR_EXPECTED_CLOSED_BRACE;
{
goto no_close_brace;
}
stream_next(stream);
*ret = top;
return READ_ERR_OK;
no_close_brace:
stream->position = old_pos;
return READ_ERR_EXPECTED_CLOSED_BRACE;
}
read_err_t read_vec(sys_t *sys, stream_t *stream, lisp_t **ret)
{
u64 old_pos = stream->position;
(void)stream_next(stream);
lisp_t *container = make_vec(sys, 0);
while (!stream_eoc(stream) && stream_peek(stream) != ']')
{
@@ -174,7 +183,7 @@ read_err_t read_vec(sys_t *sys, stream_t *stream, lisp_t **ret)
read_err_t err = read(sys, stream, &item);
if (err == READ_ERR_EOF)
{
return READ_ERR_EXPECTED_CLOSED_BRACE;
goto no_close_square_bracket;
}
else if (err)
{
@@ -187,10 +196,33 @@ read_err_t read_vec(sys_t *sys, stream_t *stream, lisp_t **ret)
}
if (stream_peek(stream) != ']')
return READ_ERR_EXPECTED_CLOSED_SQUARE_BRACKET;
goto no_close_square_bracket;
stream_next(stream);
*ret = container;
return READ_ERR_OK;
no_close_square_bracket:
stream->position = old_pos;
return READ_ERR_EXPECTED_CLOSED_SQUARE_BRACKET;
}
read_err_t read_str(sys_t *sys, stream_t *stream, lisp_t **ret)
{
u64 old_pos = stream->position;
(void)stream_next(stream);
sv_t contents = stream_till(stream, "\"");
if (stream_eoc(stream) || stream_peek(stream) != '\"')
{
stream->position = old_pos;
return READ_ERR_EXPECTED_CLOSING_SPEECHMARKS;
}
stream_next(stream);
lisp_t *lisp = make_str(sys, contents.size);
vec_append(&as_str(lisp)->data, contents.data, contents.size);
*ret = lisp;
return READ_ERR_OK;
}
read_err_t read_quote(sys_t *sys, stream_t *stream, lisp_t **ret)
@@ -200,8 +232,8 @@ read_err_t read_quote(sys_t *sys, stream_t *stream, lisp_t **ret)
read_err_t err = read(sys, stream, &to_quote);
if (err)
return err;
*ret = cons(sys, to_quote, NIL);
*ret = cons(sys, intern(sys, SV_AUTO("quote")), *ret);
lisp_t *items[] = {intern(sys, SV_AUTO("quote")), to_quote};
*ret = make_list(sys, items, ARRSIZE(items));
return READ_ERR_OK;
}
@@ -243,6 +275,8 @@ read_err_t read(sys_t *sys, stream_t *stream, lisp_t **ret)
return read_vec(sys, stream, ret);
else if (c == ']')
return READ_ERR_UNEXPECTED_CLOSED_SQUARE_BRACKET;
else if (c == '\"')
return read_str(sys, stream, ret);
return READ_ERR_UNKNOWN_CHAR;
}

View File

@@ -34,6 +34,11 @@ lisp_t *sys_alloc(sys_t *sys, tag_t type)
return NIL;
}
void sys_delete(sys_t *sys, lisp_t *lisp)
{
alloc_delete(&sys->memory, lisp);
}
u64 sys_cost(sys_t *sys)
{
return alloc_cost(&sys->memory) + sym_table_cost(&sys->symtable);
@@ -59,6 +64,17 @@ lisp_t *cons(sys_t *sys, lisp_t *car, lisp_t *cdr)
return cons;
}
lisp_t *make_list(sys_t *sys, lisp_t **lisps, u64 size)
{
lisp_t *root = NIL;
for (u64 i = size; i > 0; --i)
{
lisp_t *node = lisps[i - 1];
root = cons(sys, node, root);
}
return root;
}
lisp_t *make_vec(sys_t *sys, u64 capacity)
{
lisp_t *vec = sys_alloc(sys, TAG_VEC);

View File

@@ -38,6 +38,14 @@ void vec_free(vec_t *vec)
memset(vec, 0, sizeof(*vec));
}
void vec_reset(vec_t *vec)
{
if (!vec)
return;
memset(vec_data(vec), 0, vec->capacity);
vec->size = 0;
}
u8 *vec_data(vec_t *vec)
{
return vec->not_inlined ? vec->ptr : vec->inlined;