Compare commits

..

31 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
Aryadev Chavali
2a13c89496 doc: add r7rs PDF for reference 2026-03-05 19:48:00 +00:00
Aryadev Chavali
b925a68986 vec: FOR_VEC macro 2026-03-05 19:47:46 +00:00
Aryadev Chavali
99448f6702 allocator|lisp: support for strings 2026-03-05 19:47:03 +00:00
Aryadev Chavali
bbb66d5fb1 string: new string library
Strings are simply byte vectors.  We want a separate type so when
tagging/untagging we can have some level of type separation.
2026-03-05 19:44:58 +00:00
Aryadev Chavali
b93042fd27 lisp: INT -> SMI
when we implement big integer support, we should use INT there
instead.  SMI signals intent much better.
2026-03-05 19:41:16 +00:00
Aryadev Chavali
a50ca72b24 lisp: 63 bit -> 56 bit SMI
This massively simplifies the tagging implementation as all types now
have a 1 byte tag.  However, this does make the need for Big Integers
much greater as we've lost 8 bits of precision.
2026-03-05 18:36:43 +00:00
Aryadev Chavali
7f2dcc3ad2 alisp.org: mark TODOs 2026-03-05 17:32:52 +00:00
Aryadev Chavali
4bc615ab29 lisp_print: print verbose logs for lisp types on VERBOSE_LOGS=2 2026-03-05 17:32:52 +00:00
Aryadev Chavali
fc602f1664 allocator: free_list -> free_vec 2026-03-05 17:32:52 +00:00
Aryadev Chavali
30a87d4a1b some small updates 2026-03-05 17:32:52 +00:00
Aryadev Chavali
9940651ac0 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-03-05 17:32:52 +00:00
Aryadev Chavali
be0a6dd0c8 allocator: arena_make now takes nodes off the free list first. 2026-03-05 17:32:52 +00:00
Aryadev Chavali
9a91511e48 sys: plug in allocator 2026-03-05 17:32:52 +00:00
Aryadev Chavali
f9a044f631 allocator: implement a basic allocator 2026-03-05 17:32:52 +00:00
Aryadev Chavali
0e75b541df 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-03-05 17:32:52 +00:00
Aryadev Chavali
e6e501c5a3 lisp: tag_generic, tag_sizeof, and lisp_sizeof 2026-03-05 17:32:52 +00:00
Aryadev Chavali
a79f60a203 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-03-05 17:32:52 +00:00
20 changed files with 924 additions and 352 deletions

View File

@@ -5,21 +5,22 @@ OUT=$(DIST)/alisp.out
TEST=$(DIST)/test.out
LDFLAGS=
GFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c23 -I./include/
DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined -DVERBOSE_LOGS=1
GFLAGS=-Wall -Wextra -Wswitch-enum -Wpedantic -Werror -std=c23 -I./include/
DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined
RFLAGS=-O3
MODE=release
ifeq ($(MODE), release)
CFLAGS=$(GFLAGS) $(RFLAGS)
else ifeq ($(MODE), debug)
CFLAGS=$(GFLAGS) $(DFLAGS)
CFLAGS=$(GFLAGS) $(DFLAGS) -DVERBOSE_LOGS=1
else ifeq ($(MODE), full)
CFLAGS=$(GFLAGS) $(DFLAGS) -DTEST_VERBOSE=1
CFLAGS=$(GFLAGS) $(DFLAGS) -DVERBOSE_LOGS=2 -DTEST_VERBOSE=1
endif
# Units to compile
UNITS=src/sv.c src/vec.c src/stream.c src/symtable.c src/tag.c src/lisp.c src/reader.c
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
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).
*** TODO Design an allocator
*** DONE 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,14 +92,15 @@ 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 should be "freed".
sequence of objects that were previously tagged as unfree and
should be "freed".
- Able to allocate all the managed types we have
**** TODO Design allocation data structures
**** TODO Design allocation methods for different lisp types
- Conses
- Vectors
**** DONE Design allocation data structures
**** DONE Design allocation methods for different lisp types
- Strings (when implemented)
**** TODO Design allocation freeing methods
***** DONE Conses
***** DONE Vectors
**** DONE Design allocation freeing method
*** 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

BIN
doc/r7rs.pdf Normal file

Binary file not shown.

View File

@@ -8,6 +8,7 @@
#ifndef ALISP_H
#define ALISP_H
#include <alisp/allocator.h>
#include <alisp/base.h>
#include <alisp/lisp.h>
#include <alisp/reader.h>

61
include/alisp/allocator.h Normal file
View File

@@ -0,0 +1,61 @@
/* allocator.h: Lisp Allocator
* Created: 2026-02-12
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
*/
#ifndef ALLOCATOR_H
#define ALLOCATOR_H
#include <alisp/lisp.h>
#include <alisp/vec.h>
#define ALLOC_PAGE_DEFAULT_SIZE 512
typedef struct
{
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;
u8 data[];
} alloc_node_t;
typedef struct
{
vec_t data;
} page_t;
typedef struct
{
vec_t pages;
vec_t free_vec;
} alloc_t;
lisp_t *alloc_make(alloc_t *, tag_t type);
void alloc_delete(alloc_t *, lisp_t *);
u64 alloc_cost(alloc_t *);
void alloc_free(alloc_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,7 @@
#include <stdio.h>
#include <alisp/symtable.h>
#include <alisp/string.h>
#include <alisp/vec.h>
#define NIL 0
@@ -26,89 +26,50 @@ typedef struct
/// Tagging system
typedef enum Tag
{
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_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_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 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 SHIFT_TAG (8)
#define MASK_TAG ((1 << SHIFT_TAG) - 1)
#define INT_MAX ((((i64)1) << 62) - 1)
#define INT_MIN (-(((i64)1) << 62))
#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)
tag_t get_tag(const lisp_t *);
lisp_t *tag_int(const i64);
#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);
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);
/// System context
typedef struct
{
vec_t conses;
vec_t vectors;
u64 num_conses, num_vectors;
} sys_mem_t;
typedef struct
{
sys_mem_t memory;
sym_table_t symtable;
} sys_t;
void sys_init(sys_t *);
lisp_t *sys_alloc(sys_t *, tag_t type);
void sys_free(sys_t *);
// Debugging function: provides total memory usage from system.
u64 sys_cost(sys_t *);
/// Constructors and destructors
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 *);
i64 as_int(lisp_t *);
i64 as_smi(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)
lisp_t *car(lisp_t *);
lisp_t *cdr(lisp_t *);
void lisp_free(lisp_t *);
void lisp_free_rec(lisp_t *);
void lisp_print(FILE *, lisp_t *);
#endif
/* Copyright (C) 2026 Aryadev Chavali

View File

@@ -8,8 +8,8 @@
#ifndef READER_H
#define READER_H
#include <alisp/lisp.h>
#include <alisp/stream.h>
#include <alisp/sys.h>
typedef enum
{
@@ -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,

35
include/alisp/string.h Normal file
View File

@@ -0,0 +1,35 @@
/* 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/>.
*/

57
include/alisp/sys.h Normal file
View File

@@ -0,0 +1,57 @@
/* sys.h: System context and constructors
* Created: 2026-02-12
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
*/
#ifndef SYS_H
#define SYS_H
#include <alisp/allocator.h>
#include <alisp/lisp.h>
#include <alisp/symtable.h>
/// System context
typedef struct
{
alloc_t memory;
sym_table_t symtable;
} sys_t;
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.
u64 sys_cost(sys_t *);
/// Constructors and general Lisp API
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);
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 *);
#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

@@ -31,12 +31,18 @@ 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
void vec_append(vec_t *, const void *const, u64);
// Try to append without allocating memory
bool vec_try_append(vec_t *, const void *const, u64);
void vec_ensure_free(vec_t *, u64);
void vec_clone(vec_t *, vec_t *);

256
src/allocator.c Normal file
View File

@@ -0,0 +1,256 @@
/* allocator.c: Allocator implementations
* Created: 2026-02-12
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
*/
#include <stdlib.h>
#include <alisp/allocator.h>
#include <alisp/lisp.h>
#include <alisp/vec.h>
#include <string.h>
page_t *make_page(u64 size)
{
page_t *page = calloc(1, sizeof(*page));
vec_init(&page->data, MAX(size, ALLOC_PAGE_DEFAULT_SIZE));
return page;
}
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:
size += sizeof(cons_t);
break;
case TAG_VEC:
size += sizeof(vec_t);
break;
case TAG_STR:
size += sizeof(str_t);
break;
case TAG_NIL:
case TAG_SMI:
case TAG_SYM:
default:
FAIL("Unreachable");
return node;
}
// We must ensure size is a multiple of 8 for alignment purposes
size = (size & 0b111) == 0 ? size : size + (8 - (size & 0b111));
if (!vec_try_append(&page->data, NULL, size))
return NULL;
node = (alloc_node_t *)(vec_data(&page->data) + page->data.size - size);
node->metadata = (alloc_metadata_t){.references = 0, .tag = type};
return node;
}
alloc_node_t *lisp_to_node(lisp_t *lisp)
{
void *raw_ptr = NULL;
static_assert(NUM_TAGS == 6);
switch (tag_get(lisp))
{
case TAG_CONS:
raw_ptr = as_cons(lisp);
break;
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_SYM:
default:
FAIL("Unreachable");
return NIL;
}
alloc_node_t *node = raw_ptr;
return &node[-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_SYM:
default:
FAIL("Unreachable");
return NIL;
}
// 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)
{
alloc_node_t **nodeptr = &VEC_GET(&alloc->free_vec, i, alloc_node_t *);
// Skip any nodes that don't have the right type.
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.
alloc_node_t **lastptr =
&VEC_GET(&alloc->free_vec, free_vec_size - 1, alloc_node_t *);
alloc_node_t *val = *nodeptr;
*nodeptr = *lastptr;
*lastptr = val;
// Decrement the size of the free vector
alloc->free_vec.size -= sizeof(val);
// Then use that valid (and now unused) node as our return.
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 *)
{
page_t *page = VEC_GET(&alloc->pages, i, page_t *);
node = make_node(page, type);
if (node)
goto end;
}
// There aren't any pages we can allocate against, so we need to make a new
// page.
page_t *page = make_page(0);
vec_append(&alloc->pages, &page, sizeof(page));
node = make_node(page, type);
end:
if (!node)
FAIL("Unexpected issue with allocating to a verifiably good page");
return tag_generic(node->data, type);
}
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));
}
u64 alloc_cost(alloc_t *alloc)
{
u64 total_size = alloc->pages.size;
FOR_VEC(i, &alloc->pages, page_t *)
{
page_t *page = VEC_GET(&alloc->pages, i, page_t *);
total_size += page->data.size;
}
return total_size;
}
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 (dynamic walk)
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:
// Do nothing - will be cleaned by overall vec free anyway
break;
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_SYM:
default:
FAIL("Unreachable");
}
j += next;
}
// Each page was allocated on the heap.
vec_free(&page->data);
free(page);
}
vec_free(&alloc->pages);
vec_free(&alloc->free_vec);
memset(alloc, 0, sizeof(*alloc));
}
/* 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,207 +10,123 @@
#include <alisp/lisp.h>
void sys_init(sys_t *sys)
lisp_t *tag_smi(i64 i)
{
memset(sys, 0, sizeof(*sys));
return TAG(i, SMI);
}
lisp_t *sys_alloc(sys_t *sys, tag_t type)
lisp_t *tag_sym(const char *str)
{
return TAG(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);
}
lisp_t *tag_cons(const cons_t *cons)
{
return TAG(cons, CONS);
}
lisp_t *tag_generic(void *ptr, tag_t type)
{
static_assert(NUM_TAGS == 6);
switch (type)
{
case TAG_CONS:
{
cons_t *cons = calloc(1, sizeof(*cons));
lisp_t *lisp = tag_cons(cons);
vec_append(&sys->memory.conses, &lisp, sizeof(&lisp));
sys->memory.num_conses++;
return lisp;
}
case TAG_VEC:
{
vec_t *vec = calloc(1, sizeof(*vec));
lisp_t *lisp = tag_vec(vec);
vec_append(&sys->memory.vectors, &lisp, sizeof(&lisp));
sys->memory.num_vectors++;
return lisp;
}
// Shouldn't be registered
case TAG_NIL:
case TAG_INT:
return TAG(ptr, NIL);
case TAG_SMI:
return tag_smi((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;
}
u64 sys_cost(sys_t *sys)
{
u64 vec_capacity = 0;
for (u64 i = 0; i < sys->memory.num_vectors; ++i)
{
lisp_t *vec = VEC_GET(&sys->memory.vectors, i, lisp_t *);
vec_capacity += as_vec(vec)->capacity;
}
return sym_table_cost(&sys->symtable) +
(sys->memory.num_conses * sizeof(cons_t)) + vec_capacity;
}
void sys_free(sys_t *sys)
tag_t tag_get(const lisp_t *lisp)
{
sym_table_free(&sys->symtable);
// Iterate through each cell of memory currently allocated and free them
for (size_t i = 0; i < VEC_SIZE(&sys->memory.conses, lisp_t **); ++i)
{
lisp_t *allocated = VEC_GET(&sys->memory.conses, i, lisp_t *);
lisp_free(allocated);
return GET_TAG(lisp);
}
// Iterate through each cell of memory currently allocated and free them
for (size_t i = 0; i < VEC_SIZE(&sys->memory.vectors, lisp_t **); ++i)
i64 as_smi(lisp_t *obj)
{
lisp_t *allocated = VEC_GET(&sys->memory.vectors, i, lisp_t *);
lisp_free(allocated);
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);
}
// Free the containers
vec_free(&sys->memory.conses);
vec_free(&sys->memory.vectors);
// Ensure no one treats this as active in any sense
memset(sys, 0, sizeof(*sys));
char *as_sym(lisp_t *obj)
{
assert(IS_TAG(obj, SYM));
return (char *)UNTAG(obj);
}
lisp_t *make_int(i64 i)
cons_t *as_cons(lisp_t *obj)
{
return tag_int(i);
assert(IS_TAG(obj, CONS));
return (cons_t *)UNTAG(obj);
}
lisp_t *cons(sys_t *sys, lisp_t *car, lisp_t *cdr)
str_t *as_str(lisp_t *obj)
{
lisp_t *cons = sys_alloc(sys, TAG_CONS);
CAR(cons) = car;
CDR(cons) = cdr;
return cons;
assert(IS_TAG(obj, STR));
return (str_t *)UNTAG(obj);
}
lisp_t *make_vec(sys_t *sys, u64 capacity)
vec_t *as_vec(lisp_t *obj)
{
lisp_t *vec = sys_alloc(sys, TAG_VEC);
vec_init(as_vec(vec), capacity);
return vec;
}
lisp_t *intern(sys_t *sys, sv_t sv)
{
const 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);
}
void lisp_free(lisp_t *item)
{
switch (get_tag(item))
{
case TAG_CONS:
// 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_INT:
case TAG_SYM:
case NUM_TAGS:
// shouldn't be dealt with (either constant or dealt with elsewhere)
break;
}
}
void lisp_free_rec(lisp_t *item)
{
switch (get_tag(item))
{
case TAG_CONS:
{
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 (size_t i = 0; i < VEC_SIZE(vec, lisp_t **); ++i)
{
lisp_t *allocated = VEC_GET(vec, i, lisp_t *);
lisp_free_rec(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;
}
assert(IS_TAG(obj, VEC));
return (vec_t *)UNTAG(obj);
}
void lisp_print(FILE *fp, lisp_t *lisp)
{
if (!fp)
return;
switch (get_tag(lisp))
static_assert(NUM_TAGS == 6);
switch (tag_get(lisp))
{
case TAG_NIL:
fprintf(fp, "NIL");
break;
case TAG_INT:
#if VERBOSE_LOGS
case TAG_SMI:
#if VERBOSE_LOGS == 2
fprintf(fp, "INT[");
#endif
fprintf(fp, "%ld", as_int(lisp));
#if VERBOSE_LOGS
fprintf(fp, "%ld", as_smi(lisp));
#if VERBOSE_LOGS == 2
fprintf(fp, "]");
#endif
break;
case TAG_SYM:
#if VERBOSE_LOGS
#if VERBOSE_LOGS == 2
fprintf(fp, "SYM[");
#endif
fprintf(fp, "%s", as_sym(lisp));
#if VERBOSE_LOGS
#if VERBOSE_LOGS == 2
fprintf(fp, "]");
#endif
break;
case TAG_CONS:
{
#if VERBOSE_LOGS
#if VERBOSE_LOGS == 2
fprintf(fp, "LIST[");
#else
fprintf(fp, "(");
@@ -238,7 +154,7 @@ void lisp_print(FILE *fp, lisp_t *lisp)
break;
}
}
#if VERBOSE_LOGS
#if VERBOSE_LOGS == 2
fprintf(fp, "]");
#else
fprintf(fp, ")");
@@ -247,37 +163,115 @@ void lisp_print(FILE *fp, lisp_t *lisp)
}
case TAG_VEC:
{
#if VERBOSE_LOGS
#if VERBOSE_LOGS == 2
fprintf(fp, "VEC[");
#else
fprintf(fp, "[");
#endif
vec_t *vec = as_vec(lisp);
for (u64 i = 1; i <= VEC_SIZE(vec, lisp_t *); ++i)
FOR_VEC(i, vec, lisp_t *)
{
lisp_t *item = VEC_GET(vec, i - 1, lisp_t *);
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, " ");
}
}
#if VERBOSE_LOGS
#if VERBOSE_LOGS == 2
fprintf(fp, "]");
#else
fprintf(fp, "]");
#endif
break;
}
case NUM_TAGS:
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;
}
default:
FAIL("Unreachable");
break;
}
}
u64 tag_sizeof(tag_t tag)
{
static_assert(NUM_TAGS == 6);
switch (tag)
{
case TAG_NIL:
return 0;
case TAG_SMI:
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);
default:
FAIL("Unreachable");
return 0;
}
}
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

@@ -45,10 +45,10 @@ int main(int argc, char *argv[])
VEC_SIZE(&ast, lisp_t *) == 1 ? "expr" : "exprs");
{
for (u64 i = 0; i < VEC_SIZE(&ast, lisp_t *); ++i)
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_int(*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;
}

44
src/string.c Normal file
View File

@@ -0,0 +1,44 @@
/* 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/>.
*/

180
src/sys.c Normal file
View File

@@ -0,0 +1,180 @@
/* sys.c: System implementation
* Created: 2026-02-12
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
*/
#include <stdlib.h>
#include <string.h>
#include <alisp/sys.h>
void sys_init(sys_t *sys)
{
memset(sys, 0, sizeof(*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
case TAG_NIL:
case TAG_SMI:
case TAG_SYM:
default:
FAIL("Unreachable");
}
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);
}
void sys_free(sys_t *sys)
{
sym_table_free(&sys->symtable);
alloc_free(&sys->memory);
memset(sys, 0, sizeof(*sys));
}
lisp_t *make_int(i64 i)
{
return tag_smi(i);
}
lisp_t *cons(sys_t *sys, lisp_t *car, lisp_t *cdr)
{
lisp_t *cons = sys_alloc(sys, TAG_CONS);
CAR(cons) = car;
CDR(cons) = 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);
vec_init(as_vec(vec), 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);
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);
}
void lisp_free(sys_t *sys, lisp_t *lisp)
{
static_assert(NUM_TAGS == 6);
switch (tag_get(lisp))
{
case TAG_STR:
case TAG_VEC:
case TAG_CONS:
// Delete the underlying data
alloc_delete(&sys->memory, lisp);
break;
case TAG_NIL:
case TAG_SMI:
case TAG_SYM:
// shouldn't be dealt with (either constant or dealt with elsewhere)
break;
}
}
void lisp_free_rec(sys_t *sys, lisp_t *item)
{
static_assert(NUM_TAGS == 6);
switch (tag_get(item))
{
case TAG_CONS:
{
lisp_free_rec(sys, car(item));
lisp_free_rec(sys, cdr(item));
lisp_free(sys, item);
break;
}
case TAG_VEC:
{
vec_t *vec = as_vec(item);
FOR_VEC(i, vec, lisp_t *)
{
lisp_t *allocated = VEC_GET(vec, i, lisp_t *);
lisp_free_rec(sys, allocated);
}
lisp_free(sys, item);
break;
}
case TAG_STR:
{
lisp_free(sys, item);
break;
}
case TAG_NIL:
case TAG_SMI:
case TAG_SYM:
// shouldn't be dealt with (either constant or dealt with elsewhere)
break;
}
}
/* 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

@@ -1,82 +0,0 @@
/* tag.c: Pointer tagging
* Created: 2025-08-19
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
*/
#include <assert.h>
#include <stdlib.h>
#include <alisp/lisp.h>
lisp_t *tag_int(i64 i)
{
return TAG((u64)i, INT);
}
lisp_t *tag_sym(const char *str)
{
return TAG((u64)str, SYM);
}
lisp_t *tag_vec(const vec_t *vec)
{
return TAG((u64)vec, VEC);
}
lisp_t *tag_cons(const cons_t *cons)
{
return TAG((u64)cons, CONS);
}
tag_t get_tag(const 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);
}
/* Copyright (C) 2025, 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

@@ -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;
@@ -81,10 +89,24 @@ void vec_append(vec_t *vec, const void *const ptr, u64 size)
if (!vec)
return;
vec_ensure_free(vec, size);
if (ptr)
memcpy(vec_data(vec) + vec->size, ptr, size);
vec->size += size;
}
bool vec_try_append(vec_t *vec, const void *const ptr, u64 size)
{
if (!vec || vec->capacity - vec->size < size)
return false;
if (ptr)
{
void *newptr = vec_data(vec) + vec->size;
memcpy(newptr, ptr, size);
}
vec->size += size;
return true;
}
void vec_clone(vec_t *dest, vec_t *src)
{
if (!src || !dest)

View File

@@ -23,7 +23,7 @@ void smi_test(void)
{
i64 in = ints[i];
lisp_t *lisp = make_int(in);
i64 out = as_int(lisp);
i64 out = as_smi(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_int(lisp);
i64 out = as_smi(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("Freeing resources and deleting file `%s`\n", valid_filename);
TEST_INFO("Deleting file `%s`\n", valid_filename);
assert(valid_fp);
fclose(valid_fp);
remove(valid_filename);