Compare commits

...

10 Commits

Author SHA1 Message Date
Aryadev Chavali
bd838c02ab test_stream: basic skeleton 2026-02-05 06:06:03 +00:00
Aryadev Chavali
54e9edcba6 test_lisp_api: added sys_test 2026-02-05 05:48:00 +00:00
Aryadev Chavali
b32f420cb9 lisp: split off lisp_free as it's own function
lisp_free will do a shallow clean of any object, freeing its
associated memory.  It won't recur through any containers, nor will it
freakout if you give it something that is constant (symbols, small
integers, NIL, etc).
2026-02-05 05:39:35 +00:00
Aryadev Chavali
34d3417e74 symtable: sym_table_cleanup -> sym_table_free 2026-02-05 05:39:22 +00:00
Aryadev Chavali
66770f22d9 alisp.org: Added some tasks 2026-02-05 05:35:51 +00:00
Aryadev Chavali
3d0e373862 test_lisp_api: added sym_unique_test 2026-02-05 05:34:54 +00:00
Aryadev Chavali
0e8cdd7507 test_lisp_api: sym_test -> sym_fresh_test 2026-02-05 05:34:39 +00:00
Aryadev Chavali
4d693c8a92 test_lisp_api: int_test -> smi_test, added smi_oob_test 2026-02-05 05:24:34 +00:00
Aryadev Chavali
1c88253b3c tests: fix size of LISP_API_SUITE tests 2026-02-05 05:23:38 +00:00
Aryadev Chavali
de6fcd17ee Makefile: added mode flag for full logs
MODE=full will initialise a debug build with all logs, including test
logs.  Otherwise, MODE=debug just sets up standard debug build with
main logs but no testing logs.  MODE=release optimises and strips all
logs.
2026-02-05 05:17:25 +00:00
9 changed files with 236 additions and 35 deletions

View File

@@ -12,8 +12,10 @@ RFLAGS=-O3
MODE=release MODE=release
ifeq ($(MODE), release) ifeq ($(MODE), release)
CFLAGS=$(GFLAGS) $(RFLAGS) CFLAGS=$(GFLAGS) $(RFLAGS)
else else ifeq ($(MODE), debug)
CFLAGS=$(GFLAGS) $(DFLAGS) CFLAGS=$(GFLAGS) $(DFLAGS)
else ifeq ($(MODE), full)
CFLAGS=$(GFLAGS) $(DFLAGS) -DTEST_VERBOSE=1
endif endif
# Units to compile # Units to compile

View File

@@ -50,6 +50,11 @@ other state do we need to encode?
*** TODO Write a parser for lists *** TODO Write a parser for lists
*** TODO Write a parser for vectors *** TODO Write a parser for vectors
*** TODO Write the general parser *** TODO Write the general parser
** WIP Unit tests :tests:
*** TODO Test streams
*** DONE Test system registration of allocated units
In particular, does clean up work as we expect? Do we have situations
where we may double free or not clean up something we should've?
** Backlog ** Backlog
*** TODO Design Big Integers *** TODO Design Big Integers
We currently have 62 bit integers implemented via immediate values We currently have 62 bit integers implemented via immediate values
@@ -139,9 +144,6 @@ Latter approach time complexity:
Former approach is better time complexity wise, but latter is way Former approach is better time complexity wise, but latter is way
better in terms of simplicity of code. Must deliberate. better in terms of simplicity of code. Must deliberate.
*** TODO Test system registration of allocated units :test:
In particular, does clean up work as we expect? Do we have situations
where we may double free or not clean up something we should've?
*** TODO Design Strings *** TODO Design Strings
We have ~sv_t~ so our basic C API is done. We just need pluggable We have ~sv_t~ so our basic C API is done. We just need pluggable
functions to construct and deconstruct strings as lisps. functions to construct and deconstruct strings as lisps.
@@ -160,4 +162,4 @@ Test if ~make_vec~ works with ~as_vec~, ~cons~ with ~as_cons~ AND
We may need to think of effective ways to deal with NILs in ~car~ and We may need to think of effective ways to deal with NILs in ~car~ and
~cdr~. Maybe make functions as well as the macros so I can choose ~cdr~. Maybe make functions as well as the macros so I can choose
between them? between them?
**** DONE Write more tests *** DONE Write more tests

View File

@@ -49,6 +49,8 @@ vec_t *as_vec(lisp_t *);
lisp_t *car(lisp_t *); lisp_t *car(lisp_t *);
lisp_t *cdr(lisp_t *); lisp_t *cdr(lisp_t *);
void lisp_free(lisp_t *);
#endif #endif
/* Copyright (C) 2026 Aryadev Chavali /* Copyright (C) 2026 Aryadev Chavali

View File

@@ -22,7 +22,7 @@ typedef struct
void sym_table_init(sym_table_t *); void sym_table_init(sym_table_t *);
char *sym_table_find(sym_table_t *, sv_t); char *sym_table_find(sym_table_t *, sv_t);
void sym_table_cleanup(sym_table_t *); void sym_table_free(sym_table_t *);
#endif #endif

View File

@@ -26,7 +26,7 @@ void sys_free(sys_t *sys)
{ {
static_assert(NUM_TAGS == 5); static_assert(NUM_TAGS == 5);
sym_table_cleanup(&sys->symtable); sym_table_free(&sys->symtable);
if (sys->memory.size == 0) if (sys->memory.size == 0)
return; return;
@@ -34,26 +34,7 @@ void sys_free(sys_t *sys)
for (size_t i = 0; i < VEC_SIZE(&sys->memory, lisp_t **); ++i) for (size_t i = 0; i < VEC_SIZE(&sys->memory, lisp_t **); ++i)
{ {
lisp_t *allocated = VEC_GET(&sys->memory, i, lisp_t *); lisp_t *allocated = VEC_GET(&sys->memory, i, lisp_t *);
switch (get_tag(allocated)) lisp_free(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 // Free the container
@@ -110,6 +91,30 @@ lisp_t *cdr(lisp_t *lsp)
return CDR(lsp); 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;
}
}
/* Copyright (C) 2025, 2026 Aryadev Chavali /* Copyright (C) 2025, 2026 Aryadev Chavali
* This program is distributed in the hope that it will be useful, but WITHOUT * This program is distributed in the hope that it will be useful, but WITHOUT

View File

@@ -54,7 +54,7 @@ char *sym_table_find(sym_table_t *table, sv_t sv)
return ENTRY_GET(table, index).data; return ENTRY_GET(table, index).data;
} }
void sym_table_cleanup(sym_table_t *table) void sym_table_free(sym_table_t *table)
{ {
// Iterate through the strings and free each of them. // Iterate through the strings and free each of them.
sv_t current = {0}; sv_t current = {0};

View File

@@ -10,8 +10,10 @@
#include <alisp/lisp.h> #include <alisp/lisp.h>
void int_test(void) void smi_test(void)
{ {
// Standard old testing, checking both sides of the number line and our set
// bounds.
i64 ints[] = { i64 ints[] = {
1, -1, (1 << 10) - 1, (-1) * ((1 << 10) - 1), INT_MIN, INT_MAX, 1, -1, (1 << 10) - 1, (-1) * ((1 << 10) - 1), INT_MIN, INT_MAX,
}; };
@@ -28,11 +30,36 @@ void int_test(void)
TEST_PASSED(); TEST_PASSED();
} }
void sym_test(void) void smi_oob_test(void)
{
// These are integers that are completely out of the bounds of our standard
// tagging system due to their size. We need to use big integers for this.
i64 ints[] = {
INT_MIN - 1,
INT_MAX + 1,
INT64_MIN,
INT64_MAX,
};
for (u64 i = 0; i < ARRSIZE(ints); ++i)
{
i64 in = ints[i];
lisp_t *lisp = make_int(in);
i64 out = as_int(lisp);
TEST(in != out, "%ld != %ld", in, out);
}
TEST_PASSED();
}
void sym_fresh_test(void)
{ {
sys_t system = {0}; sys_t system = {0};
sys_init(&system); sys_init(&system);
// We expect every interned symbol to get a fresh allocation, but still be a
// valid representation of the original symbol.
for (u64 i = 0; i < ARRSIZE(words); ++i) for (u64 i = 0; i < ARRSIZE(words); ++i)
{ {
const char *in = words[i]; const char *in = words[i];
@@ -47,6 +74,37 @@ void sym_test(void)
TEST_PASSED(); TEST_PASSED();
} }
void sym_unique_test(void)
{
sys_t system = {0};
sys_init(&system);
sv_t symbols[] = {
SV("hello", 5),
SV("goodbye", 7),
SV("display", 7),
SV("@xs'a_sh;d::a-h]", 16),
};
lisp_t *ptrs[ARRSIZE(symbols)];
for (u64 i = 0; i < ARRSIZE(symbols); ++i)
{
ptrs[i] = intern(&system, symbols[i]);
TEST(ptrs[i] != 0, "%p (derived from `" PR_SV "`) is not NIL",
(void *)ptrs[i], SV_FMT(symbols[i]));
}
for (u64 i = 0; i < ARRSIZE(symbols); ++i)
{
lisp_t *newptr = intern(&system, symbols[i]);
TEST(newptr == ptrs[i], "interning again (%p) gives us the same (%p)",
(void *)newptr, (void *)ptrs[i]);
}
sys_free(&system);
TEST_PASSED();
}
void cons_test(void) void cons_test(void)
{ {
sys_t system = {0}; sys_t system = {0};
@@ -61,7 +119,13 @@ void cons_test(void)
lisp = cons(&system, lword, lisp); lisp = cons(&system, lword, lisp);
} }
// Make sure we've essentially reversed the `words` array /*
As we've cons'd each word, we'd expect the order to be reversed. This test
will allow us to verify:
1) words have actually been added to the linked list.
2) words are in the order we expect.
in one go.
*/
u64 i = ARRSIZE(words); u64 i = ARRSIZE(words);
for (lisp_t *iter = lisp; iter; iter = cdr(iter), --i) for (lisp_t *iter = lisp; iter; iter = cdr(iter), --i)
{ {
@@ -77,15 +141,63 @@ void cons_test(void)
TEST_PASSED(); TEST_PASSED();
} }
void sys_test(void)
{
sys_t sys = {0};
sys_init(&sys);
u64 old_memory_size = sys.memory.size;
// Creating integers doesn't affect memory size
(void)make_int(2000);
TEST(sys.memory.size == old_memory_size,
"Making integers doesn't affect system memory size");
// Creating symbols won't affect memory size, but does affect the symbol table
(void)intern(&sys, SV("hello world!", 12));
TEST(sys.memory.size == old_memory_size,
"Interning doesn't affect system memory size");
TEST(sys.symtable.count > 0, "Interning affects symbol table");
// Creating cons' do affect memory size
(void)cons(&sys, make_int(1), make_int(2));
TEST(sys.memory.size > 0, "Creating cons' affects memory size");
old_memory_size = sys.memory.size;
(void)cons(&sys, intern(&sys, SV("test", 4)), NIL);
TEST(sys.memory.size > old_memory_size,
"Creating cons' back to back affects memory size");
old_memory_size = sys.memory.size;
// Creating vectors does affect memory size
(void)make_vec(&sys, 8);
TEST(sys.memory.size > old_memory_size,
"Creating vectors (size 8) affects memory size");
old_memory_size = sys.memory.size;
(void)make_vec(&sys, 1000);
TEST(sys.memory.size > old_memory_size,
"Creating vectors (size 1000) affects memory size");
old_memory_size = sys.memory.size;
sys_free(&sys);
TEST(sys.memory.size == 0, "sys_free cleans up memory (shallow check)");
TEST(sys.symtable.count == 0, "sys_free cleans up symtable (shallow check)");
TEST_PASSED();
}
const test_suite_t LISP_API_SUITE = { const test_suite_t LISP_API_SUITE = {
.name = "Lisp API Tests", .name = "Lisp API Tests",
.tests = .tests =
(test_fn[]){ (test_fn[]){
MAKE_TEST_FN(int_test), MAKE_TEST_FN(smi_test),
MAKE_TEST_FN(sym_test), MAKE_TEST_FN(smi_oob_test),
MAKE_TEST_FN(sym_fresh_test),
MAKE_TEST_FN(sym_unique_test),
MAKE_TEST_FN(cons_test), MAKE_TEST_FN(cons_test),
MAKE_TEST_FN(sys_test),
}, },
.size = 3, .size = 6,
}; };
/* Copyright (C) 2026 Aryadev Chavali /* Copyright (C) 2026 Aryadev Chavali

78
test/test_stream.c Normal file
View File

@@ -0,0 +1,78 @@
/* test_stream.c: Stream tests
* Created: 2026-02-05
* Author: Aryadev Chavali
* License: See end of file
* Commentary:
*/
#include "./data.h"
#include "./test.h"
void stream_test_string(void)
{
TODO("Not implemented");
}
void stream_test_file(void)
{
TODO("Not implemented");
}
void stream_test_peek_next(void)
{
TODO("Not implemented");
}
void stream_test_seek(void)
{
TODO("Not implemented");
}
void stream_test_substr(void)
{
TODO("Not implemented");
}
void stream_test_till(void)
{
TODO("Not implemented");
}
void stream_test_while(void)
{
TODO("Not implemented");
}
void stream_test_line_col(void)
{
TODO("Not implemented");
}
const test_suite_t STREAM_SUITE = {
.name = "Stream Tests",
.tests =
(test_fn[]){
MAKE_TEST_FN(stream_test_string),
MAKE_TEST_FN(stream_test_file),
MAKE_TEST_FN(stream_test_peek_next),
MAKE_TEST_FN(stream_test_seek),
MAKE_TEST_FN(stream_test_substr),
MAKE_TEST_FN(stream_test_till),
MAKE_TEST_FN(stream_test_while),
MAKE_TEST_FN(stream_test_line_col),
},
.size = 8,
};
/* 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

@@ -18,7 +18,7 @@ void symtable_test(void)
TEST(table.count == ARRSIZE(unique_words), "%lu == %lu", table.count, TEST(table.count == ARRSIZE(unique_words), "%lu == %lu", table.count,
ARRSIZE(unique_words)); ARRSIZE(unique_words));
sym_table_cleanup(&table); sym_table_free(&table);
TEST_PASSED(); TEST_PASSED();
} }