aboutsummaryrefslogtreecommitdiff
path: root/src_c/read.c
diff options
context:
space:
mode:
Diffstat (limited to 'src_c/read.c')
-rw-r--r--src_c/read.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/src_c/read.c b/src_c/read.c
new file mode 100644
index 0000000..82250f5
--- /dev/null
+++ b/src_c/read.c
@@ -0,0 +1,428 @@
+#include "read.h"
+#include "buff.h"
+
+
+static inline lsp_bool_t is_ws(lsp_uint8_t v) {
+ return (v == ' ') || (v == '\n') || (v == '\r') || (v == '\t');
+}
+
+
+static inline lsp_bool_t is_comment_start(lsp_uint8_t v) { return v == ';'; }
+
+
+static inline lsp_bool_t is_comment_stop(lsp_uint8_t v) { return v == '\n'; }
+
+
+static inline lsp_bool_t is_digit(lsp_uint8_t v) {
+ return (v >= '0') && (v <= '9');
+}
+
+
+static inline lsp_bool_t is_list_start(lsp_uint8_t v) {
+ return (v == '(') || (v == '[') || (v == '{');
+}
+
+
+static inline lsp_bool_t is_list_stop(lsp_uint8_t v) {
+ return (v == ')') || (v == ']') || (v == '}');
+}
+
+
+static inline lsp_bool_t is_str_start_stop(lsp_uint8_t v) { return v == '"'; }
+
+
+static inline lsp_bool_t is_quote(lsp_uint8_t v) { return v == '\''; }
+
+
+static inline lsp_bool_t is_quasiquote(lsp_uint8_t v) { return v == '`'; }
+
+
+static inline lsp_bool_t is_unquote(lsp_uint8_t v) { return v == ','; }
+
+
+static lsp_status_t skip_ws(lsp_in_stream_t *s) {
+ while (true) {
+ lsp_uint8_t v;
+ lsp_status_t status = lsp_in_stream_peek(s, &v);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ if (is_comment_start(v)) {
+ while (!is_comment_stop(v)) {
+ status = lsp_in_stream_read(s, &v);
+ if (status != LSP_SUCCESS)
+ return status;
+ }
+
+ continue;
+ }
+
+ if (!is_ws(v))
+ return LSP_SUCCESS;
+
+ lsp_in_stream_read(s, &v);
+ }
+}
+
+
+static lsp_status_t read_number(lsp_mem_t *m, lsp_in_stream_t *s,
+ lsp_addr_t *addr) {
+ lsp_int32_t v = 0;
+
+ while (true) {
+ lsp_uint8_t c;
+ lsp_status_t status = lsp_in_stream_peek(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ if (is_ws(c) || is_list_start(c) || is_list_stop(c))
+ break;
+
+ if (!is_digit(c))
+ return LSP_ERR_READ;
+
+ status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ v = (v * 10) + (c - '0');
+ }
+
+ if (v == 0) {
+ *addr = m->zero;
+ return LSP_SUCCESS;
+ }
+
+ if (v == 1) {
+ *addr = m->one;
+ return LSP_SUCCESS;
+ }
+
+ return lsp_mem_create_number(m, v, addr);
+}
+
+
+static lsp_status_t read_list(lsp_mem_t *m, lsp_in_stream_t *s,
+ lsp_addr_t *addr) {
+ lsp_uint8_t c;
+ lsp_status_t status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ lsp_uint8_t list_end;
+ if (c == '(') {
+ list_end = ')';
+ } else if (c == '[') {
+ list_end = ']';
+ } else if (c == '{') {
+ list_end = '}';
+ } else {
+ return LSP_ERR_READ;
+ }
+
+ lsp_bool_t read_rest = false;
+ lsp_bool_t read_stop = false;
+ lsp_addr_t rest = m->nil;
+ lsp_addr_t last = m->nil;
+ *addr = m->nil;
+
+ while (true) {
+ status = skip_ws(s);
+ if (status != LSP_SUCCESS)
+ break;
+
+ status = lsp_in_stream_peek(s, &c);
+ if (status != LSP_SUCCESS)
+ break;
+
+ if (is_list_stop(c)) {
+ status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ break;
+
+ if (c != list_end) {
+ status = LSP_ERR_READ;
+ break;
+ }
+
+ lsp_mem_set_pair_second(m, last, rest);
+ lsp_mem_dec_ref(m, rest);
+ return LSP_SUCCESS;
+ }
+
+ if (read_stop) {
+ status = LSP_ERR_READ;
+ break;
+ }
+
+ lsp_addr_t el;
+ status = lsp_read(m, s, &el);
+ if (status != LSP_SUCCESS)
+ break;
+
+ if (lsp_mem_is_symbol(m, el) && lsp_mem_get_symbol_len(m, el) == 1 &&
+ lsp_mem_get_symbol_name(m, el, 0) == '.') {
+ lsp_mem_dec_ref(m, el);
+
+ if (read_rest) {
+ status = LSP_ERR_READ;
+ break;
+ }
+
+ read_rest = true;
+ continue;
+ }
+
+ if (read_rest) {
+ rest = el;
+ read_stop = true;
+ continue;
+ }
+
+ lsp_addr_t new_last;
+ status = lsp_mem_create_pair(m, el, m->nil, &new_last);
+ lsp_mem_dec_ref(m, el);
+ if (status != LSP_SUCCESS)
+ break;
+
+ lsp_mem_set_pair_second(m, last, new_last);
+ last = new_last;
+
+ if (*addr == m->nil) {
+ *addr = last;
+ } else {
+ lsp_mem_dec_ref(m, last);
+ }
+ }
+
+ lsp_mem_dec_ref(m, *addr);
+ lsp_mem_dec_ref(m, rest);
+ return status;
+}
+
+
+static lsp_status_t read_string(lsp_mem_t *m, lsp_in_stream_t *s,
+ lsp_addr_t *addr) {
+ lsp_uint8_t c;
+ lsp_status_t status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ if (!is_str_start_stop(c))
+ return LSP_ERR_READ;
+
+ lsp_buff_t buff;
+ lsp_buff_init(&buff, m);
+
+ lsp_bool_t read_escaped = false;
+
+ while (true) {
+ status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ goto cleanup;
+
+ if (read_escaped) {
+ if (c == 'n') {
+ status = lsp_buff_push(&buff, '\n');
+ } else if (c == 'r') {
+ status = lsp_buff_push(&buff, '\r');
+ } else if (c == 't') {
+ status = lsp_buff_push(&buff, '\t');
+ } else if (c == '\\') {
+ status = lsp_buff_push(&buff, '\\');
+ } else if (c == '"') {
+ status = lsp_buff_push(&buff, '"');
+ } else {
+ status = LSP_ERR_READ;
+ }
+
+ if (status != LSP_SUCCESS)
+ goto cleanup;
+
+ read_escaped = false;
+ continue;
+ }
+
+ if (c == '\\') {
+ read_escaped = true;
+ continue;
+ }
+
+ if (is_str_start_stop(c))
+ break;
+
+ status = lsp_buff_push(&buff, c);
+ if (status != LSP_SUCCESS)
+ goto cleanup;
+ }
+
+ status = lsp_buff_pop(&buff, addr);
+
+cleanup:
+ lsp_buff_clear(&buff);
+ return status;
+}
+
+
+static lsp_status_t read_quote(lsp_mem_t *m, lsp_in_stream_t *s,
+ lsp_addr_t *addr) {
+ lsp_uint8_t c;
+ lsp_status_t status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ if (!is_quote(c))
+ return LSP_ERR_READ;
+
+ lsp_addr_t value;
+ status = lsp_read(m, s, &value);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ lsp_addr_t list;
+ status = lsp_mem_create_pair(m, value, m->nil, &list);
+ lsp_mem_dec_ref(m, value);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ status = lsp_mem_create_pair(m, m->quote, list, addr);
+ lsp_mem_dec_ref(m, list);
+ return status;
+}
+
+
+static lsp_status_t read_quasiquote(lsp_mem_t *m, lsp_in_stream_t *s,
+ lsp_addr_t *addr) {
+ lsp_uint8_t c;
+ lsp_status_t status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ if (!is_quasiquote(c))
+ return LSP_ERR_READ;
+
+ lsp_addr_t value;
+ status = lsp_read(m, s, &value);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ lsp_addr_t list;
+ status = lsp_mem_create_pair(m, value, m->nil, &list);
+ lsp_mem_dec_ref(m, value);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ status = lsp_mem_create_pair(m, m->quasiquote, list, addr);
+ lsp_mem_dec_ref(m, list);
+ return status;
+}
+
+
+static lsp_status_t read_unquote(lsp_mem_t *m, lsp_in_stream_t *s,
+ lsp_addr_t *addr) {
+ lsp_uint8_t c;
+ lsp_status_t status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ if (!is_unquote(c))
+ return LSP_ERR_READ;
+
+ status = lsp_in_stream_peek(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ lsp_bool_t is_splicing = (c == '@');
+ if (is_splicing) {
+ status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+ }
+
+ lsp_addr_t value;
+ status = lsp_read(m, s, &value);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ lsp_addr_t list;
+ status = lsp_mem_create_pair(m, value, m->nil, &list);
+ lsp_mem_dec_ref(m, value);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ status = lsp_mem_create_pair(
+ m, (is_splicing ? m->unquote_splicing : m->unquote), list, addr);
+ lsp_mem_dec_ref(m, list);
+ return status;
+}
+
+
+static lsp_status_t read_symbol(lsp_mem_t *m, lsp_in_stream_t *s,
+ lsp_addr_t *addr) {
+ lsp_status_t status;
+
+ lsp_buff_t buff;
+ lsp_buff_init(&buff, m);
+
+ while (true) {
+ lsp_uint8_t c;
+ status = lsp_in_stream_peek(s, &c);
+ if (status != LSP_SUCCESS)
+ goto cleanup;
+
+ if (is_ws(c) || is_list_start(c) || is_list_stop(c))
+ break;
+
+ status = lsp_in_stream_read(s, &c);
+ if (status != LSP_SUCCESS)
+ goto cleanup;
+
+ status = lsp_buff_push(&buff, c);
+ if (status != LSP_SUCCESS)
+ goto cleanup;
+ }
+
+ lsp_addr_t str;
+ status = lsp_buff_pop(&buff, &str);
+ if (status != LSP_SUCCESS)
+ goto cleanup;
+
+ status = lsp_mem_create_symbol_from_string(m, str, addr);
+ lsp_mem_dec_ref(m, str);
+
+cleanup:
+ lsp_buff_clear(&buff);
+ return status;
+}
+
+
+lsp_status_t lsp_read(lsp_mem_t *m, lsp_in_stream_t *s, lsp_addr_t *addr) {
+ lsp_status_t status = skip_ws(s);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ lsp_uint8_t c;
+ status = lsp_in_stream_peek(s, &c);
+ if (status != LSP_SUCCESS)
+ return status;
+
+ if (is_list_start(c))
+ return read_list(m, s, addr);
+
+ if (is_str_start_stop(c))
+ return read_string(m, s, addr);
+
+ if (is_digit(c))
+ return read_number(m, s, addr);
+
+ if (is_quote(c))
+ return read_quote(m, s, addr);
+
+ if (is_quasiquote(c))
+ return read_quasiquote(m, s, addr);
+
+ if (is_unquote(c))
+ return read_unquote(m, s, addr);
+
+ return read_symbol(m, s, addr);
+}