Improve read.c
This commit is contained in:
+1
-1
@@ -23,4 +23,4 @@
|
|||||||
(princ datum print-char-fun)
|
(princ datum print-char-fun)
|
||||||
(funcall (or print-char-fun 'write-byte) ?\n))
|
(funcall (or print-char-fun 'write-byte) ?\n))
|
||||||
|
|
||||||
(princln (apply 'cons '(a b)))
|
(princln (list (symbolp 1) '1a '1aa))
|
||||||
|
|||||||
+1
-1
@@ -211,7 +211,7 @@ LispVal *make_builtin_function(LispVal *name, LispVal *(*cfunc)(void),
|
|||||||
obj->impl.native.no_eval_args = false;
|
obj->impl.native.no_eval_args = false;
|
||||||
obj->impl.native.addr.zero = cfunc;
|
obj->impl.native.addr.zero = cfunc;
|
||||||
ReadStream stream;
|
ReadStream stream;
|
||||||
read_stream_init(&stream, lisp_args, args_len);
|
read_stream_init_from_string(&stream, lisp_args, args_len);
|
||||||
LispVal *args_form = read(&stream);
|
LispVal *args_form = read(&stream);
|
||||||
if (!args_form) {
|
if (!args_form) {
|
||||||
fprintf(stderr, "Builtin function lambda list had a syntax error\n");
|
fprintf(stderr, "Builtin function lambda list had a syntax error\n");
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
#include "lisp_string.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "stack.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static noreturn void signal_io_error_from_errno(int n) {
|
||||||
|
char *msg = strerror(n);
|
||||||
|
lisp_signal(Qio_error, LIST(make_lisp_string(msg, strlen(msg), true, true),
|
||||||
|
MAKE_FIXNUM(n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lisp_fputc(LispVal *ch, FILE *file) {
|
||||||
|
if (NILP(ch)) {
|
||||||
|
if (fflush(file) == EOF) {
|
||||||
|
signal_io_error_from_errno(errno);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CHECK_CHAR(ch);
|
||||||
|
fixnum_t f = XFIXNUM(ch);
|
||||||
|
if (fputc(f, file) == EOF) {
|
||||||
|
signal_io_error_from_errno(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(write_byte, "write-byte", (LispVal * ch), "(ch)", "") {
|
||||||
|
lisp_fputc(ch, stdout);
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(error_write_byte, "error-write-byte", (LispVal * ch), "(ch)", "") {
|
||||||
|
lisp_fputc(ch, stderr);
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LispVal *lisp_fgetc(LispVal *unread, FILE *file) {
|
||||||
|
if (!NILP(unread)) {
|
||||||
|
CHECK_CHAR(unread);
|
||||||
|
if (ungetc(XFIXNUM(unread), file) == EOF) {
|
||||||
|
signal_io_error_from_errno(errno);
|
||||||
|
}
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
errno = 0;
|
||||||
|
int res = fgetc(file);
|
||||||
|
if (res == EOF) {
|
||||||
|
if (errno) {
|
||||||
|
signal_io_error_from_errno(errno);
|
||||||
|
}
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
return MAKE_FIXNUM(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(read_byte, "read-byte", (LispVal * unread), "(&optional unread)", "") {
|
||||||
|
return lisp_fgetc(unread, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(error_read_byte, "error-read-byte", (LispVal * unread),
|
||||||
|
"(&optional unread)", "") {
|
||||||
|
return lisp_fgetc(unread, stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_SYMBOL(io_error, "io-error");
|
||||||
|
DEFINE_CONDITION_CLASS(io_error, error);
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef INCLUDED_IO_H
|
||||||
|
#define INCLUDED_IO_H
|
||||||
|
|
||||||
|
#include "base.h"
|
||||||
|
#include "lisp_string.h"
|
||||||
|
|
||||||
|
static ALWAYS_INLINE void CHECK_CHAR(LispVal *val) {
|
||||||
|
if (!FIXNUMP(val)) {
|
||||||
|
signal_type_error(val, Qchar);
|
||||||
|
}
|
||||||
|
fixnum_t n = XFIXNUM(val);
|
||||||
|
if (n < 0 || n > 255) {
|
||||||
|
signal_type_error(val, Qchar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, a print character function takes nil to mean flush
|
||||||
|
DECLARE_FUNCTION(write_byte, (LispVal * ch));
|
||||||
|
DECLARE_FUNCTION(error_write_byte, (LispVal * ch));
|
||||||
|
|
||||||
|
DECLARE_FUNCTION(read_byte, (LispVal * unread));
|
||||||
|
DECLARE_FUNCTION(error_read_byte, (LispVal * unread));
|
||||||
|
|
||||||
|
DECLARE_SYMBOL(io_error);
|
||||||
|
MAKE_CONDITION_CLASS(io_error);
|
||||||
|
|
||||||
|
#endif
|
||||||
+2
-1
@@ -1,3 +1,4 @@
|
|||||||
|
#include "io.h"
|
||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
#include "read.h"
|
#include "read.h"
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ int main(int argc, const char **argv) {
|
|||||||
push_local_reference_frame();
|
push_local_reference_frame();
|
||||||
StackFrame *toplevel = LISP_STACK_REF();
|
StackFrame *toplevel = LISP_STACK_REF();
|
||||||
ReadStream s;
|
ReadStream s;
|
||||||
read_stream_init(&s, src, src_len);
|
read_stream_init_from_string(&s, src, src_len);
|
||||||
LispVal *r;
|
LispVal *r;
|
||||||
push_handler_bind_frame(LIST(Qt), toplevel_error_handler, NULL, NULL);
|
push_handler_bind_frame(LIST(Qt), toplevel_error_handler, NULL, NULL);
|
||||||
volatile bool had_toplevel_error = false;
|
volatile bool had_toplevel_error = false;
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ static void ensure_string_stream_space(StringStream *restrict stream,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void string_stream_putc(StringStream *restrict stream, char c) {
|
||||||
|
ensure_string_stream_space(stream, 1);
|
||||||
|
stream->buffer[stream->nchars++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
int string_stream_printf(StringStream *restrict stream,
|
int string_stream_printf(StringStream *restrict stream,
|
||||||
const char *restrict format, ...) {
|
const char *restrict format, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|||||||
@@ -186,6 +186,10 @@ static inline void string_stream_free(StringStream *restrict stream) {
|
|||||||
lisp_free(stream->buffer);
|
lisp_free(stream->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void string_stream_free_as_cleanup(void *stream) {
|
||||||
|
string_stream_free(stream);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void string_stream_steal(StringStream *restrict stream,
|
static inline void string_stream_steal(StringStream *restrict stream,
|
||||||
char **restrict out,
|
char **restrict out,
|
||||||
size_t *restrict out_length) {
|
size_t *restrict out_length) {
|
||||||
@@ -193,6 +197,7 @@ static inline void string_stream_steal(StringStream *restrict stream,
|
|||||||
*out_length = stream->nchars;
|
*out_length = stream->nchars;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void string_stream_putc(StringStream *restrict stream, char c);
|
||||||
int string_stream_printf(StringStream *restrict stream,
|
int string_stream_printf(StringStream *restrict stream,
|
||||||
const char *restrict format, ...) FORMAT(2, 3);
|
const char *restrict format, ...) FORMAT(2, 3);
|
||||||
int string_stream_vprintf(StringStream *restrict stream,
|
int string_stream_vprintf(StringStream *restrict stream,
|
||||||
|
|||||||
+1
-23
@@ -1,5 +1,6 @@
|
|||||||
#include "print.h"
|
#include "print.h"
|
||||||
|
|
||||||
|
#include "io.h"
|
||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
// for WHITESPACEP, READ_EOS, and SYMBOL_END_P
|
// for WHITESPACEP, READ_EOS, and SYMBOL_END_P
|
||||||
#include "read.h"
|
#include "read.h"
|
||||||
@@ -16,29 +17,6 @@ DEFVAR(print_precision, "print-precision", "", MAKE_FIXNUM(6));
|
|||||||
DEFVAR(print_quoted, "print-quoted", "", Qt);
|
DEFVAR(print_quoted, "print-quoted", "", Qt);
|
||||||
DEFVAR(print_empty_list, "print-empty-list", "", Qnil);
|
DEFVAR(print_empty_list, "print-empty-list", "", Qnil);
|
||||||
|
|
||||||
static void lisp_fputc(LispVal *ch, FILE *file) {
|
|
||||||
if (NILP(ch)) {
|
|
||||||
fflush(file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CHECK_TYPE(ch, TYPE_FIXNUM);
|
|
||||||
fixnum_t f = XFIXNUM(ch);
|
|
||||||
if (f < 0 || f > 255) {
|
|
||||||
signal_type_error(ch, Qchar);
|
|
||||||
}
|
|
||||||
fputc(f, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(write_byte, "write-byte", (LispVal * ch), "(ch)", "") {
|
|
||||||
lisp_fputc(ch, stdout);
|
|
||||||
return Qnil;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(error_write_byte, "error-write-byte", (LispVal * ch), "(ch)", "") {
|
|
||||||
lisp_fputc(ch, stderr);
|
|
||||||
return Qnil;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PrintOptions {
|
struct PrintOptions {
|
||||||
bool readable;
|
bool readable;
|
||||||
bool circle;
|
bool circle;
|
||||||
|
|||||||
@@ -14,10 +14,6 @@ DECLARE_VARIABLE(print_precision);
|
|||||||
DECLARE_VARIABLE(print_quoted);
|
DECLARE_VARIABLE(print_quoted);
|
||||||
DECLARE_VARIABLE(print_empty_list);
|
DECLARE_VARIABLE(print_empty_list);
|
||||||
|
|
||||||
// For now, a print character function takes nil to mean flush
|
|
||||||
DECLARE_FUNCTION(write_byte, (LispVal * ch));
|
|
||||||
DECLARE_FUNCTION(error_write_byte, (LispVal * ch));
|
|
||||||
|
|
||||||
// Pretty print
|
// Pretty print
|
||||||
DECLARE_FUNCTION(princ, (LispVal * val, LispVal *print_char_fun));
|
DECLARE_FUNCTION(princ, (LispVal * val, LispVal *print_char_fun));
|
||||||
// Quoted print
|
// Quoted print
|
||||||
|
|||||||
+156
-85
@@ -1,5 +1,7 @@
|
|||||||
#include "read.h"
|
#include "read.h"
|
||||||
|
|
||||||
|
#include "function.h"
|
||||||
|
#include "io.h"
|
||||||
#include "lisp_string.h"
|
#include "lisp_string.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "stack.h"
|
#include "stack.h"
|
||||||
@@ -13,24 +15,61 @@
|
|||||||
#include <stdnoreturn.h>
|
#include <stdnoreturn.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void read_stream_init(ReadStream *stream, const char *buffer, size_t length) {
|
void read_stream_init_from_string(ReadStream *restrict stream,
|
||||||
stream->buffer = buffer;
|
const char *buffer, size_t length) {
|
||||||
stream->len = length;
|
stream->kind = READ_STREAM_STRING;
|
||||||
stream->off = 0;
|
stream->string.buffer = buffer;
|
||||||
|
stream->string.len = length;
|
||||||
|
stream->string.off = 0;
|
||||||
stream->line = 1;
|
stream->line = 1;
|
||||||
stream->col = 0;
|
stream->col = 0;
|
||||||
stream->backquote_level = 0;
|
stream->backquote_level = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALWAYS_INLINE bool EOSP(const ReadStream *stream) {
|
void read_stream_init_from_func(ReadStream *restrict stream,
|
||||||
return stream->off == stream->len;
|
LispVal *read_char_func) {
|
||||||
|
stream->kind = READ_STREAM_FUNC;
|
||||||
|
stream->func.func = read_char_func;
|
||||||
|
stream->func.is_eof = false;
|
||||||
|
stream->line = 1;
|
||||||
|
stream->col = 0;
|
||||||
|
stream->backquote_level = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pop_char(ReadStream *stream) {
|
static int peek_char(ReadStream *restrict stream);
|
||||||
|
static ALWAYS_INLINE bool EOSP(ReadStream *restrict stream) {
|
||||||
|
switch (stream->kind) {
|
||||||
|
case READ_STREAM_STRING:
|
||||||
|
return stream->string.off == stream->string.len;
|
||||||
|
case READ_STREAM_FUNC:
|
||||||
|
peek_char(stream);
|
||||||
|
return stream->func.is_eof;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pop_char(ReadStream *restrict stream) {
|
||||||
if (EOSP(stream)) {
|
if (EOSP(stream)) {
|
||||||
return READ_EOS;
|
return READ_EOS;
|
||||||
}
|
}
|
||||||
int c = stream->buffer[stream->off++];
|
int c;
|
||||||
|
switch (stream->kind) {
|
||||||
|
case READ_STREAM_STRING:
|
||||||
|
c = stream->string.buffer[stream->string.off++];
|
||||||
|
break;
|
||||||
|
case READ_STREAM_FUNC: {
|
||||||
|
LispVal *lisp_c = CALL0(stream->func.func);
|
||||||
|
if (NILP(lisp_c)) {
|
||||||
|
stream->func.is_eof = true;
|
||||||
|
return READ_EOS;
|
||||||
|
}
|
||||||
|
CHECK_CHAR(lisp_c);
|
||||||
|
c = XFIXNUM(lisp_c);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
++stream->line;
|
++stream->line;
|
||||||
stream->col = 0;
|
stream->col = 0;
|
||||||
@@ -40,18 +79,30 @@ static int pop_char(ReadStream *stream) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int peek_nth_char(const ReadStream *stream, int n) {
|
static int peek_char(ReadStream *restrict stream) {
|
||||||
if (stream->len - stream->off <= n) {
|
if (EOSP(stream)) {
|
||||||
return READ_EOS;
|
return READ_EOS;
|
||||||
}
|
}
|
||||||
return stream->buffer[stream->off + n];
|
switch (stream->kind) {
|
||||||
|
case READ_STREAM_STRING:
|
||||||
|
return stream->string.buffer[stream->string.off];
|
||||||
|
case READ_STREAM_FUNC: {
|
||||||
|
LispVal *lisp_c = CALL0(stream->func.func);
|
||||||
|
if (NILP(lisp_c)) {
|
||||||
|
stream->func.is_eof = true;
|
||||||
|
return READ_EOS;
|
||||||
|
}
|
||||||
|
CHECK_CHAR(lisp_c);
|
||||||
|
int c = XFIXNUM(lisp_c);
|
||||||
|
CALL(stream->func.func, lisp_c); // unget the character
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int peek_char(const ReadStream *stream) {
|
static void skip_whitespace(ReadStream *restrict stream) {
|
||||||
return peek_nth_char(stream, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void skip_whitespace(ReadStream *stream) {
|
|
||||||
bool in_comment = false;
|
bool in_comment = false;
|
||||||
int c;
|
int c;
|
||||||
while ((c = peek_char(stream)) != READ_EOS
|
while ((c = peek_char(stream)) != READ_EOS
|
||||||
@@ -65,17 +116,15 @@ static void skip_whitespace(ReadStream *stream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMAT(3, 4)
|
FORMAT(2, 3)
|
||||||
static noreturn void read_error(ReadStream *stream, size_t length,
|
static noreturn void read_error(ReadStream *restrict stream,
|
||||||
const char *msg, ...) {
|
const char *restrict msg, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, msg);
|
va_start(args, msg);
|
||||||
LispVal *lmsg = lisp_vsprintf(msg, args);
|
LispVal *lmsg = lisp_vsprintf(msg, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
lisp_signal(Qread_error,
|
lisp_signal(Qread_error, LIST(MAKE_FIXNUM(stream->line),
|
||||||
LIST(MAKE_FIXNUM(stream->line), MAKE_FIXNUM(stream->col), lmsg,
|
MAKE_FIXNUM(stream->col), lmsg));
|
||||||
make_lisp_string(stream->buffer + stream->off, length,
|
|
||||||
true, true)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALWAYS_INLINE bool DOT_SYMBOL_P(LispVal *val) {
|
static ALWAYS_INLINE bool DOT_SYMBOL_P(LispVal *val) {
|
||||||
@@ -86,7 +135,7 @@ static ALWAYS_INLINE bool DOT_SYMBOL_P(LispVal *val) {
|
|||||||
return name->length == 1 && name->data[0] == '.';
|
return name->length == 1 && name->data[0] == '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
LispVal *next_list(ReadStream *stream) {
|
LispVal *next_list(ReadStream *restrict stream) {
|
||||||
pop_char(stream); // the (
|
pop_char(stream); // the (
|
||||||
skip_whitespace(stream);
|
skip_whitespace(stream);
|
||||||
LispVal *start = Qnil;
|
LispVal *start = Qnil;
|
||||||
@@ -94,20 +143,19 @@ LispVal *next_list(ReadStream *stream) {
|
|||||||
bool dotted = false;
|
bool dotted = false;
|
||||||
while (peek_char(stream) != ')') {
|
while (peek_char(stream) != ')') {
|
||||||
if (dotted) {
|
if (dotted) {
|
||||||
read_error(stream, 0,
|
read_error(stream, "invalid syntax encountered while reading list");
|
||||||
"invalid syntax encountered while reading list");
|
|
||||||
}
|
}
|
||||||
skip_whitespace(stream);
|
skip_whitespace(stream);
|
||||||
LispVal *new_val = read(stream);
|
LispVal *new_val = read(stream);
|
||||||
if (new_val && DOT_SYMBOL_P(new_val)) {
|
if (new_val && DOT_SYMBOL_P(new_val)) {
|
||||||
if (NILP(start)) {
|
if (NILP(start)) {
|
||||||
read_error(stream, 1, "list has no car");
|
read_error(stream, "list has no car");
|
||||||
}
|
}
|
||||||
dotted = true;
|
dotted = true;
|
||||||
new_val = read(stream);
|
new_val = read(stream);
|
||||||
}
|
}
|
||||||
if (!new_val) {
|
if (!new_val) {
|
||||||
read_error(stream, 0, "got EOF while reading list");
|
read_error(stream, "got EOF while reading list");
|
||||||
}
|
}
|
||||||
if (dotted) {
|
if (dotted) {
|
||||||
RPLACD(end, new_val);
|
RPLACD(end, new_val);
|
||||||
@@ -121,12 +169,12 @@ LispVal *next_list(ReadStream *stream) {
|
|||||||
skip_whitespace(stream);
|
skip_whitespace(stream);
|
||||||
}
|
}
|
||||||
if (pop_char(stream) == READ_EOS) { // the )
|
if (pop_char(stream) == READ_EOS) { // the )
|
||||||
read_error(stream, 0, "got EOF while reading list");
|
read_error(stream, "got EOF while reading list");
|
||||||
}
|
}
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
LispVal *next_vector(ReadStream *stream) {
|
LispVal *next_vector(ReadStream *restrict stream) {
|
||||||
pop_char(stream); // the [
|
pop_char(stream); // the [
|
||||||
skip_whitespace(stream);
|
skip_whitespace(stream);
|
||||||
LispVal **data = NULL;
|
LispVal **data = NULL;
|
||||||
@@ -135,7 +183,7 @@ LispVal *next_vector(ReadStream *stream) {
|
|||||||
LispVal *new_val = read(stream);
|
LispVal *new_val = read(stream);
|
||||||
if (!new_val) {
|
if (!new_val) {
|
||||||
free(data);
|
free(data);
|
||||||
read_error(stream, 0, "got EOF while reading vector");
|
read_error(stream, "got EOF while reading vector");
|
||||||
}
|
}
|
||||||
data = lisp_realloc(data, sizeof(LispVal *) * ++length);
|
data = lisp_realloc(data, sizeof(LispVal *) * ++length);
|
||||||
data[length - 1] = new_val;
|
data[length - 1] = new_val;
|
||||||
@@ -143,12 +191,12 @@ LispVal *next_vector(ReadStream *stream) {
|
|||||||
}
|
}
|
||||||
if (pop_char(stream) == READ_EOS) { // the ]
|
if (pop_char(stream) == READ_EOS) { // the ]
|
||||||
free(data);
|
free(data);
|
||||||
read_error(stream, 0, "got EOF while reading vector");
|
read_error(stream, "got EOF while reading vector");
|
||||||
}
|
}
|
||||||
return make_vector(data, length, true);
|
return make_vector(data, length, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
LispVal *next_string(ReadStream *stream) {
|
LispVal *next_string(ReadStream *restrict stream) {
|
||||||
pop_char(stream); // the "
|
pop_char(stream); // the "
|
||||||
bool backslash = false;
|
bool backslash = false;
|
||||||
char *buffer = lisp_malloc(1);
|
char *buffer = lisp_malloc(1);
|
||||||
@@ -158,7 +206,7 @@ LispVal *next_string(ReadStream *stream) {
|
|||||||
int c = pop_char(stream);
|
int c = pop_char(stream);
|
||||||
if (c == READ_EOS) {
|
if (c == READ_EOS) {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
read_error(stream, 0, "got EOF while reading string");
|
read_error(stream, "got EOF while reading string");
|
||||||
} else if (c == '\\' && !backslash) {
|
} else if (c == '\\' && !backslash) {
|
||||||
backslash = true;
|
backslash = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -179,7 +227,7 @@ LispVal *next_string(ReadStream *stream) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
free(buffer);
|
free(buffer);
|
||||||
read_error(stream, 0, "unknown escape sequence");
|
read_error(stream, "unknown escape sequence");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer = lisp_realloc(buffer, ++len + 1);
|
buffer = lisp_realloc(buffer, ++len + 1);
|
||||||
@@ -190,16 +238,16 @@ LispVal *next_string(ReadStream *stream) {
|
|||||||
}
|
}
|
||||||
if (pop_char(stream) == READ_EOS) { // the "
|
if (pop_char(stream) == READ_EOS) { // the "
|
||||||
free(buffer);
|
free(buffer);
|
||||||
read_error(stream, 0, "got EOF while reading string");
|
read_error(stream, "got EOF while reading string");
|
||||||
}
|
}
|
||||||
return make_lisp_string(buffer, len, true, false);
|
return make_lisp_string(buffer, len, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
LispVal *next_char_literal(ReadStream *stream) {
|
LispVal *next_char_literal(ReadStream *restrict stream) {
|
||||||
pop_char(stream); // the ?
|
pop_char(stream); // the ?
|
||||||
int c = pop_char(stream);
|
int c = pop_char(stream);
|
||||||
if (c == READ_EOS) {
|
if (c == READ_EOS) {
|
||||||
read_error(stream, 0, "unterminated character literal");
|
read_error(stream, "unterminated character literal");
|
||||||
} else if (c == '\\') {
|
} else if (c == '\\') {
|
||||||
ReadStream save = *stream;
|
ReadStream save = *stream;
|
||||||
switch (pop_char(stream)) {
|
switch (pop_char(stream)) {
|
||||||
@@ -212,22 +260,28 @@ LispVal *next_char_literal(ReadStream *stream) {
|
|||||||
case 's':
|
case 's':
|
||||||
return MAKE_FIXNUM(' ');
|
return MAKE_FIXNUM(' ');
|
||||||
case READ_EOS:
|
case READ_EOS:
|
||||||
read_error(stream, 0, "unterminated character escape sequence");
|
read_error(stream, "unterminated character escape sequence");
|
||||||
default:
|
default:
|
||||||
read_error(&save, 0, "unknown escape sequence");
|
read_error(&save, "unknown escape sequence");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return MAKE_FIXNUM(c);
|
return MAKE_FIXNUM(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LispVal *next_symbol(ReadStream *stream) {
|
LispVal *next_symbol(const char *restrict first, size_t first_size,
|
||||||
|
ReadStream *restrict stream) {
|
||||||
|
size_t read_from_first = 0;
|
||||||
bool backslash = false;
|
bool backslash = false;
|
||||||
char *name = lisp_malloc(1);
|
char *name = lisp_malloc(1);
|
||||||
name[0] = '\0';
|
name[0] = '\0';
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
while (backslash || !SYMBOL_END_P(peek_char(stream))) {
|
while (backslash
|
||||||
int c = pop_char(stream);
|
|| !SYMBOL_END_P(read_from_first == first_size
|
||||||
|
? peek_char(stream)
|
||||||
|
: first[read_from_first])) {
|
||||||
|
int c = read_from_first == first_size ? pop_char(stream)
|
||||||
|
: first[read_from_first++];
|
||||||
if (c == '\\' && !backslash) {
|
if (c == '\\' && !backslash) {
|
||||||
backslash = true;
|
backslash = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -235,7 +289,7 @@ LispVal *next_symbol(ReadStream *stream) {
|
|||||||
switch (c) {
|
switch (c) {
|
||||||
case READ_EOS:
|
case READ_EOS:
|
||||||
free(name);
|
free(name);
|
||||||
read_error(stream, 0, "backslash not escaping anything");
|
read_error(stream, "backslash not escaping anything");
|
||||||
case '\\':
|
case '\\':
|
||||||
// nothing to do
|
// nothing to do
|
||||||
break;
|
break;
|
||||||
@@ -251,7 +305,7 @@ LispVal *next_symbol(ReadStream *stream) {
|
|||||||
default:
|
default:
|
||||||
if (!SYMBOL_END_P(c)) {
|
if (!SYMBOL_END_P(c)) {
|
||||||
free(name);
|
free(name);
|
||||||
read_error(stream, 0,
|
read_error(stream,
|
||||||
"invalid escape sequence in symbol name");
|
"invalid escape sequence in symbol name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,56 +341,72 @@ static bool is_base_char(int base, int c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_base(const char *c, size_t left) {
|
static int parse_base(ReadStream *restrict stream) {
|
||||||
if (left >= 2 && *c == '2' && *(c + 1) == '#') {
|
int c = peek_char(stream);
|
||||||
return 2;
|
int base;
|
||||||
} else if (left >= 2 && *c == '8' && *(c + 1) == '#') {
|
switch (c) {
|
||||||
return 8;
|
case '2':
|
||||||
} else if (left >= 3 && *c == '1' && *(c + 1) == '0' && *(c + 2) == '#') {
|
case '8':
|
||||||
return 10;
|
pop_char(stream);
|
||||||
} else if (left >= 3 && *c == '1' && *(c + 1) == '6' && *(c + 2) == '#') {
|
base = c - '0';
|
||||||
return 16;
|
break;
|
||||||
} else {
|
case '1':
|
||||||
|
pop_char(stream);
|
||||||
|
c = peek_char(stream);
|
||||||
|
if (c != '0' && c != '6') {
|
||||||
|
return INVALID_BASE;
|
||||||
|
}
|
||||||
|
pop_char(stream);
|
||||||
|
base = 10 + (c - '0');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
return INVALID_BASE;
|
return INVALID_BASE;
|
||||||
}
|
}
|
||||||
|
if (peek_char(stream) != '#') {
|
||||||
|
return INVALID_BASE;
|
||||||
|
}
|
||||||
|
pop_char(stream);
|
||||||
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
LispVal *next_number_or_symbol(ReadStream *stream, int base) {
|
LispVal *next_number_or_symbol(ReadStream *restrict stream, int base) {
|
||||||
ReadStream save = *stream;
|
|
||||||
bool has_decimal = false;
|
bool has_decimal = false;
|
||||||
size_t number_start = stream->off;
|
StringStream ss;
|
||||||
|
string_stream_init(&ss);
|
||||||
|
StackFrame *stack_ref = LISP_STACK_REF();
|
||||||
|
push_unwind_protect_frame(string_stream_free_as_cleanup, &ss, NULL);
|
||||||
|
size_t number_start = 0;
|
||||||
size_t exp_start = 0;
|
size_t exp_start = 0;
|
||||||
bool had_number = false;
|
bool had_number = false;
|
||||||
bool negative = false;
|
bool negative = false;
|
||||||
int c;
|
int c;
|
||||||
while (!SYMBOL_END_P(peek_char(stream))) {
|
while (!SYMBOL_END_P(peek_char(stream))) {
|
||||||
c = pop_char(stream);
|
c = pop_char(stream);
|
||||||
|
string_stream_putc(&ss, c);
|
||||||
if (c == '#') {
|
if (c == '#') {
|
||||||
if (base != ANY_BASE) {
|
if (base != ANY_BASE) {
|
||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
}
|
}
|
||||||
base = parse_base(&stream->buffer[number_start],
|
base = parse_base(stream);
|
||||||
stream->len - number_start);
|
|
||||||
if (base == INVALID_BASE) {
|
if (base == INVALID_BASE) {
|
||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
}
|
}
|
||||||
had_number = false;
|
had_number = false;
|
||||||
number_start = stream->off;
|
number_start = ss.nchars;
|
||||||
} else if (c == '.') {
|
} else if (c == '.') {
|
||||||
if (base != ANY_BASE || has_decimal || exp_start) {
|
if (base != ANY_BASE || has_decimal || exp_start) {
|
||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
}
|
}
|
||||||
has_decimal = true;
|
has_decimal = true;
|
||||||
} else if (c == '-' || c == '+') {
|
} else if (c == '-' || c == '+') {
|
||||||
if (stream->off - 1 != number_start
|
if (ss.nchars - 1 != number_start && ss.nchars - 1 != exp_start) {
|
||||||
&& stream->off - 1 != exp_start) {
|
|
||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
}
|
}
|
||||||
// for inf
|
// for inf
|
||||||
if (c == '-' && stream->off - 1 == number_start) {
|
if (c == '-' && ss.nchars - 1 == number_start) {
|
||||||
negative = true;
|
negative = true;
|
||||||
}
|
}
|
||||||
} else if (exp_start == stream->off - 1 && base == ANY_BASE
|
} else if (exp_start == ss.nchars - 1 && base == ANY_BASE
|
||||||
&& (c == 'n' || c == 'N')) {
|
&& (c == 'n' || c == 'N')) {
|
||||||
// attempt to read "nan" or fallback to symbol
|
// attempt to read "nan" or fallback to symbol
|
||||||
if (tolower(pop_char(stream)) != 'a'
|
if (tolower(pop_char(stream)) != 'a'
|
||||||
@@ -344,10 +414,10 @@ LispVal *next_number_or_symbol(ReadStream *stream, int base) {
|
|||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
}
|
}
|
||||||
if (SYMBOL_END_P(peek_char(stream))) {
|
if (SYMBOL_END_P(peek_char(stream))) {
|
||||||
return LISP_NAN;
|
return UNWIND_AND_RETURN(stack_ref, LISP_NAN);
|
||||||
}
|
}
|
||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
} else if (exp_start == stream->off - 1 && base == ANY_BASE
|
} else if (exp_start == ss.nchars - 1 && base == ANY_BASE
|
||||||
&& (c == 'i' || c == 'I')) {
|
&& (c == 'i' || c == 'I')) {
|
||||||
// same for "inf"
|
// same for "inf"
|
||||||
if (tolower(pop_char(stream)) != 'n'
|
if (tolower(pop_char(stream)) != 'n'
|
||||||
@@ -355,13 +425,14 @@ LispVal *next_number_or_symbol(ReadStream *stream, int base) {
|
|||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
}
|
}
|
||||||
if (SYMBOL_END_P(peek_char(stream))) {
|
if (SYMBOL_END_P(peek_char(stream))) {
|
||||||
return negative ? LISP_NEG_INF : LISP_POS_INF;
|
return UNWIND_AND_RETURN(stack_ref, negative ? LISP_NEG_INF
|
||||||
|
: LISP_POS_INF);
|
||||||
}
|
}
|
||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
} else if (!is_base_char(base, c)) {
|
} else if (!is_base_char(base, c)) {
|
||||||
if ((c == 'e' || c == 'E') && !exp_start && base == ANY_BASE
|
if ((c == 'e' || c == 'E') && !exp_start && base == ANY_BASE
|
||||||
&& had_number) {
|
&& had_number) {
|
||||||
exp_start = stream->off;
|
exp_start = ss.nchars;
|
||||||
} else {
|
} else {
|
||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
}
|
}
|
||||||
@@ -372,41 +443,41 @@ LispVal *next_number_or_symbol(ReadStream *stream, int base) {
|
|||||||
if (!had_number) {
|
if (!had_number) {
|
||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
}
|
}
|
||||||
size_t len = stream->off - number_start;
|
size_t len = ss.nchars - number_start;
|
||||||
if (has_decimal || exp_start) {
|
if (has_decimal || exp_start) {
|
||||||
// float
|
// float
|
||||||
// ceil(# bytes in size_t / 3)
|
// ceil(# bytes in size_t / 3)
|
||||||
// This works because log10(2^n) is O(n) for k=3
|
// This works because log10(2^n) is O(n) for k=3
|
||||||
size_t size_t_len = ((sizeof(size_t) * 8) / 3) + 1;
|
size_t size_t_len = ((sizeof(size_t) * 8) / 3) + 1;
|
||||||
char fmt_buf[3 + size_t_len + 1];
|
char fmt_buf[3 + size_t_len + 1];
|
||||||
if (exp_start == stream->off) {
|
if (exp_start == ss.nchars) {
|
||||||
goto change_to_symbol;
|
goto change_to_symbol;
|
||||||
}
|
}
|
||||||
snprintf(fmt_buf, sizeof(fmt_buf), "%%%zu" LISP_FLOAT_SCANF, len);
|
snprintf(fmt_buf, sizeof(fmt_buf), "%%%zu" LISP_FLOAT_SCANF, len);
|
||||||
lisp_float_t value;
|
lisp_float_t value;
|
||||||
sscanf(&stream->buffer[number_start], fmt_buf, &value);
|
sscanf(&ss.buffer[number_start], fmt_buf, &value);
|
||||||
return MAKE_LISP_FLOAT(value);
|
return UNWIND_AND_RETURN(stack_ref, MAKE_LISP_FLOAT(value));
|
||||||
} else {
|
} else {
|
||||||
// integer
|
// integer
|
||||||
// TODO handle large numbers
|
// TODO handle large numbers
|
||||||
size_t fixnum_len = ((sizeof(fixnum_t) * 8) / 3) + 1;
|
size_t fixnum_len = ((sizeof(fixnum_t) * 8) / 3) + 1;
|
||||||
char read_buf[fixnum_len + 1];
|
char read_buf[fixnum_len + 1];
|
||||||
if (len > fixnum_len) {
|
if (len > fixnum_len) {
|
||||||
read_error(stream, 0, "numeric literal too large");
|
read_error(stream, "numeric literal too large");
|
||||||
}
|
}
|
||||||
memcpy(read_buf, &stream->buffer[number_start], len);
|
memcpy(read_buf, &ss.buffer[number_start], len);
|
||||||
read_buf[len] = '\0';
|
read_buf[len] = '\0';
|
||||||
intmax_t value =
|
intmax_t value =
|
||||||
strtoimax(read_buf, NULL, base == ANY_BASE ? 10 : base);
|
strtoimax(read_buf, NULL, base == ANY_BASE ? 10 : base);
|
||||||
if (value < MOST_NEGATIVE_FIXNUM || value > MOST_POSITIVE_FIXNUM) {
|
if (value < MOST_NEGATIVE_FIXNUM || value > MOST_POSITIVE_FIXNUM) {
|
||||||
read_error(stream, 0, "numeric literal too large");
|
read_error(stream, "numeric literal too large");
|
||||||
}
|
}
|
||||||
return MAKE_FIXNUM(value);
|
return UNWIND_AND_RETURN(stack_ref, MAKE_FIXNUM(value));
|
||||||
}
|
}
|
||||||
abort();
|
abort();
|
||||||
change_to_symbol:
|
change_to_symbol:
|
||||||
*stream = save;
|
return UNWIND_AND_RETURN(stack_ref,
|
||||||
return next_symbol(stream);
|
next_symbol(ss.buffer, ss.nchars, stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
LispVal *read(ReadStream *stream) {
|
LispVal *read(ReadStream *stream) {
|
||||||
@@ -418,12 +489,12 @@ LispVal *read(ReadStream *stream) {
|
|||||||
switch (next) {
|
switch (next) {
|
||||||
case ')':
|
case ')':
|
||||||
case ']':
|
case ']':
|
||||||
read_error(stream, 1, "extra closing character");
|
read_error(stream, "extra closing character");
|
||||||
case '\'': {
|
case '\'': {
|
||||||
pop_char(stream);
|
pop_char(stream);
|
||||||
skip_whitespace(stream);
|
skip_whitespace(stream);
|
||||||
if (EOSP(stream)) {
|
if (EOSP(stream)) {
|
||||||
read_error(stream, 0, "quote not quoting anything");
|
read_error(stream, "quote not quoting anything");
|
||||||
}
|
}
|
||||||
return LIST(Qquote, read(stream));
|
return LIST(Qquote, read(stream));
|
||||||
}
|
}
|
||||||
@@ -432,7 +503,7 @@ LispVal *read(ReadStream *stream) {
|
|||||||
pop_char(stream);
|
pop_char(stream);
|
||||||
skip_whitespace(stream);
|
skip_whitespace(stream);
|
||||||
if (EOSP(stream)) {
|
if (EOSP(stream)) {
|
||||||
read_error(stream, 0, "backquote not quoting anything");
|
read_error(stream, "backquote not quoting anything");
|
||||||
}
|
}
|
||||||
LispVal *to_return = LIST(Qbackquote, read(stream));
|
LispVal *to_return = LIST(Qbackquote, read(stream));
|
||||||
--stream->backquote_level;
|
--stream->backquote_level;
|
||||||
@@ -440,7 +511,7 @@ LispVal *read(ReadStream *stream) {
|
|||||||
}
|
}
|
||||||
case ',': {
|
case ',': {
|
||||||
if (!stream->backquote_level) {
|
if (!stream->backquote_level) {
|
||||||
read_error(stream, 0, "comma outside of a backquote");
|
read_error(stream, "comma outside of a backquote");
|
||||||
}
|
}
|
||||||
pop_char(stream);
|
pop_char(stream);
|
||||||
LispVal *car;
|
LispVal *car;
|
||||||
@@ -451,7 +522,7 @@ LispVal *read(ReadStream *stream) {
|
|||||||
}
|
}
|
||||||
skip_whitespace(stream);
|
skip_whitespace(stream);
|
||||||
if (EOSP(stream)) {
|
if (EOSP(stream)) {
|
||||||
read_error(stream, 0, "comma not splicing anything");
|
read_error(stream, "comma not splicing anything");
|
||||||
}
|
}
|
||||||
return LIST(car, read(stream));
|
return LIST(car, read(stream));
|
||||||
} break;
|
} break;
|
||||||
@@ -478,7 +549,7 @@ LispVal *read(ReadStream *stream) {
|
|||||||
case '9':
|
case '9':
|
||||||
return next_number_or_symbol(stream, ANY_BASE);
|
return next_number_or_symbol(stream, ANY_BASE);
|
||||||
default:
|
default:
|
||||||
return next_symbol(stream);
|
return next_symbol(NULL, 0, stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-4
@@ -17,17 +17,33 @@ static ALWAYS_INLINE bool SYMBOL_END_P(int c) {
|
|||||||
|| c == '`' || c == ';';
|
|| c == '`' || c == ';';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ReadStreamImpl {
|
||||||
|
READ_STREAM_STRING,
|
||||||
|
READ_STREAM_FUNC,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *buffer;
|
enum ReadStreamImpl kind;
|
||||||
size_t len;
|
union {
|
||||||
size_t off;
|
struct {
|
||||||
|
const char *buffer;
|
||||||
|
size_t len;
|
||||||
|
size_t off;
|
||||||
|
} string;
|
||||||
|
struct {
|
||||||
|
LispVal *func;
|
||||||
|
bool is_eof;
|
||||||
|
} func;
|
||||||
|
};
|
||||||
|
|
||||||
size_t line;
|
size_t line;
|
||||||
size_t col;
|
size_t col;
|
||||||
size_t backquote_level;
|
size_t backquote_level;
|
||||||
} ReadStream;
|
} ReadStream;
|
||||||
|
|
||||||
void read_stream_init(ReadStream *stream, const char *buffer, size_t length);
|
void read_stream_init_from_string(ReadStream *stream, const char *buffer,
|
||||||
|
size_t length);
|
||||||
|
void read_stream_init_from_func(ReadStream *stream, LispVal *read_char_func);
|
||||||
|
|
||||||
// NULL on eof
|
// NULL on eof
|
||||||
LispVal *read(ReadStream *stream);
|
LispVal *read(ReadStream *stream);
|
||||||
|
|||||||
Reference in New Issue
Block a user