| #include "lisp.h" |
| #include "plat/plat.h" |
| |
| #include <ctype.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #define MIN(a, b) (a) > (b) ? (b) : (a) |
| |
| struct alloc_list *first_a = NULL, *last_a = NULL; |
| |
| value_t nil = 0b00101111; // magic ;) |
| |
| void err(const char *msg) |
| { |
| fprintf(stderr, "ERROR: %s\n", msg); |
| exit(1); |
| } |
| |
| value_t intval(int i) |
| { |
| i <<= 2; |
| i |= INT_TAG; |
| return i; |
| } |
| |
| value_t cons(value_t car, value_t cdr) |
| { |
| struct cons *c = malloc_aligned(sizeof(struct cons)); |
| |
| c->car = car; |
| c->cdr = cdr; |
| |
| struct alloc_list *item = malloc(sizeof(struct alloc_list)); |
| item->type = T_CONS; |
| item->cons_val = c; |
| |
| if (last_a) |
| { |
| item->prev = last_a; |
| last_a->next = item; |
| item->next = NULL; |
| } |
| else |
| { |
| item->prev = item->next = NULL; |
| first_a = last_a = item; |
| } |
| |
| value_t v = (value_t)c; |
| v |= CONS_TAG; |
| |
| return v; |
| } |
| |
| void skipws(struct istream *is) |
| { |
| while (isspace(is->peek(is))) |
| is->get(is); |
| } |
| |
| bool isallowedchar(char c) |
| { |
| return (c >= '#' && c <= '\'') || (c >= '*' && c <= '/') || |
| (c >= '>' && c <= '@'); |
| } |
| |
| bool issymstart(char c) |
| { |
| return isalpha(c) || isallowedchar(c); |
| } |
| |
| bool issym(char c) |
| { |
| return isalpha(c) || isallowedchar(c) || isdigit(c); |
| } |
| |
| bool readsym(struct istream *is, value_t *val) |
| { |
| skipws(is); |
| |
| if (!issymstart(is->peek(is))) |
| return false; |
| |
| int size = 8; |
| char *s = malloc_aligned(size); |
| |
| s[0] = is->get(is); |
| |
| for (int i = 1;; i++) |
| { |
| if (issym(is->peek(is))) |
| { |
| if (i >= size) |
| { |
| size *= 2; |
| s = realloc_aligned(s, size); |
| } |
| |
| s[i] = is->get(is); |
| } |
| else |
| { |
| s[i] = 0; |
| *val = (value_t)s; |
| *val |= SYMBOL_TAG; |
| |
| return true; |
| } |
| } |
| } |
| |
| bool readstr(struct istream *is, value_t *val) |
| { |
| skipws(is); |
| |
| if (is->peek(is) != '"') |
| return false; |
| |
| bool escape = false; |
| int size = 8; |
| char *s = malloc_aligned(size); |
| |
| (void)is->get(is); |
| |
| for (int i = 0;; i++) |
| { |
| if (is->peek(is) != '"') |
| { |
| if (i >= size) |
| { |
| size *= 2; |
| s = realloc_aligned(s, size); |
| } |
| |
| char c = is->get(is); |
| |
| if (escape && c == 'n') |
| c = '\n'; |
| else if (escape && c == '\\') |
| c = '\\'; |
| |
| if (c == '\\' && !escape) |
| { |
| escape = true; |
| i--; // will be incremented again, UGLY. |
| } |
| else |
| { |
| escape = false; |
| s[i] = c; |
| } |
| } |
| else |
| { |
| is->get(is); |
| |
| *val = (value_t)s; |
| *val |= STRING_TAG; |
| |
| return true; |
| } |
| } |
| } |
| |
| void printval(value_t v, int depth) |
| { |
| for (int i = 0; i < depth; i++) |
| printf(" "); |
| |
| if (symbolp(v)) |
| { |
| printf("'%s\n", (char *)(v ^ SYMBOL_TAG)); |
| } |
| else if (stringp(v)) |
| { |
| printf("\"%s\"\n", (char *)(v ^ STRING_TAG)); |
| } |
| else if (integerp(v)) |
| { |
| printf("%d\n", v >> 2); |
| } |
| else if (consp(v)) |
| { |
| if (listp(v)) |
| { |
| printf("list:\n"); |
| |
| for (value_t n = v; !nilp(n); n = cdr(n)) |
| { |
| printval(car(n), depth + 1); |
| } |
| } |
| else |
| { |
| printf("cons:\n"); |
| printval(car(v), depth + 1); |
| printval(cdr(v), depth + 1); |
| } |
| } |
| else if (nilp(v)) |
| { |
| printf("nil\n"); |
| } |
| else |
| { |
| printf("<unknown %d>\n", v); |
| } |
| } |
| |
| bool readlist(struct istream *is, value_t *val) |
| { |
| skipws(is); |
| |
| if (is->peek(is) != '(') |
| return false; |
| |
| is->get(is); |
| |
| *val = readn(is); |
| |
| if (is->peek(is) != ')') |
| { |
| is->showpos(is, stderr); |
| err("Unterminated list"); |
| return false; |
| } |
| is->get(is); |
| |
| return true; |
| } |
| |
| bool readint(struct istream *is, value_t *val) |
| { |
| int number = 0; |
| |
| if (!isdigit(is->peek(is))) |
| return false; |
| |
| while (isdigit(is->peek(is))) |
| { |
| number *= 10; |
| number += is->get(is) - '0'; |
| } |
| |
| *val = intval(number); |
| return true; |
| } |
| |
| bool read1(struct istream *is, value_t *val) |
| { |
| if (readsym(is, val)) |
| return true; |
| |
| if (readstr(is, val)) |
| return true; |
| |
| if (readint(is, val)) |
| return true; |
| |
| if (readlist(is, val)) |
| return true; |
| |
| return false; |
| } |
| |
| value_t readn(struct istream *is) |
| { |
| value_t first = nil; |
| value_t *last = &first; |
| |
| value_t read_val; |
| |
| while (read1(is, &read_val)) |
| { |
| *last = cons(read_val, nil); |
| last = cdrref(*last); |
| } |
| |
| return first; |
| } |
| |
| struct stristream_private |
| { |
| char *val; |
| int i; |
| int length; |
| int line; |
| int fromleft; |
| int linestart; |
| }; |
| |
| int stristream_peek(struct istream *is) |
| { |
| struct stristream_private *p = is->data; |
| |
| if (p->i < p->length) |
| return p->val[p->i]; |
| else |
| return -1; |
| } |
| |
| int stristream_get(struct istream *is) |
| { |
| struct stristream_private *p = is->data; |
| |
| if (p->i < p->length) |
| { |
| char c = p->val[p->i++]; |
| |
| p->fromleft++; |
| |
| if (c == '\n') |
| { |
| p->fromleft = 1; |
| p->line++; |
| p->linestart = p->i; |
| } |
| |
| return c; |
| } |
| else |
| return -1; |
| } |
| |
| int stristream_read(struct istream *s, char *buffer, int size) |
| { |
| struct stristream_private *p = s->data; |
| |
| int len = MIN(size, p->length - p->i); |
| memcpy(buffer, p->val, len); |
| return len; |
| } |
| |
| void stristream_showpos(struct istream *s, FILE *out) |
| { |
| struct stristream_private *p = s->data; |
| |
| fprintf(out, "line: %d, char %d\n", p->line, p->fromleft); |
| |
| int end = p->length; |
| |
| for (int i = p->linestart; i < p->length; i++) |
| { |
| if (p->val[i] == '\n') |
| { |
| end = i; |
| break; |
| } |
| } |
| |
| fprintf(out, " | %.*s\n", end - p->linestart, p->val + p->linestart); |
| fprintf(out, " | "); |
| for (int i = 0; i < p->fromleft - 1; i++) |
| fprintf(out, " "); |
| |
| fprintf(out, "\033[31m^\033[0m\n"); |
| } |
| |
| struct istream *new_stristream(char *str, int length) |
| { |
| struct istream *is = malloc(sizeof(struct istream)); |
| struct stristream_private *p = malloc(sizeof(struct stristream_private)); |
| |
| p->val = strndup(str, length); |
| p->i = 0; |
| p->length = length; |
| p->line = 1; |
| p->fromleft = 1; |
| p->linestart = 0; |
| |
| is->data = p; |
| is->get = stristream_get; |
| is->peek = stristream_peek; |
| is->read = stristream_read; |
| is->showpos = stristream_showpos; |
| |
| return is; |
| } |
| |
| void del_stristream(struct istream *stristream) |
| { |
| struct stristream_private *p = stristream->data; |
| free(p->val); |
| free(p); |
| free(stristream); |
| } |
| |
| struct istream *new_stristream_nt(char *str) |
| { |
| return new_stristream(str, strlen(str)); |
| } |
| |
| bool startswith(struct istream *s, char *pattern) |
| { |
| char *check = strdup(pattern); |
| s->read(s, check, strlen(pattern)); |
| |
| bool res = strcmp(check, pattern) == 0; |
| free(check); |
| |
| return res; |
| } |
| |
| value_t strval(char *str) |
| { |
| value_t v; |
| |
| char *a = malloc_aligned(strlen(str) + 1); |
| v = (value_t)a; |
| v |= STRING_TAG; |
| |
| return v; |
| } |
| |
| bool integerp(value_t v) |
| { |
| return (v & INT_MASK) == INT_TAG; |
| } |
| |
| bool symbolp(value_t v) |
| { |
| return (v & HEAP_MASK) == SYMBOL_TAG; |
| } |
| |
| bool stringp(value_t v) |
| { |
| return (v & HEAP_MASK) == STRING_TAG; |
| } |
| |
| bool consp(value_t v) |
| { |
| return (v & HEAP_MASK) == CONS_TAG; |
| } |
| |
| bool listp(value_t v) |
| { |
| value_t next = v; |
| |
| while (consp(next)) |
| { |
| next = cdr(next); |
| } |
| |
| return nilp(next); |
| } |
| |
| value_t car(value_t v) |
| { |
| if (!consp(v)) |
| return nil; |
| |
| return *carref(v); |
| } |
| |
| value_t cdr(value_t v) |
| { |
| if (!consp(v)) |
| return nil; |
| |
| return *cdrref(v); |
| } |
| |
| value_t *carref(value_t v) |
| { |
| if (!consp(v)) |
| return NULL; |
| |
| struct cons *c = (void *)(v ^ CONS_TAG); |
| return &c->car; |
| } |
| |
| value_t *cdrref(value_t v) |
| { |
| if (!consp(v)) |
| return NULL; |
| |
| struct cons *c = (void *)(v ^ CONS_TAG); |
| return &c->cdr; |
| } |
| |
| bool nilp(value_t v) |
| { |
| return v == nil; |
| } |
| |
| int length(value_t v) |
| { |
| int i = 0; |
| |
| for (; !nilp(v); v = cdr(v)) |
| i++; |
| |
| return i; |
| } |
| |
| value_t elt(value_t v, int index) |
| { |
| for (int i = 0; i < index; i++) |
| { |
| v = cdr(v); |
| } |
| |
| return car(v); |
| } |
| |
| bool symstreq(value_t sym, char *str) |
| { |
| if ((sym & HEAP_MASK) != SYMBOL_TAG) |
| return false; |
| |
| return strcmp((char *)(sym ^ SYMBOL_TAG), str) == 0; |
| } |