Compare commits
31 Commits
b8d0ee2c7f
...
4aad62fec9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4aad62fec9 | ||
|
|
a4fb48a863 | ||
|
|
5f05a6a108 | ||
|
|
e60a7459e0 | ||
|
|
4936460b39 | ||
|
|
04be0ecad0 | ||
|
|
f09e0d33a4 | ||
|
|
d1d0783fc3 | ||
|
|
21254e77bf | ||
|
|
e772b06ae5 | ||
|
|
eca46069f8 | ||
|
|
c2f1835b4c | ||
|
|
93a52db7cc | ||
|
|
d88bc2baeb | ||
|
|
2dc0c6080e | ||
|
|
8e81e6f762 | ||
|
|
a49492b27f | ||
|
|
8b2fe97fc2 | ||
|
|
ff512986f8 | ||
|
|
47f33cb969 | ||
|
|
39e5b76f8b | ||
|
|
b91e79933b | ||
|
|
3e7734037c | ||
|
|
9f3bb57972 | ||
|
|
b646ae3f7e | ||
|
|
818d4da850 | ||
|
|
daa1d3d565 | ||
|
|
b7fc5170b0 | ||
|
|
d02174ea8b | ||
|
|
7ef6905d7a | ||
|
|
c12f4b2d2c |
2
Makefile
2
Makefile
@@ -19,7 +19,7 @@ CFLAGS=$(GFLAGS) $(DFLAGS) -DTEST_VERBOSE=1
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# Units to compile
|
# Units to compile
|
||||||
UNITS=src/sv.c src/vec.c src/stream.c src/symtable.c src/tag.c src/lisp.c
|
UNITS=src/sv.c src/vec.c src/stream.c src/symtable.c src/tag.c src/lisp.c src/reader.c
|
||||||
OBJECTS:=$(patsubst src/%.c, $(DIST)/%.o, $(UNITS))
|
OBJECTS:=$(patsubst src/%.c, $(DIST)/%.o, $(UNITS))
|
||||||
|
|
||||||
TEST_UNITS=test/main.c
|
TEST_UNITS=test/main.c
|
||||||
|
|||||||
31
alisp.org
31
alisp.org
@@ -4,7 +4,7 @@
|
|||||||
#+filetags: :alisp:
|
#+filetags: :alisp:
|
||||||
|
|
||||||
* Tasks
|
* Tasks
|
||||||
** WIP Reader system
|
** Reader system :reader:
|
||||||
We need to design a reader system. The big idea: given a "stream" of
|
We need to design a reader system. The big idea: given a "stream" of
|
||||||
data, we can break out expressions from it. An expression could be
|
data, we can break out expressions from it. An expression could be
|
||||||
either an atomic value or a container.
|
either an atomic value or a container.
|
||||||
@@ -45,13 +45,15 @@ i.e. no parsing.
|
|||||||
*** DONE Design what a "parser function" would look like
|
*** DONE Design what a "parser function" would look like
|
||||||
The general function is something like ~stream -> T | Err~. What
|
The general function is something like ~stream -> T | Err~. What
|
||||||
other state do we need to encode?
|
other state do we need to encode?
|
||||||
*** TODO Write a parser for integers
|
*** DONE Write a parser for integers
|
||||||
*** TODO Write a parser for symbols
|
*** DONE Write a parser for symbols
|
||||||
*** TODO Write a parser for lists
|
*** DONE Write a parser for lists
|
||||||
*** TODO Write a parser for vectors
|
*** DONE Write a parser for vectors
|
||||||
*** TODO Write the general parser
|
*** TODO Write a parser for strings
|
||||||
|
Requires [[*Design Strings for the Lisp]] to be complete first.
|
||||||
|
*** WIP Write the general parser
|
||||||
** Unit tests :tests:
|
** Unit tests :tests:
|
||||||
*** TODO Test streams
|
*** TODO Test streams :streams:
|
||||||
**** DONE Test file init
|
**** DONE Test file init
|
||||||
[[file:test/test_stream.c::void stream_test_file(void)]]
|
[[file:test/test_stream.c::void stream_test_file(void)]]
|
||||||
***** DONE Test successful init from real files
|
***** DONE Test successful init from real files
|
||||||
@@ -97,10 +99,11 @@ Also ensure stream_eoc is false.
|
|||||||
- line_col on bad stream (no effect on args)
|
- line_col on bad stream (no effect on args)
|
||||||
- line_col on eoc stream (should go right to the end)
|
- line_col on eoc stream (should go right to the end)
|
||||||
- line_col on random points in a stream
|
- line_col on random points in a stream
|
||||||
|
*** TODO Test reader :reader:
|
||||||
*** DONE Test system registration of allocated units
|
*** DONE Test system registration of allocated units
|
||||||
In particular, does clean up work as we expect? Do we have situations
|
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?
|
where we may double free or not clean up something we should've?
|
||||||
** String views :sv_t:
|
** String views :strings:
|
||||||
[[file:include/alisp/sv.h::/// String Views]]
|
[[file:include/alisp/sv.h::/// String Views]]
|
||||||
*** DONE sv_substr
|
*** DONE sv_substr
|
||||||
Takes an index and a size, returns a string view to that substring.
|
Takes an index and a size, returns a string view to that substring.
|
||||||
@@ -109,12 +112,12 @@ Super obvious.
|
|||||||
*** TODO Design Strings for the Lisp :api:
|
*** TODO Design Strings for the Lisp :api:
|
||||||
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.
|
||||||
** Backlog
|
** Design :design:
|
||||||
*** TODO Design Big Integers :api:
|
*** TODO Design Big Integers :api:
|
||||||
We currently have 62 bit integers implemented via immediate values
|
We currently have 62 bit integers implemented via immediate values
|
||||||
embedded in a pointer. We need to be able to support even _bigger_
|
embedded in a pointer. We need to be able to support even _bigger_
|
||||||
integers. How do we do this?
|
integers. How do we do this?
|
||||||
*** TODO Design garbage collection scheme :design:gc:
|
*** TODO Design garbage collection scheme :gc:
|
||||||
Really, regardless of what I do, we need to have some kind of garbage
|
Really, regardless of what I do, we need to have some kind of garbage
|
||||||
collection header on whatever managed objects we allocate.
|
collection header on whatever managed objects we allocate.
|
||||||
|
|
||||||
@@ -198,10 +201,16 @@ 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 Capitalise symbols (TBD) :optimisation:design:
|
*** TODO Capitalise symbols (TBD) :optimisation:
|
||||||
Should we capitalise symbols? This way, we limit the symbol table's
|
Should we capitalise symbols? This way, we limit the symbol table's
|
||||||
possible options a bit (potentially we could design a better hashing
|
possible options a bit (potentially we could design a better hashing
|
||||||
algorithm?) and it would be kinda like an actual Lisp.
|
algorithm?) and it would be kinda like an actual Lisp.
|
||||||
|
*** TODO Consider reader macros :reader:
|
||||||
|
Common Lisp has so-called "reader macros" which allows users to write
|
||||||
|
Lisp code that affects further Lisp code reading. It's quite
|
||||||
|
powerful.
|
||||||
|
|
||||||
|
Scheme doesn't have it. Should we implement this?
|
||||||
** Completed
|
** Completed
|
||||||
*** DONE Test value constructors and destructors :test:
|
*** DONE Test value constructors and destructors :test:
|
||||||
Test if ~make_int~ works with ~as_int,~ ~intern~ with ~as_sym~.
|
Test if ~make_int~ works with ~as_int,~ ~intern~ with ~as_sym~.
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#ifndef LISP_H
|
#ifndef LISP_H
|
||||||
#define LISP_H
|
#define LISP_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <alisp/symtable.h>
|
#include <alisp/symtable.h>
|
||||||
#include <alisp/vec.h>
|
#include <alisp/vec.h>
|
||||||
|
|
||||||
@@ -32,6 +34,9 @@ void sys_init(sys_t *);
|
|||||||
void sys_register(sys_t *, lisp_t *);
|
void sys_register(sys_t *, lisp_t *);
|
||||||
void sys_free(sys_t *);
|
void sys_free(sys_t *);
|
||||||
|
|
||||||
|
// Debugging function: provides total memory usage from system.
|
||||||
|
u64 sys_cost(sys_t *);
|
||||||
|
|
||||||
/// Constructors and destructors
|
/// Constructors and destructors
|
||||||
lisp_t *make_int(i64);
|
lisp_t *make_int(i64);
|
||||||
lisp_t *make_vec(sys_t *, u64);
|
lisp_t *make_vec(sys_t *, u64);
|
||||||
@@ -50,6 +55,9 @@ lisp_t *car(lisp_t *);
|
|||||||
lisp_t *cdr(lisp_t *);
|
lisp_t *cdr(lisp_t *);
|
||||||
|
|
||||||
void lisp_free(lisp_t *);
|
void lisp_free(lisp_t *);
|
||||||
|
void lisp_free_rec(lisp_t *);
|
||||||
|
|
||||||
|
void lisp_print(FILE *, lisp_t *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ typedef enum
|
|||||||
{
|
{
|
||||||
READ_ERR_OK = 0,
|
READ_ERR_OK = 0,
|
||||||
READ_ERR_EOF,
|
READ_ERR_EOF,
|
||||||
|
READ_ERR_EXPECTED_CLOSED_BRACE,
|
||||||
|
READ_ERR_EXPECTED_CLOSED_SQUARE_BRACKET,
|
||||||
|
READ_ERR_UNEXPECTED_CLOSED_BRACE,
|
||||||
|
READ_ERR_UNEXPECTED_CLOSED_SQUARE_BRACKET,
|
||||||
READ_ERR_UNKNOWN_CHAR,
|
READ_ERR_UNKNOWN_CHAR,
|
||||||
} read_err_t;
|
} read_err_t;
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,12 @@
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
u64 size;
|
u64 size;
|
||||||
char *data;
|
const char *data;
|
||||||
} sv_t;
|
} sv_t;
|
||||||
|
|
||||||
// String view macro constructor
|
// String view macro constructor
|
||||||
#define SV(DATA, SIZE) ((sv_t){.data = (DATA), .size = (SIZE)})
|
#define SV(DATA, SIZE) ((sv_t){.data = (DATA), .size = (SIZE)})
|
||||||
|
#define SV_AUTO(DATA) ((sv_t){.data = (void *)(DATA), .size = sizeof(DATA) - 1})
|
||||||
// Pretty printers
|
// Pretty printers
|
||||||
#define SV_FMT(SV) (int)(SV).size, (SV).data
|
#define SV_FMT(SV) (int)(SV).size, (SV).data
|
||||||
#define PR_SV "%.*s"
|
#define PR_SV "%.*s"
|
||||||
@@ -28,8 +29,8 @@ typedef struct
|
|||||||
sv_t sv_copy(sv_t);
|
sv_t sv_copy(sv_t);
|
||||||
sv_t sv_chop_left(sv_t, u64 size);
|
sv_t sv_chop_left(sv_t, u64 size);
|
||||||
sv_t sv_chop_right(sv_t, u64 size);
|
sv_t sv_chop_right(sv_t, u64 size);
|
||||||
sv_t sv_substr(sv_t, u64 position, u64 size);
|
|
||||||
sv_t sv_truncate(sv_t, u64 newsize);
|
sv_t sv_truncate(sv_t, u64 newsize);
|
||||||
|
sv_t sv_substr(sv_t, u64 position, u64 size);
|
||||||
|
|
||||||
sv_t sv_till(sv_t, const char *reject);
|
sv_t sv_till(sv_t, const char *reject);
|
||||||
sv_t sv_while(sv_t, const char *accept);
|
sv_t sv_while(sv_t, const char *accept);
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ typedef struct
|
|||||||
#define SYM_TABLE_INIT_SIZE (1 << 10)
|
#define SYM_TABLE_INIT_SIZE (1 << 10)
|
||||||
|
|
||||||
void sym_table_init(sym_table_t *);
|
void sym_table_init(sym_table_t *);
|
||||||
char *sym_table_find(sym_table_t *, sv_t);
|
const char *sym_table_find(sym_table_t *, sv_t);
|
||||||
void sym_table_free(sym_table_t *);
|
void sym_table_free(sym_table_t *);
|
||||||
|
|
||||||
|
// Debugging function: total memory used by symbol table.
|
||||||
|
u64 sym_table_cost(sym_table_t *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Copyright (C) 2026 Aryadev Chavali
|
/* Copyright (C) 2026 Aryadev Chavali
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ enum Mask
|
|||||||
#define IS_TAG(PTR, TYPE) (((u64)(PTR) & MASK_##TYPE) == TAG_##TYPE)
|
#define IS_TAG(PTR, TYPE) (((u64)(PTR) & MASK_##TYPE) == TAG_##TYPE)
|
||||||
#define UNTAG(PTR, TYPE) (((u64)PTR) >> SHIFT_##TYPE)
|
#define UNTAG(PTR, TYPE) (((u64)PTR) >> SHIFT_##TYPE)
|
||||||
|
|
||||||
#define INT_MAX ((1L << 62) - 1)
|
#define INT_MAX ((((i64)1) << 62) - 1)
|
||||||
#define INT_MIN (-(1L << 62))
|
#define INT_MIN (-(((i64)1) << 62))
|
||||||
|
|
||||||
tag_t get_tag(lisp_t *);
|
tag_t get_tag(const lisp_t *);
|
||||||
lisp_t *tag_int(i64);
|
lisp_t *tag_int(const i64);
|
||||||
lisp_t *tag_sym(char *);
|
lisp_t *tag_sym(const char *);
|
||||||
lisp_t *tag_cons(cons_t *);
|
lisp_t *tag_cons(const cons_t *);
|
||||||
lisp_t *tag_vec(vec_t *);
|
lisp_t *tag_vec(const vec_t *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
136
src/lisp.c
136
src/lisp.c
@@ -22,6 +22,11 @@ void sys_register(sys_t *sys, lisp_t *ptr)
|
|||||||
vec_append(&sys->memory, &ptr, sizeof(&ptr));
|
vec_append(&sys->memory, &ptr, sizeof(&ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 sys_cost(sys_t *sys)
|
||||||
|
{
|
||||||
|
return sym_table_cost(&sys->symtable) + sys->memory.capacity;
|
||||||
|
}
|
||||||
|
|
||||||
void sys_free(sys_t *sys)
|
void sys_free(sys_t *sys)
|
||||||
{
|
{
|
||||||
static_assert(NUM_TAGS == 5);
|
static_assert(NUM_TAGS == 5);
|
||||||
@@ -71,7 +76,7 @@ lisp_t *make_vec(sys_t *sys, u64 capacity)
|
|||||||
|
|
||||||
lisp_t *intern(sys_t *sys, sv_t sv)
|
lisp_t *intern(sys_t *sys, sv_t sv)
|
||||||
{
|
{
|
||||||
char *str = sym_table_find(&sys->symtable, sv);
|
const char *str = sym_table_find(&sys->symtable, sv);
|
||||||
return tag_sym(str);
|
return tag_sym(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +120,135 @@ void lisp_free(lisp_t *item)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lisp_print(FILE *fp, lisp_t *lisp)
|
||||||
|
{
|
||||||
|
if (!fp)
|
||||||
|
return;
|
||||||
|
switch (get_tag(lisp))
|
||||||
|
{
|
||||||
|
case TAG_NIL:
|
||||||
|
fprintf(fp, "NIL");
|
||||||
|
break;
|
||||||
|
case TAG_INT:
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
fprintf(fp, "INT[");
|
||||||
|
#endif
|
||||||
|
fprintf(fp, "%ld", as_int(lisp));
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
fprintf(fp, "]");
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TAG_SYM:
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
fprintf(fp, "SYM[");
|
||||||
|
#endif
|
||||||
|
fprintf(fp, "%s", as_sym(lisp));
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
fprintf(fp, "]");
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TAG_CONS:
|
||||||
|
{
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
fprintf(fp, "LIST[");
|
||||||
|
#else
|
||||||
|
fprintf(fp, "(");
|
||||||
|
#endif
|
||||||
|
for (; lisp; lisp = CDR(lisp))
|
||||||
|
{
|
||||||
|
if (IS_TAG(lisp, CONS))
|
||||||
|
{
|
||||||
|
lisp_t *car = CAR(lisp);
|
||||||
|
lisp_t *cdr = CDR(lisp);
|
||||||
|
|
||||||
|
lisp_print(fp, car);
|
||||||
|
if (cdr && !IS_TAG(cdr, CONS))
|
||||||
|
{
|
||||||
|
fprintf(fp, " . ");
|
||||||
|
}
|
||||||
|
else if (cdr)
|
||||||
|
{
|
||||||
|
fprintf(fp, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lisp_print(fp, lisp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
fprintf(fp, "]");
|
||||||
|
#else
|
||||||
|
fprintf(fp, ")");
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TAG_VEC:
|
||||||
|
{
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
fprintf(fp, "VEC[");
|
||||||
|
#else
|
||||||
|
fprintf(fp, "[");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vec_t *vec = as_vec(lisp);
|
||||||
|
for (u64 i = 1; i <= VEC_SIZE(vec, lisp_t *); ++i)
|
||||||
|
{
|
||||||
|
lisp_t *item = VEC_GET(vec, i - 1, lisp_t *);
|
||||||
|
lisp_print(fp, item);
|
||||||
|
if (i != VEC_SIZE(vec, lisp_t *))
|
||||||
|
{
|
||||||
|
fprintf(fp, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
fprintf(fp, "]");
|
||||||
|
#else
|
||||||
|
fprintf(fp, "]");
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NUM_TAGS:
|
||||||
|
default:
|
||||||
|
FAIL("Unreachable");
|
||||||
|
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
|
||||||
|
|||||||
92
src/main.c
92
src/main.c
@@ -11,27 +11,66 @@
|
|||||||
|
|
||||||
#include <alisp/alisp.h>
|
#include <alisp/alisp.h>
|
||||||
|
|
||||||
void usage(FILE *fp)
|
void usage(FILE *fp);
|
||||||
{
|
int init_stream_on_args(int argc, char *argv[], FILE **pipe, stream_t *stream);
|
||||||
fprintf(fp, "Usage: alisp [OPTIONS...] FILE\n"
|
|
||||||
"Options:\n"
|
|
||||||
"\t--help Print this usage and exit.\n"
|
|
||||||
"File:\n"
|
|
||||||
"\t<filename> Read and interpret this file from filesystem.\n"
|
|
||||||
"\t-- Read and interpret from stdin using an EOF.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
FILE *pipe = NULL;
|
FILE *pipe = NULL;
|
||||||
stream_t stream = {0};
|
stream_t stream = {0};
|
||||||
|
vec_t ast = {0};
|
||||||
|
sys_t sys = {0};
|
||||||
|
|
||||||
|
ret = init_stream_on_args(argc, argv, &pipe, &stream);
|
||||||
|
if (ret)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
LOG("[INFO]: Initialised stream for `%s`\n", stream.name);
|
||||||
|
{
|
||||||
|
read_err_t err = read_all(&sys, &stream, &ast);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
u64 line = 0, col = 0;
|
||||||
|
stream_line_col(&stream, &line, &col);
|
||||||
|
fprintf(stderr, "%s:%lu:%lu: ERROR: %s\n", stream.name, line, col,
|
||||||
|
read_err_to_cstr(err));
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("[INFO]: Utilised %lu bytes in parsing\n", sys_cost(&sys));
|
||||||
|
LOG("[INFO]: Parsed %lu %s\n", VEC_SIZE(&ast, lisp_t *),
|
||||||
|
VEC_SIZE(&ast, lisp_t *) == 1 ? "expr" : "exprs");
|
||||||
|
|
||||||
|
{
|
||||||
|
for (u64 i = 0; i < VEC_SIZE(&ast, lisp_t *); ++i)
|
||||||
|
{
|
||||||
|
lisp_t *expr = VEC_GET(&ast, i, lisp_t *);
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
printf("\t[%lu]: ", i);
|
||||||
|
lisp_print(stdout, expr);
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
sys_free(&sys);
|
||||||
|
vec_free(&ast);
|
||||||
|
stream_free(&stream);
|
||||||
|
if (pipe)
|
||||||
|
fclose(pipe);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_stream_on_args(int argc, char *argv[], FILE **pipe, stream_t *stream)
|
||||||
|
{
|
||||||
if (argc == 1)
|
if (argc == 1)
|
||||||
{
|
{
|
||||||
usage(stderr);
|
usage(stderr);
|
||||||
ret = 1;
|
return 1;
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
else if (argc != 2)
|
else if (argc != 2)
|
||||||
{
|
{
|
||||||
@@ -40,39 +79,42 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (strncmp(argv[1], "--", 2) == 0)
|
if (strncmp(argv[1], "--", 2) == 0)
|
||||||
{
|
{
|
||||||
stream_err_t err = stream_init_pipe(&stream, "stdin", stdin);
|
stream_err_t err = stream_init_pipe(stream, "stdin", stdin);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "ERROR: %s from `%s`\n", stream_err_to_cstr(err),
|
fprintf(stderr, "ERROR: %s from `%s`\n", stream_err_to_cstr(err),
|
||||||
argv[1]);
|
argv[1]);
|
||||||
ret = 1;
|
return 1;
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strncmp(argv[1], "--help", 6) == 0)
|
else if (strncmp(argv[1], "--help", 6) == 0)
|
||||||
{
|
{
|
||||||
usage(stdout);
|
usage(stdout);
|
||||||
goto end;
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pipe = fopen(argv[1], "rb");
|
*pipe = fopen(argv[1], "rb");
|
||||||
stream_err_t err = stream_init_file(&stream, argv[1], pipe);
|
stream_err_t err = stream_init_file(stream, argv[1], *pipe);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "ERROR: %s from `%s`\n", stream_err_to_cstr(err),
|
fprintf(stderr, "ERROR: %s from `%s`\n", stream_err_to_cstr(err),
|
||||||
argv[1]);
|
argv[1]);
|
||||||
ret = 1;
|
return 1;
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("[INFO]: Initialised stream for `%s`\n", stream.name);
|
return 0;
|
||||||
end:
|
}
|
||||||
stream_stop(&stream);
|
|
||||||
if (pipe)
|
void usage(FILE *fp)
|
||||||
fclose(pipe);
|
{
|
||||||
return ret;
|
fprintf(fp, "Usage: alisp [OPTIONS...] FILE\n"
|
||||||
|
"Options:\n"
|
||||||
|
"\t--help Print this usage and exit.\n"
|
||||||
|
"File:\n"
|
||||||
|
"\t<filename> Read and interpret this file from filesystem.\n"
|
||||||
|
"\t-- Read and interpret from stdin using an EOF.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copyright (C) 2025, 2026 Aryadev Chavali
|
/* Copyright (C) 2025, 2026 Aryadev Chavali
|
||||||
|
|||||||
224
src/reader.c
224
src/reader.c
@@ -8,8 +8,8 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <alisp/base.h>
|
|
||||||
#include <alisp/reader.h>
|
#include <alisp/reader.h>
|
||||||
|
#include <alisp/tag.h>
|
||||||
|
|
||||||
const char *read_err_to_cstr(read_err_t err)
|
const char *read_err_to_cstr(read_err_t err)
|
||||||
{
|
{
|
||||||
@@ -21,11 +21,233 @@ const char *read_err_to_cstr(read_err_t err)
|
|||||||
return "EOF";
|
return "EOF";
|
||||||
case READ_ERR_UNKNOWN_CHAR:
|
case READ_ERR_UNKNOWN_CHAR:
|
||||||
return "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_UNEXPECTED_CLOSED_BRACE:
|
||||||
|
return "UNEXPECTED_CLOSED_BRACE";
|
||||||
|
case READ_ERR_UNEXPECTED_CLOSED_SQUARE_BRACKET:
|
||||||
|
return "UNEXPECTED_CLOSED_SQUARE_BRACKET";
|
||||||
default:
|
default:
|
||||||
FAIL("Unreachable");
|
FAIL("Unreachable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accepted characters for symbols.
|
||||||
|
static const char *SYMBOL_CHARS =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!$%&*+,-./"
|
||||||
|
":<=>?@\\^_`{|}~0123456789";
|
||||||
|
|
||||||
|
// Little predicate using SYMBOL_CHARS
|
||||||
|
bool is_sym(char c)
|
||||||
|
{
|
||||||
|
return strchr(SYMBOL_CHARS, c) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip_comments_and_whitespace(stream_t *stream)
|
||||||
|
{
|
||||||
|
for (char c = stream_peek(stream); c != '\0' && (isspace(c) || c == ';');
|
||||||
|
c = stream_peek(stream))
|
||||||
|
{
|
||||||
|
stream_while(stream, " \t\n\0");
|
||||||
|
if (stream_peek(stream) == ';')
|
||||||
|
{
|
||||||
|
// Skip till newline
|
||||||
|
stream_till(stream, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
read_err_t read_sym(sys_t *sys, stream_t *stream, lisp_t **ret)
|
||||||
|
{
|
||||||
|
sv_t sym_sv = stream_while(stream, SYMBOL_CHARS);
|
||||||
|
*ret = intern(sys, sym_sv);
|
||||||
|
return READ_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_err_t read_int(sys_t *sys, stream_t *stream, lisp_t **ret)
|
||||||
|
{
|
||||||
|
sv_t digits_sv = stream_while(stream, "0123456789");
|
||||||
|
if (is_sym(stream_peek(stream)))
|
||||||
|
{
|
||||||
|
// This is actually a symbol
|
||||||
|
stream_seek_backward(stream, digits_sv.size);
|
||||||
|
return read_sym(sys, stream, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (digits_sv.size > 19)
|
||||||
|
{
|
||||||
|
TODO("alisp doesn't support big integers (bigger than 63 bits) yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 n = 0;
|
||||||
|
for (u64 i = 0; i < digits_sv.size; ++i)
|
||||||
|
{
|
||||||
|
char c = digits_sv.data[i];
|
||||||
|
u8 digit = c - '0';
|
||||||
|
|
||||||
|
// NOTE: 10i + digit > INT_MAX
|
||||||
|
// => 10i > INT_MAX - digit
|
||||||
|
// => i > (INT_MAX - digit) / 10
|
||||||
|
if (n > (INT_MAX - digit) / 10)
|
||||||
|
{
|
||||||
|
TODO("alisp doesn't support big integers (bigger than 63 bits) yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
n *= 10;
|
||||||
|
n += digit;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = make_int(n);
|
||||||
|
return READ_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_err_t read_negative(sys_t *sys, stream_t *stream, lisp_t **ret)
|
||||||
|
{
|
||||||
|
char c = stream_next(stream);
|
||||||
|
if (isdigit(c))
|
||||||
|
{
|
||||||
|
read_err_t err = read_int(sys, stream, ret);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
i64 n = as_int(*ret);
|
||||||
|
n *= -1;
|
||||||
|
*ret = make_int(n);
|
||||||
|
return READ_ERR_OK;
|
||||||
|
}
|
||||||
|
else if (is_sym(c) || isspace(c))
|
||||||
|
{
|
||||||
|
stream_seek_backward(stream, 1);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// skip past the open parentheses '('
|
||||||
|
(void)stream_next(stream);
|
||||||
|
|
||||||
|
lisp_t *top = NIL;
|
||||||
|
lisp_t *cur = NIL;
|
||||||
|
while (!stream_eoc(stream) && stream_peek(stream) != ')')
|
||||||
|
{
|
||||||
|
lisp_t *item = NIL;
|
||||||
|
read_err_t err = read(sys, stream, &item);
|
||||||
|
if (err == READ_ERR_EOF)
|
||||||
|
{
|
||||||
|
return READ_ERR_EXPECTED_CLOSED_BRACE;
|
||||||
|
}
|
||||||
|
else if (err)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
else if (!top)
|
||||||
|
{
|
||||||
|
top = cons(sys, item, NIL);
|
||||||
|
cur = top;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
as_cons(cur)->cdr = cons(sys, item, NIL);
|
||||||
|
cur = cdr(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream_peek(stream) != ')')
|
||||||
|
return READ_ERR_EXPECTED_CLOSED_BRACE;
|
||||||
|
|
||||||
|
stream_next(stream);
|
||||||
|
*ret = top;
|
||||||
|
return READ_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_err_t read_vec(sys_t *sys, stream_t *stream, lisp_t **ret)
|
||||||
|
{
|
||||||
|
(void)stream_next(stream);
|
||||||
|
lisp_t *container = make_vec(sys, 0);
|
||||||
|
while (!stream_eoc(stream) && stream_peek(stream) != ']')
|
||||||
|
{
|
||||||
|
lisp_t *item = NIL;
|
||||||
|
read_err_t err = read(sys, stream, &item);
|
||||||
|
if (err == READ_ERR_EOF)
|
||||||
|
{
|
||||||
|
return READ_ERR_EXPECTED_CLOSED_BRACE;
|
||||||
|
}
|
||||||
|
else if (err)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vec_append(as_vec(container), &item, sizeof(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream_peek(stream) != ']')
|
||||||
|
return READ_ERR_EXPECTED_CLOSED_SQUARE_BRACKET;
|
||||||
|
stream_next(stream);
|
||||||
|
*ret = container;
|
||||||
|
return READ_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_err_t read_quote(sys_t *sys, stream_t *stream, lisp_t **ret)
|
||||||
|
{
|
||||||
|
lisp_t *to_quote = NIL;
|
||||||
|
stream_next(stream);
|
||||||
|
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);
|
||||||
|
return READ_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_err_t read_all(sys_t *sys, stream_t *stream, vec_t *out)
|
||||||
|
{
|
||||||
|
while (!stream_eoc(stream))
|
||||||
|
{
|
||||||
|
lisp_t *item = NIL;
|
||||||
|
read_err_t err = read(sys, stream, &item);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
else
|
||||||
|
vec_append(out, &item, sizeof(item));
|
||||||
|
skip_comments_and_whitespace(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return READ_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_err_t read(sys_t *sys, stream_t *stream, lisp_t **ret)
|
||||||
|
{
|
||||||
|
skip_comments_and_whitespace(stream);
|
||||||
|
if (stream_eoc(stream))
|
||||||
|
return READ_ERR_EOF;
|
||||||
|
char c = stream_peek(stream);
|
||||||
|
if (isdigit(c))
|
||||||
|
return read_int(sys, stream, ret);
|
||||||
|
else if (c == '-')
|
||||||
|
return read_negative(sys, stream, ret);
|
||||||
|
else if (is_sym(c))
|
||||||
|
return read_sym(sys, stream, ret);
|
||||||
|
else if (c == '\'')
|
||||||
|
return read_quote(sys, stream, ret);
|
||||||
|
else if (c == '(')
|
||||||
|
return read_list(sys, stream, ret);
|
||||||
|
else if (c == ')')
|
||||||
|
return READ_ERR_UNEXPECTED_CLOSED_BRACE;
|
||||||
|
else if (c == '[')
|
||||||
|
return read_vec(sys, stream, ret);
|
||||||
|
else if (c == ']')
|
||||||
|
return READ_ERR_UNEXPECTED_CLOSED_SQUARE_BRACKET;
|
||||||
|
|
||||||
|
return READ_ERR_UNKNOWN_CHAR;
|
||||||
|
}
|
||||||
|
|
||||||
/* Copyright (C) 2026 Aryadev Chavali
|
/* Copyright (C) 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
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ void stream_free(stream_t *stream)
|
|||||||
switch (stream->type)
|
switch (stream->type)
|
||||||
{
|
{
|
||||||
case STREAM_TYPE_STRING:
|
case STREAM_TYPE_STRING:
|
||||||
free(stream->string.data);
|
free((char *)stream->string.data);
|
||||||
break;
|
break;
|
||||||
case STREAM_TYPE_FILE:
|
case STREAM_TYPE_FILE:
|
||||||
case STREAM_TYPE_PIPE:
|
case STREAM_TYPE_PIPE:
|
||||||
@@ -303,7 +303,8 @@ u64 stream_seek_backward(stream_t *stream, u64 offset)
|
|||||||
sv_t stream_sv(stream_t *stream)
|
sv_t stream_sv(stream_t *stream)
|
||||||
{
|
{
|
||||||
sv_t sv = stream_sv_abs(stream);
|
sv_t sv = stream_sv_abs(stream);
|
||||||
return sv_chop_left(sv, stream->position);
|
sv = sv_chop_left(sv, stream->position);
|
||||||
|
return sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
sv_t stream_sv_abs(stream_t *stream)
|
sv_t stream_sv_abs(stream_t *stream)
|
||||||
|
|||||||
19
src/sv.c
19
src/sv.c
@@ -36,18 +36,19 @@ sv_t sv_chop_right(sv_t sv, u64 size)
|
|||||||
return SV(sv.data, sv.size - size);
|
return SV(sv.data, sv.size - size);
|
||||||
}
|
}
|
||||||
|
|
||||||
sv_t sv_substr(sv_t sv, u64 position, u64 size)
|
|
||||||
{
|
|
||||||
return sv_chop_right(sv_chop_left(sv, position), size);
|
|
||||||
}
|
|
||||||
|
|
||||||
sv_t sv_truncate(sv_t sv, u64 newsize)
|
sv_t sv_truncate(sv_t sv, u64 newsize)
|
||||||
{
|
{
|
||||||
if (newsize >= sv.size)
|
if (newsize > sv.size)
|
||||||
return sv;
|
return SV(NULL, 0);
|
||||||
return SV(sv.data, newsize);
|
return SV(sv.data, newsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sv_t sv_substr(sv_t sv, u64 position, u64 size)
|
||||||
|
{
|
||||||
|
sv_t result = sv_truncate(sv_chop_left(sv, position), size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
sv_t sv_till(sv_t sv, const char *reject)
|
sv_t sv_till(sv_t sv, const char *reject)
|
||||||
{
|
{
|
||||||
if (sv.size == 0 || !sv.data)
|
if (sv.size == 0 || !sv.data)
|
||||||
@@ -58,8 +59,6 @@ sv_t sv_till(sv_t sv, const char *reject)
|
|||||||
++offset)
|
++offset)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (offset == sv.size)
|
|
||||||
return sv;
|
|
||||||
return sv_truncate(sv, offset);
|
return sv_truncate(sv, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,8 +72,6 @@ sv_t sv_while(sv_t sv, const char *accept)
|
|||||||
++offset)
|
++offset)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (offset == sv.size)
|
|
||||||
return sv;
|
|
||||||
return sv_truncate(sv, offset);
|
return sv_truncate(sv, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ void sym_table_init(sym_table_t *table)
|
|||||||
vec_init(&table->entries, table->capacity * sizeof(sv_t));
|
vec_init(&table->entries, table->capacity * sizeof(sv_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *sym_table_find(sym_table_t *table, sv_t sv)
|
const char *sym_table_find(sym_table_t *table, sv_t sv)
|
||||||
{
|
{
|
||||||
// Initialise the table if it's not done already
|
// Initialise the table if it's not done already
|
||||||
if (table->entries.capacity == 0)
|
if (table->entries.capacity == 0)
|
||||||
@@ -62,13 +62,29 @@ void sym_table_free(sym_table_t *table)
|
|||||||
{
|
{
|
||||||
current = ENTRY_GET(table, i);
|
current = ENTRY_GET(table, i);
|
||||||
if (current.data)
|
if (current.data)
|
||||||
free(current.data);
|
{
|
||||||
|
// NOTE: We clone all data here, so it's okay to free by hand.
|
||||||
|
free((void *)current.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Free the underlying container
|
// Free the underlying container
|
||||||
vec_free(&table->entries);
|
vec_free(&table->entries);
|
||||||
memset(table, 0, sizeof(*table));
|
memset(table, 0, sizeof(*table));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 sym_table_cost(sym_table_t *table)
|
||||||
|
{
|
||||||
|
if (!table || !table->count)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u64 total_size = 0;
|
||||||
|
for (u64 i = 0; i < table->capacity; ++i)
|
||||||
|
total_size += ENTRY_GET(table, i).size;
|
||||||
|
return total_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 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
|
||||||
|
|||||||
@@ -15,22 +15,22 @@ lisp_t *tag_int(i64 i)
|
|||||||
return TAG((u64)i, INT);
|
return TAG((u64)i, INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
lisp_t *tag_sym(char *str)
|
lisp_t *tag_sym(const char *str)
|
||||||
{
|
{
|
||||||
return TAG((u64)str, SYM);
|
return TAG((u64)str, SYM);
|
||||||
}
|
}
|
||||||
|
|
||||||
lisp_t *tag_vec(vec_t *vec)
|
lisp_t *tag_vec(const vec_t *vec)
|
||||||
{
|
{
|
||||||
return TAG((u64)vec, VEC);
|
return TAG((u64)vec, VEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
lisp_t *tag_cons(cons_t *cons)
|
lisp_t *tag_cons(const cons_t *cons)
|
||||||
{
|
{
|
||||||
return TAG((u64)cons, CONS);
|
return TAG((u64)cons, CONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
tag_t get_tag(lisp_t *lisp)
|
tag_t get_tag(const lisp_t *lisp)
|
||||||
{
|
{
|
||||||
static_assert(NUM_TAGS == 5);
|
static_assert(NUM_TAGS == 5);
|
||||||
if (!lisp)
|
if (!lisp)
|
||||||
|
|||||||
@@ -84,10 +84,10 @@ void sym_unique_test(void)
|
|||||||
sys_init(&system);
|
sys_init(&system);
|
||||||
|
|
||||||
sv_t symbols[] = {
|
sv_t symbols[] = {
|
||||||
SV("hello", 5),
|
SV_AUTO("hello"),
|
||||||
SV("goodbye", 7),
|
SV_AUTO("goodbye"),
|
||||||
SV("display", 7),
|
SV_AUTO("display"),
|
||||||
SV("@xs'a_sh;d::a-h]", 16),
|
SV_AUTO("@xs'a_sh;d::a-h]"),
|
||||||
};
|
};
|
||||||
|
|
||||||
lisp_t *ptrs[ARRSIZE(symbols)];
|
lisp_t *ptrs[ARRSIZE(symbols)];
|
||||||
@@ -159,7 +159,7 @@ void sys_test(void)
|
|||||||
"Making integers doesn't affect system memory size");
|
"Making integers doesn't affect system memory size");
|
||||||
|
|
||||||
// Creating symbols won't affect memory size, but does affect the symbol table
|
// Creating symbols won't affect memory size, but does affect the symbol table
|
||||||
(void)intern(&sys, SV("hello world!", 12));
|
(void)intern(&sys, SV_AUTO("hello world!"));
|
||||||
TEST(sys.memory.size == old_memory_size,
|
TEST(sys.memory.size == old_memory_size,
|
||||||
"Interning doesn't affect system memory size");
|
"Interning doesn't affect system memory size");
|
||||||
TEST(sys.symtable.count > 0, "Interning affects symbol table");
|
TEST(sys.symtable.count > 0, "Interning affects symbol table");
|
||||||
@@ -169,7 +169,7 @@ void sys_test(void)
|
|||||||
TEST(sys.memory.size > 0, "Creating conses affects memory size");
|
TEST(sys.memory.size > 0, "Creating conses affects memory size");
|
||||||
old_memory_size = sys.memory.size;
|
old_memory_size = sys.memory.size;
|
||||||
|
|
||||||
(void)cons(&sys, intern(&sys, SV("test", 4)), NIL);
|
(void)cons(&sys, intern(&sys, SV_AUTO("test")), NIL);
|
||||||
TEST(sys.memory.size > old_memory_size,
|
TEST(sys.memory.size > old_memory_size,
|
||||||
"Creating conses back to back affects memory size");
|
"Creating conses back to back affects memory size");
|
||||||
old_memory_size = sys.memory.size;
|
old_memory_size = sys.memory.size;
|
||||||
|
|||||||
@@ -64,9 +64,9 @@ void stream_test_string(void)
|
|||||||
{
|
{
|
||||||
TEST_START();
|
TEST_START();
|
||||||
sv_t test_strings[] = {
|
sv_t test_strings[] = {
|
||||||
SV("hello, world!", 13),
|
SV_AUTO("hello, world!"),
|
||||||
SV("another string", 14),
|
SV_AUTO("another string"),
|
||||||
SV((char *)text, ARRSIZE(text) / 2),
|
sv_truncate(SV_AUTO(text), ARRSIZE(text) / 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (u64 i = 0; i < ARRSIZE(test_strings); ++i)
|
for (u64 i = 0; i < ARRSIZE(test_strings); ++i)
|
||||||
@@ -87,7 +87,8 @@ void stream_test_string(void)
|
|||||||
"Freeing a stream does not free the underlying memory it was derived "
|
"Freeing a stream does not free the underlying memory it was derived "
|
||||||
"from");
|
"from");
|
||||||
|
|
||||||
free(copy.data);
|
// NOTE: Okay to free since we own copy.
|
||||||
|
free((void *)copy.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_t stream = {0};
|
stream_t stream = {0};
|
||||||
@@ -319,7 +320,7 @@ void stream_test_substr(void)
|
|||||||
|
|
||||||
TEST(result.size == size, "Substring has right size (%lu)", result.size);
|
TEST(result.size == size, "Substring has right size (%lu)", result.size);
|
||||||
|
|
||||||
sv_t expected = SV((char *)words_text + position, size);
|
sv_t expected = sv_substr(SV_AUTO(words_text), position, size);
|
||||||
TEST(strncmp(result.data, expected.data, result.size) == 0,
|
TEST(strncmp(result.data, expected.data, result.size) == 0,
|
||||||
"Expect the substring to be the same as the data we put in");
|
"Expect the substring to be the same as the data we put in");
|
||||||
}
|
}
|
||||||
@@ -332,7 +333,7 @@ void stream_test_substr(void)
|
|||||||
|
|
||||||
TEST(result.size == size, "Substring has right size (%lu)", result.size);
|
TEST(result.size == size, "Substring has right size (%lu)", result.size);
|
||||||
|
|
||||||
sv_t expected = SV((char *)words_text, size);
|
sv_t expected = sv_truncate(SV_AUTO(words_text), size);
|
||||||
TEST(strncmp(result.data, expected.data, result.size) == 0,
|
TEST(strncmp(result.data, expected.data, result.size) == 0,
|
||||||
"Expect the substring to be the same as the data we put in");
|
"Expect the substring to be the same as the data we put in");
|
||||||
}
|
}
|
||||||
@@ -349,7 +350,7 @@ void stream_test_substr(void)
|
|||||||
|
|
||||||
TEST(result.size == size, "Substring has right size (%lu)", result.size);
|
TEST(result.size == size, "Substring has right size (%lu)", result.size);
|
||||||
|
|
||||||
sv_t expected = SV((char *)words_text + position, size);
|
sv_t expected = sv_substr(SV_AUTO(words_text), position, size);
|
||||||
TEST(strncmp(result.data, expected.data, result.size) == 0,
|
TEST(strncmp(result.data, expected.data, result.size) == 0,
|
||||||
"Expect the substring to be the same as the data we put in");
|
"Expect the substring to be the same as the data we put in");
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ void sv_copy_test(void)
|
|||||||
TEST(strncmp(word.data, copy.data, copy.size) == 0, "`%s` == `%s`",
|
TEST(strncmp(word.data, copy.data, copy.size) == 0, "`%s` == `%s`",
|
||||||
word.data, copy.data);
|
word.data, copy.data);
|
||||||
|
|
||||||
// Obviously we can't just have this lying around.
|
// NOTE: Okay to free since we own copy.
|
||||||
free(copy.data);
|
free((void *)copy.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ void vec_test_gen_substr(void)
|
|||||||
{0, 16},
|
{0, 16},
|
||||||
{0, 32},
|
{0, 32},
|
||||||
{32, 64},
|
{32, 64},
|
||||||
{0, ARRSIZE(text)},
|
{0, ARRSIZE(text) - 1},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (u64 i = 0; i < ARRSIZE(tests); ++i)
|
for (u64 i = 0; i < ARRSIZE(tests); ++i)
|
||||||
{
|
{
|
||||||
struct Test test = tests[i];
|
struct Test test = tests[i];
|
||||||
const sv_t substr = SV((char *)text + test.start, test.size);
|
const sv_t substr = sv_substr(SV_AUTO(text), test.start, test.size);
|
||||||
const u64 size = test.size / 2;
|
const u64 size = test.size / 2;
|
||||||
|
|
||||||
lisp_t *lvec = make_vec(&system, size);
|
lisp_t *lvec = make_vec(&system, size);
|
||||||
|
|||||||
Reference in New Issue
Block a user