Commit | Line | Data |
---|---|---|
53c2ea70 JFI |
1 | package mal |
2 | ||
4121b4ee | 3 | import java.io.File |
304ec7f3 | 4 | import java.util.* |
4121b4ee | 5 | |
53c2ea70 | 6 | val ns = hashMapOf( |
99851ac7 JFI |
7 | envPair("+", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger + y as MalInteger }) }), |
8 | envPair("-", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger - y as MalInteger }) }), | |
9 | envPair("*", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger * y as MalInteger }) }), | |
10 | envPair("/", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger / y as MalInteger }) }), | |
11 | ||
12 | envPair("list", { a: ISeq -> MalList(a) }), | |
13 | envPair("list?", { a: ISeq -> if (a.first() is MalList) TRUE else FALSE }), | |
14 | envPair("empty?", { a: ISeq -> if (a.first() !is ISeq || !(a.first() as ISeq).seq().any()) TRUE else FALSE }), | |
15 | envPair("count", { a: ISeq -> | |
16 | if (a.first() is ISeq) MalInteger((a.first() as ISeq).count().toLong()) else MalInteger(0) | |
17 | }), | |
18 | ||
19 | envPair("=", { a: ISeq -> pairwiseEquals(a) }), | |
20 | envPair("<", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value < y.value }) }), | |
21 | envPair("<=", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value <= y.value }) }), | |
22 | envPair(">", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value > y.value }) }), | |
23 | envPair(">=", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value >= y.value }) }), | |
24 | ||
25 | envPair("pr-str", { a: ISeq -> | |
26 | MalString(a.seq().map({ it -> pr_str(it, print_readably = true) }).joinToString(" ")) | |
27 | }), | |
28 | envPair("str", { a: ISeq -> | |
29 | MalString(a.seq().map({ it -> pr_str(it, print_readably = false) }).joinToString("")) | |
30 | }), | |
31 | envPair("prn", { a: ISeq -> | |
32 | println(a.seq().map({ it -> pr_str(it, print_readably = true) }).joinToString(" ")) | |
33 | NIL | |
34 | }), | |
35 | envPair("println", { a: ISeq -> | |
36 | println(a.seq().map({ it -> pr_str(it, print_readably = false) }).joinToString(" ")) | |
37 | NIL | |
38 | }), | |
39 | ||
40 | envPair("read-string", { a: ISeq -> | |
4121b4ee JFI |
41 | val string = a.first() as? MalString ?: throw MalException("slurp requires a string parameter") |
42 | read_str(string.value) | |
99851ac7 JFI |
43 | }), |
44 | envPair("slurp", { a: ISeq -> | |
4121b4ee JFI |
45 | val name = a.first() as? MalString ?: throw MalException("slurp requires a filename parameter") |
46 | val text = File(name.value).readText() | |
47 | MalString(text) | |
99851ac7 | 48 | }), |
940bb6ff | 49 | |
99851ac7 | 50 | envPair("cons", { a: ISeq -> |
940bb6ff | 51 | val list = a.nth(1) as? ISeq ?: throw MalException("cons requires a list as its second parameter") |
304ec7f3 | 52 | val mutableList = list.seq().toCollection(LinkedList<MalType>()) |
940bb6ff JFI |
53 | mutableList.addFirst(a.nth(0)) |
54 | MalList(mutableList) | |
99851ac7 | 55 | }), |
304ec7f3 | 56 | envPair("concat", { a: ISeq -> MalList(a.seq().flatMap({ it -> (it as ISeq).seq() }).toCollection(LinkedList<MalType>())) }), |
d8e5df5d | 57 | |
99851ac7 | 58 | envPair("nth", { a: ISeq -> |
d8e5df5d JFI |
59 | val list = a.nth(0) as? ISeq ?: throw MalException("nth requires a list as its first parameter") |
60 | val index = a.nth(1) as? MalInteger ?: throw MalException("nth requires an integer as its second parameter") | |
2a9f3ad2 | 61 | if (index.value >= list.count()) throw MalException("index out of bounds") |
d937c5a1 | 62 | list.nth(index.value.toInt()) |
99851ac7 JFI |
63 | }), |
64 | envPair("first", { a: ISeq -> | |
d8e5df5d JFI |
65 | if (a.nth(0) == NIL) NIL |
66 | else { | |
67 | val list = a.nth(0) as? ISeq ?: throw MalException("first requires a list parameter") | |
68 | if (list.seq().any()) list.first() else NIL | |
69 | } | |
99851ac7 JFI |
70 | }), |
71 | envPair("rest", { a: ISeq -> | |
dfabec93 DM |
72 | if (a.nth(0) == NIL) MalList() |
73 | else { | |
74 | val list = a.nth(0) as? ISeq ?: throw MalException("rest requires a list parameter") | |
75 | MalList(list.rest()) | |
76 | } | |
99851ac7 | 77 | }), |
041bb7eb | 78 | |
99851ac7 | 79 | envPair("throw", { a: ISeq -> |
041bb7eb JFI |
80 | val throwable = a.nth(0) |
81 | throw MalCoreException(pr_str(throwable), throwable) | |
99851ac7 | 82 | }), |
041bb7eb | 83 | |
99851ac7 | 84 | envPair("apply", { a: ISeq -> |
041bb7eb JFI |
85 | val function = a.nth(0) as MalFunction |
86 | val params = MalList() | |
87 | a.seq().drop(1).forEach({ it -> | |
88 | if (it is ISeq) { | |
89 | it.seq().forEach({ x -> params.conj_BANG(x) }) | |
90 | } else { | |
91 | params.conj_BANG(it) | |
92 | } | |
93 | }) | |
94 | function.apply(params) | |
99851ac7 | 95 | }), |
041bb7eb | 96 | |
99851ac7 | 97 | envPair("map", { a: ISeq -> |
041bb7eb JFI |
98 | val function = a.nth(0) as MalFunction |
99 | MalList((a.nth(1) as ISeq).seq().map({ it -> | |
100 | val params = MalList() | |
101 | params.conj_BANG(it) | |
102 | function.apply(params) | |
304ec7f3 | 103 | }).toCollection(LinkedList<MalType>())) |
99851ac7 | 104 | }), |
041bb7eb | 105 | |
99851ac7 JFI |
106 | envPair("nil?", { a: ISeq -> if (a.nth(0) == NIL) TRUE else FALSE }), |
107 | envPair("true?", { a: ISeq -> if (a.nth(0) == TRUE) TRUE else FALSE }), | |
108 | envPair("false?", { a: ISeq -> if (a.nth(0) == FALSE) TRUE else FALSE }), | |
7ccabbff JM |
109 | envPair("string?", { a: ISeq -> |
110 | if (a.nth(0) is MalString && !(a.nth(0) is MalKeyword)) TRUE else FALSE | |
111 | }), | |
99851ac7 | 112 | envPair("symbol?", { a: ISeq -> if (a.nth(0) is MalSymbol) TRUE else FALSE }), |
041bb7eb | 113 | |
99851ac7 JFI |
114 | envPair("symbol", { a: ISeq -> MalSymbol((a.nth(0) as MalString).value) }), |
115 | envPair("keyword", { a: ISeq -> | |
041bb7eb JFI |
116 | val param = a.nth(0) |
117 | if (param is MalKeyword) param else MalKeyword((a.nth(0) as MalString).value) | |
99851ac7 JFI |
118 | }), |
119 | envPair("keyword?", { a: ISeq -> if (a.nth(0) is MalKeyword) TRUE else FALSE }), | |
120 | envPair("vector", { a: ISeq -> MalVector(a) }), | |
121 | envPair("vector?", { a: ISeq -> if (a.nth(0) is MalVector) TRUE else FALSE }), | |
041bb7eb | 122 | |
99851ac7 | 123 | envPair("hash-map", { a: ISeq -> |
26f0b60f | 124 | val map = MalHashMap() |
7ee7a06b | 125 | pairwise(a).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) }) |
26f0b60f | 126 | map |
99851ac7 JFI |
127 | }), |
128 | envPair("map?", { a: ISeq -> if (a.nth(0) is MalHashMap) TRUE else FALSE }), | |
129 | envPair("assoc", { a: ISeq -> | |
7ee7a06b JFI |
130 | val map = MalHashMap(a.first() as MalHashMap) |
131 | pairwise(a.rest()).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) }) | |
26f0b60f | 132 | map |
99851ac7 JFI |
133 | }), |
134 | envPair("dissoc", { a: ISeq -> | |
7ee7a06b JFI |
135 | val map = MalHashMap(a.first() as MalHashMap) |
136 | a.rest().seq().forEach({ it -> map.dissoc_BANG(it as MalString) }) | |
26f0b60f | 137 | map |
99851ac7 JFI |
138 | }), |
139 | envPair("get", { a: ISeq -> | |
26f0b60f JFI |
140 | val map = a.nth(0) as? MalHashMap |
141 | val key = a.nth(1) as MalString | |
142 | map?.elements?.get(key) ?: NIL | |
99851ac7 JFI |
143 | }), |
144 | envPair("contains?", { a: ISeq -> | |
26f0b60f JFI |
145 | val map = a.nth(0) as? MalHashMap |
146 | val key = a.nth(1) as MalString | |
147 | if (map?.elements?.get(key) != null) TRUE else FALSE | |
99851ac7 JFI |
148 | }), |
149 | envPair("keys", { a: ISeq -> | |
26f0b60f | 150 | val map = a.nth(0) as MalHashMap |
0ab4fa14 | 151 | MalList(map.elements.keys.toCollection(LinkedList<MalType>())) |
99851ac7 JFI |
152 | }), |
153 | envPair("vals", { a: ISeq -> | |
26f0b60f | 154 | val map = a.nth(0) as MalHashMap |
0ab4fa14 | 155 | MalList(map.elements.values.toCollection(LinkedList<MalType>())) |
99851ac7 JFI |
156 | }), |
157 | envPair("count", { a: ISeq -> | |
26f0b60f | 158 | val seq = a.nth(0) as? ISeq |
d937c5a1 | 159 | if (seq != null) MalInteger(seq.count().toLong()) else ZERO |
99851ac7 JFI |
160 | }), |
161 | envPair("sequential?", { a: ISeq -> if (a.nth(0) is ISeq) TRUE else FALSE }), | |
2ec7fa0b | 162 | |
99851ac7 | 163 | envPair("with-meta", { a: ISeq -> |
2eb2cac2 JFI |
164 | val obj = a.nth(0) |
165 | val metadata = a.nth(1) | |
166 | obj.with_meta(metadata) | |
99851ac7 JFI |
167 | }), |
168 | envPair("meta", { a: ISeq -> a.first().metadata }), | |
7ccabbff | 169 | |
99851ac7 | 170 | envPair("conj", { a: ISeq -> (a.first() as ISeq).conj(a.rest()) }), |
7ccabbff JM |
171 | envPair("seq", { a: ISeq -> |
172 | val obj = a.nth(0) | |
173 | if (obj is ISeq) { | |
174 | if (obj.count() == 0) NIL | |
175 | else MalList(obj.seq().toCollection(LinkedList<MalType>())) | |
176 | } else if (obj is MalString && !(obj is MalKeyword)) { | |
177 | if (obj.value.length == 0) NIL | |
178 | else { | |
179 | var strs = obj.value.map({ c -> MalString(c.toString()) }) | |
180 | MalList(strs.toCollection(LinkedList<MalType>())) | |
181 | } | |
182 | } else { | |
183 | NIL | |
184 | } | |
185 | }), | |
99851ac7 JFI |
186 | |
187 | envPair("atom", { a: ISeq -> MalAtom(a.first()) }), | |
188 | envPair("atom?", { a: ISeq -> if (a.first() is MalAtom) TRUE else FALSE }), | |
189 | envPair("deref", { a: ISeq -> (a.first() as MalAtom).value }), | |
190 | envPair("reset!", { a: ISeq -> | |
7ee7a06b | 191 | val atom = a.nth(0) as MalAtom |
a779c814 JFI |
192 | val value = a.nth(1) |
193 | atom.value = value | |
194 | value | |
99851ac7 JFI |
195 | }), |
196 | envPair("swap!", { a: ISeq -> | |
6132da85 | 197 | val atom = a.nth(0) as MalAtom |
a779c814 JFI |
198 | val function = a.nth(1) as MalFunction |
199 | ||
a779c814 JFI |
200 | val params = MalList() |
201 | params.conj_BANG(atom.value) | |
6132da85 | 202 | a.seq().drop(2).forEach({ it -> params.conj_BANG(it) }) |
a779c814 JFI |
203 | |
204 | val value = function.apply(params) | |
205 | atom.value = value | |
206 | ||
207 | value | |
99851ac7 | 208 | }), |
a779c814 | 209 | |
99851ac7 | 210 | envPair("readline", { a: ISeq -> |
a779c814 JFI |
211 | val prompt = a.first() as MalString |
212 | try { | |
213 | MalString(readline(prompt.value)) | |
214 | } catch (e: java.io.IOException) { | |
215 | throw MalException(e.message) | |
216 | } catch (e: EofException) { | |
217 | NIL | |
218 | } | |
99851ac7 | 219 | }), |
d937c5a1 | 220 | |
99851ac7 | 221 | envPair("time-ms", { a: ISeq -> MalInteger(System.currentTimeMillis()) }) |
53c2ea70 JFI |
222 | ) |
223 | ||
99851ac7 JFI |
224 | private fun envPair(k: String, v: (ISeq) -> MalType): Pair<MalSymbol, MalType> = Pair(MalSymbol(k), MalFunction(v)) |
225 | ||
226 | private fun pairwise(s: ISeq): List<Pair<MalType, MalType>> { | |
7ee7a06b JFI |
227 | val (keys, vals) = s.seq().withIndex().partition({ it -> it.index % 2 == 0 }) |
228 | return keys.map({ it -> it.value }).zip(vals.map({ it -> it.value })) | |
229 | } | |
230 | ||
99851ac7 | 231 | private fun pairwiseCompare(s: ISeq, pred: (MalInteger, MalInteger) -> Boolean): MalConstant = |
7ee7a06b | 232 | if (pairwise(s).all({ it -> pred(it.first as MalInteger, it.second as MalInteger) })) TRUE else FALSE |
53c2ea70 | 233 | |
99851ac7 | 234 | private fun pairwiseEquals(s: ISeq): MalConstant = |
7ee7a06b | 235 | if (pairwise(s).all({ it -> it.first == it.second })) TRUE else FALSE |