aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAryadev Chavali <aryadev@aryadevchavali.com>2025-08-28 22:55:41 +0100
committerAryadev Chavali <aryadev@aryadevchavali.com>2025-08-28 22:55:41 +0100
commitcc56a2ee2b5703f9ea5ac63a86870af188845c30 (patch)
tree2660c03c1c90ad069d44e29af4365bca572926aa
parent66c64007317cf5b646c1f816463f4bc730e3b99f (diff)
downloadalisp-cc56a2ee2b5703f9ea5ac63a86870af188845c30.tar.gz
alisp-cc56a2ee2b5703f9ea5ac63a86870af188845c30.tar.bz2
alisp-cc56a2ee2b5703f9ea5ac63a86870af188845c30.zip
Still got some failures, but a basic stream implementation
Need to fix what's going on with the example in main.c using stdin.
-rw-r--r--alisp.h59
-rw-r--r--build.sh2
-rw-r--r--main.c17
-rw-r--r--stream.c296
-rw-r--r--symtable.c2
5 files changed, 373 insertions, 3 deletions
diff --git a/alisp.h b/alisp.h
index e9694d9..dba0485 100644
--- a/alisp.h
+++ b/alisp.h
@@ -17,14 +17,18 @@
#include <assert.h>
#include <stdalign.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+#include <stdio.h>
/// The bare fucking minimum
#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)
typedef uint8_t u8;
typedef uint16_t u16;
@@ -91,6 +95,61 @@ void sym_table_init(sym_table_t *);
char *sym_table_find(sym_table_t *, sv_t);
void sym_table_cleanup(sym_table_t *);
+/// Streams
+typedef enum
+{
+ STREAM_TYPE_STRING,
+ 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_file(stream_t *, char *, FILE *);
+void stream_stop(stream_t *);
+
+// end of stream
+bool stream_eos(stream_t *);
+// end of cache
+bool stream_eoc(stream_t *);
+u64 stream_size(stream_t *);
+
+bool stream_chunk(stream_t *);
+char stream_next(stream_t *);
+char stream_peek(stream_t *);
+bool stream_seek(stream_t *, i64);
+bool stream_seek_forward(stream_t *, u64);
+bool stream_seek_backward(stream_t *, u64);
+sv_t stream_substr(stream_t *, u64);
+sv_t stream_substr_abs(stream_t *, u64, u64);
+
/// Basic defintions for a Lisp
#define NIL 0
diff --git a/build.sh b/build.sh
index 544f623..6e42066 100644
--- a/build.sh
+++ b/build.sh
@@ -4,7 +4,7 @@ set -xe
CFLAGS="-Wall -Wextra -std=c11 -ggdb -fsanitize=address -fsanitize=undefined"
LINK=""
-LIB="sv.c vec.c symtable.c tag.c constructor.c sys.c"
+LIB="sv.c vec.c symtable.c tag.c constructor.c stream.c sys.c"
OUT="alisp.out"
build() {
diff --git a/main.c b/main.c
index f7f290f..98f2878 100644
--- a/main.c
+++ b/main.c
@@ -20,6 +20,21 @@
int main(void)
{
- puts("Watch this space\n");
+ stream_t stream = {0};
+ // const char data[] = "Hello, world!";
+ // const sv_t sv = SV(data, ARRSIZE(data) - 1);
+ // stream_init_string(&stream, NULL, sv);
+
+ // stream_init_file(&stream, "test.txt");
+
+ stream_init_file(&stream, "stdin", stdin);
+ printf("[debug]: setup stream pipe\n");
+ do
+ {
+ printf("%s[%lu]: `%c`\n", stream.name, stream.position,
+ stream_next(&stream));
+ } while (!stream_eoc(&stream));
+ printf("%lu/%lu\n", stream.position, stream_size(&stream));
+ stream_stop(&stream);
return 0;
}
diff --git a/stream.c b/stream.c
new file mode 100644
index 0000000..a7fb72e
--- /dev/null
+++ b/stream.c
@@ -0,0 +1,296 @@
+/* Copyright (C) 2025 Aryadev Chavali
+
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the Unlicense for details.
+
+ * You may distribute and modify this code under the terms of the Unlicense,
+ * which you should have received a copy of along with this program. If not,
+ * please go to <https://unlicense.org/>.
+
+ * Created: 2025-08-26
+ * Description: Stream implementation
+ */
+
+#include <string.h>
+
+#include "./alisp.h"
+
+stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents)
+{
+ if (!stream)
+ return STREAM_ERR_INVALID_PTR;
+ name = name ? name : "<stream>";
+ memset(stream, 0, sizeof(*stream));
+
+ stream->type = STREAM_TYPE_STRING;
+ stream->name = name;
+ stream->string = contents;
+
+ return STREAM_ERR_OK;
+}
+
+stream_err_t stream_init_file(stream_t *stream, char *name, FILE *pipe)
+{
+ if (!stream || !pipe)
+ return STREAM_ERR_INVALID_PTR;
+ name = name ? name : "<stream>";
+ memset(stream, 0, sizeof(*stream));
+
+ stream->type = STREAM_TYPE_FILE;
+ stream->name = name;
+ stream->pipe.file = pipe;
+
+ vec_init(&stream->pipe.cache, STREAM_DEFAULT_CHUNK);
+
+ return STREAM_ERR_OK;
+}
+
+void stream_stop(stream_t *stream)
+{
+ if (!stream)
+ return;
+ switch (stream->type)
+ {
+ case STREAM_TYPE_STRING:
+ // Nothing to do, all dealt with outside of stream
+ break;
+ case STREAM_TYPE_FILE:
+ // Must cleanup vector
+ vec_free(&stream->pipe.cache);
+ break;
+ }
+ memset(stream, 0, sizeof(*stream));
+}
+
+u64 stream_size(stream_t *stream)
+{
+ assert(stream);
+ switch (stream->type)
+ {
+ case STREAM_TYPE_STRING:
+ return stream->string.size;
+ case STREAM_TYPE_FILE:
+ return stream->pipe.cache.size;
+ default:
+ FAIL("Unreachable");
+ return 0;
+ }
+}
+
+bool stream_eos(stream_t *stream)
+{
+ assert(stream);
+ switch (stream->type)
+ {
+ case STREAM_TYPE_STRING:
+ return stream->position >= stream->string.size;
+ case STREAM_TYPE_FILE:
+ return feof(stream->pipe.file);
+ default:
+ FAIL("Unreachable");
+ return 0;
+ }
+}
+
+bool stream_eoc(stream_t *stream)
+{
+ assert(stream);
+ switch (stream->type)
+ {
+ case STREAM_TYPE_STRING:
+ return stream->position >= stream->string.size;
+ case STREAM_TYPE_FILE:
+ return stream->position >= stream->pipe.cache.size;
+ default:
+ FAIL("Unreachable");
+ return 0;
+ }
+}
+
+bool stream_chunk(stream_t *stream)
+{
+ assert(stream);
+ switch (stream->type)
+ {
+ case STREAM_TYPE_STRING:
+ // vacuously true
+ return true;
+ case STREAM_TYPE_FILE:
+ {
+ if (feof(stream->pipe.file))
+ return false;
+ vec_ensure_free(&stream->pipe.cache, STREAM_DEFAULT_CHUNK);
+ int read = fread(vec_data(&stream->pipe.cache), 1, STREAM_DEFAULT_CHUNK,
+ stream->pipe.file);
+ stream->pipe.cache.size += read;
+ return true;
+ }
+ default:
+ FAIL("Unreachable");
+ return 0;
+ }
+}
+
+char stream_next(stream_t *stream)
+{
+ char c = stream_peek(stream);
+ if (c != '\0')
+ ++stream->position;
+ return c;
+}
+
+char stream_peek(stream_t *stream)
+{
+ if (stream_eos(stream))
+ return '\0';
+
+ switch (stream->type)
+ {
+ case STREAM_TYPE_STRING:
+ return stream->string.data[stream->position];
+ case STREAM_TYPE_FILE:
+ {
+ // Cached already? We are done.
+ if (stream->position < stream->pipe.cache.size)
+ return ((char *)vec_data(&stream->pipe.cache))[stream->position];
+
+ // Try to read chunks in till we've reached it or we're at the end of the
+ // file.
+ for (bool read_chunk = stream_chunk(stream);
+ read_chunk && stream->position >= stream->pipe.cache.size;
+ read_chunk = stream_chunk(stream))
+ continue;
+
+ // Same principle as the stream_eos(stream) check.
+ if (stream->position >= stream->pipe.cache.size)
+ return '\0';
+ return ((char *)vec_data(&stream->pipe.cache))[stream->position];
+ }
+ default:
+ FAIL("Unreachable");
+ return 0;
+ }
+}
+
+bool stream_seek(stream_t *stream, i64 offset)
+{
+ if (offset < 0)
+ return stream_seek_backward(stream, offset * -1);
+ else if (offset > 0)
+ return stream_seek_forward(stream, offset);
+ else
+ // vacuously successful
+ return true;
+}
+
+bool stream_seek_forward(stream_t *stream, u64 offset)
+{
+ if (stream_eos(stream))
+ return false;
+ switch (stream->type)
+ {
+ case STREAM_TYPE_STRING:
+ {
+ if (stream->position + offset < stream->string.size)
+ {
+ stream->position += offset;
+ return true;
+ }
+ return false;
+ }
+ case STREAM_TYPE_FILE:
+ {
+ // Similar principle as stream_peek really...
+
+ // Cached already? We are done.
+ if (stream->position + offset < stream->pipe.cache.size)
+ {
+ stream->position += offset;
+ return true;
+ }
+
+ // Try to read chunks in till we've reached it or we're at the end of the
+ // file.
+ for (bool read_chunk = stream_chunk(stream);
+ read_chunk && stream->position + offset >= stream->pipe.cache.size;
+ read_chunk = stream_chunk(stream))
+ continue;
+
+ // Same principle as the stream_eos(stream) check.
+ if (stream->position + offset >= stream->pipe.cache.size)
+ return false;
+ stream->position += offset;
+ return true;
+ }
+ default:
+ FAIL("Unreachable");
+ return 0;
+ }
+}
+
+bool stream_seek_backward(stream_t *stream, u64 offset)
+{
+ assert(stream);
+ if (stream->position < offset)
+ return false;
+ stream->position -= offset;
+ return true;
+}
+
+sv_t stream_substr(stream_t *stream, u64 size)
+{
+ if (stream_eos(stream))
+ return SV(NULL, 0);
+ u64 current_position = stream->position;
+ bool successful = stream_seek_forward(stream, size);
+ // In case we did happen to move forward
+ stream->position = current_position;
+ if (!successful)
+ return SV(NULL, 0);
+
+ char *ptr = NULL;
+ switch (stream->type)
+ {
+ case STREAM_TYPE_STRING:
+ ptr = stream->string.data;
+ break;
+ case STREAM_TYPE_FILE:
+ ptr = vec_data(&stream->pipe.cache);
+ break;
+ default:
+ FAIL("Unreachable");
+ return SV(NULL, 0);
+ }
+
+ return SV(ptr + stream->position, size);
+}
+
+sv_t stream_substr_abs(stream_t *stream, u64 index, u64 size)
+{
+
+ switch (stream->type)
+ {
+ case STREAM_TYPE_STRING:
+ if (index + size < stream_size(stream))
+ return SV(stream->string.data + index, size);
+ return SV(NULL, 0);
+ case STREAM_TYPE_FILE:
+ {
+ if (index + size < stream_size(stream))
+ return SV(vec_data(&stream->pipe.cache) + index, size);
+ // stream_size(stream) <= index + size => try reading chunks
+ for (bool read_chunk = stream_chunk(stream);
+ read_chunk && index + size >= stream->pipe.cache.size;
+ read_chunk = stream_chunk(stream))
+ continue;
+
+ if (index + size >= stream_size(stream))
+ return SV(NULL, 0);
+ return SV(vec_data(&stream->pipe.cache) + index, size);
+ }
+ default:
+ assert("Unreachable");
+ return SV(NULL, 0);
+ }
+}
diff --git a/symtable.c b/symtable.c
index 15c6cd0..39139eb 100644
--- a/symtable.c
+++ b/symtable.c
@@ -38,7 +38,7 @@ char *sym_table_find(sym_table_t *table, sv_t sv)
if (table->entries.capacity == 0)
sym_table_init(table);
- // WIP: Deal with resizing this when table->count > table->size / 2
+ // TODO: Deal with resizing this when table->count > table->size / 2
u64 index = djb2(sv) & (table->capacity - 1);
for (sv_t comp = VEC_GET(&table->entries, sv_t)[index]; comp.data; index += 1,