summaryrefslogtreecommitdiff
path: root/read.c
blob: 26dcfd5717aa72a0ff608d9d0222ea7e6ff33960 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include "val.h"
#include "mem.h"
#include "util.h"

#include <string.h>

typedef struct _reader {
	State *S;
	int len;
	char *c;
} Reader;


static void next(Reader *R) { 
	if(*R->c == '\0') 
		ERROR("end of string reached");
	R->c++; }
static bool is_ws(char x) {	return x == ' ' || x == '\t' || x == '\n';}
static bool is_num(char x) { return '0' <= x && x <= '9'; }
static bool is_special(char x) {
	switch(x) {
	case '(': case ')': case '[': case ']': case '"':
	return true; break;
	default: return false; break;
	}
}
static bool is_normal(char x) {
	return !(is_ws(x) || is_special(x));
}
static void skipws(Reader *R) {	while (is_ws(*R->c)) next(R); }

static Val symbol(Reader *R);
static Val string(Reader *R);
static Val list(Reader *R, char closer);

// en("quote", x) -> (quote x)   etc
// i guess in the good universe this is just cons or whatver
static Val en(Reader *R, char *sym, Val v) {
	ObjArr *a = objarr_new(R->S);
	objarr_append(R->S, a, VAL_OBJ(objstring_copy_cstr(R->S, sym)));
	objarr_append(R->S, a, v);
	return VAL_OBJ(a);
}

static Val expr(Reader *R) {
	skipws(R);
	switch (*R->c) {
	case '"':
		next(R); return string(R);
		break;
	case '(':
		next(R); return list(R, ')');
		break;
	case '[':
		next(R); 
		return en(R, "arrlit", list(R, ']'));
		break;
	case '\'': 
		next(R);
		return en(R, "quote", expr(R));
		break;
	default: return symbol(R); break;
	}
}
static Val symbol(Reader *R) {
	char *start = R->c;
	int len = 0;
	bool num = true;
	while (is_normal(*R->c)) { num &= is_num(*R->c); len += 1; next(R); }

	// i LOVE 0-terminated strings
	char *tmp = malloc(len+1);
	memcpy(tmp, start, len);
	tmp[len] = 0;

	if (num) {
		int x = atoi(tmp);
		free(tmp);
		return VAL_NUM((double)x);
	} else {
		Val v;
		if (0 == strcmp(tmp, "nil")) v = VAL_NIL;
		else if (0 == strcmp(tmp, "true")) v = VAL_TRUE;
		else if (0 == strcmp(tmp, "false")) v = VAL_FALSE;
		else v = VAL_OBJ(objstring_copy(R->S, start, len));
		free(tmp);
		return v;
	}
}
static Val string(Reader *R) {
	struct {
		size_t len;
		size_t cap;
		char *d;
	} str;
	str.len = 0;
	str.cap = 0;
	str.d = NULL;

	#define append(c) do { ENSURE_CAP(R->S, str, char, str.cap+1); str.d[str.len++] = c; } while (0)
	
	while (*R->c != '"') { 
		if (*R->c == '\\') {
			next(R);
			switch (*R->c) {
			case '\\': append('\\'); break;
			case 't': append('\t'); break;
			case 'n': append('\n'); break;
			case '"': append('"'); break;
			}
			next(R);
		} else {
			append(*R->c);
			next(R);
		}
	}
	append('\0');
	#undef append
	next(R);
	ObjString *s = objstring_take(R->S, str.d, str.len-1);
	return en(R, "quote", VAL_OBJ(s));
}
static Val list(Reader *R, char closer) {
	ObjArr *a = objarr_new(R->S);
	while (true) {
		skipws(R);
		if (*R->c == closer) {
			if (closer != '\0') next(R);
			return VAL_OBJ(a);
		}
		objarr_append(R->S, a, expr(R));
	}
}


// both need null terminated strings
Val read_expr(State *S, char *str) {
	Reader r = (Reader){.S=S, .c=str};
	return expr(&r);
}
ObjArr *read_exprs(State *S, char *str) {
	Reader r = (Reader){.S=S, .c=str};
	return AS_ARR(list(&r, '\0'));
}