Compare commits
12 Commits
500661d68e
...
10d6876de4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10d6876de4 | ||
|
|
75daad53ea | ||
|
|
1e451c57e8 | ||
|
|
d14f015c38 | ||
|
|
26e545a732 | ||
|
|
169a165cfc | ||
|
|
438a494ac7 | ||
|
|
068e4aa9f0 | ||
|
|
271e0bff9b | ||
|
|
010895331d | ||
|
|
6a75c1d9d4 | ||
|
|
01b695eb6a |
7
Makefile
7
Makefile
@@ -5,8 +5,8 @@ OUT=$(DIST)/alisp.out
|
|||||||
TEST=$(DIST)/test.out
|
TEST=$(DIST)/test.out
|
||||||
|
|
||||||
LDFLAGS=
|
LDFLAGS=
|
||||||
GFLAGS=-Wall -Wextra -Wpedantic -std=c23 -I./include/
|
GFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c23 -I./include/
|
||||||
DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined
|
DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined -DVERBOSE_LOGS=1
|
||||||
RFLAGS=-O3
|
RFLAGS=-O3
|
||||||
|
|
||||||
MODE=release
|
MODE=release
|
||||||
@@ -61,6 +61,9 @@ run: $(OUT)
|
|||||||
test: $(TEST)
|
test: $(TEST)
|
||||||
./$^
|
./$^
|
||||||
|
|
||||||
|
examples: $(OUT)
|
||||||
|
./$^ ./examples/hello-world.lisp
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(DIST)
|
rm -rf $(DIST)
|
||||||
|
|
||||||
|
|||||||
14
examples/hello-world.lisp
Normal file
14
examples/hello-world.lisp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
;;; hello-world.lisp - 2026-02-04
|
||||||
|
|
||||||
|
;; 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 GNU General Public License Version 2 for
|
||||||
|
;; details.
|
||||||
|
|
||||||
|
;; You may distribute and modify this code under the terms of the GNU General
|
||||||
|
;; Public License Version 2, which you should have received a copy of along with
|
||||||
|
;; this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
(display "Hello, world!")
|
||||||
@@ -19,6 +19,20 @@
|
|||||||
#define FAIL(MSG) assert(false && "FAIL: " #MSG)
|
#define FAIL(MSG) assert(false && "FAIL: " #MSG)
|
||||||
#define TODO(MSG) assert(false && "TODO: " #MSG)
|
#define TODO(MSG) assert(false && "TODO: " #MSG)
|
||||||
|
|
||||||
|
#ifndef VERBOSE_LOGS
|
||||||
|
#define VERBOSE_LOGS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if VERBOSE_LOGS
|
||||||
|
#define LOG(...) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
printf(__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define LOG(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Numeric aliases
|
/// Numeric aliases
|
||||||
typedef uint8_t u8;
|
typedef uint8_t u8;
|
||||||
typedef uint16_t u16;
|
typedef uint16_t u16;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ typedef struct
|
|||||||
|
|
||||||
void sys_init(sys_t *);
|
void sys_init(sys_t *);
|
||||||
void sys_register(sys_t *, lisp_t *);
|
void sys_register(sys_t *, lisp_t *);
|
||||||
void sys_cleanup(sys_t *);
|
void sys_free(sys_t *);
|
||||||
|
|
||||||
/// Constructors and destructors
|
/// Constructors and destructors
|
||||||
lisp_t *make_int(i64);
|
lisp_t *make_int(i64);
|
||||||
|
|||||||
@@ -8,13 +8,18 @@
|
|||||||
#ifndef READER_H
|
#ifndef READER_H
|
||||||
#define READER_H
|
#define READER_H
|
||||||
|
|
||||||
|
#include <alisp/lisp.h>
|
||||||
#include <alisp/stream.h>
|
#include <alisp/stream.h>
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
READ_OK = 0,
|
READ_ERR_OK = 0,
|
||||||
|
READ_ERR_EOF,
|
||||||
|
READ_ERR_UNKNOWN_CHAR,
|
||||||
} read_err_t;
|
} read_err_t;
|
||||||
|
|
||||||
|
const char *read_err_to_cstr(read_err_t);
|
||||||
|
|
||||||
// Attempt to read an expression from the stream, storing it in a pointer,
|
// Attempt to read an expression from the stream, storing it in a pointer,
|
||||||
// returning any errors if failed.
|
// returning any errors if failed.
|
||||||
read_err_t read(sys_t *, stream_t *, lisp_t **);
|
read_err_t read(sys_t *, stream_t *, lisp_t **);
|
||||||
|
|||||||
@@ -22,13 +22,15 @@ typedef enum
|
|||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
STREAM_ERR_INVALID_PTR = -4,
|
STREAM_ERR_OK = 0,
|
||||||
STREAM_ERR_FILE_NONEXISTENT = -3,
|
STREAM_ERR_INVALID_PTR,
|
||||||
STREAM_ERR_FILE_READ = -2,
|
STREAM_ERR_FILE_NONEXISTENT,
|
||||||
STREAM_ERR_PIPE_NONEXISTENT = -1,
|
STREAM_ERR_FILE_READ,
|
||||||
STREAM_ERR_OK = 0,
|
STREAM_ERR_PIPE_NONEXISTENT,
|
||||||
} stream_err_t;
|
} stream_err_t;
|
||||||
|
|
||||||
|
const char *stream_err_to_cstr(stream_err_t);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
vec_t cache;
|
vec_t cache;
|
||||||
@@ -80,6 +82,9 @@ sv_t stream_till(stream_t *, const char *);
|
|||||||
// present.
|
// present.
|
||||||
sv_t stream_while(stream_t *, const char *);
|
sv_t stream_while(stream_t *, const char *);
|
||||||
|
|
||||||
|
// Get the line and column of the stream at its current position.
|
||||||
|
void stream_line_col(stream_t *, u64 *line, u64 *col);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Copyright (C) 2026 Aryadev Chavali
|
/* Copyright (C) 2026 Aryadev Chavali
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void sys_register(sys_t *sys, lisp_t *ptr)
|
|||||||
vec_append(&sys->memory, &ptr, sizeof(&ptr));
|
vec_append(&sys->memory, &ptr, sizeof(&ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sys_cleanup(sys_t *sys)
|
void sys_free(sys_t *sys)
|
||||||
{
|
{
|
||||||
static_assert(NUM_TAGS == 5);
|
static_assert(NUM_TAGS == 5);
|
||||||
|
|
||||||
|
|||||||
43
src/reader.c
Normal file
43
src/reader.c
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/* reader.c: Stream reader implementation
|
||||||
|
* Created: 2026-02-04
|
||||||
|
* Author: Aryadev Chavali
|
||||||
|
* License: See end of file
|
||||||
|
* Commentary:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <alisp/base.h>
|
||||||
|
#include <alisp/reader.h>
|
||||||
|
|
||||||
|
const char *read_err_to_cstr(read_err_t err)
|
||||||
|
{
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case READ_ERR_OK:
|
||||||
|
return "OK";
|
||||||
|
break;
|
||||||
|
case READ_ERR_EOF:
|
||||||
|
return "EOF";
|
||||||
|
break;
|
||||||
|
case READ_ERR_UNKNOWN_CHAR:
|
||||||
|
return "UNKNOWN_CHAR";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FAIL("Unreachable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 GNU General Public License Version 2 for
|
||||||
|
* details.
|
||||||
|
|
||||||
|
* You may distribute and modify this code under the terms of the GNU General
|
||||||
|
* Public License Version 2, which you should have received a copy of along with
|
||||||
|
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
70
src/stream.c
70
src/stream.c
@@ -8,8 +8,33 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <alisp/base.h>
|
||||||
#include <alisp/stream.h>
|
#include <alisp/stream.h>
|
||||||
|
|
||||||
|
const char *stream_err_to_cstr(stream_err_t err)
|
||||||
|
{
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case STREAM_ERR_INVALID_PTR:
|
||||||
|
return "INVALID PTR";
|
||||||
|
break;
|
||||||
|
case STREAM_ERR_FILE_NONEXISTENT:
|
||||||
|
return "FILE NONEXISTENT";
|
||||||
|
break;
|
||||||
|
case STREAM_ERR_FILE_READ:
|
||||||
|
return "FILE READ";
|
||||||
|
break;
|
||||||
|
case STREAM_ERR_PIPE_NONEXISTENT:
|
||||||
|
return "PIPE NONEXISTENT";
|
||||||
|
break;
|
||||||
|
case STREAM_ERR_OK:
|
||||||
|
return "OK";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FAIL("Unreachable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents)
|
stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents)
|
||||||
{
|
{
|
||||||
if (!stream)
|
if (!stream)
|
||||||
@@ -26,8 +51,11 @@ stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents)
|
|||||||
|
|
||||||
stream_err_t stream_init_pipe(stream_t *stream, char *name, FILE *pipe)
|
stream_err_t stream_init_pipe(stream_t *stream, char *name, FILE *pipe)
|
||||||
{
|
{
|
||||||
if (!stream || !pipe)
|
if (!stream)
|
||||||
return STREAM_ERR_INVALID_PTR;
|
return STREAM_ERR_INVALID_PTR;
|
||||||
|
else if (!pipe)
|
||||||
|
return STREAM_ERR_PIPE_NONEXISTENT;
|
||||||
|
|
||||||
name = name ? name : "<stream>";
|
name = name ? name : "<stream>";
|
||||||
memset(stream, 0, sizeof(*stream));
|
memset(stream, 0, sizeof(*stream));
|
||||||
|
|
||||||
@@ -42,8 +70,11 @@ stream_err_t stream_init_pipe(stream_t *stream, char *name, FILE *pipe)
|
|||||||
|
|
||||||
stream_err_t stream_init_file(stream_t *stream, char *name, FILE *pipe)
|
stream_err_t stream_init_file(stream_t *stream, char *name, FILE *pipe)
|
||||||
{
|
{
|
||||||
if (!stream || !pipe)
|
if (!stream)
|
||||||
return STREAM_ERR_INVALID_PTR;
|
return STREAM_ERR_INVALID_PTR;
|
||||||
|
else if (!pipe)
|
||||||
|
return STREAM_ERR_FILE_NONEXISTENT;
|
||||||
|
|
||||||
name = name ? name : "<stream>";
|
name = name ? name : "<stream>";
|
||||||
memset(stream, 0, sizeof(*stream));
|
memset(stream, 0, sizeof(*stream));
|
||||||
|
|
||||||
@@ -361,6 +392,41 @@ sv_t stream_while(stream_t *stream, const char *str)
|
|||||||
return stream_substr_abs(stream, current_position, size - 1);
|
return stream_substr_abs(stream, current_position, size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void stream_line_col(stream_t *stream, u64 *line, u64 *col)
|
||||||
|
{
|
||||||
|
if (!stream || !line || !col)
|
||||||
|
return;
|
||||||
|
// Go through the cache, byte by byte.
|
||||||
|
char *cache = NULL;
|
||||||
|
u64 size = 0;
|
||||||
|
if (stream->type == STREAM_TYPE_STRING)
|
||||||
|
{
|
||||||
|
cache = stream->string.data;
|
||||||
|
size = stream->string.size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cache = (char *)vec_data(&stream->pipe.cache);
|
||||||
|
size = stream->pipe.cache.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
*line = 1;
|
||||||
|
*col = 0;
|
||||||
|
for (u64 i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
char c = cache[i];
|
||||||
|
if (c == '\n')
|
||||||
|
{
|
||||||
|
*line += 1;
|
||||||
|
*col = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Copyright (C) 2025, 2026 Aryadev Chavali
|
/* Copyright (C) 2025, 2026 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ void sym_test(void)
|
|||||||
TEST(strncmp(in, out, strlen(in)) == 0, "%d", strncmp(in, out, strlen(in)));
|
TEST(strncmp(in, out, strlen(in)) == 0, "%d", strncmp(in, out, strlen(in)));
|
||||||
}
|
}
|
||||||
TEST_PASSED();
|
TEST_PASSED();
|
||||||
sys_cleanup(&system);
|
sys_free(&system);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cons_test(void)
|
void cons_test(void)
|
||||||
@@ -91,7 +91,7 @@ void cons_test(void)
|
|||||||
|
|
||||||
TEST_PASSED();
|
TEST_PASSED();
|
||||||
|
|
||||||
sys_cleanup(&system);
|
sys_free(&system);
|
||||||
}
|
}
|
||||||
|
|
||||||
const test_fn TESTS_LISP_API[] = {
|
const test_fn TESTS_LISP_API[] = {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ void vec_test1(void)
|
|||||||
strncmp((char *)vec_data(vec), words_text, vec->size));
|
strncmp((char *)vec_data(vec), words_text, vec->size));
|
||||||
|
|
||||||
TEST_PASSED();
|
TEST_PASSED();
|
||||||
sys_cleanup(&system);
|
sys_free(&system);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vec_test2(void)
|
void vec_test2(void)
|
||||||
@@ -65,7 +65,7 @@ void vec_test2(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_PASSED();
|
TEST_PASSED();
|
||||||
sys_cleanup(&system);
|
sys_free(&system);
|
||||||
}
|
}
|
||||||
|
|
||||||
const test_fn TESTS_VEC[] = {
|
const test_fn TESTS_VEC[] = {
|
||||||
|
|||||||
Reference in New Issue
Block a user