/* reader.c: Stream reader implementation * Created: 2026-02-04 * Author: Aryadev Chavali * License: See end of file * Commentary: */ #include #include #include #include const char *read_err_to_cstr(read_err_t err) { switch (err) { case READ_ERR_OK: return "OK"; case READ_ERR_EOF: return "EOF"; case READ_ERR_UNKNOWN_CHAR: return "UNKNOWN_CHAR"; default: FAIL("Unreachable"); } } // Accepted characters for symbols. static const char *SYMBOL_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!$%&*+,-./" ":<=>?@\\^_`{|}~0123456789"; // Little predicate using SYMBOL_CHARS bool is_sym(char c) { return strchr(SYMBOL_CHARS, c) != NULL; } void skip_comments_and_whitespace(stream_t *stream) { for (char c = stream_peek(stream); c != '\0' && (isspace(c) || c == ';'); c = stream_peek(stream)) { stream_while(stream, " \t\n\0"); if (stream_peek(stream) == ';') { // Skip till newline stream_till(stream, "\n"); } } } read_err_t read_sym(sys_t *sys, stream_t *stream, lisp_t **ret) { sv_t sym_sv = stream_while(stream, SYMBOL_CHARS); *ret = intern(sys, sym_sv); return READ_ERR_OK; } read_err_t read_int(sys_t *sys, stream_t *stream, lisp_t **ret) { sv_t digits_sv = stream_while(stream, "0123456789"); if (is_sym(stream_peek(stream))) { // This is actually a symbol stream_seek_backward(stream, digits_sv.size); return read_sym(sys, stream, ret); } if (digits_sv.size > 19) { TODO("alisp doesn't support big integers (bigger than 63 bits) yet"); } i64 n = 0; for (u64 i = 0; i < digits_sv.size; ++i) { char c = digits_sv.data[i]; u8 digit = c - '0'; // NOTE: 10i + digit > INT_MAX // => 10i > INT_MAX - digit // => i > (INT_MAX - digit) / 10 if (n > (INT_MAX - digit) / 10) { TODO("alisp doesn't support big integers (bigger than 63 bits) yet"); } n *= 10; n += digit; } *ret = make_int(n); return READ_ERR_OK; } read_err_t read_negative(sys_t *sys, stream_t *stream, lisp_t **ret) { char c = stream_next(stream); if (isdigit(c)) { read_err_t err = read_int(sys, stream, ret); if (err) return err; i64 n = as_int(*ret); n *= -1; *ret = make_int(n); return READ_ERR_OK; } else if (is_sym(c) || isspace(c)) { stream_seek_backward(stream, 1); return read_sym(sys, stream, ret); } else return READ_ERR_UNKNOWN_CHAR; } read_err_t read_list(sys_t *sys, stream_t *stream, lisp_t **ret) { // skip past the open parentheses '(' (void)stream_next(stream); lisp_t *top = NIL; lisp_t *cur = NIL; while (stream_peek(stream) != ')') { lisp_t *item = NIL; read_err_t err = read(sys, stream, &item); if (err) { return err; } else if (!top) { top = cons(sys, item, NIL); cur = top; } else { as_cons(cur)->cdr = cons(sys, item, NIL); cur = cdr(cur); } } if (stream_peek(stream) != ')') return READ_ERR_EXPECTED_CLOSED_BRACE; stream_next(stream); *ret = top; return READ_ERR_OK; } read_err_t read_vec(sys_t *, stream_t *, lisp_t **) { TODO("read_vec: not implemented"); } read_err_t read_quote(sys_t *sys, stream_t *stream, lisp_t **ret) { lisp_t *to_quote = NIL; stream_next(stream); read_err_t err = read(sys, stream, &to_quote); if (err) return err; *ret = cons(sys, to_quote, NIL); *ret = cons(sys, intern(sys, SV_AUTO("quote")), *ret); return READ_ERR_OK; } read_err_t read_all(sys_t *sys, stream_t *stream, vec_t *out) { while (!stream_eoc(stream)) { lisp_t *item = NIL; read_err_t err = read(sys, stream, &item); if (err) return err; else vec_append(out, &item, sizeof(item)); skip_comments_and_whitespace(stream); } return READ_ERR_OK; } read_err_t read(sys_t *sys, stream_t *stream, lisp_t **ret) { skip_comments_and_whitespace(stream); if (stream_eoc(stream)) return READ_ERR_EOF; char c = stream_peek(stream); if (isdigit(c)) return read_int(sys, stream, ret); else if (c == '-') return read_negative(sys, stream, ret); else if (is_sym(c)) return read_sym(sys, stream, ret); else if (c == '\'') return read_quote(sys, stream, ret); else if (c == '(') return read_list(sys, stream, ret); else if (c == '[') return read_vec(sys, stream, ret); return READ_ERR_UNKNOWN_CHAR; } /* 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 . */