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>())) }), |
fbfe6784 NB |
57 | envPair("vec", { a: ISeq -> |
58 | val list = a.first() as? ISeq ?: throw MalException("vec requires a sequence") | |
59 | MalVector(list) | |
60 | }), | |
99851ac7 | 61 | envPair("nth", { a: ISeq -> |
d8e5df5d JFI |
62 | val list = a.nth(0) as? ISeq ?: throw MalException("nth requires a list as its first parameter") |
63 | val index = a.nth(1) as? MalInteger ?: throw MalException("nth requires an integer as its second parameter") | |
2a9f3ad2 | 64 | if (index.value >= list.count()) throw MalException("index out of bounds") |
d937c5a1 | 65 | list.nth(index.value.toInt()) |
99851ac7 JFI |
66 | }), |
67 | envPair("first", { a: ISeq -> | |
d8e5df5d JFI |
68 | if (a.nth(0) == NIL) NIL |
69 | else { | |
70 | val list = a.nth(0) as? ISeq ?: throw MalException("first requires a list parameter") | |
71 | if (list.seq().any()) list.first() else NIL | |
72 | } | |
99851ac7 JFI |
73 | }), |
74 | envPair("rest", { a: ISeq -> | |
dfabec93 DM |
75 | if (a.nth(0) == NIL) MalList() |
76 | else { | |
77 | val list = a.nth(0) as? ISeq ?: throw MalException("rest requires a list parameter") | |
78 | MalList(list.rest()) | |
79 | } | |
99851ac7 | 80 | }), |
041bb7eb | 81 | |
99851ac7 | 82 | envPair("throw", { a: ISeq -> |
041bb7eb JFI |
83 | val throwable = a.nth(0) |
84 | throw MalCoreException(pr_str(throwable), throwable) | |
99851ac7 | 85 | }), |
041bb7eb | 86 | |
99851ac7 | 87 | envPair("apply", { a: ISeq -> |
041bb7eb JFI |
88 | val function = a.nth(0) as MalFunction |
89 | val params = MalList() | |
90 | a.seq().drop(1).forEach({ it -> | |
91 | if (it is ISeq) { | |
92 | it.seq().forEach({ x -> params.conj_BANG(x) }) | |
93 | } else { | |
94 | params.conj_BANG(it) | |
95 | } | |
96 | }) | |
97 | function.apply(params) | |
99851ac7 | 98 | }), |
041bb7eb | 99 | |
99851ac7 | 100 | envPair("map", { a: ISeq -> |
041bb7eb JFI |
101 | val function = a.nth(0) as MalFunction |
102 | MalList((a.nth(1) as ISeq).seq().map({ it -> | |
103 | val params = MalList() | |
104 | params.conj_BANG(it) | |
105 | function.apply(params) | |
304ec7f3 | 106 | }).toCollection(LinkedList<MalType>())) |
99851ac7 | 107 | }), |
041bb7eb | 108 | |
99851ac7 JFI |
109 | envPair("nil?", { a: ISeq -> if (a.nth(0) == NIL) TRUE else FALSE }), |
110 | envPair("true?", { a: ISeq -> if (a.nth(0) == TRUE) TRUE else FALSE }), | |
111 | envPair("false?", { a: ISeq -> if (a.nth(0) == FALSE) TRUE else FALSE }), | |
7ccabbff JM |
112 | envPair("string?", { a: ISeq -> |
113 | if (a.nth(0) is MalString && !(a.nth(0) is MalKeyword)) TRUE else FALSE | |
114 | }), | |
99851ac7 | 115 | envPair("symbol?", { a: ISeq -> if (a.nth(0) is MalSymbol) TRUE else FALSE }), |
041bb7eb | 116 | |
99851ac7 JFI |
117 | envPair("symbol", { a: ISeq -> MalSymbol((a.nth(0) as MalString).value) }), |
118 | envPair("keyword", { a: ISeq -> | |
041bb7eb JFI |
119 | val param = a.nth(0) |
120 | if (param is MalKeyword) param else MalKeyword((a.nth(0) as MalString).value) | |
99851ac7 JFI |
121 | }), |
122 | envPair("keyword?", { a: ISeq -> if (a.nth(0) is MalKeyword) TRUE else FALSE }), | |
9d7af552 DM |
123 | envPair("number?", { a: ISeq -> if (a.nth(0) is MalInteger) TRUE else FALSE }), |
124 | envPair("fn?", { a: ISeq -> if ((a.nth(0) as? MalFunction)?.is_macro ?: true) FALSE else TRUE }), | |
125 | envPair("macro?", { a: ISeq -> if ((a.nth(0) as? MalFunction)?.is_macro ?: false) TRUE else FALSE }), | |
126 | ||
99851ac7 JFI |
127 | envPair("vector", { a: ISeq -> MalVector(a) }), |
128 | envPair("vector?", { a: ISeq -> if (a.nth(0) is MalVector) TRUE else FALSE }), | |
041bb7eb | 129 | |
99851ac7 | 130 | envPair("hash-map", { a: ISeq -> |
26f0b60f | 131 | val map = MalHashMap() |
7ee7a06b | 132 | pairwise(a).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) }) |
26f0b60f | 133 | map |
99851ac7 JFI |
134 | }), |
135 | envPair("map?", { a: ISeq -> if (a.nth(0) is MalHashMap) TRUE else FALSE }), | |
136 | envPair("assoc", { a: ISeq -> | |
7ee7a06b JFI |
137 | val map = MalHashMap(a.first() as MalHashMap) |
138 | pairwise(a.rest()).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) }) | |
26f0b60f | 139 | map |
99851ac7 JFI |
140 | }), |
141 | envPair("dissoc", { a: ISeq -> | |
7ee7a06b JFI |
142 | val map = MalHashMap(a.first() as MalHashMap) |
143 | a.rest().seq().forEach({ it -> map.dissoc_BANG(it as MalString) }) | |
26f0b60f | 144 | map |
99851ac7 JFI |
145 | }), |
146 | envPair("get", { a: ISeq -> | |
26f0b60f JFI |
147 | val map = a.nth(0) as? MalHashMap |
148 | val key = a.nth(1) as MalString | |
149 | map?.elements?.get(key) ?: NIL | |
99851ac7 JFI |
150 | }), |
151 | envPair("contains?", { a: ISeq -> | |
26f0b60f JFI |
152 | val map = a.nth(0) as? MalHashMap |
153 | val key = a.nth(1) as MalString | |
154 | if (map?.elements?.get(key) != null) TRUE else FALSE | |
99851ac7 JFI |
155 | }), |
156 | envPair("keys", { a: ISeq -> | |
26f0b60f | 157 | val map = a.nth(0) as MalHashMap |
0ab4fa14 | 158 | MalList(map.elements.keys.toCollection(LinkedList<MalType>())) |
99851ac7 JFI |
159 | }), |
160 | envPair("vals", { a: ISeq -> | |
26f0b60f | 161 | val map = a.nth(0) as MalHashMap |
0ab4fa14 | 162 | MalList(map.elements.values.toCollection(LinkedList<MalType>())) |
99851ac7 JFI |
163 | }), |
164 | envPair("count", { a: ISeq -> | |
26f0b60f | 165 | val seq = a.nth(0) as? ISeq |
d937c5a1 | 166 | if (seq != null) MalInteger(seq.count().toLong()) else ZERO |
99851ac7 JFI |
167 | }), |
168 | envPair("sequential?", { a: ISeq -> if (a.nth(0) is ISeq) TRUE else FALSE }), | |
2ec7fa0b | 169 | |
99851ac7 | 170 | envPair("with-meta", { a: ISeq -> |
2eb2cac2 JFI |
171 | val obj = a.nth(0) |
172 | val metadata = a.nth(1) | |
173 | obj.with_meta(metadata) | |
99851ac7 JFI |
174 | }), |
175 | envPair("meta", { a: ISeq -> a.first().metadata }), | |
7ccabbff | 176 | |
99851ac7 | 177 | envPair("conj", { a: ISeq -> (a.first() as ISeq).conj(a.rest()) }), |
7ccabbff JM |
178 | envPair("seq", { a: ISeq -> |
179 | val obj = a.nth(0) | |
180 | if (obj is ISeq) { | |
181 | if (obj.count() == 0) NIL | |
182 | else MalList(obj.seq().toCollection(LinkedList<MalType>())) | |
183 | } else if (obj is MalString && !(obj is MalKeyword)) { | |
184 | if (obj.value.length == 0) NIL | |
185 | else { | |
186 | var strs = obj.value.map({ c -> MalString(c.toString()) }) | |
187 | MalList(strs.toCollection(LinkedList<MalType>())) | |
188 | } | |
189 | } else { | |
190 | NIL | |
191 | } | |
192 | }), | |
99851ac7 JFI |
193 | |
194 | envPair("atom", { a: ISeq -> MalAtom(a.first()) }), | |
195 | envPair("atom?", { a: ISeq -> if (a.first() is MalAtom) TRUE else FALSE }), | |
196 | envPair("deref", { a: ISeq -> (a.first() as MalAtom).value }), | |
197 | envPair("reset!", { a: ISeq -> | |
7ee7a06b | 198 | val atom = a.nth(0) as MalAtom |
a779c814 JFI |
199 | val value = a.nth(1) |
200 | atom.value = value | |
201 | value | |
99851ac7 JFI |
202 | }), |
203 | envPair("swap!", { a: ISeq -> | |
6132da85 | 204 | val atom = a.nth(0) as MalAtom |
a779c814 JFI |
205 | val function = a.nth(1) as MalFunction |
206 | ||
a779c814 JFI |
207 | val params = MalList() |
208 | params.conj_BANG(atom.value) | |
6132da85 | 209 | a.seq().drop(2).forEach({ it -> params.conj_BANG(it) }) |
a779c814 JFI |
210 | |
211 | val value = function.apply(params) | |
212 | atom.value = value | |
213 | ||
214 | value | |
99851ac7 | 215 | }), |
a779c814 | 216 | |
99851ac7 | 217 | envPair("readline", { a: ISeq -> |
a779c814 JFI |
218 | val prompt = a.first() as MalString |
219 | try { | |
220 | MalString(readline(prompt.value)) | |
221 | } catch (e: java.io.IOException) { | |
222 | throw MalException(e.message) | |
223 | } catch (e: EofException) { | |
224 | NIL | |
225 | } | |
99851ac7 | 226 | }), |
d937c5a1 | 227 | |
99851ac7 | 228 | envPair("time-ms", { a: ISeq -> MalInteger(System.currentTimeMillis()) }) |
53c2ea70 JFI |
229 | ) |
230 | ||
99851ac7 JFI |
231 | private fun envPair(k: String, v: (ISeq) -> MalType): Pair<MalSymbol, MalType> = Pair(MalSymbol(k), MalFunction(v)) |
232 | ||
233 | private fun pairwise(s: ISeq): List<Pair<MalType, MalType>> { | |
7ee7a06b JFI |
234 | val (keys, vals) = s.seq().withIndex().partition({ it -> it.index % 2 == 0 }) |
235 | return keys.map({ it -> it.value }).zip(vals.map({ it -> it.value })) | |
236 | } | |
237 | ||
99851ac7 | 238 | private fun pairwiseCompare(s: ISeq, pred: (MalInteger, MalInteger) -> Boolean): MalConstant = |
7ee7a06b | 239 | if (pairwise(s).all({ it -> pred(it.first as MalInteger, it.second as MalInteger) })) TRUE else FALSE |
53c2ea70 | 240 | |
99851ac7 | 241 | private fun pairwiseEquals(s: ISeq): MalConstant = |
7ee7a06b | 242 | if (pairwise(s).all({ it -> it.first == it.second })) TRUE else FALSE |