Improve read.c

This commit is contained in:
2026-09-07 08:33:32 -07:00
parent 9dd2ad2413
commit 3dfac40e7e
11 changed files with 288 additions and 119 deletions
+1 -1
View File
@@ -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
View File
@@ -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");
+70
View File
@@ -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);
+27
View File
@@ -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
View File
@@ -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;
+5
View File
@@ -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;
+5
View File
@@ -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
View File
@@ -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;
-4
View File
@@ -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
View File
@@ -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
View File
@@ -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);