diff --git a/Makefile b/Makefile index f176f36..b25c7f3 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ UNITS=main.c $(shell find ./runtime -type 'f') OBJECTS:=$(patsubst %.c, $(DIST)/%.o, $(UNITS)) LDFLAGS= -GFLAGS=-Wall -Wextra -Wpedantic -std=c23 -I./ +GFLAGS=-Wall -Wextra -Wpedantic -std=c23 -I./include/ DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined RFLAGS=-O3 diff --git a/alisp.h b/alisp.h deleted file mode 100644 index 29effac..0000000 --- a/alisp.h +++ /dev/null @@ -1,274 +0,0 @@ -/* alisp.h: Single header for all the definitions. - * Created: 2025-08-19 - * Author: Aryadev Chavali - * License: See end of file - * Commentary: - */ - -#ifndef ALISP_H -#define ALISP_H - -#include -#include -#include -#include -#include -#include - -/// Essential macros -#define MAX(A, B) ((A) > (B) ? (A) : (B)) -#define MIN(A, B) ((A) < (B) ? (A) : (B)) -#define ARRSIZE(A) (sizeof(A) / sizeof((A)[0])) -#define NTH_BYTE(X, N) (((X) >> (8 * N)) & ((1 << 8) - 1)) -#define FAIL(MSG) assert(false && "FAIL: " #MSG) -#define TODO(MSG) assert(false && "TODO: " #MSG) - -/// Numeric aliases -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -typedef int8_t i8; -typedef int16_t i16; -typedef int32_t i32; -typedef int64_t i64; - -/// String Views -typedef struct -{ - u64 size; - char *data; -} sv_t; - -// String view macro constructor -#define SV(DATA, SIZE) ((sv_t){.data = (DATA), .size = (SIZE)}) -// Pretty printers -#define SV_FMT(SV) (int)(SV).size, (SV).data -#define PR_SV "%.*s" -#define PRD_SV "%d@%p" - -// String view functions -sv_t sv_copy(sv_t); - -/// Dynamic arrays -#define VEC_INLINE_CAPACITY 32 -#define VEC_MULT 2 - -typedef struct Vector -{ - u64 size, capacity; - // Small buffer optimisation - u8 not_inlined; - union - { - void *ptr; - alignas(max_align_t) u8 inlined[VEC_INLINE_CAPACITY]; - }; -} vec_t; - -static_assert(sizeof(vec_t) == 64, "vec_t has to be 64 bytes as part of SBO"); - -#define VEC_GET(V, I, T) (((T *)vec_data(V))[I]) -#define VEC_SIZE(V, T) ((V)->size / (sizeof(T))) - -void vec_init(vec_t *, u64); -void vec_free(vec_t *); -u8 *vec_data(vec_t *); -void vec_ensure_free(vec_t *, u64); -void vec_append(vec_t *, const void *const, u64); -void vec_clone(vec_t *, vec_t *); - -/// Symbol table -typedef struct -{ - u64 count; // How many strings? - u64 capacity; // How many entry buckets? - vec_t entries; -} sym_table_t; - -#define SYM_TABLE_INIT_SIZE (1 << 10) - -void sym_table_init(sym_table_t *); -char *sym_table_find(sym_table_t *, sv_t); -void sym_table_cleanup(sym_table_t *); -// Hashing algorithm -u64 djb2(sv_t string); - -/// Streams -typedef enum -{ - STREAM_TYPE_STRING = 0, - STREAM_TYPE_PIPE, - STREAM_TYPE_FILE, -} stream_type_t; - -typedef enum -{ - STREAM_ERR_INVALID_PTR = -4, - STREAM_ERR_FILE_NONEXISTENT = -3, - STREAM_ERR_FILE_READ = -2, - STREAM_ERR_PIPE_NONEXISTENT = -1, - STREAM_ERR_OK = 0, -} stream_err_t; - -typedef struct -{ - vec_t cache; - FILE *file; -} stream_pipe_t; - -typedef struct -{ - stream_type_t type; - char *name; - u64 position; - union - { - sv_t string; - stream_pipe_t pipe; - }; -} stream_t; - -#define STREAM_DEFAULT_CHUNK 64 - -stream_err_t stream_init_string(stream_t *, char *, sv_t); -stream_err_t stream_init_pipe(stream_t *, char *, FILE *); -stream_err_t stream_init_file(stream_t *, char *, FILE *); -void stream_stop(stream_t *); - -// End of Content (i.e. we've consumed all cached content/file) -bool stream_eoc(stream_t *); -// size of immediately accessible content -u64 stream_size(stream_t *); - -// Return current character, push position by 1 -char stream_next(stream_t *); -// Peek current character, do not push position -char stream_peek(stream_t *); -// Move forward or backward in the stream, return success of operation -bool stream_seek(stream_t *, i64); -bool stream_seek_forward(stream_t *, u64); -bool stream_seek_backward(stream_t *, u64); - -// Return a relative substring of a given size -sv_t stream_substr(stream_t *, u64); -// Return an absolute substring at given index and of given size. -sv_t stream_substr_abs(stream_t *, u64, u64); - -// Skip forward in stream till one of the characters in the given C string is -// encountered. -sv_t stream_till(stream_t *, const char *); -// Skip forward in stream while one of the characters in the given C string is -// present. -sv_t stream_while(stream_t *, const char *); - -/// Lisp type definitions -#define NIL 0 - -// Opaque object for tagged pointers -typedef struct Obj lisp_t; - -typedef struct -{ - lisp_t *car, *cdr; -} cons_t; - -/// System context -typedef struct -{ - vec_t memory; - sym_table_t symtable; -} sys_t; - -void sys_init(sys_t *); -void sys_register(sys_t *, lisp_t *); -void sys_cleanup(sys_t *); - -/// Constructors and destructors -lisp_t *make_int(i64); -lisp_t *make_vec(sys_t *, u64); -lisp_t *intern(sys_t *, sv_t); -lisp_t *cons(sys_t *, lisp_t *, lisp_t *); - -i64 as_int(lisp_t *); -char *as_sym(lisp_t *); -cons_t *as_cons(lisp_t *); -vec_t *as_vec(lisp_t *); - -#define CAR(L) (as_cons(L)->car) -#define CDR(L) (as_cons(L)->cdr) - -lisp_t *car(lisp_t *); -lisp_t *cdr(lisp_t *); - -/// Tagging scheme -typedef enum Tag -{ - TAG_NIL = 0b00000000, // Start of atomic types - TAG_INT = 0b00000001, // special so we can encode 63 bit integers - TAG_SYM = 0b00000100, - TAG_CONS = 0b00000010, // Start of container types - TAG_VEC = 0b00000110, - NUM_TAGS = 5, -} tag_t; - -static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum SHIFT"); -enum Shift -{ - SHIFT_INT = 1, - SHIFT_SYM = 8, - SHIFT_CONS = 8, - SHIFT_VEC = 8, -}; - -static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum MASK"); -enum Mask -{ - MASK_INT = 0b00000001, - MASK_SYM = 0b11111111, - MASK_CONS = 0b11111111, - MASK_VEC = 0b11111111, -}; - -// Some helper macros for tagging -#define TAG(PTR, TYPE) ((lisp_t *)(((PTR) << SHIFT_##TYPE) | TAG_##TYPE)) -#define IS_TAG(PTR, TYPE) (((u64)(PTR) & MASK_##TYPE) == TAG_##TYPE) -#define UNTAG(PTR, TYPE) (((u64)PTR) >> SHIFT_##TYPE) - -#define INT_MAX ((1L << 62) - 1) -#define INT_MIN (-(1L << 62)) - -tag_t get_tag(lisp_t *); -lisp_t *tag_int(i64); -lisp_t *tag_sym(char *); -lisp_t *tag_cons(cons_t *); -lisp_t *tag_vec(vec_t *); - -/// Reader -typedef enum -{ - READ_OK = 0, -} read_err_t; - -// Attempt to read an expression from the stream, storing it in a pointer, -// returning any errors if failed. -read_err_t read(sys_t *, stream_t *, lisp_t **); - -// Attempt to read all expressions from a stream till end of content, storing -// them in the given vector. Return any error at any point during the read. -read_err_t read_all(sys_t *, stream_t *, vec_t *); - -#endif - -/* 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 . - - */ diff --git a/include/alisp/alisp.h b/include/alisp/alisp.h new file mode 100644 index 0000000..b955e62 --- /dev/null +++ b/include/alisp/alisp.h @@ -0,0 +1,31 @@ +/* alisp.h: All includes + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef ALISP_H +#define ALISP_H + +#include +#include +#include +#include +#include +#include +#include + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the 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 . + + */ diff --git a/include/alisp/base.h b/include/alisp/base.h new file mode 100644 index 0000000..266e0fd --- /dev/null +++ b/include/alisp/base.h @@ -0,0 +1,45 @@ +/* base.h: Basic macros and type definitions + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef BASE_H +#define BASE_H + +#include +#include + +/// Essential macros +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define ARRSIZE(A) (sizeof(A) / sizeof((A)[0])) +#define NTH_BYTE(X, N) (((X) >> (8 * N)) & ((1 << 8) - 1)) +#define FAIL(MSG) assert(false && "FAIL: " #MSG) +#define TODO(MSG) assert(false && "TODO: " #MSG) + +/// Numeric aliases +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the 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 . + + */ diff --git a/include/alisp/lisp.h b/include/alisp/lisp.h new file mode 100644 index 0000000..097baac --- /dev/null +++ b/include/alisp/lisp.h @@ -0,0 +1,64 @@ +/* lisp.h: Basic API for the Lisp + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef LISP_H +#define LISP_H + +#include +#include + +#define NIL 0 + +// Opaque object for tagged pointers +typedef struct Obj lisp_t; + +typedef struct +{ + lisp_t *car, *cdr; +} cons_t; + +/// System context +typedef struct +{ + vec_t memory; + sym_table_t symtable; +} sys_t; + +void sys_init(sys_t *); +void sys_register(sys_t *, lisp_t *); +void sys_cleanup(sys_t *); + +/// Constructors and destructors +lisp_t *make_int(i64); +lisp_t *make_vec(sys_t *, u64); +lisp_t *intern(sys_t *, sv_t); +lisp_t *cons(sys_t *, lisp_t *, lisp_t *); + +i64 as_int(lisp_t *); +char *as_sym(lisp_t *); +cons_t *as_cons(lisp_t *); +vec_t *as_vec(lisp_t *); + +#define CAR(L) (as_cons(L)->car) +#define CDR(L) (as_cons(L)->cdr) + +lisp_t *car(lisp_t *); +lisp_t *cdr(lisp_t *); + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the 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 . + + */ diff --git a/include/alisp/reader.h b/include/alisp/reader.h new file mode 100644 index 0000000..8f0dfa6 --- /dev/null +++ b/include/alisp/reader.h @@ -0,0 +1,38 @@ +/* reader.h: Stream reader + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef READER_H +#define READER_H + +#include + +typedef enum +{ + READ_OK = 0, +} read_err_t; + +// Attempt to read an expression from the stream, storing it in a pointer, +// returning any errors if failed. +read_err_t read(sys_t *, stream_t *, lisp_t **); + +// Attempt to read all expressions from a stream till end of content, storing +// them in the given vector. Return any error at any point during the read. +read_err_t read_all(sys_t *, stream_t *, vec_t *); + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the 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 . + + */ diff --git a/include/alisp/stream.h b/include/alisp/stream.h new file mode 100644 index 0000000..f858a50 --- /dev/null +++ b/include/alisp/stream.h @@ -0,0 +1,95 @@ +/* stream.h: Read/Write streams + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef STREAM_H +#define STREAM_H + +#include + +#include +#include + +typedef enum +{ + STREAM_TYPE_STRING = 0, + STREAM_TYPE_PIPE, + STREAM_TYPE_FILE, +} stream_type_t; + +typedef enum +{ + STREAM_ERR_INVALID_PTR = -4, + STREAM_ERR_FILE_NONEXISTENT = -3, + STREAM_ERR_FILE_READ = -2, + STREAM_ERR_PIPE_NONEXISTENT = -1, + STREAM_ERR_OK = 0, +} stream_err_t; + +typedef struct +{ + vec_t cache; + FILE *file; +} stream_pipe_t; + +typedef struct +{ + stream_type_t type; + char *name; + u64 position; + union + { + sv_t string; + stream_pipe_t pipe; + }; +} stream_t; + +#define STREAM_DEFAULT_CHUNK 64 + +stream_err_t stream_init_string(stream_t *, char *, sv_t); +stream_err_t stream_init_pipe(stream_t *, char *, FILE *); +stream_err_t stream_init_file(stream_t *, char *, FILE *); +void stream_stop(stream_t *); + +// End of Content (i.e. we've consumed all cached content/file) +bool stream_eoc(stream_t *); +// size of immediately accessible content +u64 stream_size(stream_t *); + +// Return current character, push position by 1 +char stream_next(stream_t *); +// Peek current character, do not push position +char stream_peek(stream_t *); +// Move forward or backward in the stream, return success of operation +bool stream_seek(stream_t *, i64); +bool stream_seek_forward(stream_t *, u64); +bool stream_seek_backward(stream_t *, u64); + +// Return a relative substring of a given size +sv_t stream_substr(stream_t *, u64); +// Return an absolute substring at given index and of given size. +sv_t stream_substr_abs(stream_t *, u64, u64); + +// Skip forward in stream till one of the characters in the given C string is +// encountered. +sv_t stream_till(stream_t *, const char *); +// Skip forward in stream while one of the characters in the given C string is +// present. +sv_t stream_while(stream_t *, const char *); + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the 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 . + + */ diff --git a/include/alisp/sv.h b/include/alisp/sv.h new file mode 100644 index 0000000..b7070a4 --- /dev/null +++ b/include/alisp/sv.h @@ -0,0 +1,42 @@ +/* sv.h: String views + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef SV_H +#define SV_H + +#include + +/// String Views +typedef struct +{ + u64 size; + char *data; +} sv_t; + +// String view macro constructor +#define SV(DATA, SIZE) ((sv_t){.data = (DATA), .size = (SIZE)}) +// Pretty printers +#define SV_FMT(SV) (int)(SV).size, (SV).data +#define PR_SV "%.*s" +#define PRD_SV "%d@%p" + +// String view functions +sv_t sv_copy(sv_t); + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the 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 . + + */ diff --git a/include/alisp/symtable.h b/include/alisp/symtable.h new file mode 100644 index 0000000..6eef052 --- /dev/null +++ b/include/alisp/symtable.h @@ -0,0 +1,39 @@ +/* symtable.h: Symbol tables + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef SYMTABLE_H +#define SYMTABLE_H + +#include +#include + +typedef struct +{ + u64 count; // How many strings? + u64 capacity; // How many entry buckets? + vec_t entries; +} sym_table_t; + +#define SYM_TABLE_INIT_SIZE (1 << 10) + +void sym_table_init(sym_table_t *); +char *sym_table_find(sym_table_t *, sv_t); +void sym_table_cleanup(sym_table_t *); + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the 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 . + + */ diff --git a/include/alisp/tag.h b/include/alisp/tag.h new file mode 100644 index 0000000..d591da9 --- /dev/null +++ b/include/alisp/tag.h @@ -0,0 +1,67 @@ +/* tag.h: Pointer tagging + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef TAG_H +#define TAG_H + +#include + +typedef enum Tag +{ + TAG_NIL = 0b00000000, // Start of atomic types + TAG_INT = 0b00000001, // Special tag so we can encode 63 bit integers + TAG_SYM = 0b00000100, + TAG_CONS = 0b00000010, // Start of container types + TAG_VEC = 0b00000110, + NUM_TAGS = 5, +} tag_t; + +static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum SHIFT"); +enum Shift +{ + SHIFT_INT = 1, + SHIFT_SYM = 8, + SHIFT_CONS = 8, + SHIFT_VEC = 8, +}; + +static_assert(NUM_TAGS == 5, "Expected NUM_TAGS == 5 for enum MASK"); +enum Mask +{ + MASK_INT = 0b00000001, + MASK_SYM = 0b11111111, + MASK_CONS = 0b11111111, + MASK_VEC = 0b11111111, +}; + +// Some helper macros for tagging +#define TAG(PTR, TYPE) ((lisp_t *)(((PTR) << SHIFT_##TYPE) | TAG_##TYPE)) +#define IS_TAG(PTR, TYPE) (((u64)(PTR) & MASK_##TYPE) == TAG_##TYPE) +#define UNTAG(PTR, TYPE) (((u64)PTR) >> SHIFT_##TYPE) + +#define INT_MAX ((1L << 62) - 1) +#define INT_MIN (-(1L << 62)) + +tag_t get_tag(lisp_t *); +lisp_t *tag_int(i64); +lisp_t *tag_sym(char *); +lisp_t *tag_cons(cons_t *); +lisp_t *tag_vec(vec_t *); + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the 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 . + + */ diff --git a/include/alisp/vec.h b/include/alisp/vec.h new file mode 100644 index 0000000..d31d6be --- /dev/null +++ b/include/alisp/vec.h @@ -0,0 +1,55 @@ +/* vec.h: Dynamic Arrays with SBO + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef VEC_H +#define VEC_H + +#include + +#include + +#define VEC_INLINE_CAPACITY 32 +#define VEC_MULT 2 + +typedef struct Vector +{ + u64 size, capacity; + // Small buffer optimisation + u8 not_inlined; + union + { + void *ptr; + alignas(max_align_t) u8 inlined[VEC_INLINE_CAPACITY]; + }; +} vec_t; + +static_assert(sizeof(vec_t) == 64, "vec_t has to be 64 bytes as part of SBO"); + +#define VEC_GET(V, I, T) (((T *)vec_data(V))[I]) +#define VEC_SIZE(V, T) ((V)->size / (sizeof(T))) + +void vec_init(vec_t *, u64); +void vec_free(vec_t *); +u8 *vec_data(vec_t *); + +void vec_append(vec_t *, const void *const, u64); +void vec_ensure_free(vec_t *, u64); +void vec_clone(vec_t *, vec_t *); + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the 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 . + + */ diff --git a/main.c b/main.c index 7eccac3..4e17353 100644 --- a/main.c +++ b/main.c @@ -9,12 +9,15 @@ #include #include -#include "./alisp.h" +#include 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}; @@ -25,12 +28,24 @@ int main(void) // Skip forward any delimiters stream_while(&stream, TOKEN_DELIM); // Get the token (up until delimiter) - sv_t token = stream_till(&stream, TOKEN_DELIM); - printf("%s[%lu] => `" PR_SV "`\n", stream.name, token_no, SV_FMT(token)); + 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; } diff --git a/runtime/constructor.c b/runtime/constructor.c index e767081..1e7b140 100644 --- a/runtime/constructor.c +++ b/runtime/constructor.c @@ -7,7 +7,8 @@ #include -#include "../alisp.h" +#include +#include lisp_t *make_int(i64 i) { diff --git a/runtime/stream.c b/runtime/stream.c index 52fbb37..4905e1b 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -8,7 +8,7 @@ #include #include -#include "../alisp.h" +#include stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents) { diff --git a/runtime/sv.c b/runtime/sv.c index ce6aca7..7f41df4 100644 --- a/runtime/sv.c +++ b/runtime/sv.c @@ -8,7 +8,7 @@ #include #include -#include "../alisp.h" +#include sv_t sv_copy(sv_t old) { diff --git a/runtime/symtable.c b/runtime/symtable.c index 2f4dbd8..fc3a0ed 100644 --- a/runtime/symtable.c +++ b/runtime/symtable.c @@ -5,13 +5,17 @@ * Commentary: */ -#include "../alisp.h" +#include #include #include +#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)); @@ -31,38 +35,36 @@ char *sym_table_find(sym_table_t *table, sv_t sv) if (table->entries.capacity == 0) sym_table_init(table); - // TODO: Deal with resizing this when table->count > table->size / 2 + // TODO: Consider resizing table when count >= capacity / 2 u64 index = djb2(sv) & (table->capacity - 1); - for (sv_t comp = VEC_GET(&table->entries, index, sv_t); comp.data; index += 1, - index = index & (table->capacity - 1), - comp = VEC_GET(&table->entries, index, sv_t)) - // Is it present in the table? + // 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) - break; + return comp.data; - // we couldn't find it in our linear search (worst case scenario) - if (!VEC_GET(&table->entries, index, sv_t).data) - { - sv_t newsv = sv_copy(sv); - VEC_GET(&table->entries, index, sv_t) = newsv; - ++table->count; - } - - return VEC_GET(&table->entries, index, sv_t).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) { - // kill the data + // Iterate through the strings and free each of them. sv_t current = {0}; for (u64 i = 0; i < table->capacity; ++i) { - current = VEC_GET(&table->entries, i, sv_t); + current = ENTRY_GET(table, i); if (current.data) free(current.data); } - // kill the container + // Free the underlying container vec_free(&table->entries); memset(table, 0, sizeof(*table)); } diff --git a/runtime/sys.c b/runtime/sys.c index e3a0357..0dd4077 100644 --- a/runtime/sys.c +++ b/runtime/sys.c @@ -5,22 +5,22 @@ * Commentary: */ -#include "../alisp.h" - #include #include #include +#include +#include + void sys_init(sys_t *sys) { memset(sys, 0, sizeof(*sys)); - vec_init(&sys->conses, 0); } void sys_register(sys_t *sys, lisp_t *ptr) { // Simply append it to the list of currently active conses - vec_append(&sys->conses, &ptr, sizeof(&ptr)); + vec_append(&sys->memory, &ptr, sizeof(&ptr)); } void sys_cleanup(sys_t *sys) @@ -28,13 +28,13 @@ void sys_cleanup(sys_t *sys) static_assert(NUM_TAGS == 5); sym_table_cleanup(&sys->symtable); - if (sys->conses.size == 0) + if (sys->memory.size == 0) return; - // Iterate through each cons currently allocated and free them - for (size_t i = 0; i < VEC_SIZE(&sys->conses, lisp_t **); ++i) + // 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->conses, i, lisp_t *); + lisp_t *allocated = VEC_GET(&sys->memory, i, lisp_t *); switch (get_tag(allocated)) { case TAG_CONS: @@ -58,7 +58,7 @@ void sys_cleanup(sys_t *sys) } // Free the container - vec_free(&sys->conses); + vec_free(&sys->memory); // Ensure no one treats this as active in any sense memset(sys, 0, sizeof(*sys)); diff --git a/runtime/tag.c b/runtime/tag.c index 9e27267..7c8f5ac 100644 --- a/runtime/tag.c +++ b/runtime/tag.c @@ -8,7 +8,7 @@ #include #include -#include "../alisp.h" +#include lisp_t *tag_int(i64 i) { diff --git a/runtime/vec.c b/runtime/vec.c index e837ba1..4d5574a 100644 --- a/runtime/vec.c +++ b/runtime/vec.c @@ -8,7 +8,7 @@ #include #include -#include "../alisp.h" +#include void vec_init(vec_t *vec, u64 size) { @@ -47,6 +47,14 @@ 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);