#include #include #include #include #include #include "gc.h" #include "builtin.h" typedef struct reader_state { FILE *file; char ch; gc_root_t ref_alist; gc_root_t weak_list; gc_root_t ref_list; } reader_state_t; static value_t read_one_value(reader_state_t *state); static value_t read_special(reader_state_t *state); static value_t read_list(reader_state_t *state); static value_t read_number(reader_state_t *state); static value_t read_string(reader_state_t *state); static value_t read_box(reader_state_t *state); static value_t read_vector(reader_state_t *state); static value_t read_struct(reader_state_t *state); static value_t read_weak_box(reader_state_t *state); static value_t read_definition(reader_state_t *state); static value_t read_backref(reader_state_t *state); static void next_char(reader_state_t *state); static value_t make_placeholder(reader_state_t *state, fixnum_t ref); static value_t get_placeholder(reader_state_t *state, fixnum_t ref); static value_t patch_placeholders(reader_state_t *state, value_t v); static void set_placeholder(value_t place, value_t value); static void tree_replace(value_t *in, value_t oldval, value_t newval); value_t read_value(FILE *f) { reader_state_t state; value_t result; register_gc_root(&state.ref_alist, NIL); register_gc_root(&state.weak_list, NIL); register_gc_root(&state.ref_list, NIL); state.file = f; next_char(&state); result = read_one_value(&state); ungetc(state.ch, f); result = patch_placeholders(&state, result); unregister_gc_root(&state.ref_list); unregister_gc_root(&state.weak_list); unregister_gc_root(&state.ref_alist); return result; } static value_t read_one_value(reader_state_t *state) { while (isspace(state->ch)) next_char(state); release_assert(state->ch != EOF); switch (state->ch) { case '#': return read_special(state); case '(': return read_list(state); case '0' ... '9': return read_number(state); case '\"': return read_string(state); default: release_assert(NOTREACHED("Unexpected character in input.")); return UNDEFINED; } } bool issymbol(int ch) { switch (ch) { case 'A' ... 'Z': case 'a' ... 'z': case '0' ... '9': case '!': case '$': case '%': case '&': case '*': case '+': case '-': case '/': case '<': case '=': case '>': case '?': case '@': case '\\': case '^': case '_': case '|': case '~': return true; default: return false; } } static value_t read_special(reader_state_t *state) { next_char(state); release_assert(state->ch != EOF); switch (state->ch) { case 'F': case 'f': next_char(state); release_assert(!issymbol(state->ch)); return FALSE_VALUE; case 'T': case 't': next_char(state); release_assert(!issymbol(state->ch)); return TRUE_VALUE; case '&': return read_box(state); case '(': return read_vector(state); case 'S': case 's': next_char(state); release_assert(state->ch == '('); return read_struct(state); case 'W': case 'w': next_char(state); release_assert(state->ch == '&'); return read_weak_box(state); case '0' ... '9': return read_definition(state); case '=': return read_backref(state); default: release_assert(NOTREACHED("Invalid character in special value.")); return UNDEFINED; } } static void reverse_list(value_t *list, value_t newcdr) { value_t lst = *list; while (is_pair(lst)) { value_t temp = _get_pair(lst)->cdr; _get_pair(lst)->cdr = newcdr; newcdr = lst; lst = temp; } *list = newcdr; } static value_t read_list(reader_state_t *state) { gc_root_t list_root; bool done = false; register_gc_root(&list_root, NIL); next_char(state); while (!done) { while (isspace(state->ch)) next_char(state); release_assert(state->ch != EOF); switch (state->ch) { case '.': { release_assert(!is_nil(list_root.value)); next_char(state); value_t temp = read_one_value(state); reverse_list(&list_root.value, temp); while (isspace(state->ch)) next_char(state); release_assert(state->ch == ')'); next_char(state); done = true; } break; case ')': { reverse_list(&list_root.value, NIL); next_char(state); done = true; } break; default: { value_t temp; list_root.value = cons(UNDEFINED, list_root.value); temp = read_one_value(state); _CAR(list_root.value) = temp; } break; } } unregister_gc_root(&list_root); return list_root.value; } static value_t read_number(reader_state_t *state) { fixnum_t num = 0; while (isdigit(state->ch)) { release_assert(num <= (FIXNUM_MAX/10)); num *= 10; num += (state->ch - '0'); next_char(state); } return fixnum_value(num); } static value_t read_string(reader_state_t *state) { char *buffer = (char*)malloc(128); size_t buffer_size = 128; size_t length = 0; value_t value; release_assert(buffer != NULL); next_char(state); while (state->ch != '"') { bool skip_whitespace = false; char ch; if ((buffer_size - length) < 2) { release_assert(buffer_size <= INT32_MAX / 3); buffer_size = (3 * buffer_size) / 2; buffer = realloc(buffer, buffer_size); release_assert(buffer != NULL); } ch = state->ch; next_char(state); if (ch == '\\') { switch (state->ch) { case 'o': next_char(state); release_assert(('0' <= state->ch) && (state->ch <= '7')); /* fall through */ case '0' ... '7': ch = 0; /* One to three octal digits */ for (int i = 0; i < 3; ++i) { ch = 8 * ch + (state->ch - '0'); next_char(state); if ((state->ch < '0') || (state->ch > '7')) break; } break; case 'X': case 'x': ch = 0; next_char(state); release_assert(isxdigit(state->ch)); /* One or two hex digits */ for (int i = 0; i < 2; ++i) { int n = isdigit(state->ch) ? (state->ch - '0') : (10 + toupper(state->ch) - 'A'); ch = 16 * ch + n; next_char(state); if (!isxdigit(state->ch)) break; } break; case ' ': case '\t': case '\v': case '\n': skip_whitespace = true; break; case '\\': ch = '\\'; next_char(state); break; case '\'': ch = '\''; next_char(state); break; case '\"': ch = '\"'; next_char(state); break; case 'a': ch = '\a'; next_char(state); break; case 'b': ch = '\b'; next_char(state); break; case 'f': ch = '\f'; next_char(state); break; case 'n': ch = '\n'; next_char(state); break; case 'r': ch = '\r'; next_char(state); break; case 't': ch = '\t'; next_char(state); break; case 'v': ch = '\v'; next_char(state); break; default: release_assert(NOTREACHED("Invalid escape sequence in string.")); ch = '#'; next_char(state); break; } } if (skip_whitespace) { do { next_char(state); } while (isspace(state->ch) && (state->ch != '\n')); } else { buffer[length++] = ch; } } next_char(state); buffer[length] = '\0'; value = make_byte_string(length, '\0'); memcpy(_get_byte_string(value)->bytes, buffer, length); free(buffer); return value; } static value_t read_box(reader_state_t *state) { gc_root_t root; value_t v; next_char(state); register_gc_root(&root, make_box(UNDEFINED)); v = read_one_value(state); _get_box(root.value)->value = v; unregister_gc_root(&root); return root.value; } static value_t read_vector(reader_state_t *state) { gc_root_t list_root; gc_root_t vec_root; size_t length = 0; value_t v; register_gc_root(&list_root, read_list(state)); for (value_t v = list_root.value; !is_nil(v); v = CDR(v)) ++length; register_gc_root(&vec_root, make_vector(length, UNDEFINED)); v = list_root.value; for (size_t i = 0; i < length; ++i, v = _CDR(v)) _get_vector(vec_root.value)->elements[i] = _CAR(v); unregister_gc_root(&list_root); unregister_gc_root(&vec_root); return vec_root.value; } static value_t read_struct(reader_state_t *state) { gc_root_t list_root; gc_root_t struct_root; size_t slots = 0; value_t v; register_gc_root(&list_root, read_list(state)); for (value_t v = CDR(list_root.value); !is_nil(v); v = CDR(v)) ++slots; register_gc_root(&struct_root, make_struct(_CAR(list_root.value), slots)); v = _CDR(list_root.value); for (size_t i = 0; i < slots; ++i, v = _CDR(v)) _get_struct(struct_root.value)->slots[i] = _CAR(v); unregister_gc_root(&list_root); unregister_gc_root(&struct_root); return struct_root.value; } static value_t read_weak_box(reader_state_t *state) { gc_root_t box_root; gc_root_t value_root; next_char(state); register_gc_root(&box_root, make_weak_box(UNDEFINED)); register_gc_root(&value_root, read_one_value(state)); _get_weak_box(box_root.value)->value = value_root.value; state->weak_list.value = cons(value_root.value, state->weak_list.value); unregister_gc_root(&box_root); unregister_gc_root(&value_root); return box_root.value; } static value_t read_definition(reader_state_t *state) { fixnum_t ref = get_fixnum(read_number(state)); gc_root_t place_root; value_t v; release_assert(state->ch == '='); next_char(state); register_gc_root(&place_root, make_placeholder(state, ref)); v = read_one_value(state); set_placeholder(place_root.value, v); unregister_gc_root(&place_root); return v; } static value_t read_backref(reader_state_t *state) { next_char(state); release_assert(state->ch != EOF); if (state->ch == '"') { char *name = value_to_string(read_string(state)); value_t bi = lookup_builtin(name); free(name); return bi; } else { return get_placeholder(state, get_fixnum(read_number(state))); } } static value_t make_placeholder(reader_state_t *state, fixnum_t ref) { state->ref_alist.value = cons(UNDEFINED, state->ref_alist.value); state->ref_alist.value = cons(fixnum_value(ref), state->ref_alist.value); return state->ref_alist.value; } static value_t get_placeholder(reader_state_t *state, fixnum_t ref) { value_t refval = fixnum_value(ref); value_t item = state->ref_alist.value; while (!is_nil(item)) { if (_CAR(item) == refval) return item; else item = _CDDR(item); } release_assert(NOTREACHED("Back-reference without definition!")); return UNDEFINED; } static value_t patch_placeholders(reader_state_t *state, value_t v) { value_t item = state->ref_alist.value; while (!is_nil(item)) { tree_replace(&v, item, _CADR(item)); item = _CDDR(item); } return v; } static void set_placeholder(value_t place, value_t value) { CAR(CDR(place)) = value; } static void tree_replace(value_t *in, value_t oldval, value_t newval) { if (*in == oldval) { *in = newval; } else if (is_box(*in)) { tree_replace(&_get_box(*in)->value, oldval, newval); } else if (is_pair(*in)) { tree_replace(&_get_pair(*in)->car, oldval, newval); tree_replace(&_get_pair(*in)->cdr, oldval, newval); } else if (is_vector(*in)) { for (size_t i = 0; i < _get_vector(*in)->size; ++i) { tree_replace(&_get_vector(*in)->elements[i], oldval, newval); } } else if (is_struct(*in)) { tree_replace(&_get_struct(*in)->type, oldval, newval); for (size_t i = 0; i < _get_struct(*in)->nslots; ++i) tree_replace(&_get_struct(*in)->slots[i], oldval, newval); } else if (is_weak_box(*in)) { tree_replace(&_get_weak_box(*in)->value, oldval, newval); } } static void next_char(reader_state_t *state) { state->ch = fgetc(state->file); } /* vim:set sw=2 expandtab: */