Compare commits

..

7 Commits

Author SHA1 Message Date
Aryadev Chavali
67e1fabccc alloc: arena_t -> alloc_t
arena_t doesn't really make sense given we also have a free list.
Better to name it generic.
2026-02-13 03:32:08 +00:00
Aryadev Chavali
d19c64714a allocator: arena_make now takes nodes off the free list first. 2026-02-13 03:30:34 +00:00
Aryadev Chavali
8957723dad sys: plug in allocator 2026-02-13 00:08:10 +00:00
Aryadev Chavali
929ed2b276 allocator: implement a basic allocator 2026-02-13 00:08:01 +00:00
Aryadev Chavali
7019d402cd vec: vec_try_append
Essentially a method to attempt to append data but without doing any
reallocation - stay within the bounds of the capacity.
2026-02-13 00:07:53 +00:00
Aryadev Chavali
a28d1d8c60 lisp: tag_generic, tag_sizeof, and lisp_sizeof 2026-02-13 00:07:15 +00:00
Aryadev Chavali
faeda576ce lisp: split lisp into lisp and sys
Generic definition of tagged pointers, along with simple
constructor/destructors should be in lisp.h for use by other headers.

sys.h on the other hand contains all the general system methods.
2026-02-12 23:07:23 +00:00
18 changed files with 170 additions and 431 deletions

View File

@@ -5,22 +5,22 @@ OUT=$(DIST)/alisp.out
TEST=$(DIST)/test.out
LDFLAGS=
GFLAGS=-Wall -Wextra -Wswitch-enum -Wpedantic -Werror -std=c23 -I./include/
DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined
GFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c23 -I./include/
DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined -DVERBOSE_LOGS=1
RFLAGS=-O3
MODE=release
ifeq ($(MODE), release)
CFLAGS=$(GFLAGS) $(RFLAGS)
else ifeq ($(MODE), debug)
CFLAGS=$(GFLAGS) $(DFLAGS) -DVERBOSE_LOGS=1
CFLAGS=$(GFLAGS) $(DFLAGS)
else ifeq ($(MODE), full)
CFLAGS=$(GFLAGS) $(DFLAGS) -DVERBOSE_LOGS=2 -DTEST_VERBOSE=1
CFLAGS=$(GFLAGS) $(DFLAGS) -DTEST_VERBOSE=1
endif
# Units to compile
UNITS=src/sv.c src/vec.c src/string.c src/stream.c src/symtable.c src/lisp.c \
src/allocator.c src/sys.c src/reader.c
UNITS=src/sv.c src/vec.c src/stream.c src/symtable.c src/lisp.c src/allocator.c \
src/sys.c src/reader.c
OBJECTS:=$(patsubst src/%.c, $(DIST)/%.o, $(UNITS))
TEST_UNITS=test/main.c

View File

@@ -84,7 +84,7 @@ Scheme doesn't have it. Should we implement this?
- Unmanaged objects are allocations we do as part of the runtime.
These are things that we expect to have near infinite lifetimes
(such as the symbol table, vector of allocated objects, etc).
*** DONE Design an allocator
*** TODO Design an allocator
We'll need an allocator for all our managed objects. Requirements:
- Stable pointers (memory that has already been allocated should be
free to utilise via the same pointer for the lifetime of the
@@ -92,15 +92,14 @@ We'll need an allocator for all our managed objects. Requirements:
- Able to tag allocations as unused (i.e. "free") and able to reuse
these allocations
- This will link into the garbage collector, which should yield a
sequence of objects that were previously tagged as unfree and
should be "freed".
sequence of objects that should be "freed".
- Able to allocate all the managed types we have
**** DONE Design allocation data structures
**** DONE Design allocation methods for different lisp types
**** TODO Design allocation data structures
**** TODO Design allocation methods for different lisp types
- Conses
- Vectors
- Strings (when implemented)
***** DONE Conses
***** DONE Vectors
**** DONE Design allocation freeing method
**** TODO Design allocation freeing methods
*** TODO Design garbage collection scheme :gc:
Really, regardless of what I do, we need to have some kind of garbage
collection header on whatever managed objects we allocate. We need to

Binary file not shown.

View File

@@ -15,14 +15,10 @@
typedef struct
{
u64 padding : 56;
tag_t tag : 8;
u64 references;
tag_t tag : 8;
} alloc_metadata_t;
static_assert(sizeof(alloc_metadata_t) == 16,
"16 byte metadata required for alignment purposes");
typedef struct
{
alloc_metadata_t metadata;
@@ -37,7 +33,7 @@ typedef struct
typedef struct
{
vec_t pages;
vec_t free_vec;
vec_t free_list;
} alloc_t;
lisp_t *alloc_make(alloc_t *, tag_t type);

View File

@@ -10,7 +10,7 @@
#include <stdio.h>
#include <alisp/string.h>
#include <alisp/symtable.h>
#include <alisp/vec.h>
#define NIL 0
@@ -26,50 +26,59 @@ typedef struct
/// Tagging system
typedef enum Tag
{
TAG_SMI = 0b00000001, // Atomic types
TAG_SYM = 0b00000011,
TAG_NIL = 0b00000000, // Container types (0 LSB)
TAG_CONS = 0b00000010,
TAG_VEC = 0b00000100,
TAG_STR = 0b00000110,
NUM_TAGS = 6,
TAG_NIL = 0b00000000, // Start of atomic types
TAG_INT = 0b00000001, // Special tag so we can encode 63 bit integers
TAG_SYM = 0b00000100,
TAG_CONS = 0b00000010, // Start of container types
TAG_VEC = 0b00000110,
NUM_TAGS = 5,
} tag_t;
static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum SHIFT");
enum Shift
{
SHIFT_INT = 1,
SHIFT_SYM = 8,
SHIFT_CONS = 8,
SHIFT_VEC = 8,
};
static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum MASK");
enum Mask
{
MASK_INT = 0b00000001,
MASK_SYM = 0b11111111,
MASK_CONS = 0b11111111,
MASK_VEC = 0b11111111,
};
// Some helper macros for tagging
#define SHIFT_TAG (8)
#define MASK_TAG ((1 << SHIFT_TAG) - 1)
#define TAG(PTR, TYPE) ((lisp_t *)(((PTR) << SHIFT_##TYPE) | TAG_##TYPE))
#define IS_TAG(PTR, TYPE) (((u64)(PTR) & MASK_##TYPE) == TAG_##TYPE)
#define UNTAG(PTR, TYPE) (((u64)PTR) >> SHIFT_##TYPE)
#define TAG(PTR, TYPE) ((lisp_t *)((((u64)(PTR)) << SHIFT_TAG) | TAG_##TYPE))
#define UNTAG(PTR) (((u64)PTR) >> SHIFT_TAG)
#define GET_TAG(PTR) ((tag_t)(((u64)(PTR)) & MASK_TAG))
#define IS_TAG(PTR, TYPE) (GET_TAG(PTR) == TAG_##TYPE)
#define INT_MAX ((((i64)1) << 62) - 1)
#define INT_MIN (-(((i64)1) << 62))
#define INT_BITS ((sizeof(i64) * 8) - SHIFT_TAG)
#define INT_MAX ((((i64)1) << (INT_BITS - 1)) - 1)
#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);
tag_t get_tag(const lisp_t *);
lisp_t *tag_int(const i64);
lisp_t *tag_sym(const char *);
lisp_t *tag_cons(const cons_t *);
lisp_t *tag_vec(const vec_t *);
lisp_t *tag_str(const str_t *);
lisp_t *tag_generic(void *, tag_t);
i64 as_smi(lisp_t *);
i64 as_int(lisp_t *);
char *as_sym(lisp_t *);
cons_t *as_cons(lisp_t *);
vec_t *as_vec(lisp_t *);
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,7 +17,6 @@ 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

@@ -1,35 +0,0 @@
/* string.h: String library
* Created: 2026-03-05
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
*/
#ifndef STRING_H
#define STRING_H
#include <alisp/sv.h>
#include <alisp/vec.h>
typedef struct
{
vec_t data;
} str_t;
str_t string_make(sv_t sv);
sv_t string_sv(str_t *);
#endif
/* Copyright (C) 2026 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 GNU General Public License Version 2 for
* details.
* You may distribute and modify this code under the terms of the GNU General
* Public License Version 2, which you should have received a copy of along with
* this program. If not, please go to <https://www.gnu.org/licenses/>.
*/

View File

@@ -10,7 +10,6 @@
#include <alisp/allocator.h>
#include <alisp/lisp.h>
#include <alisp/symtable.h>
/// System context
typedef struct
@@ -21,7 +20,6 @@ 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.
@@ -29,17 +27,15 @@ u64 sys_cost(sys_t *);
/// Constructors and general Lisp API
lisp_t *make_int(i64);
lisp_t *make_vec(sys_t *, u64);
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);
lisp_t *car(lisp_t *);
lisp_t *cdr(lisp_t *);
void lisp_free(sys_t *, lisp_t *);
void lisp_free_rec(sys_t *, lisp_t *);
void lisp_free(lisp_t *);
void lisp_free_rec(lisp_t *);
#endif

View File

@@ -31,12 +31,9 @@ static_assert(sizeof(vec_t) == 64, "vec_t has to be 64 bytes as part of SBO");
#define VEC_GET(V, I, T) (((T *)vec_data(V))[I])
#define VEC_SIZE(V, T) ((V)->size / (sizeof(T)))
#define FOR_VEC(INDEX, V, T) \
for (size_t INDEX = 0; INDEX < VEC_SIZE(V, T); ++INDEX)
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

@@ -23,8 +23,6 @@ alloc_node_t *make_node(page_t *page, tag_t type)
{
alloc_node_t *node = NULL;
u64 size = sizeof(*node);
static_assert(NUM_TAGS == 6);
switch (type)
{
case TAG_CONS:
@@ -33,11 +31,8 @@ alloc_node_t *make_node(page_t *page, tag_t type)
case TAG_VEC:
size += sizeof(vec_t);
break;
case TAG_STR:
size += sizeof(str_t);
break;
case TAG_NIL:
case TAG_SMI:
case TAG_INT:
case TAG_SYM:
default:
FAIL("Unreachable");
@@ -57,9 +52,7 @@ alloc_node_t *make_node(page_t *page, tag_t type)
alloc_node_t *lisp_to_node(lisp_t *lisp)
{
void *raw_ptr = NULL;
static_assert(NUM_TAGS == 6);
switch (tag_get(lisp))
switch (get_tag(lisp))
{
case TAG_CONS:
raw_ptr = as_cons(lisp);
@@ -67,32 +60,27 @@ alloc_node_t *lisp_to_node(lisp_t *lisp)
case TAG_VEC:
raw_ptr = as_vec(lisp);
break;
case TAG_STR:
raw_ptr = as_str(lisp);
break;
case TAG_NIL: // These shouldn't be allocated
case TAG_SMI:
case TAG_INT:
case TAG_SYM:
default:
FAIL("Unreachable");
return NIL;
}
alloc_node_t *node = raw_ptr;
return &node[-1];
alloc_metadata_t *data = raw_ptr;
return (alloc_node_t *)(&data[-1]);
}
lisp_t *alloc_make(alloc_t *alloc, tag_t type)
{
static_assert(NUM_TAGS == 6);
switch (type)
{
case TAG_CONS:
case TAG_VEC:
case TAG_STR:
break;
case TAG_NIL: // These shouldn't be allocated
case TAG_SMI:
case TAG_INT:
case TAG_SYM:
default:
FAIL("Unreachable");
@@ -102,40 +90,34 @@ lisp_t *alloc_make(alloc_t *alloc, tag_t type)
// We want to try to fill this node with an allocation of this type.
alloc_node_t *node = NULL;
// Try to get something from the free vector
u64 free_vec_size = VEC_SIZE(&alloc->free_vec, alloc_node_t *);
for (u64 i = 0; i < free_vec_size; ++i)
// Try to get something from the free_list.
u64 free_list_size = VEC_SIZE(&alloc->free_list, alloc_node_t *);
for (u64 i = 0; i < free_list_size; ++i)
{
alloc_node_t **nodeptr = &VEC_GET(&alloc->free_vec, i, alloc_node_t *);
// Skip any nodes that don't have the right type.
alloc_node_t **nodeptr = &VEC_GET(&alloc->free_list, i, alloc_node_t *);
if (nodeptr[0]->metadata.tag != type)
continue;
assert("Expected free node to have no references" &&
nodeptr[0]->metadata.references == 0);
// Pop this node off the free vector by swapping it with the last item and
// decrementing the size of the vector.
// Swap this node with the last item of the free_list
alloc_node_t **lastptr =
&VEC_GET(&alloc->free_vec, free_vec_size - 1, alloc_node_t *);
alloc_node_t *val = *nodeptr;
&VEC_GET(&alloc->free_list, free_list_size - 1, alloc_node_t *);
alloc_node_t *val = *lastptr;
*nodeptr = *lastptr;
*lastptr = val;
// Decrement the size of the free vector
alloc->free_vec.size -= sizeof(val);
// Decrement the size of the free list
alloc->free_list.size -= sizeof(val);
// Then use that valid (and now unused) node as our return.
// Get the valid node and goto the end.
node = *lastptr;
goto end;
}
// We couldn't get anything from the free vector, so try to allocate a fresh
// one against one of the pages.
FOR_VEC(i, &alloc->pages, page_t *)
// We couldn't get anything from the free list, so try to allocate a fresh one
// against one of the pages.
for (u64 i = 0; i < VEC_SIZE(&alloc->pages, page_t *); ++i)
{
page_t *page = VEC_GET(&alloc->pages, i, page_t *);
node = make_node(page, type);
@@ -158,42 +140,15 @@ 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));
vec_append(&alloc->free_list, &node, sizeof(node));
}
u64 alloc_cost(alloc_t *alloc)
{
u64 total_size = alloc->pages.size;
FOR_VEC(i, &alloc->pages, page_t *)
for (u64 i = 0; i < VEC_SIZE(&alloc->pages, page_t *); ++i)
{
page_t *page = VEC_GET(&alloc->pages, i, page_t *);
total_size += page->data.size;
@@ -203,16 +158,14 @@ u64 alloc_cost(alloc_t *alloc)
void alloc_free(alloc_t *alloc)
{
FOR_VEC(i, &alloc->pages, page_t *)
for (u64 i = 0; i < VEC_SIZE(&alloc->pages, page_t *); ++i)
{
page_t *page = VEC_GET(&alloc->pages, i, page_t *);
// Iterate through every alloc_node in this page (dynamic walk)
// Iterate through every alloc_node in this page
for (u64 j = 0; j < VEC_SIZE(&page->data, u8);)
{
alloc_node_t *node = (alloc_node_t *)(vec_data(&page->data) + j);
u64 next = sizeof(*node) + tag_sizeof(node->metadata.tag);
static_assert(NUM_TAGS == 6);
switch (node->metadata.tag)
{
case TAG_CONS:
@@ -221,11 +174,8 @@ void alloc_free(alloc_t *alloc)
case TAG_VEC:
vec_free((vec_t *)node->data);
break;
case TAG_STR:
vec_free(&((str_t *)node->data)->data);
break;
case TAG_NIL:
case TAG_SMI:
case TAG_INT:
case TAG_SYM:
default:
FAIL("Unreachable");
@@ -238,7 +188,7 @@ void alloc_free(alloc_t *alloc)
free(page);
}
vec_free(&alloc->pages);
vec_free(&alloc->free_vec);
vec_free(&alloc->free_list);
memset(alloc, 0, sizeof(*alloc));
}

View File

@@ -10,123 +10,114 @@
#include <alisp/lisp.h>
lisp_t *tag_smi(i64 i)
lisp_t *tag_int(i64 i)
{
return TAG(i, SMI);
return TAG((u64)i, INT);
}
lisp_t *tag_sym(const char *str)
{
return TAG(str, SYM);
return TAG((u64)str, SYM);
}
lisp_t *tag_vec(const vec_t *vec)
{
return TAG(vec, VEC);
}
lisp_t *tag_str(const str_t *str)
{
return TAG(str, STR);
return TAG((u64)vec, VEC);
}
lisp_t *tag_cons(const cons_t *cons)
{
return TAG(cons, CONS);
return TAG((u64)cons, CONS);
}
lisp_t *tag_generic(void *ptr, tag_t type)
{
static_assert(NUM_TAGS == 6);
switch (type)
{
case TAG_NIL:
return TAG(ptr, NIL);
case TAG_SMI:
return tag_smi((i64)ptr);
return NIL;
case TAG_INT:
return tag_int((i64)ptr);
case TAG_SYM:
return tag_sym(ptr);
case TAG_CONS:
return tag_cons(ptr);
case TAG_VEC:
return tag_vec(ptr);
case TAG_STR:
return tag_str(ptr);
default:
FAIL("Unreachable");
return NIL;
}
}
tag_t tag_get(const lisp_t *lisp)
tag_t get_tag(const lisp_t *lisp)
{
return GET_TAG(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_smi(lisp_t *obj)
i64 as_int(lisp_t *obj)
{
assert(IS_TAG(obj, SMI));
u64 raw_obj = UNTAG(obj);
u64 msb = (NTH_BYTE(raw_obj, 6) & 0x80) >> 7;
msb = ((1LU << 8) - msb) << 56;
return (i64)(raw_obj | msb);
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);
return (char *)UNTAG(obj, SYM);
}
cons_t *as_cons(lisp_t *obj)
{
assert(IS_TAG(obj, CONS));
return (cons_t *)UNTAG(obj);
}
str_t *as_str(lisp_t *obj)
{
assert(IS_TAG(obj, STR));
return (str_t *)UNTAG(obj);
return (cons_t *)UNTAG(obj, CONS);
}
vec_t *as_vec(lisp_t *obj)
{
assert(IS_TAG(obj, VEC));
return (vec_t *)UNTAG(obj);
return (vec_t *)UNTAG(obj, VEC);
}
void lisp_print(FILE *fp, lisp_t *lisp)
{
if (!fp)
return;
static_assert(NUM_TAGS == 6);
switch (tag_get(lisp))
switch (get_tag(lisp))
{
case TAG_NIL:
fprintf(fp, "NIL");
break;
case TAG_SMI:
#if VERBOSE_LOGS == 2
case TAG_INT:
#if VERBOSE_LOGS
fprintf(fp, "INT[");
#endif
fprintf(fp, "%ld", as_smi(lisp));
#if VERBOSE_LOGS == 2
fprintf(fp, "%ld", as_int(lisp));
#if VERBOSE_LOGS
fprintf(fp, "]");
#endif
break;
case TAG_SYM:
#if VERBOSE_LOGS == 2
#if VERBOSE_LOGS
fprintf(fp, "SYM[");
#endif
fprintf(fp, "%s", as_sym(lisp));
#if VERBOSE_LOGS == 2
#if VERBOSE_LOGS
fprintf(fp, "]");
#endif
break;
case TAG_CONS:
{
#if VERBOSE_LOGS == 2
#if VERBOSE_LOGS
fprintf(fp, "LIST[");
#else
fprintf(fp, "(");
@@ -154,7 +145,7 @@ void lisp_print(FILE *fp, lisp_t *lisp)
break;
}
}
#if VERBOSE_LOGS == 2
#if VERBOSE_LOGS
fprintf(fp, "]");
#else
fprintf(fp, ")");
@@ -163,48 +154,31 @@ void lisp_print(FILE *fp, lisp_t *lisp)
}
case TAG_VEC:
{
#if VERBOSE_LOGS == 2
#if VERBOSE_LOGS
fprintf(fp, "VEC[");
#else
fprintf(fp, "[");
#endif
vec_t *vec = as_vec(lisp);
FOR_VEC(i, vec, lisp_t *)
for (u64 i = 1; i <= VEC_SIZE(vec, lisp_t *); ++i)
{
lisp_t *item = VEC_GET(vec, i, lisp_t *);
lisp_t *item = VEC_GET(vec, i - 1, lisp_t *);
lisp_print(fp, item);
if (i < VEC_SIZE(vec, lisp_t *) - 1)
if (i != VEC_SIZE(vec, lisp_t *))
{
fprintf(fp, " ");
}
}
#if VERBOSE_LOGS == 2
#if VERBOSE_LOGS
fprintf(fp, "]");
#else
fprintf(fp, "]");
#endif
break;
}
case TAG_STR:
{
#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;
}
case NUM_TAGS:
default:
FAIL("Unreachable");
break;
@@ -213,20 +187,18 @@ void lisp_print(FILE *fp, lisp_t *lisp)
u64 tag_sizeof(tag_t tag)
{
static_assert(NUM_TAGS == 6);
switch (tag)
{
case TAG_NIL:
return 0;
case TAG_SMI:
case TAG_INT:
case TAG_SYM:
return sizeof(lisp_t *);
case TAG_CONS:
return sizeof(cons_t);
case TAG_VEC:
return sizeof(vec_t);
case TAG_STR:
return sizeof(str_t);
case NUM_TAGS:
default:
FAIL("Unreachable");
return 0;
@@ -235,41 +207,7 @@ u64 tag_sizeof(tag_t tag)
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;
}
}
return tag_sizeof(get_tag(lisp));
}
/* Copyright (C) 2025, 2026 Aryadev Chavali

View File

@@ -45,10 +45,10 @@ int main(int argc, char *argv[])
VEC_SIZE(&ast, lisp_t *) == 1 ? "expr" : "exprs");
{
FOR_VEC(i, &ast, lisp_t *)
for (u64 i = 0; i < VEC_SIZE(&ast, lisp_t *); ++i)
{
#if VERBOSE_LOGS
lisp_t *expr = VEC_GET(&ast, i, lisp_t *);
#if VERBOSE_LOGS
printf("\t[%lu]: ", i);
lisp_print(stdout, expr);
printf("\n");

View File

@@ -20,12 +20,11 @@ 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:
@@ -38,7 +37,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)
@@ -77,9 +76,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 >= 18)
if (digits_sv.size > 19)
{
TODO("alisp doesn't support big integers (bigger than 56 bits) yet");
TODO("alisp doesn't support big integers (bigger than 63 bits) yet");
}
i64 n = 0;
@@ -93,7 +92,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 56 bits) yet");
TODO("alisp doesn't support big integers (bigger than 63 bits) yet");
}
n *= 10;
@@ -112,7 +111,9 @@ 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;
*ret = make_int(as_smi(*ret) * -1);
i64 n = as_int(*ret);
n *= -1;
*ret = make_int(n);
return READ_ERR_OK;
}
else if (is_sym(c) || isspace(c))
@@ -121,14 +122,11 @@ 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);
@@ -140,7 +138,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)
{
goto no_close_brace;
return READ_ERR_EXPECTED_CLOSED_BRACE;
}
else if (err)
{
@@ -159,23 +157,16 @@ read_err_t read_list(sys_t *sys, stream_t *stream, lisp_t **ret)
}
if (stream_peek(stream) != ')')
{
goto no_close_brace;
}
return READ_ERR_EXPECTED_CLOSED_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) != ']')
{
@@ -183,7 +174,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)
{
goto no_close_square_bracket;
return READ_ERR_EXPECTED_CLOSED_BRACE;
}
else if (err)
{
@@ -196,33 +187,10 @@ read_err_t read_vec(sys_t *sys, stream_t *stream, lisp_t **ret)
}
if (stream_peek(stream) != ']')
goto no_close_square_bracket;
return READ_ERR_EXPECTED_CLOSED_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)
@@ -232,8 +200,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;
lisp_t *items[] = {intern(sys, SV_AUTO("quote")), to_quote};
*ret = make_list(sys, items, ARRSIZE(items));
*ret = cons(sys, to_quote, NIL);
*ret = cons(sys, intern(sys, SV_AUTO("quote")), *ret);
return READ_ERR_OK;
}
@@ -275,8 +243,6 @@ 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

@@ -1,44 +0,0 @@
/* string.c: String library implementation
* Created: 2026-03-05
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
*/
#include <string.h>
#include <alisp/string.h>
str_t string_make(sv_t sv)
{
str_t string = {0};
if (sv.size)
{
vec_init(&string.data, sv.size);
if (sv.data)
{
memcpy(vec_data(&string.data), sv.data, sv.size);
}
}
return string;
}
sv_t string_sv(str_t *str)
{
if (!str)
return SV(NULL, 0);
return SV((char *)vec_data(&str->data), str->data.size);
}
/* Copyright (C) 2026 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 GNU General Public License Version 2 for
* details.
* You may distribute and modify this code under the terms of the GNU General
* Public License Version 2, which you should have received a copy of along with
* this program. If not, please go to <https://www.gnu.org/licenses/>.
*/

View File

@@ -17,16 +17,14 @@ void sys_init(sys_t *sys)
lisp_t *sys_alloc(sys_t *sys, tag_t type)
{
static_assert(NUM_TAGS == 6);
switch (type)
{
case TAG_CONS:
case TAG_VEC:
case TAG_STR:
return alloc_make(&sys->memory, type);
// Shouldn't be allocated
// Shouldn't be registered
case TAG_NIL:
case TAG_SMI:
case TAG_INT:
case TAG_SYM:
default:
FAIL("Unreachable");
@@ -34,11 +32,6 @@ 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);
@@ -53,7 +46,7 @@ void sys_free(sys_t *sys)
lisp_t *make_int(i64 i)
{
return tag_smi(i);
return tag_int(i);
}
lisp_t *cons(sys_t *sys, lisp_t *car, lisp_t *cdr)
@@ -64,17 +57,6 @@ 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);
@@ -82,13 +64,6 @@ lisp_t *make_vec(sys_t *sys, u64 capacity)
return vec;
}
lisp_t *make_str(sys_t *sys, u64 capacity)
{
lisp_t *str = sys_alloc(sys, TAG_STR);
vec_init(&as_str(str)->data, capacity);
return str;
}
lisp_t *intern(sys_t *sys, sv_t sv)
{
const char *str = sym_table_find(&sys->symtable, sv);
@@ -111,56 +86,57 @@ lisp_t *cdr(lisp_t *lsp)
return CDR(lsp);
}
void lisp_free(sys_t *sys, lisp_t *lisp)
void lisp_free(lisp_t *item)
{
static_assert(NUM_TAGS == 6);
switch (tag_get(lisp))
switch (get_tag(item))
{
case TAG_STR:
case TAG_VEC:
case TAG_CONS:
// Delete the underlying data
alloc_delete(&sys->memory, lisp);
// Delete the cons
free(as_cons(item));
break;
case TAG_VEC:
{
vec_t *vec = as_vec(item);
vec_free(vec);
free(vec);
break;
}
case TAG_NIL:
case TAG_SMI:
case TAG_INT:
case TAG_SYM:
case NUM_TAGS:
// shouldn't be dealt with (either constant or dealt with elsewhere)
break;
}
}
void lisp_free_rec(sys_t *sys, lisp_t *item)
void lisp_free_rec(lisp_t *item)
{
static_assert(NUM_TAGS == 6);
switch (tag_get(item))
switch (get_tag(item))
{
case TAG_CONS:
{
lisp_free_rec(sys, car(item));
lisp_free_rec(sys, cdr(item));
lisp_free(sys, item);
lisp_free_rec(car(item));
lisp_free_rec(cdr(item));
free(as_cons(item));
break;
}
case TAG_VEC:
{
vec_t *vec = as_vec(item);
FOR_VEC(i, vec, lisp_t *)
for (size_t i = 0; i < VEC_SIZE(vec, lisp_t **); ++i)
{
lisp_t *allocated = VEC_GET(vec, i, lisp_t *);
lisp_free_rec(sys, allocated);
lisp_free_rec(allocated);
}
lisp_free(sys, item);
break;
}
case TAG_STR:
{
lisp_free(sys, item);
vec_free(vec);
free(vec);
break;
}
case TAG_NIL:
case TAG_SMI:
case TAG_INT:
case TAG_SYM:
case NUM_TAGS:
// shouldn't be dealt with (either constant or dealt with elsewhere)
break;
}

View File

@@ -38,14 +38,6 @@ 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;

View File

@@ -23,7 +23,7 @@ void smi_test(void)
{
i64 in = ints[i];
lisp_t *lisp = make_int(in);
i64 out = as_smi(lisp);
i64 out = as_int(lisp);
TEST(in == out, "%ld == %ld", in, out);
}
@@ -47,7 +47,7 @@ void smi_oob_test(void)
{
i64 in = ints[i];
lisp_t *lisp = make_int(in);
i64 out = as_smi(lisp);
i64 out = as_int(lisp);
TEST(in != out, "%ld != %ld", in, out);
}

View File

@@ -54,7 +54,7 @@ void stream_test_prologue(void)
void stream_test_epilogue(void)
{
TEST_INFO("Deleting file `%s`\n", valid_filename);
TEST_INFO("Freeing resources and deleting file `%s`\n", valid_filename);
assert(valid_fp);
fclose(valid_fp);
remove(valid_filename);