-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreader.ts
More file actions
159 lines (142 loc) · 3.91 KB
/
reader.ts
File metadata and controls
159 lines (142 loc) · 3.91 KB
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import { Mal, Colon, Null, List, Vector, Map, String, Number, Symbol, True, False, Nil } from "./types"
export function read_str(str: string): Mal {
let tokens = tokenizer(str)
if (tokens.length > 0) {
let rdr = reader(tokens)
return read_form(rdr)
}
return new Null()
}
function tokenizer(str: string): string[] {
const regx = /[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)/g
let result: string[] = []
while (true) {
let match = regx.exec(str)
if (!match || match[1] == "")
break;
if (match[0][0] != ';')
result.push(match[1])
}
if (result == null || result == [])
return []
else
return result.filter(x => x != "").map(x => x.trim())
}
function reader(tokens: string[]): Reader {
var i = 0
return {
next: (): string => {
if (i >= tokens.length)
throw "expected ')', got EOF"
else
return tokens[i++]
},
peek: (): string => {
if (i >= tokens.length)
throw "expected ')', got EOF"
else
return tokens[i]
},
hasNext: (): boolean => {
return i < tokens.length - 1
}
}
}
function read_form(rdr: Reader): Mal {
let token = rdr.peek()
switch(token[0]) {
case '(':
return read_list(rdr)
case '[':
return read_vector(rdr)
case '~':
return read_unquote(rdr)
case '{':
return read_map(rdr)
case '`':
return read_quasiquote(rdr)
case '\'':
return read_quote(rdr)
case ':':
return read_colon(rdr)
case '@':
return read_atom(rdr)
default:
return read_token(rdr)
}
}
function read_atom(rdr: Reader): Mal {
rdr.next()
return new List([
new Symbol("deref"),
new Symbol(rdr.next())
])
}
function read_map(rdr: Reader): Mal {
rdr.next()
let key = read_form(rdr)
let value = read_form(rdr)
rdr.next()
return new Map(key, value)
}
function read_vector(rdr: Reader): Mal {
rdr.next()
var contents : Mal[] = []
while (rdr.peek()[0] != ']') {
contents.push(read_form(rdr))
}
rdr.next()
return new Vector(contents)
}
function read_quote(rdr: Reader): Mal {
rdr.next()
var contents : Mal = read_form(rdr)
return new List([new Symbol("quote"), contents])
}
function read_unquote(rdr: Reader): Mal {
let sym =
rdr.peek() == "~@" ?
new Symbol("splice-unquote") : new Symbol("unquote")
rdr.next()
var contents : Mal = read_form(rdr)
return new List([sym, contents])
}
function read_quasiquote(rdr: Reader): Mal {
rdr.next()
var contents : Mal = read_form(rdr)
return new List([new Symbol("quasiquote"), contents])
}
function read_list(rdr: Reader): Mal {
rdr.next()
var contents : Mal[] = []
while (rdr.peek()[0] != ')') {
contents.push(read_form(rdr))
}
rdr.next()
return new List(contents)
}
function read_colon(rdr: Reader): Mal {
let contents = rdr.next()
return new Colon(contents)
}
function read_token(rdr: Reader): Mal {
let contents = rdr.next()
if (contents[0] == "\"") {
return new String(JSON.parse(contents))
} else if (contents == "true") {
return new True()
} else if (contents == "false") {
return new False()
} else if (contents == "nil") {
return new Nil()
} else if (!isNaN(+contents)) {
return new Number(+contents)
} else {
return new Symbol(contents)
}
}
interface Reader {
next: () => string
peek: () => string
hasNext: () => boolean
}