Move everything to src/ folder
This commit is contained in:
70
src/constructor.c
Normal file
70
src/constructor.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/* constructor.c: Lisp constructors/destructors
|
||||
* Created: 2025-08-20
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
#include <alisp/lisp.h>
|
||||
#include <alisp/tag.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);
|
||||
}
|
||||
|
||||
/* 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 Unlicense for details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the Unlicense,
|
||||
* which you should have received a copy of along with this program. If not,
|
||||
* please go to <https://unlicense.org/>.
|
||||
|
||||
*/
|
||||
62
src/main.c
Normal file
62
src/main.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/* main.c: Entrypoint
|
||||
* Created: 2025-08-19
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <alisp/alisp.h>
|
||||
|
||||
const char *TOKEN_DELIM = "\n ";
|
||||
|
||||
int main(void)
|
||||
{
|
||||
sym_table_t table = {0};
|
||||
sym_table_init(&table);
|
||||
|
||||
char filename[] = "./lorem.txt";
|
||||
FILE *fp = fopen(filename, "r");
|
||||
stream_t stream = {0};
|
||||
stream_init_file(&stream, filename, fp);
|
||||
|
||||
for (u64 token_no = 1; !stream_eoc(&stream); ++token_no)
|
||||
{
|
||||
// Skip forward any delimiters
|
||||
stream_while(&stream, TOKEN_DELIM);
|
||||
// Get the token (up until delimiter)
|
||||
sv_t token = stream_till(&stream, TOKEN_DELIM);
|
||||
char *interned = sym_table_find(&table, token);
|
||||
printf("%s[%lu] => `%s`\n", stream.name, token_no, interned);
|
||||
}
|
||||
|
||||
printf("\nTable count=%lu\n", table.count);
|
||||
for (u64 i = 0, j = 0; i < table.capacity; ++i)
|
||||
{
|
||||
sv_t token = VEC_GET(&table.entries, i, sv_t);
|
||||
if (!token.data)
|
||||
continue;
|
||||
printf("[%lu]@[%lu] => `" PR_SV "`\n", j, i, SV_FMT(token));
|
||||
++j;
|
||||
}
|
||||
|
||||
stream_stop(&stream);
|
||||
fclose(fp);
|
||||
sym_table_cleanup(&table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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 Unlicense for details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the Unlicense,
|
||||
* which you should have received a copy of along with this program. If not,
|
||||
* please go to <https://unlicense.org/>.
|
||||
|
||||
*/
|
||||
374
src/stream.c
Normal file
374
src/stream.c
Normal file
@@ -0,0 +1,374 @@
|
||||
/* stream.c: Stream implementation
|
||||
* Created: 2025-08-26
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <alisp/stream.h>
|
||||
|
||||
stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents)
|
||||
{
|
||||
if (!stream)
|
||||
return STREAM_ERR_INVALID_PTR;
|
||||
name = name ? name : "<stream>";
|
||||
memset(stream, 0, sizeof(*stream));
|
||||
|
||||
stream->type = STREAM_TYPE_STRING;
|
||||
stream->name = name;
|
||||
stream->string = sv_copy(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 : "<stream>";
|
||||
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 : "<stream>";
|
||||
memset(stream, 0, sizeof(*stream));
|
||||
|
||||
stream->type = STREAM_TYPE_FILE;
|
||||
stream->name = name;
|
||||
stream->pipe.file = pipe;
|
||||
|
||||
vec_init(&stream->pipe.cache, STREAM_DEFAULT_CHUNK);
|
||||
|
||||
return STREAM_ERR_OK;
|
||||
}
|
||||
|
||||
void stream_stop(stream_t *stream)
|
||||
{
|
||||
if (!stream)
|
||||
return;
|
||||
switch (stream->type)
|
||||
{
|
||||
case STREAM_TYPE_STRING:
|
||||
free(stream->string.data);
|
||||
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 = (char *)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 = (char *)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((char *)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((char *)vec_data(&stream->pipe.cache) + index, size);
|
||||
}
|
||||
default:
|
||||
assert("Unreachable");
|
||||
return SV(NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
sv_t stream_till(stream_t *stream, const char *str)
|
||||
{
|
||||
if (stream_eoc(stream))
|
||||
return SV(NULL, 0);
|
||||
u64 current_position = stream->position;
|
||||
for (char c = stream_peek(stream); c != '\0' && strchr(str, c) == NULL;
|
||||
c = stream_next(stream))
|
||||
continue;
|
||||
u64 size = stream->position - current_position;
|
||||
if (size == 0)
|
||||
return SV(NULL, 0);
|
||||
return stream_substr_abs(stream, current_position, size - 1);
|
||||
}
|
||||
|
||||
sv_t stream_while(stream_t *stream, const char *str)
|
||||
{
|
||||
if (stream_eoc(stream))
|
||||
return SV(NULL, 0);
|
||||
u64 current_position = stream->position;
|
||||
for (char c = stream_peek(stream); c != '\0' && strchr(str, c);
|
||||
c = stream_next(stream))
|
||||
continue;
|
||||
u64 size = stream->position - current_position;
|
||||
if (size == 0)
|
||||
return SV(NULL, 0);
|
||||
return stream_substr_abs(stream, current_position, size - 1);
|
||||
}
|
||||
|
||||
/* 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 Unlicense for details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the Unlicense,
|
||||
* which you should have received a copy of along with this program. If not,
|
||||
* please go to <https://unlicense.org/>.
|
||||
|
||||
*/
|
||||
31
src/sv.c
Normal file
31
src/sv.c
Normal file
@@ -0,0 +1,31 @@
|
||||
/* sv.c: String views
|
||||
* Created: 2025-08-21
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <alisp/sv.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);
|
||||
}
|
||||
|
||||
/* 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 Unlicense for details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the Unlicense,
|
||||
* which you should have received a copy of along with this program. If not,
|
||||
* please go to <https://unlicense.org/>.
|
||||
|
||||
*/
|
||||
82
src/symtable.c
Normal file
82
src/symtable.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/* symtable.c: Symbol Table implementation
|
||||
* Created: 2025-08-19
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include <alisp/symtable.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ENTRY_GET(TABLE, INDEX) (VEC_GET(&(TABLE)->entries, (INDEX), sv_t))
|
||||
|
||||
u64 djb2(sv_t string)
|
||||
{
|
||||
// magic number (see
|
||||
// https://stackoverflow.com/questions/10696223/reason-for-the-number-5381-in-the-djb-hash-function)
|
||||
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: Consider resizing table when count >= capacity / 2
|
||||
u64 index = djb2(sv) & (table->capacity - 1);
|
||||
|
||||
// FIXME: Since we don't implement resizing currently, this will infinite loop
|
||||
// when the table capacity SYM_TABLE_INIT_SIZE=(1 << 10)=1024 is filled.
|
||||
for (sv_t comp = ENTRY_GET(table, index); comp.data;
|
||||
index = (index + 1) & (table->capacity - 1),
|
||||
comp = ENTRY_GET(table, index))
|
||||
// If present in the table already, stop.
|
||||
if (sv.size == comp.size && strncmp(sv.data, comp.data, sv.size) == 0)
|
||||
return comp.data;
|
||||
|
||||
// Worst case: not present in the table already. Since index is certainly
|
||||
// free (based on loop condition before) we can fill it.
|
||||
ENTRY_GET(table, index) = sv_copy(sv);
|
||||
++table->count;
|
||||
return ENTRY_GET(table, index).data;
|
||||
}
|
||||
|
||||
void sym_table_cleanup(sym_table_t *table)
|
||||
{
|
||||
// Iterate through the strings and free each of them.
|
||||
sv_t current = {0};
|
||||
for (u64 i = 0; i < table->capacity; ++i)
|
||||
{
|
||||
current = ENTRY_GET(table, i);
|
||||
if (current.data)
|
||||
free(current.data);
|
||||
}
|
||||
// Free the underlying container
|
||||
vec_free(&table->entries);
|
||||
memset(table, 0, sizeof(*table));
|
||||
}
|
||||
|
||||
/* 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 Unlicense for details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the Unlicense,
|
||||
* which you should have received a copy of along with this program. If not,
|
||||
* please go to <https://unlicense.org/>.
|
||||
|
||||
*/
|
||||
77
src/sys.c
Normal file
77
src/sys.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/* sys.c: System management
|
||||
* Created: 2025-08-20
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <alisp/lisp.h>
|
||||
#include <alisp/tag.h>
|
||||
|
||||
void sys_init(sys_t *sys)
|
||||
{
|
||||
memset(sys, 0, sizeof(*sys));
|
||||
}
|
||||
|
||||
void sys_register(sys_t *sys, lisp_t *ptr)
|
||||
{
|
||||
// Simply append it to the list of currently active conses
|
||||
vec_append(&sys->memory, &ptr, sizeof(&ptr));
|
||||
}
|
||||
|
||||
void sys_cleanup(sys_t *sys)
|
||||
{
|
||||
static_assert(NUM_TAGS == 5);
|
||||
|
||||
sym_table_cleanup(&sys->symtable);
|
||||
if (sys->memory.size == 0)
|
||||
return;
|
||||
|
||||
// Iterate through each cell of memory currently allocated and free them
|
||||
for (size_t i = 0; i < VEC_SIZE(&sys->memory, lisp_t **); ++i)
|
||||
{
|
||||
lisp_t *allocated = VEC_GET(&sys->memory, i, lisp_t *);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Free the container
|
||||
vec_free(&sys->memory);
|
||||
|
||||
// Ensure no one treats this as active in any sense
|
||||
memset(sys, 0, sizeof(*sys));
|
||||
}
|
||||
|
||||
/* 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 Unlicense for details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the Unlicense,
|
||||
* which you should have received a copy of along with this program. If not,
|
||||
* please go to <https://unlicense.org/>.
|
||||
|
||||
*/
|
||||
81
src/tag.c
Normal file
81
src/tag.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/* 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/tag.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);
|
||||
}
|
||||
|
||||
/* 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 Unlicense for details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the Unlicense,
|
||||
* which you should have received a copy of along with this program. If not,
|
||||
* please go to <https://unlicense.org/>.
|
||||
|
||||
*/
|
||||
106
src/vec.c
Normal file
106
src/vec.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/* vec.c: Stable Vector implementation
|
||||
* Created: 2025-08-20
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <alisp/vec.h>
|
||||
|
||||
void vec_init(vec_t *vec, u64 size)
|
||||
{
|
||||
memset(vec, 0, sizeof(*vec));
|
||||
if (!vec)
|
||||
return;
|
||||
else if (size <= VEC_INLINE_CAPACITY)
|
||||
{
|
||||
vec->not_inlined = 0;
|
||||
vec->capacity = VEC_INLINE_CAPACITY;
|
||||
vec->ptr = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec->not_inlined = 1;
|
||||
vec->capacity = size;
|
||||
vec->ptr = calloc(1, vec->capacity);
|
||||
}
|
||||
}
|
||||
|
||||
void vec_free(vec_t *vec)
|
||||
{
|
||||
if (!vec)
|
||||
return;
|
||||
if (vec->not_inlined && vec->ptr)
|
||||
free(vec->ptr);
|
||||
memset(vec, 0, sizeof(*vec));
|
||||
}
|
||||
|
||||
u8 *vec_data(vec_t *vec)
|
||||
{
|
||||
return vec->not_inlined ? vec->ptr : vec->inlined;
|
||||
}
|
||||
|
||||
void vec_ensure_free(vec_t *vec, u64 size)
|
||||
{
|
||||
if (!vec)
|
||||
return;
|
||||
if (vec->capacity == 0)
|
||||
{
|
||||
// We need to initialise this ourselves.
|
||||
vec->capacity = VEC_INLINE_CAPACITY;
|
||||
vec->size = 0;
|
||||
memset(vec->inlined, 0, sizeof(vec->inlined));
|
||||
}
|
||||
|
||||
if (vec->capacity - vec->size < size)
|
||||
{
|
||||
vec->capacity = MAX(vec->capacity * VEC_MULT, vec->size + size);
|
||||
if (!vec->not_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->not_inlined = 1;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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 Unlicense for details.
|
||||
|
||||
* You may distribute and modify this code under the terms of the Unlicense,
|
||||
* which you should have received a copy of along with this program. If not,
|
||||
* please go to <https://unlicense.org/>.
|
||||
|
||||
*/
|
||||
Reference in New Issue
Block a user