diff options
| author | Aryadev Chavali <aryadev@aryadevchavali.com> | 2025-08-28 22:55:41 +0100 | 
|---|---|---|
| committer | Aryadev Chavali <aryadev@aryadevchavali.com> | 2025-08-28 22:55:41 +0100 | 
| commit | cc56a2ee2b5703f9ea5ac63a86870af188845c30 (patch) | |
| tree | 2660c03c1c90ad069d44e29af4365bca572926aa | |
| parent | 66c64007317cf5b646c1f816463f4bc730e3b99f (diff) | |
| download | alisp-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.h | 59 | ||||
| -rw-r--r-- | build.sh | 2 | ||||
| -rw-r--r-- | main.c | 17 | ||||
| -rw-r--r-- | stream.c | 296 | ||||
| -rw-r--r-- | symtable.c | 2 | 
5 files changed, 373 insertions, 3 deletions
@@ -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 @@ -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() { @@ -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); +  } +} @@ -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,  | 
