Still got some failures, but a basic stream implementation

Need to fix what's going on with the example in main.c using stdin.
This commit is contained in:
2025-08-28 22:55:41 +01:00
parent 66c6400731
commit cc56a2ee2b
5 changed files with 373 additions and 3 deletions

59
alisp.h
View File

@@ -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

View File

@@ -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() {

17
main.c
View File

@@ -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;
}

296
stream.c Normal file
View File

@@ -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);
}
}

View File

@@ -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,