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:
59
alisp.h
59
alisp.h
@@ -17,14 +17,18 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdalign.h>
|
#include <stdalign.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
/// The bare fucking minimum
|
/// The bare fucking minimum
|
||||||
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
||||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||||
#define ARRSIZE(A) (sizeof(A) / sizeof((A)[0]))
|
#define ARRSIZE(A) (sizeof(A) / sizeof((A)[0]))
|
||||||
#define NTH_BYTE(X, N) (((X) >> (8 * N)) & ((1 << 8) - 1))
|
#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 uint8_t u8;
|
||||||
typedef uint16_t u16;
|
typedef uint16_t u16;
|
||||||
@@ -91,6 +95,61 @@ 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_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
|
/// Basic defintions for a Lisp
|
||||||
#define NIL 0
|
#define NIL 0
|
||||||
|
|
||||||
|
|||||||
2
build.sh
2
build.sh
@@ -4,7 +4,7 @@ set -xe
|
|||||||
|
|
||||||
CFLAGS="-Wall -Wextra -std=c11 -ggdb -fsanitize=address -fsanitize=undefined"
|
CFLAGS="-Wall -Wextra -std=c11 -ggdb -fsanitize=address -fsanitize=undefined"
|
||||||
LINK=""
|
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"
|
OUT="alisp.out"
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
|
|||||||
17
main.c
17
main.c
@@ -20,6 +20,21 @@
|
|||||||
|
|
||||||
int main(void)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
296
stream.c
Normal file
296
stream.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ char *sym_table_find(sym_table_t *table, sv_t sv)
|
|||||||
if (table->entries.capacity == 0)
|
if (table->entries.capacity == 0)
|
||||||
sym_table_init(table);
|
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);
|
u64 index = djb2(sv) & (table->capacity - 1);
|
||||||
|
|
||||||
for (sv_t comp = VEC_GET(&table->entries, sv_t)[index]; comp.data; index += 1,
|
for (sv_t comp = VEC_GET(&table->entries, sv_t)[index]; comp.data; index += 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user