Compare commits
10 Commits
91264d96e4
...
bd838c02ab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd838c02ab | ||
|
|
54e9edcba6 | ||
|
|
b32f420cb9 | ||
|
|
34d3417e74 | ||
|
|
66770f22d9 | ||
|
|
3d0e373862 | ||
|
|
0e8cdd7507 | ||
|
|
4d693c8a92 | ||
|
|
1c88253b3c | ||
|
|
de6fcd17ee |
4
Makefile
4
Makefile
@@ -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
|
||||||
|
|||||||
10
alisp.org
10
alisp.org
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
47
src/lisp.c
47
src/lisp.c
@@ -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
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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
78
test/test_stream.c
Normal 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/>.
|
||||||
|
|
||||||
|
*/
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user